776 lines
31 KiB
C++
776 lines
31 KiB
C++
#include "ImplicitOctreeLoader.h"
|
|
|
|
#include <Cesium3DTilesContent/SubtreeAvailability.h>
|
|
#include <Cesium3DTilesContent/registerAllTileContentTypes.h>
|
|
#include <Cesium3DTilesSelection/Tile.h>
|
|
#include <Cesium3DTilesSelection/TileContent.h>
|
|
#include <Cesium3DTilesSelection/TileLoadResult.h>
|
|
#include <Cesium3DTilesSelection/TilesetContentLoader.h>
|
|
#include <CesiumAsync/AsyncSystem.h>
|
|
#include <CesiumGeometry/OrientedBoundingBox.h>
|
|
#include <CesiumGeospatial/BoundingRegion.h>
|
|
#include <CesiumGeospatial/Ellipsoid.h>
|
|
#include <CesiumGltf/Model.h>
|
|
#include <CesiumNativeTests/SimpleAssetAccessor.h>
|
|
#include <CesiumNativeTests/SimpleAssetRequest.h>
|
|
#include <CesiumNativeTests/SimpleAssetResponse.h>
|
|
#include <CesiumNativeTests/SimpleTaskProcessor.h>
|
|
#include <CesiumNativeTests/readFile.h>
|
|
#include <CesiumUtility/Math.h>
|
|
|
|
#include <doctest/doctest.h>
|
|
#include <glm/ext/matrix_double3x3.hpp>
|
|
#include <glm/ext/vector_double3.hpp>
|
|
#include <spdlog/spdlog.h>
|
|
|
|
#include <algorithm>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <filesystem>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <span>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <variant>
|
|
#include <vector>
|
|
|
|
using namespace doctest;
|
|
using namespace Cesium3DTilesContent;
|
|
using namespace Cesium3DTilesSelection;
|
|
using namespace CesiumGeometry;
|
|
using namespace CesiumGeospatial;
|
|
using namespace CesiumUtility;
|
|
using namespace CesiumNativeTests;
|
|
|
|
namespace {
|
|
std::filesystem::path testDataPath = Cesium3DTilesSelection_TEST_DATA_DIR;
|
|
}
|
|
|
|
TEST_CASE("Test implicit octree loader") {
|
|
Cesium3DTilesContent::registerAllTileContentTypes();
|
|
|
|
auto pMockedAssetAccessor = std::make_shared<SimpleAssetAccessor>(
|
|
std::map<std::string, std::shared_ptr<SimpleAssetRequest>>{});
|
|
|
|
CesiumAsync::AsyncSystem asyncSystem{std::make_shared<SimpleTaskProcessor>()};
|
|
|
|
ImplicitOctreeLoader loader{
|
|
"tileset.json",
|
|
"content/{level}.{x}.{y}.{z}.b3dm",
|
|
"subtrees/{level}.{x}.{y}.{z}.json",
|
|
5,
|
|
5,
|
|
OrientedBoundingBox(glm::dvec3(0.0), glm::dmat3(20.0))};
|
|
|
|
SUBCASE("Load tile that does not have quadtree ID") {
|
|
Tile tile(&loader);
|
|
tile.setTileID("This is a test tile");
|
|
|
|
TileLoadInput loadInput{
|
|
tile,
|
|
{},
|
|
asyncSystem,
|
|
pMockedAssetAccessor,
|
|
spdlog::default_logger(),
|
|
{}};
|
|
|
|
auto tileLoadResultFuture = loader.loadTileContent(loadInput);
|
|
|
|
asyncSystem.dispatchMainThreadTasks();
|
|
|
|
auto tileLoadResult = tileLoadResultFuture.wait();
|
|
CHECK(tileLoadResult.state == TileLoadResultState::Failed);
|
|
}
|
|
|
|
SUBCASE("Load empty tile") {
|
|
// add subtree with all empty tiles
|
|
loader.addSubtreeAvailability(
|
|
OctreeTileID{0, 0, 0, 0},
|
|
SubtreeAvailability{
|
|
ImplicitTileSubdivisionScheme::Octree,
|
|
5,
|
|
SubtreeAvailability::SubtreeConstantAvailability{true},
|
|
SubtreeAvailability::SubtreeConstantAvailability{false},
|
|
{SubtreeAvailability::SubtreeConstantAvailability{false}},
|
|
{}});
|
|
|
|
// check that this tile will have empty content
|
|
Tile tile(&loader);
|
|
tile.setTileID(OctreeTileID{1, 0, 1, 1});
|
|
|
|
TileLoadInput loadInput{
|
|
tile,
|
|
{},
|
|
asyncSystem,
|
|
pMockedAssetAccessor,
|
|
spdlog::default_logger(),
|
|
{}};
|
|
|
|
auto tileLoadResultFuture = loader.loadTileContent(loadInput);
|
|
|
|
asyncSystem.dispatchMainThreadTasks();
|
|
|
|
auto tileLoadResult = tileLoadResultFuture.wait();
|
|
CHECK(std::holds_alternative<TileEmptyContent>(tileLoadResult.contentKind));
|
|
CHECK(!tileLoadResult.updatedBoundingVolume);
|
|
CHECK(!tileLoadResult.updatedContentBoundingVolume);
|
|
CHECK(!tileLoadResult.tileInitializer);
|
|
CHECK(tileLoadResult.state == TileLoadResultState::Success);
|
|
}
|
|
|
|
SUBCASE("Load tile with render content") {
|
|
// add subtree with all available tiles
|
|
loader.addSubtreeAvailability(
|
|
OctreeTileID{0, 0, 0, 0},
|
|
SubtreeAvailability{
|
|
ImplicitTileSubdivisionScheme::Quadtree,
|
|
5,
|
|
SubtreeAvailability::SubtreeConstantAvailability{true},
|
|
SubtreeAvailability::SubtreeConstantAvailability{false},
|
|
{SubtreeAvailability::SubtreeConstantAvailability{true}},
|
|
{}});
|
|
|
|
// mock tile content b3dm
|
|
auto pMockCompletedResponse = std::make_unique<SimpleAssetResponse>(
|
|
static_cast<uint16_t>(200),
|
|
"doesn't matter",
|
|
CesiumAsync::HttpHeaders{},
|
|
readFile(testDataPath / "BatchTables" / "batchedWithJson.b3dm"));
|
|
|
|
auto pMockCompletedRequest = std::make_shared<SimpleAssetRequest>(
|
|
"GET",
|
|
"doesn't matter",
|
|
CesiumAsync::HttpHeaders{},
|
|
std::move(pMockCompletedResponse));
|
|
|
|
pMockedAssetAccessor->mockCompletedRequests.insert(
|
|
{"content/3.1.0.1.b3dm", std::move(pMockCompletedRequest)});
|
|
|
|
// check that this tile has render content
|
|
Tile tile(&loader);
|
|
tile.setTileID(OctreeTileID{3, 1, 0, 1});
|
|
|
|
TileLoadInput loadInput{
|
|
tile,
|
|
{},
|
|
asyncSystem,
|
|
pMockedAssetAccessor,
|
|
spdlog::default_logger(),
|
|
{}};
|
|
|
|
auto tileLoadResultFuture = loader.loadTileContent(loadInput);
|
|
|
|
asyncSystem.dispatchMainThreadTasks();
|
|
|
|
auto tileLoadResult = tileLoadResultFuture.wait();
|
|
CHECK(
|
|
std::holds_alternative<CesiumGltf::Model>(tileLoadResult.contentKind));
|
|
CHECK(!tileLoadResult.updatedBoundingVolume);
|
|
CHECK(!tileLoadResult.updatedContentBoundingVolume);
|
|
CHECK(!tileLoadResult.tileInitializer);
|
|
CHECK(tileLoadResult.state == TileLoadResultState::Success);
|
|
}
|
|
|
|
SUBCASE("Load unknown tile content") {
|
|
// add subtree with all available tiles
|
|
loader.addSubtreeAvailability(
|
|
OctreeTileID{0, 0, 0, 0},
|
|
SubtreeAvailability{
|
|
ImplicitTileSubdivisionScheme::Quadtree,
|
|
5,
|
|
SubtreeAvailability::SubtreeConstantAvailability{true},
|
|
SubtreeAvailability::SubtreeConstantAvailability{false},
|
|
{SubtreeAvailability::SubtreeConstantAvailability{true}},
|
|
{}});
|
|
|
|
// mock random tile content
|
|
auto pMockCompletedResponse = std::make_unique<SimpleAssetResponse>(
|
|
static_cast<uint16_t>(200),
|
|
"doesn't matter",
|
|
CesiumAsync::HttpHeaders{},
|
|
std::vector<std::byte>(20));
|
|
|
|
auto pMockCompletedRequest = std::make_shared<SimpleAssetRequest>(
|
|
"GET",
|
|
"doesn't matter",
|
|
CesiumAsync::HttpHeaders{},
|
|
std::move(pMockCompletedResponse));
|
|
|
|
pMockedAssetAccessor->mockCompletedRequests.insert(
|
|
{"content/1.0.1.0.b3dm", std::move(pMockCompletedRequest)});
|
|
|
|
// check that this tile has render content
|
|
Tile tile(&loader);
|
|
tile.setTileID(OctreeTileID{1, 0, 1, 0});
|
|
|
|
TileLoadInput loadInput{
|
|
tile,
|
|
{},
|
|
asyncSystem,
|
|
pMockedAssetAccessor,
|
|
spdlog::default_logger(),
|
|
{}};
|
|
|
|
auto tileLoadResultFuture = loader.loadTileContent(loadInput);
|
|
|
|
asyncSystem.dispatchMainThreadTasks();
|
|
|
|
auto tileLoadResult = tileLoadResultFuture.wait();
|
|
CHECK(tileLoadResult.state == TileLoadResultState::Failed);
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
const Tile&
|
|
findTile(const std::span<const Tile>& children, const OctreeTileID& tileID) {
|
|
auto it = std::find_if(
|
|
children.begin(),
|
|
children.end(),
|
|
[tileID](const Tile& tile) {
|
|
const OctreeTileID* pID = std::get_if<OctreeTileID>(&tile.getTileID());
|
|
if (!pID)
|
|
return false;
|
|
return *pID == tileID;
|
|
});
|
|
REQUIRE(it != children.end());
|
|
return *it;
|
|
}
|
|
|
|
const Tile&
|
|
findTile(const std::vector<Tile>& children, const OctreeTileID& tileID) {
|
|
return findTile(std::span<const Tile>(children), tileID);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST_CASE("Test tile subdivision for implicit octree loader") {
|
|
Cesium3DTilesContent::registerAllTileContentTypes();
|
|
|
|
auto pMockedAssetAccessor = std::make_shared<SimpleAssetAccessor>(
|
|
std::map<std::string, std::shared_ptr<SimpleAssetRequest>>{});
|
|
|
|
CesiumAsync::AsyncSystem asyncSystem{std::make_shared<SimpleTaskProcessor>()};
|
|
|
|
SUBCASE("Subdivide oriented bounding box") {
|
|
OrientedBoundingBox loaderBoundingVolume{glm::dvec3(0.0), glm::dmat3(20.0)};
|
|
ImplicitOctreeLoader loader{
|
|
"tileset.json",
|
|
"content/{level}.{x}.{y}.{z}.b3dm",
|
|
"subtrees/{level}.{x}.{y}.{z}.json",
|
|
5,
|
|
5,
|
|
loaderBoundingVolume};
|
|
|
|
// add subtree with all available tiles
|
|
loader.addSubtreeAvailability(
|
|
OctreeTileID{0, 0, 0, 0},
|
|
SubtreeAvailability{
|
|
ImplicitTileSubdivisionScheme::Octree,
|
|
5,
|
|
SubtreeAvailability::SubtreeConstantAvailability{true},
|
|
SubtreeAvailability::SubtreeConstantAvailability{false},
|
|
{SubtreeAvailability::SubtreeConstantAvailability{true}},
|
|
{}});
|
|
|
|
// check subdivide root tile first
|
|
Tile tile(&loader);
|
|
tile.setTileID(OctreeTileID(0, 0, 0, 0));
|
|
tile.setBoundingVolume(loaderBoundingVolume);
|
|
|
|
{
|
|
auto tileChildrenResult = loader.createTileChildren(tile);
|
|
CHECK(tileChildrenResult.state == TileLoadResultState::Success);
|
|
|
|
const auto& tileChildren = tileChildrenResult.children;
|
|
CHECK(tileChildren.size() == 8);
|
|
|
|
const auto& tile_1_0_0_0 =
|
|
findTile(tileChildren, OctreeTileID(1, 0, 0, 0));
|
|
const auto& box_1_0_0_0 =
|
|
std::get<OrientedBoundingBox>(tile_1_0_0_0.getBoundingVolume());
|
|
CHECK(box_1_0_0_0.getCenter() == glm::dvec3(-10.0, -10.0, -10.0));
|
|
CHECK(box_1_0_0_0.getHalfAxes()[0] == glm::dvec3(10.0, 0.0, 0.0));
|
|
CHECK(box_1_0_0_0.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0));
|
|
CHECK(box_1_0_0_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 10.0));
|
|
|
|
const auto& tile_1_1_0_0 =
|
|
findTile(tileChildren, OctreeTileID(1, 1, 0, 0));
|
|
const auto& box_1_1_0_0 =
|
|
std::get<OrientedBoundingBox>(tile_1_1_0_0.getBoundingVolume());
|
|
CHECK(box_1_1_0_0.getCenter() == glm::dvec3(10.0, -10.0, -10.0));
|
|
CHECK(box_1_1_0_0.getHalfAxes()[0] == glm::dvec3(10.0, 0.0, 0.0));
|
|
CHECK(box_1_1_0_0.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0));
|
|
CHECK(box_1_1_0_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 10.0));
|
|
|
|
const auto& tile_1_0_0_1 =
|
|
findTile(tileChildren, OctreeTileID(1, 0, 0, 1));
|
|
const auto& box_1_0_0_1 =
|
|
std::get<OrientedBoundingBox>(tile_1_0_0_1.getBoundingVolume());
|
|
CHECK(box_1_0_0_1.getCenter() == glm::dvec3(-10.0, -10.0, 10.0));
|
|
CHECK(box_1_0_0_1.getHalfAxes()[0] == glm::dvec3(10.0, 0.0, 0.0));
|
|
CHECK(box_1_0_0_1.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0));
|
|
CHECK(box_1_0_0_1.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 10.0));
|
|
|
|
const auto& tile_1_1_0_1 =
|
|
findTile(tileChildren, OctreeTileID(1, 1, 0, 1));
|
|
const auto& box_1_1_0_1 =
|
|
std::get<OrientedBoundingBox>(tile_1_1_0_1.getBoundingVolume());
|
|
CHECK(box_1_1_0_1.getCenter() == glm::dvec3(10.0, -10.0, 10.0));
|
|
CHECK(box_1_1_0_1.getHalfAxes()[0] == glm::dvec3(10.0, 0.0, 0.0));
|
|
CHECK(box_1_1_0_1.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0));
|
|
CHECK(box_1_1_0_1.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 10.0));
|
|
|
|
const auto& tile_1_0_1_0 =
|
|
findTile(tileChildren, OctreeTileID(1, 0, 1, 0));
|
|
const auto& box_1_0_1_0 =
|
|
std::get<OrientedBoundingBox>(tile_1_0_1_0.getBoundingVolume());
|
|
CHECK(box_1_0_1_0.getCenter() == glm::dvec3(-10.0, 10.0, -10.0));
|
|
CHECK(box_1_0_1_0.getHalfAxes()[0] == glm::dvec3(10.0, 0.0, 0.0));
|
|
CHECK(box_1_0_1_0.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0));
|
|
CHECK(box_1_0_1_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 10.0));
|
|
|
|
const auto& tile_1_1_1_0 =
|
|
findTile(tileChildren, OctreeTileID(1, 1, 1, 0));
|
|
const auto& box_1_1_1_0 =
|
|
std::get<OrientedBoundingBox>(tile_1_1_1_0.getBoundingVolume());
|
|
CHECK(box_1_1_1_0.getCenter() == glm::dvec3(10.0, 10.0, -10.0));
|
|
CHECK(box_1_1_1_0.getHalfAxes()[0] == glm::dvec3(10.0, 0.0, 0.0));
|
|
CHECK(box_1_1_1_0.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0));
|
|
CHECK(box_1_1_1_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 10.0));
|
|
|
|
const auto& tile_1_0_1_1 =
|
|
findTile(tileChildren, OctreeTileID(1, 0, 1, 1));
|
|
const auto& box_1_0_1_1 =
|
|
std::get<OrientedBoundingBox>(tile_1_0_1_1.getBoundingVolume());
|
|
CHECK(box_1_0_1_1.getCenter() == glm::dvec3(-10.0, 10.0, 10.0));
|
|
CHECK(box_1_0_1_1.getHalfAxes()[0] == glm::dvec3(10.0, 0.0, 0.0));
|
|
CHECK(box_1_0_1_1.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0));
|
|
CHECK(box_1_0_1_1.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 10.0));
|
|
|
|
const auto& tile_1_1_1_1 =
|
|
findTile(tileChildren, OctreeTileID(1, 1, 1, 1));
|
|
const auto& box_1_1_1_1 =
|
|
std::get<OrientedBoundingBox>(tile_1_1_1_1.getBoundingVolume());
|
|
CHECK(box_1_1_1_1.getCenter() == glm::dvec3(10.0, 10.0, 10.0));
|
|
CHECK(box_1_1_1_1.getHalfAxes()[0] == glm::dvec3(10.0, 0.0, 0.0));
|
|
CHECK(box_1_1_1_1.getHalfAxes()[1] == glm::dvec3(0.0, 10.0, 0.0));
|
|
CHECK(box_1_1_1_1.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 10.0));
|
|
|
|
tile.createChildTiles(std::move(tileChildrenResult.children));
|
|
}
|
|
|
|
// check subdivide one of the root children
|
|
{
|
|
const auto& tile_1_1_0_0 =
|
|
findTile(tile.getChildren(), OctreeTileID(1, 1, 0, 0));
|
|
|
|
auto tileChildrenResult = loader.createTileChildren(tile_1_1_0_0);
|
|
CHECK(tileChildrenResult.state == TileLoadResultState::Success);
|
|
|
|
const auto& tileChildren = tileChildrenResult.children;
|
|
CHECK(tileChildren.size() == 8);
|
|
|
|
const auto& tile_2_2_0_0 =
|
|
findTile(tileChildren, OctreeTileID(2, 2, 0, 0));
|
|
const auto& box_2_2_0_0 =
|
|
std::get<OrientedBoundingBox>(tile_2_2_0_0.getBoundingVolume());
|
|
CHECK(box_2_2_0_0.getCenter() == glm::dvec3(5.0, -15.0, -15.0));
|
|
CHECK(box_2_2_0_0.getHalfAxes()[0] == glm::dvec3(5.0, 0.0, 0.0));
|
|
CHECK(box_2_2_0_0.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0));
|
|
CHECK(box_2_2_0_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 5.0));
|
|
|
|
const auto& tile_2_3_0_0 =
|
|
findTile(tileChildren, OctreeTileID(2, 3, 0, 0));
|
|
const auto& box_2_3_0_0 =
|
|
std::get<OrientedBoundingBox>(tile_2_3_0_0.getBoundingVolume());
|
|
CHECK(box_2_3_0_0.getCenter() == glm::dvec3(15.0, -15.0, -15.0));
|
|
CHECK(box_2_3_0_0.getHalfAxes()[0] == glm::dvec3(5.0, 0.0, 0.0));
|
|
CHECK(box_2_3_0_0.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0));
|
|
CHECK(box_2_3_0_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 5.0));
|
|
|
|
const auto& tile_2_2_0_1 =
|
|
findTile(tileChildren, OctreeTileID(2, 2, 0, 1));
|
|
const auto& box_2_2_0_1 =
|
|
std::get<OrientedBoundingBox>(tile_2_2_0_1.getBoundingVolume());
|
|
CHECK(box_2_2_0_1.getCenter() == glm::dvec3(5.0, -15.0, -5.0));
|
|
CHECK(box_2_2_0_1.getHalfAxes()[0] == glm::dvec3(5.0, 0.0, 0.0));
|
|
CHECK(box_2_2_0_1.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0));
|
|
CHECK(box_2_2_0_1.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 5.0));
|
|
|
|
const auto& tile_2_3_0_1 =
|
|
findTile(tileChildren, OctreeTileID(2, 3, 0, 1));
|
|
const auto& box_2_3_0_1 =
|
|
std::get<OrientedBoundingBox>(tile_2_3_0_1.getBoundingVolume());
|
|
CHECK(box_2_3_0_1.getCenter() == glm::dvec3(15.0, -15.0, -5.0));
|
|
CHECK(box_2_3_0_1.getHalfAxes()[0] == glm::dvec3(5.0, 0.0, 0.0));
|
|
CHECK(box_2_3_0_1.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0));
|
|
CHECK(box_2_3_0_1.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 5.0));
|
|
|
|
const auto& tile_2_2_1_0 =
|
|
findTile(tileChildren, OctreeTileID(2, 2, 1, 0));
|
|
const auto& box_2_2_1_0 =
|
|
std::get<OrientedBoundingBox>(tile_2_2_1_0.getBoundingVolume());
|
|
CHECK(box_2_2_1_0.getCenter() == glm::dvec3(5.0, -5.0, -15.0));
|
|
CHECK(box_2_2_1_0.getHalfAxes()[0] == glm::dvec3(5.0, 0.0, 0.0));
|
|
CHECK(box_2_2_1_0.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0));
|
|
CHECK(box_2_2_1_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 5.0));
|
|
|
|
const auto& tile_2_3_1_0 =
|
|
findTile(tileChildren, OctreeTileID(2, 3, 1, 0));
|
|
const auto& box_2_3_1_0 =
|
|
std::get<OrientedBoundingBox>(tile_2_3_1_0.getBoundingVolume());
|
|
CHECK(box_2_3_1_0.getCenter() == glm::dvec3(15.0, -5.0, -15.0));
|
|
CHECK(box_2_3_1_0.getHalfAxes()[0] == glm::dvec3(5.0, 0.0, 0.0));
|
|
CHECK(box_2_3_1_0.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0));
|
|
CHECK(box_2_3_1_0.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 5.0));
|
|
|
|
const auto& tile_2_2_1_1 =
|
|
findTile(tileChildren, OctreeTileID(2, 2, 1, 1));
|
|
const auto& box_2_2_1_1 =
|
|
std::get<OrientedBoundingBox>(tile_2_2_1_1.getBoundingVolume());
|
|
CHECK(box_2_2_1_1.getCenter() == glm::dvec3(5.0, -5.0, -5.0));
|
|
CHECK(box_2_2_1_1.getHalfAxes()[0] == glm::dvec3(5.0, 0.0, 0.0));
|
|
CHECK(box_2_2_1_1.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0));
|
|
CHECK(box_2_2_1_1.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 5.0));
|
|
|
|
const auto& tile_2_3_1_1 =
|
|
findTile(tileChildren, OctreeTileID(2, 3, 1, 1));
|
|
const auto& box_2_3_1_1 =
|
|
std::get<OrientedBoundingBox>(tile_2_3_1_1.getBoundingVolume());
|
|
CHECK(box_2_3_1_1.getCenter() == glm::dvec3(15.0, -5.0, -5.0));
|
|
CHECK(box_2_3_1_1.getHalfAxes()[0] == glm::dvec3(5.0, 0.0, 0.0));
|
|
CHECK(box_2_3_1_1.getHalfAxes()[1] == glm::dvec3(0.0, 5.0, 0.0));
|
|
CHECK(box_2_3_1_1.getHalfAxes()[2] == glm::dvec3(0.0, 0.0, 5.0));
|
|
}
|
|
}
|
|
|
|
SUBCASE("Subdivide bounding region") {
|
|
BoundingRegion loaderBoundingVolume{
|
|
GlobeRectangle{
|
|
-Math::OnePi,
|
|
-Math::PiOverTwo,
|
|
Math::OnePi,
|
|
Math::PiOverTwo},
|
|
0.0,
|
|
100.0,
|
|
Ellipsoid::WGS84};
|
|
|
|
ImplicitOctreeLoader loader{
|
|
"tileset.json",
|
|
"content/{level}.{x}.{y}.{z}.b3dm",
|
|
"subtrees/{level}.{x}.{y}.{z}.json",
|
|
5,
|
|
5,
|
|
loaderBoundingVolume};
|
|
|
|
// add subtree with all available tiles
|
|
loader.addSubtreeAvailability(
|
|
OctreeTileID{0, 0, 0, 0},
|
|
SubtreeAvailability{
|
|
ImplicitTileSubdivisionScheme::Octree,
|
|
5,
|
|
SubtreeAvailability::SubtreeConstantAvailability{true},
|
|
SubtreeAvailability::SubtreeConstantAvailability{false},
|
|
{SubtreeAvailability::SubtreeConstantAvailability{true}},
|
|
{}});
|
|
|
|
// check subdivide root tile first
|
|
Tile tile(&loader);
|
|
tile.setTileID(OctreeTileID(0, 0, 0, 0));
|
|
tile.setBoundingVolume(loaderBoundingVolume);
|
|
|
|
{
|
|
auto tileChildrenResult = loader.createTileChildren(tile);
|
|
CHECK(tileChildrenResult.state == TileLoadResultState::Success);
|
|
|
|
const auto& tileChildren = tileChildrenResult.children;
|
|
CHECK(tileChildren.size() == 8);
|
|
|
|
const auto& tile_1_0_0_0 =
|
|
findTile(tileChildren, OctreeTileID(1, 0, 0, 0));
|
|
const auto& region_1_0_0_0 =
|
|
std::get<BoundingRegion>(tile_1_0_0_0.getBoundingVolume());
|
|
CHECK(region_1_0_0_0.getRectangle().getWest() == Approx(-Math::OnePi));
|
|
CHECK(
|
|
region_1_0_0_0.getRectangle().getSouth() == Approx(-Math::PiOverTwo));
|
|
CHECK(region_1_0_0_0.getRectangle().getEast() == Approx(0.0));
|
|
CHECK(region_1_0_0_0.getRectangle().getNorth() == Approx(0.0));
|
|
CHECK(region_1_0_0_0.getMinimumHeight() == Approx(0.0));
|
|
CHECK(region_1_0_0_0.getMaximumHeight() == Approx(50.0));
|
|
|
|
const auto& tile_1_1_0_0 =
|
|
findTile(tileChildren, OctreeTileID(1, 1, 0, 0));
|
|
const auto& region_1_1_0_0 =
|
|
std::get<BoundingRegion>(tile_1_1_0_0.getBoundingVolume());
|
|
CHECK(region_1_1_0_0.getRectangle().getWest() == Approx(0.0));
|
|
CHECK(
|
|
region_1_1_0_0.getRectangle().getSouth() == Approx(-Math::PiOverTwo));
|
|
CHECK(region_1_1_0_0.getRectangle().getEast() == Approx(Math::OnePi));
|
|
CHECK(region_1_1_0_0.getRectangle().getNorth() == Approx(0.0));
|
|
CHECK(region_1_1_0_0.getMinimumHeight() == Approx(0.0));
|
|
CHECK(region_1_1_0_0.getMaximumHeight() == Approx(50.0));
|
|
|
|
const auto& tile_1_0_0_1 =
|
|
findTile(tileChildren, OctreeTileID(1, 0, 0, 1));
|
|
const auto& region_1_0_0_1 =
|
|
std::get<BoundingRegion>(tile_1_0_0_1.getBoundingVolume());
|
|
CHECK(region_1_0_0_0.getRectangle().getWest() == Approx(-Math::OnePi));
|
|
CHECK(
|
|
region_1_0_0_0.getRectangle().getSouth() == Approx(-Math::PiOverTwo));
|
|
CHECK(region_1_0_0_0.getRectangle().getEast() == Approx(0.0));
|
|
CHECK(region_1_0_0_0.getRectangle().getNorth() == Approx(0.0));
|
|
CHECK(region_1_0_0_1.getMinimumHeight() == Approx(50.0));
|
|
CHECK(region_1_0_0_1.getMaximumHeight() == Approx(100.0));
|
|
|
|
const auto& tile_1_1_0_1 =
|
|
findTile(tileChildren, OctreeTileID(1, 1, 0, 1));
|
|
const auto& region_1_1_0_1 =
|
|
std::get<BoundingRegion>(tile_1_1_0_1.getBoundingVolume());
|
|
CHECK(region_1_1_0_0.getRectangle().getWest() == Approx(0.0));
|
|
CHECK(
|
|
region_1_1_0_0.getRectangle().getSouth() == Approx(-Math::PiOverTwo));
|
|
CHECK(region_1_1_0_0.getRectangle().getEast() == Approx(Math::OnePi));
|
|
CHECK(region_1_1_0_0.getRectangle().getNorth() == Approx(0.0));
|
|
CHECK(region_1_1_0_1.getMinimumHeight() == Approx(50.0));
|
|
CHECK(region_1_1_0_1.getMaximumHeight() == Approx(100.0));
|
|
|
|
const auto& tile_1_0_1_0 =
|
|
findTile(tileChildren, OctreeTileID(1, 0, 1, 0));
|
|
const auto& region_1_0_1_0 =
|
|
std::get<BoundingRegion>(tile_1_0_1_0.getBoundingVolume());
|
|
CHECK(region_1_0_1_0.getRectangle().getWest() == Approx(-Math::OnePi));
|
|
CHECK(region_1_0_1_0.getRectangle().getSouth() == Approx(0.0));
|
|
CHECK(region_1_0_1_0.getRectangle().getEast() == Approx(0.0));
|
|
CHECK(
|
|
region_1_0_1_0.getRectangle().getNorth() == Approx(Math::PiOverTwo));
|
|
CHECK(region_1_0_1_0.getMinimumHeight() == Approx(0.0));
|
|
CHECK(region_1_0_1_0.getMaximumHeight() == Approx(50.0));
|
|
|
|
const auto& tile_1_1_1_0 =
|
|
findTile(tileChildren, OctreeTileID(1, 1, 1, 0));
|
|
const auto& region_1_1_1_0 =
|
|
std::get<BoundingRegion>(tile_1_1_1_0.getBoundingVolume());
|
|
CHECK(region_1_1_1_0.getRectangle().getWest() == Approx(0.0));
|
|
CHECK(region_1_1_1_0.getRectangle().getSouth() == Approx(0.0));
|
|
CHECK(region_1_1_1_0.getRectangle().getEast() == Approx(Math::OnePi));
|
|
CHECK(
|
|
region_1_1_1_0.getRectangle().getNorth() == Approx(Math::PiOverTwo));
|
|
CHECK(region_1_1_1_0.getMinimumHeight() == Approx(0.0));
|
|
CHECK(region_1_1_1_0.getMaximumHeight() == Approx(50.0));
|
|
|
|
const auto& tile_1_0_1_1 =
|
|
findTile(tileChildren, OctreeTileID(1, 0, 1, 1));
|
|
const auto& region_1_0_1_1 =
|
|
std::get<BoundingRegion>(tile_1_0_1_1.getBoundingVolume());
|
|
CHECK(region_1_0_1_1.getRectangle().getWest() == Approx(-Math::OnePi));
|
|
CHECK(region_1_0_1_1.getRectangle().getSouth() == Approx(0.0));
|
|
CHECK(region_1_0_1_1.getRectangle().getEast() == Approx(0.0));
|
|
CHECK(
|
|
region_1_0_1_1.getRectangle().getNorth() == Approx(Math::PiOverTwo));
|
|
CHECK(region_1_0_1_1.getMinimumHeight() == Approx(50.0));
|
|
CHECK(region_1_0_1_1.getMaximumHeight() == Approx(100.0));
|
|
|
|
const auto& tile_1_1_1_1 =
|
|
findTile(tileChildren, OctreeTileID(1, 1, 1, 1));
|
|
const auto& region_1_1_1_1 =
|
|
std::get<BoundingRegion>(tile_1_1_1_1.getBoundingVolume());
|
|
CHECK(region_1_1_1_1.getRectangle().getWest() == Approx(0.0));
|
|
CHECK(region_1_1_1_1.getRectangle().getSouth() == Approx(0.0));
|
|
CHECK(region_1_1_1_1.getRectangle().getEast() == Approx(Math::OnePi));
|
|
CHECK(
|
|
region_1_1_1_1.getRectangle().getNorth() == Approx(Math::PiOverTwo));
|
|
CHECK(region_1_1_1_1.getMinimumHeight() == Approx(50.0));
|
|
CHECK(region_1_1_1_1.getMaximumHeight() == Approx(100.0));
|
|
|
|
tile.createChildTiles(std::move(tileChildrenResult.children));
|
|
}
|
|
|
|
// check subdivide one of the root children
|
|
{
|
|
auto& tile_1_1_0_0 =
|
|
findTile(tile.getChildren(), OctreeTileID(1, 1, 0, 0));
|
|
auto tileChildrenResult = loader.createTileChildren(tile_1_1_0_0);
|
|
CHECK(tileChildrenResult.state == TileLoadResultState::Success);
|
|
|
|
const auto& tileChildren = tileChildrenResult.children;
|
|
CHECK(tileChildren.size() == 8);
|
|
|
|
const auto& tile_2_2_0_0 =
|
|
findTile(tileChildren, OctreeTileID(2, 2, 0, 0));
|
|
const auto& region_2_2_0_0 =
|
|
std::get<BoundingRegion>(tile_2_2_0_0.getBoundingVolume());
|
|
CHECK(region_2_2_0_0.getRectangle().getWest() == Approx(0.0));
|
|
CHECK(
|
|
region_2_2_0_0.getRectangle().getSouth() == Approx(-Math::PiOverTwo));
|
|
CHECK(region_2_2_0_0.getRectangle().getEast() == Approx(Math::PiOverTwo));
|
|
CHECK(
|
|
region_2_2_0_0.getRectangle().getNorth() ==
|
|
Approx(-Math::OnePi / 4.0));
|
|
CHECK(region_2_2_0_0.getMinimumHeight() == Approx(0.0));
|
|
CHECK(region_2_2_0_0.getMaximumHeight() == Approx(25.0));
|
|
|
|
const auto& tile_2_3_0_0 =
|
|
findTile(tileChildren, OctreeTileID(2, 3, 0, 0));
|
|
const auto& region_2_3_0_0 =
|
|
std::get<BoundingRegion>(tile_2_3_0_0.getBoundingVolume());
|
|
CHECK(region_2_3_0_0.getRectangle().getWest() == Approx(Math::PiOverTwo));
|
|
CHECK(
|
|
region_2_3_0_0.getRectangle().getSouth() == Approx(-Math::PiOverTwo));
|
|
CHECK(region_2_3_0_0.getRectangle().getEast() == Approx(Math::OnePi));
|
|
CHECK(
|
|
region_2_3_0_0.getRectangle().getNorth() ==
|
|
Approx(-Math::OnePi / 4.0));
|
|
CHECK(region_2_3_0_0.getMinimumHeight() == Approx(0.0));
|
|
CHECK(region_2_3_0_0.getMaximumHeight() == Approx(25.0));
|
|
|
|
const auto& tile_2_2_0_1 =
|
|
findTile(tileChildren, OctreeTileID(2, 2, 0, 1));
|
|
const auto& region_2_2_0_1 =
|
|
std::get<BoundingRegion>(tile_2_2_0_1.getBoundingVolume());
|
|
CHECK(region_2_2_0_1.getRectangle().getWest() == Approx(0.0));
|
|
CHECK(
|
|
region_2_2_0_1.getRectangle().getSouth() == Approx(-Math::PiOverTwo));
|
|
CHECK(region_2_2_0_1.getRectangle().getEast() == Approx(Math::PiOverTwo));
|
|
CHECK(
|
|
region_2_2_0_1.getRectangle().getNorth() ==
|
|
Approx(-Math::OnePi / 4.0));
|
|
CHECK(region_2_2_0_1.getMinimumHeight() == Approx(25.0));
|
|
CHECK(region_2_2_0_1.getMaximumHeight() == Approx(50.0));
|
|
|
|
const auto& tile_2_3_0_1 =
|
|
findTile(tileChildren, OctreeTileID(2, 3, 0, 1));
|
|
const auto& region_2_3_0_1 =
|
|
std::get<BoundingRegion>(tile_2_3_0_1.getBoundingVolume());
|
|
CHECK(region_2_3_0_1.getRectangle().getWest() == Approx(Math::PiOverTwo));
|
|
CHECK(
|
|
region_2_3_0_1.getRectangle().getSouth() == Approx(-Math::PiOverTwo));
|
|
CHECK(region_2_3_0_1.getRectangle().getEast() == Approx(Math::OnePi));
|
|
CHECK(
|
|
region_2_3_0_1.getRectangle().getNorth() ==
|
|
Approx(-Math::OnePi / 4.0));
|
|
CHECK(region_2_3_0_1.getMinimumHeight() == Approx(25.0));
|
|
CHECK(region_2_3_0_1.getMaximumHeight() == Approx(50.0));
|
|
|
|
const auto& tile_2_2_1_0 =
|
|
findTile(tileChildren, OctreeTileID(2, 2, 1, 0));
|
|
const auto& region_2_2_1_0 =
|
|
std::get<BoundingRegion>(tile_2_2_1_0.getBoundingVolume());
|
|
CHECK(region_2_2_1_0.getRectangle().getWest() == Approx(0.0));
|
|
CHECK(
|
|
region_2_2_1_0.getRectangle().getSouth() ==
|
|
Approx(-Math::OnePi / 4.0));
|
|
CHECK(
|
|
region_2_2_1_0.getRectangle().getEast() == Approx(Math::OnePi / 2.0));
|
|
CHECK(region_2_2_1_0.getRectangle().getNorth() == Approx(0.0));
|
|
CHECK(region_2_2_1_0.getMinimumHeight() == Approx(0.0));
|
|
CHECK(region_2_2_1_0.getMaximumHeight() == Approx(25.0));
|
|
|
|
const auto& tile_2_3_1_0 =
|
|
findTile(tileChildren, OctreeTileID(2, 3, 1, 0));
|
|
const auto& region_2_3_1_0 =
|
|
std::get<BoundingRegion>(tile_2_3_1_0.getBoundingVolume());
|
|
CHECK(region_2_3_1_0.getRectangle().getWest() == Approx(Math::PiOverTwo));
|
|
CHECK(
|
|
region_2_3_1_0.getRectangle().getSouth() ==
|
|
Approx(-Math::OnePi / 4.0));
|
|
CHECK(region_2_3_1_0.getRectangle().getEast() == Approx(Math::OnePi));
|
|
CHECK(region_2_3_1_0.getRectangle().getNorth() == Approx(0.0));
|
|
CHECK(region_2_3_1_0.getMinimumHeight() == Approx(0.0));
|
|
CHECK(region_2_3_1_0.getMaximumHeight() == Approx(25.0));
|
|
|
|
const auto& tile_2_2_1_1 =
|
|
findTile(tileChildren, OctreeTileID(2, 2, 1, 1));
|
|
const auto& region_2_2_1_1 =
|
|
std::get<BoundingRegion>(tile_2_2_1_1.getBoundingVolume());
|
|
CHECK(region_2_2_1_1.getRectangle().getWest() == Approx(0.0));
|
|
CHECK(
|
|
region_2_2_1_1.getRectangle().getSouth() ==
|
|
Approx(-Math::OnePi / 4.0));
|
|
CHECK(
|
|
region_2_2_1_1.getRectangle().getEast() == Approx(Math::OnePi / 2.0));
|
|
CHECK(region_2_2_1_1.getRectangle().getNorth() == Approx(0.0));
|
|
CHECK(region_2_2_1_1.getMinimumHeight() == Approx(25.0));
|
|
CHECK(region_2_2_1_1.getMaximumHeight() == Approx(50.0));
|
|
|
|
const auto& tile_2_3_1_1 =
|
|
findTile(tileChildren, OctreeTileID(2, 3, 1, 1));
|
|
const auto& region_2_3_1_1 =
|
|
std::get<BoundingRegion>(tile_2_3_1_1.getBoundingVolume());
|
|
CHECK(region_2_3_1_1.getRectangle().getWest() == Approx(Math::PiOverTwo));
|
|
CHECK(
|
|
region_2_3_1_1.getRectangle().getSouth() ==
|
|
Approx(-Math::OnePi / 4.0));
|
|
CHECK(region_2_3_1_1.getRectangle().getEast() == Approx(Math::OnePi));
|
|
CHECK(region_2_3_1_1.getRectangle().getNorth() == Approx(0.0));
|
|
CHECK(region_2_3_1_1.getMinimumHeight() == Approx(25.0));
|
|
CHECK(region_2_3_1_1.getMaximumHeight() == Approx(50.0));
|
|
}
|
|
}
|
|
|
|
SUBCASE("Child tiles without content initially have empty content") {
|
|
OrientedBoundingBox loaderBoundingVolume{glm::dvec3(0.0), glm::dmat3(20.0)};
|
|
ImplicitOctreeLoader loader{
|
|
"tileset.json",
|
|
"content/{level}.{x}.{y}.{z}.b3dm",
|
|
"subtrees/{level}.{x}.{y}.{z}.json",
|
|
5,
|
|
5,
|
|
loaderBoundingVolume};
|
|
|
|
// add subtree with all tiles, but no content available.
|
|
std::optional<SubtreeAvailability> maybeAvailability =
|
|
SubtreeAvailability::createEmpty(
|
|
ImplicitTileSubdivisionScheme::Octree,
|
|
5,
|
|
true);
|
|
REQUIRE(maybeAvailability);
|
|
SubtreeAvailability availability = std::move(*maybeAvailability);
|
|
|
|
// Mark content for a tile available
|
|
availability.setContentAvailable(
|
|
OctreeTileID{0, 0, 0, 0},
|
|
OctreeTileID{1, 1, 1, 1},
|
|
0,
|
|
true);
|
|
loader.addSubtreeAvailability(
|
|
OctreeTileID{0, 0, 0, 0},
|
|
std::move(availability));
|
|
|
|
// check subdivide root tile first
|
|
Tile tile(&loader);
|
|
tile.setTileID(OctreeTileID(0, 0, 0, 0));
|
|
tile.setBoundingVolume(loaderBoundingVolume);
|
|
|
|
auto tileChildrenResult = loader.createTileChildren(tile);
|
|
CHECK(tileChildrenResult.state == TileLoadResultState::Success);
|
|
|
|
REQUIRE(tileChildrenResult.children.size() == 8);
|
|
|
|
size_t childrenWithReferencingContent = 0;
|
|
|
|
for (Tile& child : tileChildrenResult.children) {
|
|
if (child.hasReferencingContent()) {
|
|
++childrenWithReferencingContent;
|
|
|
|
REQUIRE(child.getReferenceCount() == 1);
|
|
child.getContent().setContentKind(TileUnknownContent());
|
|
|
|
// In a perfect world the above might release the reference, but it
|
|
// won't. Release it manually so we won't assert when the Tile goes out
|
|
// of scope.
|
|
REQUIRE(child.getReferenceCount() == 1);
|
|
child.releaseReference();
|
|
} else {
|
|
REQUIRE(child.getReferenceCount() == 0);
|
|
}
|
|
}
|
|
|
|
// The seven tiles without content should have referencing EmptyContent.
|
|
// The eighth should not yet have any referencing content because it has not
|
|
// been loaded.
|
|
CHECK(childrenWithReferencingContent == 7);
|
|
}
|
|
}
|