Skip to content

Commit

Permalink
Merge branch 'main' into 3dtiles-voxel-content
Browse files Browse the repository at this point in the history
  • Loading branch information
lilleyse committed Nov 8, 2024
2 parents 542f8f3 + 544b2b3 commit f2d3547
Show file tree
Hide file tree
Showing 4 changed files with 309 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
##### Additions :tada:

- Added support for `EXT_accessor_additional_types` in `AccessorView`.
- Added `EllipsoidTilesetLoader` that will generate a tileset by tesselating the surface of an ellipsoid, producing a simple globe tileset without any terrain features.

### v0.41.0 - 2024-11-01

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

#include <Cesium3DTilesSelection/Tileset.h>
#include <CesiumGeometry/QuadtreeTilingScheme.h>

namespace Cesium3DTilesSelection {
/**
* @brief A loader that will generate a tileset by tesselating the surface of an
* ellipsoid, producing a simple globe tileset without any terrain features.
*/
class EllipsoidTilesetLoader : public TilesetContentLoader {
public:
/**
* @brief Constructs a new instance.
*
* @param ellipsoid The {@link Ellipsoid}.
*/
EllipsoidTilesetLoader(
const CesiumGeospatial::Ellipsoid& ellipsoid CESIUM_DEFAULT_ELLIPSOID);

/**
* @brief Creates a new tileset with this loader.
*
* @param externals The external interfaces to use.
* @param options Additional options for the tileset.
*/
static std::unique_ptr<Tileset> createTileset(
const TilesetExternals& externals,
const TilesetOptions& options = TilesetOptions{});

CesiumAsync::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 CesiumGeometry::QuadtreeTileID& childID) const;

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

CesiumGeospatial::GeographicProjection _projection;
CesiumGeometry::QuadtreeTilingScheme _tilingScheme;
};
} // namespace Cesium3DTilesSelection
245 changes: 245 additions & 0 deletions Cesium3DTilesSelection/src/EllipsoidTilesetLoader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
#include <Cesium3DTilesContent/ImplicitTilingUtilities.h>
#include <Cesium3DTilesSelection/EllipsoidTilesetLoader.h>
#include <CesiumGeospatial/calcQuadtreeMaxGeometricError.h>

#include <glm/ext/matrix_transform.hpp>

using namespace CesiumGltf;
using namespace CesiumAsync;
using namespace CesiumUtility;
using namespace CesiumGeometry;
using namespace CesiumGeospatial;
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*/) {
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(
8.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.asset.version = "2.0";
model.extras["gltfUpAxis"] = JsonValue(std::underlying_type_t<Axis>(Axis::Z));

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
5 changes: 5 additions & 0 deletions CesiumGltf/include/CesiumGltf/ImageAsset.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ struct CESIUMGLTF_API ImageAsset final
*/
int64_t sizeBytes = -1;

/**
* @brief Constructs an empty image asset.
*/
ImageAsset() = default;

/**
* @brief Gets the size of this asset, in bytes.
*
Expand Down

0 comments on commit f2d3547

Please sign in to comment.