diff --git a/examples/libs/CMakeLists.txt b/examples/libs/CMakeLists.txt index d4c1dd2a..7ef23a76 100644 --- a/examples/libs/CMakeLists.txt +++ b/examples/libs/CMakeLists.txt @@ -29,18 +29,26 @@ add_library(pathfinding INTERFACE ) target_include_directories(pathfinding INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") -add_library(geo-threepp INTERFACE +add_library(geothreepp "${CMAKE_CURRENT_SOURCE_DIR}/geo/lod/LODControl.hpp" "${CMAKE_CURRENT_SOURCE_DIR}/geo/lod/LODFrustum.hpp" "${CMAKE_CURRENT_SOURCE_DIR}/geo/lod/LODRadial.hpp" "${CMAKE_CURRENT_SOURCE_DIR}/geo/lod/LODRaycast.hpp" + "${CMAKE_CURRENT_SOURCE_DIR}/geo/nodes/MapNode.hpp" + "${CMAKE_CURRENT_SOURCE_DIR}/geo/nodes/MapNode.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/geo/nodes/MapPlaneNode.hpp" + "${CMAKE_CURRENT_SOURCE_DIR}/geo/geometries/MapNodeGeometry.hpp" + "${CMAKE_CURRENT_SOURCE_DIR}/geo/providers/MapProvider.hpp" "${CMAKE_CURRENT_SOURCE_DIR}/geo/providers/OpenStreetMapsProvider.hpp" + + "${CMAKE_CURRENT_SOURCE_DIR}/geo/utils/UnitUtils.hpp" + "${CMAKE_CURRENT_SOURCE_DIR}/geo/MapView.hpp" + "${CMAKE_CURRENT_SOURCE_DIR}/geo/MapView.cpp" ) -target_include_directories(geo-threepp INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") -target_link_libraries(geo-threepp INTERFACE threepp::threepp CURL::libcurl) +target_include_directories(geothreepp PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") +target_link_libraries(geothreepp PUBLIC threepp::threepp CURL::libcurl) diff --git a/examples/libs/geo/MapView.cpp b/examples/libs/geo/MapView.cpp new file mode 100644 index 00000000..147fa736 --- /dev/null +++ b/examples/libs/geo/MapView.cpp @@ -0,0 +1,65 @@ + +#include "MapView.hpp" + +#include "lod/LODControl.hpp" +#include "lod/LODFrustum.hpp" +#include "lod/LODRaycast.hpp" + +using namespace threepp; + + +namespace { + + void subdivide(MapNode& node, float depth) { + if (depth <= 0) { + return; + } + + node.subdivide(); + + for (unsigned i = 0; i < node.children.size(); i++) { + if (auto child = node.children[i]->as()) { + + subdivide(*child, depth - 1); + } + } + } + +}// namespace + + +MapView::MapView(std::unique_ptr provider, std::unique_ptr lod): provider(std::move(provider)), lod(std::move(lod)) { + + onBeforeRender = [this](void* renderer, Object3D* scene, Camera* camera, BufferGeometry*, Material*, std::optional) { + this->lod->updateLOD(*this, *camera, *static_cast(renderer), *scene); + }; + + root = std::make_unique(nullptr, this); + + geometry_ = root->baseGeometry(); + material()->transparent = false; + material()->as()->wireframe = true; + material()->opacity = 0.0; + material()->depthWrite = false; + material()->colorWrite = false; + + scale.copy(root->baseScale()); + + add(*root); + root->initialize(); + + preSubDivide(); +} + +void MapView::preSubDivide() { + + const auto minZoom = this->provider->minZoom; + if (minZoom > 0) { + subdivide(*this->root, minZoom); + } +} + +MapProvider* MapView::getProvider() const { + + return provider ? provider.get() : nullptr; +} diff --git a/examples/libs/geo/MapView.hpp b/examples/libs/geo/MapView.hpp index 7ff4a487..6e0df983 100644 --- a/examples/libs/geo/MapView.hpp +++ b/examples/libs/geo/MapView.hpp @@ -5,38 +5,39 @@ #include "threepp/objects/Mesh.hpp" #include "geo/lod/LODControl.hpp" -#include "geo/lod/LODRaycast.hpp" -#include "geo/providers/MapProvider.hpp" +#include "nodes/MapPlaneNode.hpp" +#include "providers/MapProvider.hpp" namespace threepp { class MapView: public Mesh { public: - std::unique_ptr lod; - std::unique_ptr provider; - - MapView(std::unique_ptr provider): provider(std::move(provider)) { - onBeforeRender = [](void*, Object3D*, Camera*, BufferGeometry*, Material*, std::optional) { + explicit MapView(std::unique_ptr provider, std::unique_ptr lod); - }; - - lod = std::make_unique(); - } + void preSubDivide(); - float minZoom() { + [[nodiscard]] float minZoom() const { return this->provider->minZoom; } - float maxZoom() { + [[nodiscard]] float maxZoom() const { return this->provider->maxZoom; } + MapProvider* getProvider() const; + + void raycast(const Raycaster& raycaster, std::vector& intersects) override {} private: + + std::unique_ptr root; + std::unique_ptr lod; + std::unique_ptr provider; }; + }// namespace threepp #endif//THREEPP_MAPVIEW_HPP diff --git a/examples/libs/geo/geometries/MapNodeGeometry.hpp b/examples/libs/geo/geometries/MapNodeGeometry.hpp index ca74e33b..8303e9ca 100644 --- a/examples/libs/geo/geometries/MapNodeGeometry.hpp +++ b/examples/libs/geo/geometries/MapNodeGeometry.hpp @@ -9,14 +9,12 @@ namespace threepp { class MapNodeGeometry: public BufferGeometry { public: + static std::shared_ptr create(int width = 1, int height = 1, int widthSegments = 1, int heightSegments = 1, bool skirt = true, int skirtDepth = 10) { - static std::shared_ptr create(int width = 1, int height = 1, int widthSegments = 1, int heightSegments = 1, bool skirt = true, int skirtDepth = 10) { - - return std::shared_ptr(new MapNodeGeometry(width, height, widthSegments, heightSegments, skirt, skirtDepth)); - } + return std::shared_ptr(new MapNodeGeometry(width, height, widthSegments, heightSegments, skirt, skirtDepth)); + } private: - explicit MapNodeGeometry(int width, int height, int widthSegments, int heightSegments, bool skirt, int skirtDepth) { std::vector indices; @@ -34,7 +32,6 @@ namespace threepp { setAttribute("position", FloatBufferAttribute::create(vertices, 3)); setAttribute("normal", FloatBufferAttribute::create(normals, 3)); setAttribute("uv", FloatBufferAttribute::create(uvs, 2)); - } void buildPlane(std::vector& indices, std::vector& vertices, diff --git a/examples/libs/geo/lod/LODControl.hpp b/examples/libs/geo/lod/LODControl.hpp index 3d95001c..1ec958d3 100644 --- a/examples/libs/geo/lod/LODControl.hpp +++ b/examples/libs/geo/lod/LODControl.hpp @@ -5,10 +5,10 @@ #include "threepp/cameras/Camera.hpp" #include "threepp/renderers/GLRenderer.hpp" -#include "geo/MapView.hpp" - namespace threepp { + class MapView; + class LODControl { public: diff --git a/examples/libs/geo/lod/LODFrustum.hpp b/examples/libs/geo/lod/LODFrustum.hpp index 95a4227f..0e3a3530 100644 --- a/examples/libs/geo/lod/LODFrustum.hpp +++ b/examples/libs/geo/lod/LODFrustum.hpp @@ -5,17 +5,46 @@ #include "threepp/math/Frustum.hpp" #include "threepp/math/Matrix4.hpp" -#include "geo/lod/LODRadial.hpp" +#include "../lod/LODRadial.hpp" +#include "../MapView.hpp" namespace threepp { class LODFrustum: public LODRadial { + public: + + bool testCenter = true; + bool pointOnly = true; + + explicit LODFrustum(float subdivideDistance = 150, float simplifyDistance = 400) + : LODRadial(subdivideDistance, simplifyDistance) {} + + void updateLOD(MapView& view, Camera& camera, GLRenderer& renderer, Object3D& scene) override { + + projection.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); + frustum.setFromProjectionMatrix(projection); + camera.getWorldPosition(pov); + + view.children[0]->traverseType([&](MapNode& node) { + node.getWorldPosition(position); + + auto distance = pov.distanceTo(position); + distance /= std::pow(2, view.getProvider()->maxZoom - node.getLevel()); + + const auto inFrustum = this->pointOnly ? frustum.containsPoint(position) : frustum.intersectsObject(node); + + if (distance < this->subdivideDistance && inFrustum) { + node.subdivide(); + } else if (distance > this->simplifyDistance && node.parentNode) { + node.parentNode->simplify(); + } + }); + } + private: Matrix4 projection; - Vector3 pov; Frustum frustum; - Vector3 position; }; }// namespace threepp diff --git a/examples/libs/geo/lod/LODRadial.hpp b/examples/libs/geo/lod/LODRadial.hpp index 693335b2..f5e799f5 100644 --- a/examples/libs/geo/lod/LODRadial.hpp +++ b/examples/libs/geo/lod/LODRadial.hpp @@ -4,8 +4,9 @@ #include "threepp/core/Object3D.hpp" -#include "geo/lod/LODControl.hpp" -#include "geo/nodes/MapNode.hpp" +#include "../lod/LODControl.hpp" +#include "../nodes/MapNode.hpp" +#include "../MapView.hpp" #include @@ -17,25 +18,29 @@ namespace threepp { explicit LODRadial(float subdivideDistance = 50, float simplifyDistance = 300) : subdivideDistance(subdivideDistance), simplifyDistance(simplifyDistance) {} - void updateLOD(MapView& view, Camera& camera, GLRenderer& renderer, Object3D& scene) { + void updateLOD(MapView& view, Camera& camera, GLRenderer& renderer, Object3D& scene) override { camera.getWorldPosition(pov); - view.children[0]->traverseType([&](MapNode& node) { - node.getWorldPosition(position); + view.children[0]->traverse([&](Object3D& o) { - auto distance = pov.distanceTo(position); - distance /= std::pow(2, view.provider->maxZoom - node.getLevel()); - // - if (distance < this->subdivideDistance) { - node.subdivide(); - } else if (distance > this->simplifyDistance && node.parentNode) { - node.parentNode->simplify(); + if (auto node = o.as()) { + + node->getWorldPosition(position); + + auto distance = pov.distanceTo(position); + distance /= std::pow(2, view.getProvider()->maxZoom - node->getLevel()); + // + if (distance < this->subdivideDistance) { + node->subdivide(); + } else if (distance > this->simplifyDistance && node->parentNode) { + node->parentNode->simplify(); + } } }); } - private: + protected: float subdivideDistance; float simplifyDistance; diff --git a/examples/libs/geo/lod/LODRaycast.hpp b/examples/libs/geo/lod/LODRaycast.hpp index 42aedeb7..e4517067 100644 --- a/examples/libs/geo/lod/LODRaycast.hpp +++ b/examples/libs/geo/lod/LODRaycast.hpp @@ -6,79 +6,69 @@ #include "threepp/core/Raycaster.hpp" #include "threepp/math/MathUtils.hpp" -#include "geo/lod/LODControl.hpp" -#include "geo/nodes/MapNode.hpp" +#include "../MapView.hpp" +#include "../lod/LODControl.hpp" +#include "../nodes/MapNode.hpp" + namespace threepp { class LODRaycast: public LODControl { public: - int subdivisionRays = 1; - float thresholdUp = 0.6; - float thresholdDown = 0.15; + bool powerDistance = false; + bool scaleDistance = true; - Raycaster raycaster; + void updateLOD(MapView& view, Camera& camera, GLRenderer& renderer, Object3D& scene) override; + private: + Raycaster raycaster; Vector2 mouse; + }; - bool powerDistance = false; - bool scaleDistance = true; + void LODRaycast::updateLOD(MapView& view, Camera& camera, GLRenderer& renderer, Object3D& scene) { + std::vector intersects; - float getLevel(...) { - return 0; - } + for (unsigned t = 0; t < this->subdivisionRays; t++) { + // Generate random point in viewport + this->mouse.set(math::randFloat() * 2 - 1, math::randFloat() * 2 - 1); - float getLevel(MapNode* node) { - return node->getLevel(); + // Check intersection + this->raycaster.setFromCamera(this->mouse, camera); + intersects = this->raycaster.intersectObjects(view.children, true); } - void updateLOD(MapView& view, Camera& camera, GLRenderer& renderer, Object3D& scene) override { - std::vector intersects; - - for (unsigned t = 0; t < this->subdivisionRays; t++) - { - // Generate random point in viewport - this->mouse.set(math::randFloat() * 2 - 1, math::randFloat() * 2 - 1); - - // Check intersection - this->raycaster.setFromCamera(this->mouse, camera); - intersects = this->raycaster.intersectObjects(view.children, true); - } - - for (unsigned i = 0; i < intersects.size(); i++) - { - auto* obj = intersects[i].object; + for (auto& intersect : intersects) { + auto* obj = intersect.object; - if (auto node = dynamic_cast(obj)) { + if (auto node = dynamic_cast(obj)) { - auto distance = intersects[i].distance; + auto distance = intersect.distance; - if (this->powerDistance) { - distance = std::pow(distance * 2, node->getLevel()); - } + if (this->powerDistance) { + distance = std::pow(distance * 2, node->getLevel()); + } - if (this->scaleDistance) { - // Get scale from transformation matrix directly - const auto& matrix = node->matrixWorld->elements; - const auto vector = Vector3(matrix[0], matrix[1], matrix[2]); - distance = vector.length() / distance; - } + if (this->scaleDistance) { + // Get scale from transformation matrix directly + const auto& matrix = node->matrixWorld->elements; + const auto vector = Vector3(matrix[0], matrix[1], matrix[2]); + distance = vector.length() / distance; + } - if (distance > this->thresholdUp) { - node->subdivide(); - } else if (distance < this->thresholdDown && node->parentNode) { - node->parentNode->simplify(); - } + if (distance > this->thresholdUp) { + node->subdivide(); + } else if (distance < this->thresholdDown && node->parentNode) { + node->parentNode->simplify(); } } } - }; + } -} +}// namespace threepp #endif//THREEPP_LODRAYCAST_HPP diff --git a/examples/libs/geo/nodes/MapNode.cpp b/examples/libs/geo/nodes/MapNode.cpp new file mode 100644 index 00000000..0ce40665 --- /dev/null +++ b/examples/libs/geo/nodes/MapNode.cpp @@ -0,0 +1,98 @@ + +#include "MapNode.hpp" +#include "../MapView.hpp" + +#include "threepp/materials/interfaces.hpp" + +#include + +using namespace threepp; + + +MapNode::MapNode( + MapNode* parentNode, MapView* mapView, int location, int level, float x, float y, + const std::shared_ptr& geometry, const std::shared_ptr& material) + : Mesh(geometry, material), parentNode(parentNode), + mapView(mapView), location(location), level(level), x(x), y(y) {} + +void MapNode::subdivide() { + const auto maxZoom = this->mapView->maxZoom(); + if (!this->children.empty() || this->level + 1 > maxZoom || this->parentNode && this->parentNode->nodesLoaded < MapNode::childrens) { + return; + } + + this->createChildNodes(); + + this->subdivided = true; +} + +void MapNode::simplify() { + const auto minZoom = this->mapView->minZoom(); + if (this->level - 1 < minZoom) { + return; + } + + // Clear children and reset flags + this->subdivided = false; + this->layers.enable(0); + this->clear(); + this->nodesLoaded = 0; +} + +void MapNode::loadData() { + if (this->level < this->mapView->getProvider()->minZoom || this->level > this->mapView->getProvider()->maxZoom) { + std::cerr << "Geo-Three: Loading tile outside of provider range." << std::endl; + + this->material()->as()->map = nullptr; + this->material()->needsUpdate(); + return; + } + + auto image = mapView->getProvider()->fetchTile(this->level, this->x, this->y); + + if (this->disposed) { + return; + } + + const auto texture = Texture::create(image); + texture->generateMipmaps = false; + texture->format = Format::RGBA; + texture->magFilter = Filter::Linear; + texture->minFilter = Filter::Linear; + texture->needsUpdate(); + + this->material()->as()->map = texture; + this->material()->needsUpdate(); +} + +void MapNode::nodeReady() { + if (this->disposed) { + std::cerr << "Geo-Three: nodeReady() called for disposed node" << std::endl; + // this->dispose(); + disposed = true; + return; + } + + if (this->parentNode) { + this->parentNode->nodesLoaded++; + + if (this->parentNode->nodesLoaded == MapNode::childrens) { + if (this->parentNode->subdivided == true) { + + this->parentNode->layers.disable(0); + } + + for (unsigned i = 0; i < this->parentNode->children.size(); i++) { + this->parentNode->children[i]->visible = true; + } + } + + if (this->parentNode->nodesLoaded > MapNode::childrens) { + std::cerr << "Geo-Three: Loaded more children objects than expected." << std::endl; + } + } + // If its the root object just set visible + else { + this->visible = true; + } +} diff --git a/examples/libs/geo/nodes/MapNode.hpp b/examples/libs/geo/nodes/MapNode.hpp index 4164c479..a61388f3 100644 --- a/examples/libs/geo/nodes/MapNode.hpp +++ b/examples/libs/geo/nodes/MapNode.hpp @@ -4,65 +4,46 @@ #include "threepp/objects/Mesh.hpp" -#include "geo/MapView.hpp" - namespace threepp { + class MapView; + struct QuadTreePosition { - static int root; - static int topLeft; - static int topRight; - static int bottomLeft; - static int bottomRight; + static inline int root = -1; + static inline int topLeft = 0; + static inline int topRight = 1; + static inline int bottomLeft = 2; + static inline int bottomRight = 3; }; - int QuadTreePosition::root = 1; - int QuadTreePosition::topLeft = 0; - int QuadTreePosition::topRight = 1; - int QuadTreePosition::bottomLeft = 2; - int QuadTreePosition::bottomRight = 3; - class MapNode: public Mesh { public: + MapNode* parentNode = nullptr; - MapNode(MapNode* parentNode, const std::shared_ptr& mapView, + MapNode(MapNode* parentNode, MapView* mapView, int location = QuadTreePosition::root, int level = 0, float x = 0, float y = 0, const std::shared_ptr& geometry = nullptr, - const std::shared_ptr& material = nullptr) - : Mesh(geometry, material), parentNode(parentNode), - mapView(mapView), location(location), level(level), x(x), y(y) {} + const std::shared_ptr& material = nullptr); - virtual void initialize(){}; + virtual void initialize() = 0; - virtual void createChildNodes(){}; + virtual void createChildNodes() = 0; - void subdivide() { - const auto maxZoom = this->mapView->maxZoom(); - if (!this->children.empty() || this->level + 1 > maxZoom || this->parentNode && this->parentNode->nodesLoaded < MapNode::childrens) { - return; - } + void subdivide(); - this->createChildNodes(); + void simplify(); - this->subdivided = true; - } + void loadData(); - void simplify() { - const auto minZoom = this->mapView->minZoom(); - if (this->level - 1 < minZoom) { - return; - } + void nodeReady(); - // Clear children and reset flags - this->subdivided = false; - this->clear(); - this->nodesLoaded = 0; - } + [[nodiscard]] virtual Vector3 baseScale() const = 0; + virtual std::shared_ptr baseGeometry() = 0; [[nodiscard]] int getLevel() const { @@ -71,8 +52,8 @@ namespace threepp { ~MapNode() override = default; - private: - std::shared_ptr mapView; + protected: + MapView* mapView; int location; int level; @@ -85,16 +66,9 @@ namespace threepp { int nodesLoaded = 0; - std::shared_ptr baseGeometry; - - static std::optional baseScale; - - static int childrens; + inline static int childrens = 4; }; - std::optional MapNode::baseScale = std::nullopt; - int MapNode::childrens = 4; - }// namespace threepp #endif//THREEPP_MAPNODE_HPP diff --git a/examples/libs/geo/nodes/MapPlaneNode.hpp b/examples/libs/geo/nodes/MapPlaneNode.hpp index 877fc366..fa0488c8 100644 --- a/examples/libs/geo/nodes/MapPlaneNode.hpp +++ b/examples/libs/geo/nodes/MapPlaneNode.hpp @@ -3,16 +3,90 @@ #ifndef THREEPP_MAPPLANENODE_HPP #define THREEPP_MAPPLANENODE_HPP -#include "geo/nodes/MapNode.hpp" +#include "../utils/UnitUtils.hpp" +#include "MapNode.hpp" +#include "geo/geometries/MapNodeGeometry.hpp" +#include "threepp/materials/MeshBasicMaterial.hpp" namespace threepp { - class MapPlaneNode { + namespace { + std::shared_ptr baseGeom = MapNodeGeometry::create(1, 1, 1, 1, false); + } + + class MapPlaneNode: public MapNode { public: + MapPlaneNode(MapNode* parent, MapView* mapView, int location = QuadTreePosition::root, int level = 0, float x = 0, float y = 0) + : MapNode(parent, mapView, location, level, x, y, baseGeom, MeshBasicMaterial::create({{"wireframe", false}})) { + + this->matrixAutoUpdate = false; + this->layers.enable(0); + this->visible = false; + } + + void initialize() override { + + loadData(); + nodeReady(); + } + + [[nodiscard]] Vector3 baseScale() const override { + + return {utils::EARTH_PERIMETER, 1, utils::EARTH_PERIMETER}; + } + + std::shared_ptr baseGeometry() override { + + return baseGeom; + } + + void createChildNodes() override { + + const auto level = this->level + 1; + const auto x = this->x * 2; + const auto y = this->y * 2; + + auto node = std::make_shared(this, this->mapView, QuadTreePosition::topLeft, level, x, y); + node->initialize(); + node->scale.set(0.5, 1.0, 0.5); + node->position.set(-0.25, 0, -0.25); + this->add(node); + node->updateMatrix(); + node->updateMatrixWorld(true); + + node = std::make_shared(this, this->mapView, QuadTreePosition::topRight, level, x + 1, y); + node->initialize(); + node->scale.set(0.5, 1.0, 0.5); + node->position.set(0.25, 0, -0.25); + this->add(node); + node->updateMatrix(); + node->updateMatrixWorld(true); + + node = std::make_shared(this, this->mapView, QuadTreePosition::bottomLeft, level, x, y + 1); + node->initialize(); + node->scale.set(0.5, 1.0, 0.5); + node->position.set(-0.25, 0, 0.25); + this->add(node); + node->updateMatrix(); + node->updateMatrixWorld(true); + + node = std::make_shared(this, this->mapView, QuadTreePosition::bottomRight, level, x + 1, y + 1); + node->initialize(); + node->scale.set(0.5, 1.0, 0.5); + node->position.set(0.25, 0, 0.25); + this->add(node); + node->updateMatrix(); + node->updateMatrixWorld(true); + } + void raycast(const Raycaster& raycaster, std::vector& intersects) override { + if (layers.test(Layers())) { + Mesh::raycast(raycaster, intersects); + } + } }; -} +}// namespace threepp #endif//THREEPP_MAPPLANENODE_HPP diff --git a/examples/libs/geo/providers/MapProvider.hpp b/examples/libs/geo/providers/MapProvider.hpp index 5bf83166..1077f445 100644 --- a/examples/libs/geo/providers/MapProvider.hpp +++ b/examples/libs/geo/providers/MapProvider.hpp @@ -2,8 +2,8 @@ #ifndef THREEPP_MAPPROVIDER_HPP #define THREEPP_MAPPROVIDER_HPP -#include #include +#include #include "threepp/textures/Image.hpp" @@ -12,16 +12,14 @@ namespace threepp { class MapProvider { public: - float minZoom = 0; float maxZoom = 20; - virtual std::string name() const = 0; - virtual Image fetchTile(float zoom, float x, float y) = 0; + virtual ~MapProvider() = default; }; -} +}// namespace threepp #endif//THREEPP_MAPPROVIDER_HPP diff --git a/examples/libs/geo/providers/OpenStreetMapsProvider.hpp b/examples/libs/geo/providers/OpenStreetMapsProvider.hpp index 3d3ee7eb..d8377132 100644 --- a/examples/libs/geo/providers/OpenStreetMapsProvider.hpp +++ b/examples/libs/geo/providers/OpenStreetMapsProvider.hpp @@ -2,23 +2,24 @@ #ifndef THREEPP_OPENSTREETMAPSPROVIDER_HPP #define THREEPP_OPENSTREETMAPSPROVIDER_HPP -#include "geo/providers/MapProvider.hpp" +#include "../../utility/URLFetcher.hpp" +#include "../providers/MapProvider.hpp" #include "threepp/loaders/ImageLoader.hpp" -#include "threepp/utils/URLFetcher.hpp" #include #include +#include namespace threepp { class OpenStreetMapProvider: public MapProvider { + public: explicit OpenStreetMapProvider(std::string address = "https://a.tile.openstreetmap.org/") - : address(std::move(address)) {} + : address(std::move(address)) { - [[nodiscard]] std::string name() const override { - return "OpenStreetMapProvider"; + this->maxZoom = 19; } Image fetchTile(float zoom, float x, float y) override { @@ -36,8 +37,6 @@ namespace threepp { std::string address; std::string format = "png"; - float maxZoom = 19; - utils::UrlFetcher urlFetcher; ImageLoader loader; }; diff --git a/examples/libs/geo/utils/UnitUtils.hpp b/examples/libs/geo/utils/UnitUtils.hpp new file mode 100644 index 00000000..02d2610a --- /dev/null +++ b/examples/libs/geo/utils/UnitUtils.hpp @@ -0,0 +1,48 @@ + +#ifndef THREEPP_UNITUTILS_HPP +#define THREEPP_UNITUTILS_HPP + +#include "threepp/math/MathUtils.hpp" +#include "threepp/math/Vector2.hpp" + +#include + +namespace threepp::utils { + + /** + * Average radius of earth in meters. + */ + constexpr float EARTH_RADIUS = 6371008.f; + + /** + * Earth radius in semi-major axis A as defined in WGS84. + */ + const float EARTH_RADIUS_A = 6378137.0f; + + /** + * Earth radius in semi-minor axis B as defined in WGS84. + */ + const float EARTH_RADIUS_B = 6356752.314245f; + + /** + * Earth equator perimeter in meters. + */ + const float EARTH_PERIMETER = 2 * math::PI * EARTH_RADIUS; + + /** + * Earth equator perimeter in meters. + */ + const float EARTH_ORIGIN = EARTH_PERIMETER / 2.0f; + + inline Vector2 datumsToSpherical(float latitude, float longitude) { + const auto x = longitude * EARTH_ORIGIN / 180.0f; + auto y = std::log(std::tan((90 + latitude) * math::PI / 360.0f)) / (math::PI / 180.0f); + + y = y * EARTH_ORIGIN / 180.0f; + + return {x, y}; + } + +}// namespace threepp::utils + +#endif//THREEPP_UNITUTILS_HPP diff --git a/include/threepp/utils/URLFetcher.hpp b/examples/libs/utility/URLFetcher.hpp similarity index 72% rename from include/threepp/utils/URLFetcher.hpp rename to examples/libs/utility/URLFetcher.hpp index 047c3d2d..85520789 100644 --- a/include/threepp/utils/URLFetcher.hpp +++ b/examples/libs/utility/URLFetcher.hpp @@ -30,18 +30,27 @@ namespace threepp::utils { return false; } #else - UrlFetcher() { - curl = curl_easy_init(); + UrlFetcher(): curl(curl_easy_init()) { curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &write_data); + curl_easy_setopt(curl, CURLOPT_USERAGENT, "threepp/1.0 (Cross-Platform; C++)"); } bool fetch(const std::string& url, std::vector& data) { + if (cache_.count(url)) { + data = cache_[url]; + return true; + } + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data); curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); CURLcode res = curl_easy_perform(curl); - return res == 0; + if (res == CURLE_OK) { + cache_[url] = data; + } + + return res == CURLE_OK; } ~UrlFetcher() { @@ -50,6 +59,7 @@ namespace threepp::utils { private: CURL* curl; + std::unordered_map> cache_{}; #endif }; diff --git a/examples/projects/CMakeLists.txt b/examples/projects/CMakeLists.txt index 59890aa5..ecf94cb9 100644 --- a/examples/projects/CMakeLists.txt +++ b/examples/projects/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(Crane3R) add_subdirectory(InvertedPendulum) +add_subdirectory(MapView) add_subdirectory(MotorControl) add_subdirectory(Pathfinding) add_subdirectory(Snake) diff --git a/examples/projects/MapView/CMakeLists.txt b/examples/projects/MapView/CMakeLists.txt new file mode 100644 index 00000000..3dc962d6 --- /dev/null +++ b/examples/projects/MapView/CMakeLists.txt @@ -0,0 +1,9 @@ + +if (CURL_FOUND) + + add_example(NAME "MapView" SOURCES "main.cpp") + if (TARGET "MapView") + target_link_libraries("MapView" PRIVATE geothreepp) + endif () + +endif () \ No newline at end of file diff --git a/examples/projects/MapView/main.cpp b/examples/projects/MapView/main.cpp new file mode 100644 index 00000000..339cf196 --- /dev/null +++ b/examples/projects/MapView/main.cpp @@ -0,0 +1,45 @@ + +#include "threepp/threepp.hpp" + +#include "geo/MapView.hpp" +#include "geo/providers/OpenStreetMapsProvider.hpp" +#include "geo/utils/UnitUtils.hpp" +#include "geo/lod/LODRadial.hpp" + +using namespace threepp; + +int main() { + + Canvas canvas("MapView"); + GLRenderer renderer(canvas.size()); + + Scene scene; + scene.background = Color::aliceblue; + + scene.add(AmbientLight::create(0x777777)); + + PerspectiveCamera camera(80, canvas.aspect(), 0.1, 1e12); + camera.layers.enable(0); + + OrbitControls controls{camera, canvas}; + + MapView map(std::make_unique(), std::make_unique()); + scene.add(map); + map.updateMatrixWorld(true); + + const auto coords = utils::datumsToSpherical(62.50094228364612, 6.09138498277564); + controls.target.set(coords.x, 0, -coords.y); + camera.position.set(coords.x, 100000, -coords.y); + controls.update(); + + canvas.onWindowResize([&](WindowSize size) { + camera.aspect = size.aspect(); + camera.updateProjectionMatrix(); + + renderer.setSize(size); + }); + + canvas.animate([&] { + renderer.render(scene, camera); + }); +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cbb3cc1d..05665a6f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -171,7 +171,6 @@ set(publicHeaders "threepp/utils/BufferGeometryUtils.hpp" "threepp/utils/StringUtils.hpp" - "threepp/utils/URLFetcher.hpp" "threepp/lights/lights.hpp" "threepp/lights/Light.hpp"