Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- Added full support for custom ellipsoids by setting `TilesetOptions::ellipsoid` when creating a tileset.
- Many methods have been updated with an additional ellipsoid parameter to support this. The WGS84 ellipsoid is used as a default parameter here to ensure API compatibility.
- `CESIUM_DISABLE_DEFAULT_ELLIPSOID` can be defined to disable the WGS84 default parameter, exposing through errors the places in your code that are still assuming a WGS84 ellipsoid.
- Added `EllipsoidTilesetLoader` that will generate a tileset by tesselating the surface of an ellipsoid, producing a simple globe tileset without any terrain features.

##### Fixes :wrench:

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#pragma once

#include <Cesium3DTilesSelection/Tile.h>
#include <Cesium3DTilesSelection/TileLoadResult.h>
#include <Cesium3DTilesSelection/Tileset.h>
#include <Cesium3DTilesSelection/TilesetContentLoader.h>
#include <Cesium3DTilesSelection/TilesetExternals.h>
#include <Cesium3DTilesSelection/TilesetOptions.h>
#include <CesiumAsync/Future.h>
#include <CesiumGeometry/QuadtreeTileID.h>
#include <CesiumGeometry/QuadtreeTilingScheme.h>
#include <CesiumGeospatial/BoundingRegion.h>
#include <CesiumGeospatial/Ellipsoid.h>
#include <CesiumGeospatial/GeographicProjection.h>
#include <CesiumGltf/Model.h>

#include <glm/fwd.hpp>

#include <cstdint>
#include <memory>
#include <vector>

using namespace CesiumGltf;
using namespace CesiumAsync;
using namespace CesiumUtility;
using namespace CesiumGeometry;
using namespace CesiumGeospatial;

namespace Cesium3DTilesSelection {
class EllipsoidTilesetLoader : public TilesetContentLoader {
public:
EllipsoidTilesetLoader(const Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID);

static std::unique_ptr<Tileset> createTileset(
const TilesetExternals& externals,
const TilesetOptions& options = TilesetOptions{});

Future<TileLoadResult> loadTileContent(const TileLoadInput& input) override;
TileChildrenResult createTileChildren(
const Tile& tile,
const CesiumGeospatial::Ellipsoid& ellipsoid
CESIUM_DEFAULT_ELLIPSOID) override;

private:
struct Geometry {
std::vector<uint16_t> indices;
std::vector<glm::vec3> vertices;
std::vector<glm::vec3> normals;
};

void createChildTile(
const Tile& parent,
std::vector<Tile>& children,
const QuadtreeTileID& childID) const;

BoundingRegion createBoundingRegion(const QuadtreeTileID& quadtreeID) const;
Geometry createGeometry(const Tile& tile) const;
Model createModel(const Geometry& geometry) const;

GeographicProjection _projection;
QuadtreeTilingScheme _tilingScheme;
};
} // namespace Cesium3DTilesSelection
279 changes: 279 additions & 0 deletions Cesium3DTilesSelection/src/EllipsoidTilesetLoader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
#include <Cesium3DTilesContent/ImplicitTilingUtilities.h>
#include <Cesium3DTilesSelection/EllipsoidTilesetLoader.h>
#include <Cesium3DTilesSelection/Tile.h>
#include <Cesium3DTilesSelection/TileContent.h>
#include <Cesium3DTilesSelection/TileLoadResult.h>
#include <Cesium3DTilesSelection/TileRefine.h>
#include <Cesium3DTilesSelection/Tileset.h>
#include <Cesium3DTilesSelection/TilesetContentLoader.h>
#include <Cesium3DTilesSelection/TilesetExternals.h>
#include <Cesium3DTilesSelection/TilesetOptions.h>
#include <CesiumAsync/Future.h>
#include <CesiumGeometry/Axis.h>
#include <CesiumGeometry/QuadtreeTileID.h>
#include <CesiumGeometry/QuadtreeTilingScheme.h>
#include <CesiumGeospatial/BoundingRegion.h>
#include <CesiumGeospatial/Cartographic.h>
#include <CesiumGeospatial/Ellipsoid.h>
#include <CesiumGeospatial/GeographicProjection.h>
#include <CesiumGeospatial/GlobeRectangle.h>
#include <CesiumGeospatial/calcQuadtreeMaxGeometricError.h>
#include <CesiumGltf/Accessor.h>
#include <CesiumGltf/AccessorSpec.h>
#include <CesiumGltf/Buffer.h>
#include <CesiumGltf/BufferCesium.h>
#include <CesiumGltf/BufferView.h>
#include <CesiumGltf/Mesh.h>
#include <CesiumGltf/MeshPrimitive.h>
#include <CesiumGltf/Model.h>
#include <CesiumGltf/Node.h>
#include <CesiumGltf/Scene.h>

#include <glm/detail/type_vec3.hpp>
#include <glm/ext/matrix_transform.hpp>
#include <glm/fwd.hpp>

#include <cstddef>
#include <cstdint>
#include <cstring>
#include <memory>
#include <optional>
#include <type_traits>
#include <variant>
#include <vector>

using namespace Cesium3DTilesContent;

namespace Cesium3DTilesSelection {
EllipsoidTilesetLoader::EllipsoidTilesetLoader(const Ellipsoid& ellipsoid)
: _projection(ellipsoid),
_tilingScheme(
_projection.project(_projection.MAXIMUM_GLOBE_RECTANGLE),
2,
1) {}

/*static*/ std::unique_ptr<Tileset> EllipsoidTilesetLoader::createTileset(
const TilesetExternals& externals,
const TilesetOptions& options) {
std::unique_ptr<EllipsoidTilesetLoader> pCustomLoader =
std::make_unique<EllipsoidTilesetLoader>(options.ellipsoid);
std::unique_ptr<Tile> pRootTile =
std::make_unique<Tile>(pCustomLoader.get(), TileEmptyContent{});

pRootTile->setRefine(TileRefine::Replace);
pRootTile->setUnconditionallyRefine();

std::vector<Tile> children;
uint32_t rootTilesX = pCustomLoader->_tilingScheme.getRootTilesX();
children.reserve(rootTilesX);

for (uint32_t x = 0; x < rootTilesX; x++) {
pCustomLoader->createChildTile(
*pRootTile,
children,
QuadtreeTileID{0, x, 0});
}

pRootTile->createChildTiles(std::move(children));

return std::make_unique<Tileset>(
externals,
std::move(pCustomLoader),
std::move(pRootTile),
options);
}

Future<TileLoadResult>
EllipsoidTilesetLoader::loadTileContent(const TileLoadInput& input) {
return input.asyncSystem.createResolvedFuture(TileLoadResult{
createModel(createGeometry(input.tile)),
Axis::Z,
std::nullopt,
std::nullopt,
std::nullopt,
nullptr,
{},
TileLoadResultState::Success});
}

TileChildrenResult EllipsoidTilesetLoader::createTileChildren(
const Tile& tile,
const CesiumGeospatial::Ellipsoid& ellipsoid) {
// We don't use the ellipsoid parameter, since we already have an ellipsoid
// specified, but if we don't use the variable we'll get an error. This line
// counts as using it.
(void)ellipsoid;

const QuadtreeTileID* pParentID =
std::get_if<QuadtreeTileID>(&tile.getTileID());

if (pParentID) {
std::vector<Tile> children;
QuadtreeChildren childIDs =
ImplicitTilingUtilities::getChildren(*pParentID);
children.reserve(childIDs.size());

for (const QuadtreeTileID& childID : childIDs) {
createChildTile(tile, children, childID);
}

return TileChildrenResult{
std::move(children),
TileLoadResultState::Success};
}

return TileChildrenResult{{}, TileLoadResultState::Failed};
}

void EllipsoidTilesetLoader::createChildTile(
const Tile& parent,
std::vector<Tile>& children,
const QuadtreeTileID& childID) const {
BoundingRegion boundingRegion = createBoundingRegion(childID);
const GlobeRectangle& globeRectangle = boundingRegion.getRectangle();

Tile& child = children.emplace_back(parent.getLoader());
child.setTileID(childID);
child.setRefine(parent.getRefine());
child.setTransform(glm::translate(
glm::dmat4x4(1.0),
_projection.getEllipsoid().cartographicToCartesian(
globeRectangle.getNorthwest())));
child.setBoundingVolume(boundingRegion);
child.setGeometricError(
6.0 * calcQuadtreeMaxGeometricError(_projection.getEllipsoid()) *
globeRectangle.computeWidth());
}

BoundingRegion EllipsoidTilesetLoader::createBoundingRegion(
const QuadtreeTileID& quadtreeID) const {
return BoundingRegion(
_projection.unproject(_tilingScheme.tileToRectangle(quadtreeID)),
0.0,
0.0,
_projection.getEllipsoid());
}

EllipsoidTilesetLoader::Geometry
EllipsoidTilesetLoader::createGeometry(const Tile& tile) const {
static constexpr uint16_t resolution = 24;

std::vector<uint16_t> indices(6 * (resolution - 1) * (resolution - 1));
std::vector<glm::vec3> vertices(resolution * resolution);
std::vector<glm::vec3> normals(vertices.size());

const Ellipsoid& ellipsoid = _projection.getEllipsoid();
const GlobeRectangle& rectangle =
std::get<BoundingRegion>(tile.getBoundingVolume()).getRectangle();

double west = rectangle.getWest();
double east = rectangle.getEast();
double north = rectangle.getNorth();
double south = rectangle.getSouth();

double lonStep = (east - west) / (resolution - 1);
double latStep = (south - north) / (resolution - 1);

glm::dmat4 inverseTransform = glm::inverse(tile.getTransform());

for (uint16_t x = 0; x < resolution; x++) {
double longitude = (lonStep * x) + west;
for (uint16_t y = 0; y < resolution; y++) {
double latitude = (latStep * y) + north;
Cartographic cartographic(longitude, latitude);

uint16_t index = static_cast<uint16_t>((resolution * x) + y);
vertices[index] = glm::dvec3(
inverseTransform *
glm::dvec4(ellipsoid.cartographicToCartesian(cartographic), 1.0));
normals[index] = ellipsoid.geodeticSurfaceNormal(cartographic);

if (x < resolution - 1 && y < resolution - 1) {
uint16_t a = index + 1;
uint16_t b = index + resolution;
uint16_t c = b + 1;
indices.insert(indices.end(), {b, index, a, b, a, c});
}
}
}

return Geometry{std::move(indices), std::move(vertices), std::move(normals)};
}

Model EllipsoidTilesetLoader::createModel(const Geometry& geometry) const {
const std::vector<uint16_t>& indices = geometry.indices;
const std::vector<glm::vec3>& vertices = geometry.vertices;
const std::vector<glm::vec3>& normals = geometry.normals;

size_t indicesSize = indices.size() * sizeof(uint16_t);
size_t verticesSize = vertices.size() * sizeof(glm::vec3);
size_t normalsSize = verticesSize;

Model model;
model.buffers.resize(1);
model.bufferViews.resize(3);
model.accessors.resize(3);
model.materials.resize(1);
model.meshes.resize(1);
model.scenes.resize(1);
model.nodes.resize(1);

model.meshes[0].primitives.resize(1);
model.scenes[0].nodes.emplace_back(0);
model.nodes[0].mesh = 0;

std::vector<std::byte>& buffer = model.buffers[0].cesium.data;
buffer.resize(indicesSize + verticesSize + normalsSize);
std::memcpy(buffer.data(), indices.data(), indicesSize);
std::memcpy(buffer.data() + indicesSize, vertices.data(), verticesSize);
std::memcpy(
buffer.data() + indicesSize + verticesSize,
normals.data(),
normalsSize);

BufferView& bufferViewIndices = model.bufferViews[0];
bufferViewIndices.buffer = 0;
bufferViewIndices.byteOffset = 0;
bufferViewIndices.byteLength = static_cast<int64_t>(indicesSize);
bufferViewIndices.target = BufferView::Target::ELEMENT_ARRAY_BUFFER;

BufferView& bufferViewVertices = model.bufferViews[1];
bufferViewVertices.buffer = 0;
bufferViewVertices.byteOffset = static_cast<int64_t>(indicesSize);
bufferViewVertices.byteLength = static_cast<int64_t>(verticesSize);
bufferViewVertices.target = BufferView::Target::ARRAY_BUFFER;

BufferView& bufferViewNormals = model.bufferViews[2];
bufferViewNormals.buffer = 0;
bufferViewNormals.byteOffset =
static_cast<int64_t>(indicesSize + verticesSize);
bufferViewNormals.byteLength = static_cast<int64_t>(normalsSize);
bufferViewNormals.target = BufferView::Target::ARRAY_BUFFER;

Accessor& accessorIndices = model.accessors[0];
accessorIndices.bufferView = 0;
accessorIndices.count = static_cast<int64_t>(indices.size());
accessorIndices.componentType = Accessor::ComponentType::UNSIGNED_SHORT;
accessorIndices.type = Accessor::Type::SCALAR;

Accessor& accessorVertices = model.accessors[1];
accessorVertices.bufferView = 1;
accessorVertices.count = static_cast<int64_t>(vertices.size());
accessorVertices.componentType = Accessor::ComponentType::FLOAT;
accessorVertices.type = Accessor::Type::VEC3;

Accessor& accessorNormals = model.accessors[2];
accessorNormals.bufferView = 2;
accessorNormals.count = static_cast<int64_t>(normals.size());
accessorNormals.componentType = Accessor::ComponentType::FLOAT;
accessorNormals.type = Accessor::Type::VEC3;

MeshPrimitive& primitive = model.meshes[0].primitives[0];
primitive.attributes["POSITION"] = 1;
primitive.attributes["NORMAL"] = 2;
primitive.indices = 0;
primitive.material = 0;

return model;
}
} // namespace Cesium3DTilesSelection