2393 lines
77 KiB
C++
2393 lines
77 KiB
C++
#include <CesiumGeometry/QuadtreeTileID.h>
|
|
#include <CesiumGeospatial/Cartographic.h>
|
|
#include <CesiumGeospatial/Ellipsoid.h>
|
|
#include <CesiumGltf/Accessor.h>
|
|
#include <CesiumGltf/AccessorView.h>
|
|
#include <CesiumGltf/AccessorWriter.h>
|
|
#include <CesiumGltf/Buffer.h>
|
|
#include <CesiumGltf/BufferView.h>
|
|
#include <CesiumGltf/Mesh.h>
|
|
#include <CesiumGltf/MeshPrimitive.h>
|
|
#include <CesiumGltf/Node.h>
|
|
#include <CesiumGltfContent/SkirtMeshMetadata.h>
|
|
#include <CesiumGltfReader/GltfReader.h>
|
|
#include <CesiumNativeTests/readFile.h>
|
|
#include <CesiumRasterOverlays/RasterOverlayUtilities.h>
|
|
#include <CesiumUtility/Math.h>
|
|
|
|
#include <doctest/doctest.h>
|
|
#include <glm/ext/vector_bool3.hpp>
|
|
#include <glm/ext/vector_double3.hpp>
|
|
#include <glm/ext/vector_float2.hpp>
|
|
#include <glm/ext/vector_float3.hpp>
|
|
#include <glm/gtc/epsilon.hpp>
|
|
#include <glm/trigonometric.hpp>
|
|
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <random>
|
|
#include <vector>
|
|
|
|
using namespace CesiumAsync;
|
|
using namespace CesiumUtility;
|
|
using namespace CesiumGeospatial;
|
|
using namespace CesiumGltf;
|
|
using namespace CesiumGltfContent;
|
|
using namespace CesiumRasterOverlays;
|
|
using namespace CesiumGltfReader;
|
|
|
|
static void checkSkirt(
|
|
const Ellipsoid& ellipsoid,
|
|
const glm::vec3& edgeUpsampledPosition,
|
|
const glm::vec3& skirtUpsampledPosition,
|
|
glm::dvec3 center,
|
|
double skirtHeight) {
|
|
glm::dvec3 edgePosition =
|
|
static_cast<glm::dvec3>(edgeUpsampledPosition) + center;
|
|
glm::dvec3 geodeticNormal = ellipsoid.geodeticSurfaceNormal(edgePosition);
|
|
glm::dvec3 expectedPosition = edgePosition - skirtHeight * geodeticNormal;
|
|
|
|
glm::dvec3 skirtPosition =
|
|
static_cast<glm::dvec3>(skirtUpsampledPosition) + center;
|
|
REQUIRE(
|
|
Math::equalsEpsilon(expectedPosition.x, skirtPosition.x, Math::Epsilon7));
|
|
REQUIRE(
|
|
Math::equalsEpsilon(expectedPosition.y, skirtPosition.y, Math::Epsilon7));
|
|
REQUIRE(
|
|
Math::equalsEpsilon(expectedPosition.z, skirtPosition.z, Math::Epsilon7));
|
|
}
|
|
|
|
TEST_CASE("upsampleGltfForRasterOverlay with UNSIGNED_SHORT indices") {
|
|
const Ellipsoid& ellipsoid = CesiumGeospatial::Ellipsoid::WGS84;
|
|
Cartographic bottomLeftCart{glm::radians(110.0), glm::radians(32.0), 0.0};
|
|
Cartographic topLeftCart{
|
|
bottomLeftCart.longitude,
|
|
bottomLeftCart.latitude + glm::radians(1.0),
|
|
0.0};
|
|
Cartographic topRightCart{
|
|
bottomLeftCart.longitude + glm::radians(1.0),
|
|
bottomLeftCart.latitude + glm::radians(1.0),
|
|
0.0};
|
|
Cartographic bottomRightCart{
|
|
bottomLeftCart.longitude + glm::radians(1.0),
|
|
bottomLeftCart.latitude,
|
|
0.0};
|
|
Cartographic centerCart{
|
|
(bottomLeftCart.longitude + topRightCart.longitude) / 2.0,
|
|
(bottomLeftCart.latitude + topRightCart.latitude) / 2.0,
|
|
0.0};
|
|
glm::dvec3 center = ellipsoid.cartographicToCartesian(centerCart);
|
|
std::vector<glm::vec3> positions{
|
|
static_cast<glm::vec3>(
|
|
ellipsoid.cartographicToCartesian(bottomLeftCart) - center),
|
|
static_cast<glm::vec3>(
|
|
ellipsoid.cartographicToCartesian(topLeftCart) - center),
|
|
static_cast<glm::vec3>(
|
|
ellipsoid.cartographicToCartesian(topRightCart) - center),
|
|
static_cast<glm::vec3>(
|
|
ellipsoid.cartographicToCartesian(bottomRightCart) - center),
|
|
};
|
|
std::vector<glm::vec2> uvs{
|
|
glm::vec2{0.0, 0.0},
|
|
glm::vec2{0.0, 1.0},
|
|
glm::vec2{1.0, 0.0},
|
|
glm::vec2{1.0, 1.0}};
|
|
std::vector<uint16_t> indices{0, 2, 1, 1, 2, 3};
|
|
uint32_t positionsBufferSize =
|
|
static_cast<uint32_t>(positions.size() * sizeof(glm::vec3));
|
|
uint32_t uvsBufferSize =
|
|
static_cast<uint32_t>(uvs.size() * sizeof(glm::vec2));
|
|
uint32_t indicesBufferSize =
|
|
static_cast<uint32_t>(indices.size() * sizeof(uint16_t));
|
|
|
|
Model model;
|
|
|
|
// create buffer
|
|
model.buffers.emplace_back();
|
|
Buffer& buffer = model.buffers.back();
|
|
buffer.cesium.data.resize(
|
|
positionsBufferSize + uvsBufferSize + indicesBufferSize);
|
|
std::memcpy(buffer.cesium.data.data(), positions.data(), positionsBufferSize);
|
|
std::memcpy(
|
|
buffer.cesium.data.data() + positionsBufferSize,
|
|
uvs.data(),
|
|
uvsBufferSize);
|
|
std::memcpy(
|
|
buffer.cesium.data.data() + positionsBufferSize + uvsBufferSize,
|
|
indices.data(),
|
|
indicesBufferSize);
|
|
|
|
// create position
|
|
model.bufferViews.emplace_back();
|
|
BufferView& positionBufferView = model.bufferViews.emplace_back();
|
|
positionBufferView.buffer = static_cast<int>(model.buffers.size() - 1);
|
|
positionBufferView.byteOffset = 0;
|
|
positionBufferView.byteLength = positionsBufferSize;
|
|
|
|
model.accessors.emplace_back();
|
|
Accessor& positionAccessor = model.accessors.back();
|
|
positionAccessor.bufferView =
|
|
static_cast<int32_t>(model.bufferViews.size() - 1);
|
|
positionAccessor.byteOffset = 0;
|
|
positionAccessor.count = static_cast<int64_t>(positions.size());
|
|
positionAccessor.componentType = Accessor::ComponentType::FLOAT;
|
|
positionAccessor.type = Accessor::Type::VEC3;
|
|
|
|
int32_t positionAccessorIdx =
|
|
static_cast<int32_t>(model.accessors.size() - 1);
|
|
|
|
// create uv
|
|
model.bufferViews.emplace_back();
|
|
BufferView& uvBufferView = model.bufferViews.emplace_back();
|
|
uvBufferView.buffer = static_cast<int32_t>(model.buffers.size() - 1);
|
|
uvBufferView.byteOffset = positionsBufferSize;
|
|
uvBufferView.byteLength = uvsBufferSize;
|
|
|
|
model.accessors.emplace_back();
|
|
Accessor& uvAccessor = model.accessors.back();
|
|
uvAccessor.bufferView = static_cast<int32_t>(model.bufferViews.size() - 1);
|
|
uvAccessor.byteOffset = 0;
|
|
uvAccessor.count = static_cast<int64_t>(uvs.size());
|
|
uvAccessor.componentType = Accessor::ComponentType::FLOAT;
|
|
uvAccessor.type = Accessor::Type::VEC2;
|
|
|
|
int32_t uvAccessorIdx = static_cast<int32_t>(model.accessors.size() - 1);
|
|
|
|
// create indices
|
|
model.bufferViews.emplace_back();
|
|
BufferView& indicesBufferView = model.bufferViews.emplace_back();
|
|
indicesBufferView.buffer = static_cast<int>(model.buffers.size() - 1);
|
|
indicesBufferView.byteOffset = positionsBufferSize + uvsBufferSize;
|
|
indicesBufferView.byteLength = indicesBufferSize;
|
|
|
|
model.accessors.emplace_back();
|
|
Accessor& indicesAccessor = model.accessors.back();
|
|
indicesAccessor.bufferView = static_cast<int>(model.bufferViews.size() - 1);
|
|
indicesAccessor.byteOffset = 0;
|
|
indicesAccessor.count = static_cast<int64_t>(indices.size());
|
|
indicesAccessor.componentType = Accessor::ComponentType::UNSIGNED_SHORT;
|
|
indicesAccessor.type = Accessor::Type::SCALAR;
|
|
|
|
int indicesAccessorIdx = static_cast<int>(model.accessors.size() - 1);
|
|
|
|
model.meshes.emplace_back();
|
|
Mesh& mesh = model.meshes.back();
|
|
mesh.primitives.emplace_back();
|
|
|
|
MeshPrimitive& primitive = mesh.primitives.back();
|
|
primitive.mode = MeshPrimitive::Mode::TRIANGLES;
|
|
primitive.attributes["_CESIUMOVERLAY_0"] = uvAccessorIdx;
|
|
primitive.attributes["POSITION"] = positionAccessorIdx;
|
|
primitive.indices = indicesAccessorIdx;
|
|
|
|
// create node and update bounding volume
|
|
model.nodes.emplace_back();
|
|
Node& node = model.nodes[0];
|
|
node.mesh = static_cast<int>(model.meshes.size() - 1);
|
|
node.matrix = {
|
|
1.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
-1.0,
|
|
0.0,
|
|
0.0,
|
|
1.0,
|
|
0.0,
|
|
0.0,
|
|
center.x,
|
|
center.z,
|
|
-center.y,
|
|
1.0};
|
|
|
|
CesiumGeometry::UpsampledQuadtreeNode lowerLeft{
|
|
CesiumGeometry::QuadtreeTileID(1, 0, 0)};
|
|
CesiumGeometry::UpsampledQuadtreeNode upperLeft{
|
|
CesiumGeometry::QuadtreeTileID(1, 0, 1)};
|
|
CesiumGeometry::UpsampledQuadtreeNode lowerRight{
|
|
CesiumGeometry::QuadtreeTileID(1, 1, 0)};
|
|
CesiumGeometry::UpsampledQuadtreeNode upperRight{
|
|
CesiumGeometry::QuadtreeTileID(1, 1, 1)};
|
|
|
|
SUBCASE("Upsample bottom left child") {
|
|
Model upsampledModel =
|
|
*RasterOverlayUtilities::upsampleGltfForRasterOverlays(
|
|
model,
|
|
lowerLeft,
|
|
false);
|
|
|
|
REQUIRE(upsampledModel.meshes.size() == 1);
|
|
const Mesh& upsampledMesh = upsampledModel.meshes.back();
|
|
|
|
REQUIRE(upsampledMesh.primitives.size() == 1);
|
|
const MeshPrimitive& upsampledPrimitive = upsampledMesh.primitives.back();
|
|
|
|
REQUIRE(upsampledPrimitive.indices >= 0);
|
|
REQUIRE(
|
|
upsampledPrimitive.attributes.find("POSITION") !=
|
|
upsampledPrimitive.attributes.end());
|
|
AccessorView<glm::vec3> upsampledPosition(
|
|
upsampledModel,
|
|
upsampledPrimitive.attributes.at("POSITION"));
|
|
AccessorView<uint32_t> upsampledIndices(
|
|
upsampledModel,
|
|
upsampledPrimitive.indices);
|
|
|
|
glm::vec3 p0 = upsampledPosition[0];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p0,
|
|
positions[0],
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p1 = upsampledPosition[1];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p1,
|
|
(positions[0] + positions[2]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p2 = upsampledPosition[2];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p2,
|
|
(upsampledPosition[1] + positions[1]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p3 = upsampledPosition[3];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p3,
|
|
(positions[0] + positions[1]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p4 = upsampledPosition[4];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p4,
|
|
(positions[0] + positions[2]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p5 = upsampledPosition[5];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p5,
|
|
(positions[1] + positions[2]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p6 = upsampledPosition[6];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p6,
|
|
(upsampledPosition[4] + positions[1]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
}
|
|
|
|
SUBCASE("Upsample bottom left child with inverted texture coordinates") {
|
|
// Invert the V coordinate
|
|
AccessorWriter<glm::vec2> uvWriter(model, 1);
|
|
for (int64_t i = 0; i < uvWriter.size(); ++i) {
|
|
uvWriter[i].y = 1.0f - uvWriter[i].y;
|
|
}
|
|
|
|
Model upsampledModel =
|
|
*RasterOverlayUtilities::upsampleGltfForRasterOverlays(
|
|
model,
|
|
lowerLeft,
|
|
true);
|
|
|
|
REQUIRE(upsampledModel.meshes.size() == 1);
|
|
const Mesh& upsampledMesh = upsampledModel.meshes.back();
|
|
|
|
REQUIRE(upsampledMesh.primitives.size() == 1);
|
|
const MeshPrimitive& upsampledPrimitive = upsampledMesh.primitives.back();
|
|
|
|
REQUIRE(upsampledPrimitive.indices >= 0);
|
|
REQUIRE(
|
|
upsampledPrimitive.attributes.find("POSITION") !=
|
|
upsampledPrimitive.attributes.end());
|
|
AccessorView<glm::vec3> upsampledPosition(
|
|
upsampledModel,
|
|
upsampledPrimitive.attributes.at("POSITION"));
|
|
AccessorView<uint32_t> upsampledIndices(
|
|
upsampledModel,
|
|
upsampledPrimitive.indices);
|
|
|
|
glm::vec3 p0 = upsampledPosition[0];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p0,
|
|
positions[0],
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p1 = upsampledPosition[1];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p1,
|
|
(positions[0] + positions[2]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p2 = upsampledPosition[2];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p2,
|
|
(upsampledPosition[1] + positions[1]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p3 = upsampledPosition[3];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p3,
|
|
(positions[0] + positions[1]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p4 = upsampledPosition[4];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p4,
|
|
(positions[0] + positions[2]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p5 = upsampledPosition[5];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p5,
|
|
(positions[1] + positions[2]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p6 = upsampledPosition[6];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p6,
|
|
(upsampledPosition[4] + positions[1]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
}
|
|
|
|
SUBCASE("Upsample upper left child") {
|
|
Model upsampledModel =
|
|
*RasterOverlayUtilities::upsampleGltfForRasterOverlays(
|
|
model,
|
|
upperLeft,
|
|
false);
|
|
|
|
REQUIRE(upsampledModel.meshes.size() == 1);
|
|
const Mesh& upsampledMesh = upsampledModel.meshes.back();
|
|
|
|
REQUIRE(upsampledMesh.primitives.size() == 1);
|
|
const MeshPrimitive& upsampledPrimitive = upsampledMesh.primitives.back();
|
|
|
|
REQUIRE(upsampledPrimitive.indices >= 0);
|
|
REQUIRE(
|
|
upsampledPrimitive.attributes.find("POSITION") !=
|
|
upsampledPrimitive.attributes.end());
|
|
AccessorView<glm::vec3> upsampledPosition(
|
|
upsampledModel,
|
|
upsampledPrimitive.attributes.at("POSITION"));
|
|
AccessorView<uint32_t> upsampledIndices(
|
|
upsampledModel,
|
|
upsampledPrimitive.indices);
|
|
|
|
glm::vec3 p0 = upsampledPosition[0];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p0,
|
|
positions[1],
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p1 = upsampledPosition[1];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p1,
|
|
(positions[0] + positions[1]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p2 = upsampledPosition[2];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p2,
|
|
(positions[1] + 0.5f * (positions[0] + positions[2])) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p3 = upsampledPosition[3];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p3,
|
|
(positions[1] + positions[2]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p4 = upsampledPosition[4];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p4,
|
|
p2,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p5 = upsampledPosition[5];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p5,
|
|
(positions[1] + positions[2]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p6 = upsampledPosition[6];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p6,
|
|
(positions[1] + positions[3]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
}
|
|
|
|
SUBCASE("Upsample upper right child") {
|
|
Model upsampledModel =
|
|
*RasterOverlayUtilities::upsampleGltfForRasterOverlays(
|
|
model,
|
|
upperRight,
|
|
false);
|
|
|
|
REQUIRE(upsampledModel.meshes.size() == 1);
|
|
const Mesh& upsampledMesh = upsampledModel.meshes.back();
|
|
|
|
REQUIRE(upsampledMesh.primitives.size() == 1);
|
|
const MeshPrimitive& upsampledPrimitive = upsampledMesh.primitives.back();
|
|
|
|
REQUIRE(upsampledPrimitive.indices >= 0);
|
|
REQUIRE(
|
|
upsampledPrimitive.attributes.find("POSITION") !=
|
|
upsampledPrimitive.attributes.end());
|
|
AccessorView<glm::vec3> upsampledPosition(
|
|
upsampledModel,
|
|
upsampledPrimitive.attributes.at("POSITION"));
|
|
AccessorView<uint32_t> upsampledIndices(
|
|
upsampledModel,
|
|
upsampledPrimitive.indices);
|
|
|
|
glm::vec3 p0 = upsampledPosition[0];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p0,
|
|
positions[3],
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p1 = upsampledPosition[1];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p1,
|
|
(positions[1] + positions[3]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p2 = upsampledPosition[2];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p2,
|
|
(positions[2] + 0.5f * (positions[1] + positions[3])) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p3 = upsampledPosition[3];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p3,
|
|
(positions[3] + positions[2]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p4 = upsampledPosition[4];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p4,
|
|
(positions[1] + positions[3]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p5 = upsampledPosition[5];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p5,
|
|
(positions[1] + positions[2]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p6 = upsampledPosition[6];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p6,
|
|
p2,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
}
|
|
|
|
SUBCASE("Upsample bottom right child") {
|
|
Model upsampledModel =
|
|
*RasterOverlayUtilities::upsampleGltfForRasterOverlays(
|
|
model,
|
|
lowerRight,
|
|
false);
|
|
|
|
REQUIRE(upsampledModel.meshes.size() == 1);
|
|
const Mesh& upsampledMesh = upsampledModel.meshes.back();
|
|
|
|
REQUIRE(upsampledMesh.primitives.size() == 1);
|
|
const MeshPrimitive& upsampledPrimitive = upsampledMesh.primitives.back();
|
|
|
|
REQUIRE(upsampledPrimitive.indices >= 0);
|
|
REQUIRE(
|
|
upsampledPrimitive.attributes.find("POSITION") !=
|
|
upsampledPrimitive.attributes.end());
|
|
AccessorView<glm::vec3> upsampledPosition(
|
|
upsampledModel,
|
|
upsampledPrimitive.attributes.at("POSITION"));
|
|
AccessorView<uint32_t> upsampledIndices(
|
|
upsampledModel,
|
|
upsampledPrimitive.indices);
|
|
|
|
glm::vec3 p0 = upsampledPosition[0];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p0,
|
|
positions[2],
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p1 = upsampledPosition[1];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p1,
|
|
(positions[1] + positions[2]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p2 = upsampledPosition[2];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p2,
|
|
(positions[0] + positions[2]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p3 = upsampledPosition[3];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p3,
|
|
(positions[2] + positions[3]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p4 = upsampledPosition[4];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p4,
|
|
(positions[2] + (positions[1] + positions[3]) * 0.5f) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p5 = upsampledPosition[5];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p5,
|
|
(positions[1] + positions[2]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p6 = upsampledPosition[6];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p6,
|
|
p4,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
}
|
|
|
|
SUBCASE("Check skirt") {
|
|
// add skirts info to primitive extra in case we need to upsample from it
|
|
double skirtHeight = 12.0;
|
|
SkirtMeshMetadata skirtMeshMetadata;
|
|
skirtMeshMetadata.noSkirtIndicesBegin = 0;
|
|
skirtMeshMetadata.noSkirtIndicesCount =
|
|
static_cast<uint32_t>(indices.size());
|
|
skirtMeshMetadata.meshCenter = center;
|
|
skirtMeshMetadata.skirtWestHeight = skirtHeight;
|
|
skirtMeshMetadata.skirtSouthHeight = skirtHeight;
|
|
skirtMeshMetadata.skirtEastHeight = skirtHeight;
|
|
skirtMeshMetadata.skirtNorthHeight = skirtHeight;
|
|
|
|
primitive.extras = SkirtMeshMetadata::createGltfExtras(skirtMeshMetadata);
|
|
|
|
SUBCASE("Check bottom left skirt") {
|
|
Model upsampledModel =
|
|
*RasterOverlayUtilities::upsampleGltfForRasterOverlays(
|
|
model,
|
|
lowerLeft,
|
|
false);
|
|
|
|
REQUIRE(upsampledModel.meshes.size() == 1);
|
|
const Mesh& upsampledMesh = upsampledModel.meshes.back();
|
|
|
|
REQUIRE(upsampledMesh.primitives.size() == 1);
|
|
const MeshPrimitive& upsampledPrimitive = upsampledMesh.primitives.back();
|
|
|
|
REQUIRE(upsampledPrimitive.indices >= 0);
|
|
REQUIRE(
|
|
upsampledPrimitive.attributes.find("POSITION") !=
|
|
upsampledPrimitive.attributes.end());
|
|
AccessorView<glm::vec3> upsampledPosition(
|
|
upsampledModel,
|
|
upsampledPrimitive.attributes.at("POSITION"));
|
|
AccessorView<uint32_t> upsampledIndices(
|
|
upsampledModel,
|
|
upsampledPrimitive.indices);
|
|
|
|
// check west edge
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[0],
|
|
upsampledPosition[7],
|
|
center,
|
|
skirtHeight);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[3],
|
|
upsampledPosition[8],
|
|
center,
|
|
skirtHeight);
|
|
|
|
// check south edge
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[1],
|
|
upsampledPosition[9],
|
|
center,
|
|
skirtHeight);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[4],
|
|
upsampledPosition[10],
|
|
center,
|
|
skirtHeight);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[0],
|
|
upsampledPosition[11],
|
|
center,
|
|
skirtHeight);
|
|
|
|
// check east edge
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[5],
|
|
upsampledPosition[12],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[1],
|
|
upsampledPosition[13],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[4],
|
|
upsampledPosition[14],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
|
|
// check north edge
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[3],
|
|
upsampledPosition[15],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[2],
|
|
upsampledPosition[16],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[6],
|
|
upsampledPosition[17],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[5],
|
|
upsampledPosition[18],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
}
|
|
|
|
SUBCASE("Check upper left skirt") {
|
|
Model upsampledModel =
|
|
*RasterOverlayUtilities::upsampleGltfForRasterOverlays(
|
|
model,
|
|
upperLeft,
|
|
false);
|
|
|
|
REQUIRE(upsampledModel.meshes.size() == 1);
|
|
const Mesh& upsampledMesh = upsampledModel.meshes.back();
|
|
|
|
REQUIRE(upsampledMesh.primitives.size() == 1);
|
|
const MeshPrimitive& upsampledPrimitive = upsampledMesh.primitives.back();
|
|
|
|
REQUIRE(upsampledPrimitive.indices >= 0);
|
|
REQUIRE(
|
|
upsampledPrimitive.attributes.find("POSITION") !=
|
|
upsampledPrimitive.attributes.end());
|
|
AccessorView<glm::vec3> upsampledPosition(
|
|
upsampledModel,
|
|
upsampledPrimitive.attributes.at("POSITION"));
|
|
AccessorView<uint32_t> upsampledIndices(
|
|
upsampledModel,
|
|
upsampledPrimitive.indices);
|
|
|
|
// check west edge
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[1],
|
|
upsampledPosition[7],
|
|
center,
|
|
skirtHeight);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[0],
|
|
upsampledPosition[8],
|
|
center,
|
|
skirtHeight);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[0],
|
|
upsampledPosition[9],
|
|
center,
|
|
skirtHeight);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[0],
|
|
upsampledPosition[10],
|
|
center,
|
|
skirtHeight);
|
|
|
|
// check south edge
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[3],
|
|
upsampledPosition[11],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[5],
|
|
upsampledPosition[12],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[2],
|
|
upsampledPosition[13],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[4],
|
|
upsampledPosition[14],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[1],
|
|
upsampledPosition[15],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
|
|
// check east edge
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[6],
|
|
upsampledPosition[16],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[3],
|
|
upsampledPosition[17],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[5],
|
|
upsampledPosition[18],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
|
|
// check north edge
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[0],
|
|
upsampledPosition[19],
|
|
center,
|
|
skirtHeight);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[0],
|
|
upsampledPosition[20],
|
|
center,
|
|
skirtHeight);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[0],
|
|
upsampledPosition[21],
|
|
center,
|
|
skirtHeight);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[6],
|
|
upsampledPosition[22],
|
|
center,
|
|
skirtHeight);
|
|
}
|
|
|
|
SUBCASE("Check upper right skirt") {
|
|
Model upsampledModel =
|
|
*RasterOverlayUtilities::upsampleGltfForRasterOverlays(
|
|
model,
|
|
upperRight,
|
|
false);
|
|
|
|
REQUIRE(upsampledModel.meshes.size() == 1);
|
|
const Mesh& upsampledMesh = upsampledModel.meshes.back();
|
|
|
|
REQUIRE(upsampledMesh.primitives.size() == 1);
|
|
const MeshPrimitive& upsampledPrimitive = upsampledMesh.primitives.back();
|
|
|
|
REQUIRE(upsampledPrimitive.indices >= 0);
|
|
REQUIRE(
|
|
upsampledPrimitive.attributes.find("POSITION") !=
|
|
upsampledPrimitive.attributes.end());
|
|
AccessorView<glm::vec3> upsampledPosition(
|
|
upsampledModel,
|
|
upsampledPrimitive.attributes.at("POSITION"));
|
|
AccessorView<uint32_t> upsampledIndices(
|
|
upsampledModel,
|
|
upsampledPrimitive.indices);
|
|
|
|
// check west edge
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[5],
|
|
upsampledPosition[7],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[1],
|
|
upsampledPosition[8],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[4],
|
|
upsampledPosition[9],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
|
|
// check south edge
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[3],
|
|
upsampledPosition[10],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[2],
|
|
upsampledPosition[11],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[6],
|
|
upsampledPosition[12],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[5],
|
|
upsampledPosition[13],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
|
|
// check east edge
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[0],
|
|
upsampledPosition[14],
|
|
center,
|
|
skirtHeight);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[3],
|
|
upsampledPosition[15],
|
|
center,
|
|
skirtHeight);
|
|
|
|
// check north edge
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[1],
|
|
upsampledPosition[16],
|
|
center,
|
|
skirtHeight);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[4],
|
|
upsampledPosition[17],
|
|
center,
|
|
skirtHeight);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[0],
|
|
upsampledPosition[18],
|
|
center,
|
|
skirtHeight);
|
|
}
|
|
|
|
SUBCASE("Check bottom right skirt") {
|
|
Model upsampledModel =
|
|
*RasterOverlayUtilities::upsampleGltfForRasterOverlays(
|
|
model,
|
|
lowerRight,
|
|
false);
|
|
|
|
REQUIRE(upsampledModel.meshes.size() == 1);
|
|
const Mesh& upsampledMesh = upsampledModel.meshes.back();
|
|
|
|
REQUIRE(upsampledMesh.primitives.size() == 1);
|
|
const MeshPrimitive& upsampledPrimitive = upsampledMesh.primitives.back();
|
|
|
|
REQUIRE(upsampledPrimitive.indices >= 0);
|
|
REQUIRE(
|
|
upsampledPrimitive.attributes.find("POSITION") !=
|
|
upsampledPrimitive.attributes.end());
|
|
AccessorView<glm::vec3> upsampledPosition(
|
|
upsampledModel,
|
|
upsampledPrimitive.attributes.at("POSITION"));
|
|
AccessorView<uint32_t> upsampledIndices(
|
|
upsampledModel,
|
|
upsampledPrimitive.indices);
|
|
|
|
// check west edge
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[2],
|
|
upsampledPosition[7],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[1],
|
|
upsampledPosition[8],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[5],
|
|
upsampledPosition[9],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
|
|
// check south edge
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[0],
|
|
upsampledPosition[10],
|
|
center,
|
|
skirtHeight);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[0],
|
|
upsampledPosition[11],
|
|
center,
|
|
skirtHeight);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[0],
|
|
upsampledPosition[12],
|
|
center,
|
|
skirtHeight);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[2],
|
|
upsampledPosition[13],
|
|
center,
|
|
skirtHeight);
|
|
|
|
// check east edge
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[3],
|
|
upsampledPosition[14],
|
|
center,
|
|
skirtHeight);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[0],
|
|
upsampledPosition[15],
|
|
center,
|
|
skirtHeight);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[0],
|
|
upsampledPosition[16],
|
|
center,
|
|
skirtHeight);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[0],
|
|
upsampledPosition[17],
|
|
center,
|
|
skirtHeight);
|
|
|
|
// check north edge
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[1],
|
|
upsampledPosition[18],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[5],
|
|
upsampledPosition[19],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[4],
|
|
upsampledPosition[20],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[6],
|
|
upsampledPosition[21],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
checkSkirt(
|
|
ellipsoid,
|
|
upsampledPosition[3],
|
|
upsampledPosition[22],
|
|
center,
|
|
skirtHeight * 0.5);
|
|
}
|
|
}
|
|
|
|
SUBCASE("Check water mask properties come through on their own") {
|
|
primitive.extras["OnlyWater"] = false;
|
|
primitive.extras["OnlyLand"] = false;
|
|
primitive.extras["WaterMaskTex"] = 1;
|
|
primitive.extras["WaterMaskTranslationX"] = 0.0;
|
|
primitive.extras["WaterMaskTranslationY"] = 0.0;
|
|
primitive.extras["WaterMaskScale"] = 1.0;
|
|
|
|
Model upsampledModel =
|
|
*RasterOverlayUtilities::upsampleGltfForRasterOverlays(
|
|
model,
|
|
lowerLeft,
|
|
false);
|
|
|
|
REQUIRE(upsampledModel.meshes.size() == 1);
|
|
const Mesh& upsampledMesh = upsampledModel.meshes.back();
|
|
|
|
REQUIRE(upsampledMesh.primitives.size() == 1);
|
|
const MeshPrimitive& upsampledPrimitive = upsampledMesh.primitives.back();
|
|
|
|
auto it = upsampledPrimitive.extras.find("OnlyWater");
|
|
REQUIRE(it != upsampledPrimitive.extras.end());
|
|
REQUIRE(it->second.isBool());
|
|
CHECK(it->second.getBool() == false);
|
|
|
|
it = upsampledPrimitive.extras.find("WaterMaskScale");
|
|
REQUIRE(it != upsampledPrimitive.extras.end());
|
|
REQUIRE(it->second.isDouble());
|
|
CHECK(it->second.getDouble() == 0.5);
|
|
}
|
|
|
|
SUBCASE("Check water mask properties come through when there is also skirt "
|
|
"metadata") {
|
|
double skirtHeight = 12.0;
|
|
SkirtMeshMetadata skirtMeshMetadata;
|
|
skirtMeshMetadata.noSkirtIndicesBegin = 0;
|
|
skirtMeshMetadata.noSkirtIndicesCount =
|
|
static_cast<uint32_t>(indices.size());
|
|
skirtMeshMetadata.meshCenter = center;
|
|
skirtMeshMetadata.skirtWestHeight = skirtHeight;
|
|
skirtMeshMetadata.skirtSouthHeight = skirtHeight;
|
|
skirtMeshMetadata.skirtEastHeight = skirtHeight;
|
|
skirtMeshMetadata.skirtNorthHeight = skirtHeight;
|
|
|
|
primitive.extras = SkirtMeshMetadata::createGltfExtras(skirtMeshMetadata);
|
|
|
|
primitive.extras["OnlyWater"] = true;
|
|
primitive.extras["OnlyLand"] = false;
|
|
|
|
Model upsampledModel =
|
|
*RasterOverlayUtilities::upsampleGltfForRasterOverlays(
|
|
model,
|
|
lowerLeft,
|
|
false);
|
|
|
|
REQUIRE(upsampledModel.meshes.size() == 1);
|
|
const Mesh& upsampledMesh = upsampledModel.meshes.back();
|
|
|
|
REQUIRE(upsampledMesh.primitives.size() == 1);
|
|
const MeshPrimitive& upsampledPrimitive = upsampledMesh.primitives.back();
|
|
|
|
auto it = upsampledPrimitive.extras.find("OnlyWater");
|
|
REQUIRE(it != upsampledPrimitive.extras.end());
|
|
REQUIRE(it->second.isBool());
|
|
CHECK(it->second.getBool() == true);
|
|
|
|
it = upsampledPrimitive.extras.find("skirtMeshMetadata");
|
|
REQUIRE(it != upsampledPrimitive.extras.end());
|
|
CHECK(it->second.isObject());
|
|
}
|
|
}
|
|
|
|
TEST_CASE("upsampleGltfForRasterOverlay with UNSIGNED_BYTE indices") {
|
|
const Ellipsoid& ellipsoid = CesiumGeospatial::Ellipsoid::WGS84;
|
|
Cartographic bottomLeftCart{glm::radians(110.0), glm::radians(32.0), 0.0};
|
|
Cartographic topLeftCart{
|
|
bottomLeftCart.longitude,
|
|
bottomLeftCart.latitude + glm::radians(1.0),
|
|
0.0};
|
|
Cartographic topRightCart{
|
|
bottomLeftCart.longitude + glm::radians(1.0),
|
|
bottomLeftCart.latitude + glm::radians(1.0),
|
|
0.0};
|
|
Cartographic bottomRightCart{
|
|
bottomLeftCart.longitude + glm::radians(1.0),
|
|
bottomLeftCart.latitude,
|
|
0.0};
|
|
Cartographic centerCart{
|
|
(bottomLeftCart.longitude + topRightCart.longitude) / 2.0,
|
|
(bottomLeftCart.latitude + topRightCart.latitude) / 2.0,
|
|
0.0};
|
|
glm::dvec3 center = ellipsoid.cartographicToCartesian(centerCart);
|
|
std::vector<glm::vec3> positions{
|
|
static_cast<glm::vec3>(
|
|
ellipsoid.cartographicToCartesian(bottomLeftCart) - center),
|
|
static_cast<glm::vec3>(
|
|
ellipsoid.cartographicToCartesian(topLeftCart) - center),
|
|
static_cast<glm::vec3>(
|
|
ellipsoid.cartographicToCartesian(topRightCart) - center),
|
|
static_cast<glm::vec3>(
|
|
ellipsoid.cartographicToCartesian(bottomRightCart) - center),
|
|
};
|
|
std::vector<glm::vec2> uvs{
|
|
glm::vec2{0.0, 0.0},
|
|
glm::vec2{0.0, 1.0},
|
|
glm::vec2{1.0, 0.0},
|
|
glm::vec2{1.0, 1.0}};
|
|
std::vector<uint8_t> indices{0, 2, 1, 1, 2, 3};
|
|
uint32_t positionsBufferSize =
|
|
static_cast<uint32_t>(positions.size() * sizeof(glm::vec3));
|
|
uint32_t uvsBufferSize =
|
|
static_cast<uint32_t>(uvs.size() * sizeof(glm::vec2));
|
|
uint32_t indicesBufferSize =
|
|
static_cast<uint32_t>(indices.size() * sizeof(uint8_t));
|
|
|
|
Model model;
|
|
|
|
// create buffer
|
|
model.buffers.emplace_back();
|
|
Buffer& buffer = model.buffers.back();
|
|
buffer.cesium.data.resize(
|
|
positionsBufferSize + uvsBufferSize + indicesBufferSize);
|
|
std::memcpy(buffer.cesium.data.data(), positions.data(), positionsBufferSize);
|
|
std::memcpy(
|
|
buffer.cesium.data.data() + positionsBufferSize,
|
|
uvs.data(),
|
|
uvsBufferSize);
|
|
std::memcpy(
|
|
buffer.cesium.data.data() + positionsBufferSize + uvsBufferSize,
|
|
indices.data(),
|
|
indicesBufferSize);
|
|
|
|
// create position
|
|
model.bufferViews.emplace_back();
|
|
BufferView& positionBufferView = model.bufferViews.emplace_back();
|
|
positionBufferView.buffer = static_cast<int>(model.buffers.size() - 1);
|
|
positionBufferView.byteOffset = 0;
|
|
positionBufferView.byteLength = positionsBufferSize;
|
|
|
|
model.accessors.emplace_back();
|
|
Accessor& positionAccessor = model.accessors.back();
|
|
positionAccessor.bufferView =
|
|
static_cast<int32_t>(model.bufferViews.size() - 1);
|
|
positionAccessor.byteOffset = 0;
|
|
positionAccessor.count = static_cast<int64_t>(positions.size());
|
|
positionAccessor.componentType = Accessor::ComponentType::FLOAT;
|
|
positionAccessor.type = Accessor::Type::VEC3;
|
|
|
|
int32_t positionAccessorIdx =
|
|
static_cast<int32_t>(model.accessors.size() - 1);
|
|
|
|
// create uv
|
|
model.bufferViews.emplace_back();
|
|
BufferView& uvBufferView = model.bufferViews.emplace_back();
|
|
uvBufferView.buffer = static_cast<int32_t>(model.buffers.size() - 1);
|
|
uvBufferView.byteOffset = positionsBufferSize;
|
|
uvBufferView.byteLength = uvsBufferSize;
|
|
|
|
model.accessors.emplace_back();
|
|
Accessor& uvAccessor = model.accessors.back();
|
|
uvAccessor.bufferView = static_cast<int32_t>(model.bufferViews.size() - 1);
|
|
uvAccessor.byteOffset = 0;
|
|
uvAccessor.count = static_cast<int64_t>(uvs.size());
|
|
uvAccessor.componentType = Accessor::ComponentType::FLOAT;
|
|
uvAccessor.type = Accessor::Type::VEC2;
|
|
|
|
int32_t uvAccessorIdx = static_cast<int32_t>(model.accessors.size() - 1);
|
|
|
|
// create indices
|
|
model.bufferViews.emplace_back();
|
|
BufferView& indicesBufferView = model.bufferViews.emplace_back();
|
|
indicesBufferView.buffer = static_cast<int>(model.buffers.size() - 1);
|
|
indicesBufferView.byteOffset = positionsBufferSize + uvsBufferSize;
|
|
indicesBufferView.byteLength = indicesBufferSize;
|
|
|
|
model.accessors.emplace_back();
|
|
Accessor& indicesAccessor = model.accessors.back();
|
|
indicesAccessor.bufferView = static_cast<int>(model.bufferViews.size() - 1);
|
|
indicesAccessor.byteOffset = 0;
|
|
indicesAccessor.count = static_cast<int64_t>(indices.size());
|
|
indicesAccessor.componentType = Accessor::ComponentType::UNSIGNED_BYTE;
|
|
indicesAccessor.type = Accessor::Type::SCALAR;
|
|
|
|
int indicesAccessorIdx = static_cast<int>(model.accessors.size() - 1);
|
|
|
|
model.meshes.emplace_back();
|
|
Mesh& mesh = model.meshes.back();
|
|
mesh.primitives.emplace_back();
|
|
|
|
MeshPrimitive& primitive = mesh.primitives.back();
|
|
primitive.mode = MeshPrimitive::Mode::TRIANGLES;
|
|
primitive.attributes["_CESIUMOVERLAY_0"] = uvAccessorIdx;
|
|
primitive.attributes["POSITION"] = positionAccessorIdx;
|
|
primitive.indices = indicesAccessorIdx;
|
|
|
|
// create node and update bounding volume
|
|
model.nodes.emplace_back();
|
|
Node& node = model.nodes[0];
|
|
node.mesh = static_cast<int>(model.meshes.size() - 1);
|
|
node.matrix = {
|
|
1.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
-1.0,
|
|
0.0,
|
|
0.0,
|
|
1.0,
|
|
0.0,
|
|
0.0,
|
|
center.x,
|
|
center.z,
|
|
-center.y,
|
|
1.0};
|
|
|
|
CesiumGeometry::UpsampledQuadtreeNode lowerLeft{
|
|
CesiumGeometry::QuadtreeTileID(1, 0, 0)};
|
|
|
|
Model upsampledModel = *RasterOverlayUtilities::upsampleGltfForRasterOverlays(
|
|
model,
|
|
lowerLeft,
|
|
false);
|
|
|
|
REQUIRE(upsampledModel.meshes.size() == 1);
|
|
const Mesh& upsampledMesh = upsampledModel.meshes.back();
|
|
|
|
REQUIRE(upsampledMesh.primitives.size() == 1);
|
|
const MeshPrimitive& upsampledPrimitive = upsampledMesh.primitives.back();
|
|
|
|
REQUIRE(upsampledPrimitive.indices >= 0);
|
|
REQUIRE(
|
|
upsampledPrimitive.attributes.find("POSITION") !=
|
|
upsampledPrimitive.attributes.end());
|
|
AccessorView<glm::vec3> upsampledPosition(
|
|
upsampledModel,
|
|
upsampledPrimitive.attributes.at("POSITION"));
|
|
AccessorView<uint32_t> upsampledIndices(
|
|
upsampledModel,
|
|
upsampledPrimitive.indices);
|
|
|
|
glm::vec3 p0 = upsampledPosition[0];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p0,
|
|
positions[0],
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p1 = upsampledPosition[1];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p1,
|
|
(positions[0] + positions[2]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p2 = upsampledPosition[2];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p2,
|
|
(upsampledPosition[1] + positions[1]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p3 = upsampledPosition[3];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p3,
|
|
(positions[0] + positions[1]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p4 = upsampledPosition[4];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p4,
|
|
(positions[0] + positions[2]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p5 = upsampledPosition[5];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p5,
|
|
(positions[1] + positions[2]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p6 = upsampledPosition[6];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p6,
|
|
(upsampledPosition[4] + positions[1]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
}
|
|
|
|
TEST_CASE("upsampleGltfForRasterOverlay with non-indexed triangles") {
|
|
const Ellipsoid& ellipsoid = CesiumGeospatial::Ellipsoid::WGS84;
|
|
Cartographic bottomLeftCart{glm::radians(110.0), glm::radians(32.0), 0.0};
|
|
Cartographic topLeftCart{
|
|
bottomLeftCart.longitude,
|
|
bottomLeftCart.latitude + glm::radians(1.0),
|
|
0.0};
|
|
Cartographic topRightCart{
|
|
bottomLeftCart.longitude + glm::radians(1.0),
|
|
bottomLeftCart.latitude + glm::radians(1.0),
|
|
0.0};
|
|
Cartographic bottomRightCart{
|
|
bottomLeftCart.longitude + glm::radians(1.0),
|
|
bottomLeftCart.latitude,
|
|
0.0};
|
|
Cartographic centerCart{
|
|
(bottomLeftCart.longitude + topRightCart.longitude) / 2.0,
|
|
(bottomLeftCart.latitude + topRightCart.latitude) / 2.0,
|
|
0.0};
|
|
glm::dvec3 center = ellipsoid.cartographicToCartesian(centerCart);
|
|
std::vector<glm::vec3> positions{
|
|
static_cast<glm::vec3>(
|
|
ellipsoid.cartographicToCartesian(bottomLeftCart) - center),
|
|
static_cast<glm::vec3>(
|
|
ellipsoid.cartographicToCartesian(topRightCart) - center),
|
|
static_cast<glm::vec3>(
|
|
ellipsoid.cartographicToCartesian(topLeftCart) - center),
|
|
static_cast<glm::vec3>(
|
|
ellipsoid.cartographicToCartesian(topLeftCart) - center),
|
|
static_cast<glm::vec3>(
|
|
ellipsoid.cartographicToCartesian(topRightCart) - center),
|
|
static_cast<glm::vec3>(
|
|
ellipsoid.cartographicToCartesian(bottomRightCart) - center),
|
|
};
|
|
std::vector<glm::vec2> uvs{
|
|
glm::vec2{0.0, 0.0},
|
|
glm::vec2{1.0, 0.0},
|
|
glm::vec2{0.0, 1.0},
|
|
glm::vec2{0.0, 1.0},
|
|
glm::vec2{1.0, 0.0},
|
|
glm::vec2{1.0, 1.0}};
|
|
uint32_t positionsBufferSize =
|
|
static_cast<uint32_t>(positions.size() * sizeof(glm::vec3));
|
|
uint32_t uvsBufferSize =
|
|
static_cast<uint32_t>(uvs.size() * sizeof(glm::vec2));
|
|
|
|
Model model;
|
|
|
|
// create buffer
|
|
model.buffers.emplace_back();
|
|
Buffer& buffer = model.buffers.back();
|
|
buffer.cesium.data.resize(positionsBufferSize + uvsBufferSize);
|
|
std::memcpy(buffer.cesium.data.data(), positions.data(), positionsBufferSize);
|
|
std::memcpy(
|
|
buffer.cesium.data.data() + positionsBufferSize,
|
|
uvs.data(),
|
|
uvsBufferSize);
|
|
|
|
// create position
|
|
model.bufferViews.emplace_back();
|
|
BufferView& positionBufferView = model.bufferViews.emplace_back();
|
|
positionBufferView.buffer = static_cast<int>(model.buffers.size() - 1);
|
|
positionBufferView.byteOffset = 0;
|
|
positionBufferView.byteLength = positionsBufferSize;
|
|
|
|
model.accessors.emplace_back();
|
|
Accessor& positionAccessor = model.accessors.back();
|
|
positionAccessor.bufferView =
|
|
static_cast<int32_t>(model.bufferViews.size() - 1);
|
|
positionAccessor.byteOffset = 0;
|
|
positionAccessor.count = static_cast<int64_t>(positions.size());
|
|
positionAccessor.componentType = Accessor::ComponentType::FLOAT;
|
|
positionAccessor.type = Accessor::Type::VEC3;
|
|
|
|
int32_t positionAccessorIdx =
|
|
static_cast<int32_t>(model.accessors.size() - 1);
|
|
|
|
// create uv
|
|
model.bufferViews.emplace_back();
|
|
BufferView& uvBufferView = model.bufferViews.emplace_back();
|
|
uvBufferView.buffer = static_cast<int32_t>(model.buffers.size() - 1);
|
|
uvBufferView.byteOffset = positionsBufferSize;
|
|
uvBufferView.byteLength = uvsBufferSize;
|
|
|
|
model.accessors.emplace_back();
|
|
Accessor& uvAccessor = model.accessors.back();
|
|
uvAccessor.bufferView = static_cast<int32_t>(model.bufferViews.size() - 1);
|
|
uvAccessor.byteOffset = 0;
|
|
uvAccessor.count = static_cast<int64_t>(uvs.size());
|
|
uvAccessor.componentType = Accessor::ComponentType::FLOAT;
|
|
uvAccessor.type = Accessor::Type::VEC2;
|
|
|
|
int32_t uvAccessorIdx = static_cast<int32_t>(model.accessors.size() - 1);
|
|
|
|
model.meshes.emplace_back();
|
|
Mesh& mesh = model.meshes.back();
|
|
mesh.primitives.emplace_back();
|
|
|
|
MeshPrimitive& primitive = mesh.primitives.back();
|
|
primitive.mode = MeshPrimitive::Mode::TRIANGLES;
|
|
primitive.attributes["_CESIUMOVERLAY_0"] = uvAccessorIdx;
|
|
primitive.attributes["POSITION"] = positionAccessorIdx;
|
|
|
|
// create node and update bounding volume
|
|
model.nodes.emplace_back();
|
|
Node& node = model.nodes[0];
|
|
node.mesh = static_cast<int>(model.meshes.size() - 1);
|
|
node.matrix = {
|
|
1.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
-1.0,
|
|
0.0,
|
|
0.0,
|
|
1.0,
|
|
0.0,
|
|
0.0,
|
|
center.x,
|
|
center.z,
|
|
-center.y,
|
|
1.0};
|
|
|
|
CesiumGeometry::UpsampledQuadtreeNode lowerLeft{
|
|
CesiumGeometry::QuadtreeTileID(1, 0, 0)};
|
|
|
|
std::optional<Model> upsampledModelOptional =
|
|
RasterOverlayUtilities::upsampleGltfForRasterOverlays(
|
|
model,
|
|
lowerLeft,
|
|
false);
|
|
|
|
REQUIRE(upsampledModelOptional);
|
|
|
|
Model& upsampledModel = *upsampledModelOptional;
|
|
|
|
REQUIRE(upsampledModel.meshes.size() == 1);
|
|
const Mesh& upsampledMesh = upsampledModel.meshes.back();
|
|
|
|
REQUIRE(upsampledMesh.primitives.size() == 1);
|
|
const MeshPrimitive& upsampledPrimitive = upsampledMesh.primitives.back();
|
|
|
|
REQUIRE(upsampledPrimitive.indices >= 0);
|
|
REQUIRE(
|
|
upsampledPrimitive.attributes.find("POSITION") !=
|
|
upsampledPrimitive.attributes.end());
|
|
AccessorView<glm::vec3> upsampledPosition(
|
|
upsampledModel,
|
|
upsampledPrimitive.attributes.at("POSITION"));
|
|
AccessorView<uint32_t> upsampledIndices(
|
|
upsampledModel,
|
|
upsampledPrimitive.indices);
|
|
|
|
glm::vec3 p0 = upsampledPosition[0];
|
|
CHECK(
|
|
glm::epsilonEqual(
|
|
p0,
|
|
positions[0],
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p1 = upsampledPosition[1];
|
|
CHECK(
|
|
glm::epsilonEqual(
|
|
p1,
|
|
(positions[0] + positions[1]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p2 = upsampledPosition[2];
|
|
CHECK(
|
|
glm::epsilonEqual(
|
|
p2,
|
|
(upsampledPosition[1] + positions[2]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p3 = upsampledPosition[3];
|
|
CHECK(
|
|
glm::epsilonEqual(
|
|
p3,
|
|
(positions[0] + positions[2]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p4 = upsampledPosition[4];
|
|
CHECK(
|
|
glm::epsilonEqual(
|
|
p4,
|
|
(positions[0] + positions[1]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p5 = upsampledPosition[5];
|
|
CHECK(
|
|
glm::epsilonEqual(
|
|
p5,
|
|
(positions[2] + positions[1]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p6 = upsampledPosition[6];
|
|
CHECK(
|
|
glm::epsilonEqual(
|
|
p6,
|
|
(upsampledPosition[4] + positions[2]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
}
|
|
|
|
TEST_CASE("upsampleGltfForRasterOverlay with a triangle strip primitive") {
|
|
const Ellipsoid& ellipsoid = CesiumGeospatial::Ellipsoid::WGS84;
|
|
Cartographic bottomLeftCart{glm::radians(110.0), glm::radians(32.0), 0.0};
|
|
Cartographic topLeftCart{
|
|
bottomLeftCart.longitude,
|
|
bottomLeftCart.latitude + glm::radians(1.0),
|
|
0.0};
|
|
Cartographic topRightCart{
|
|
bottomLeftCart.longitude + glm::radians(1.0),
|
|
bottomLeftCart.latitude + glm::radians(1.0),
|
|
0.0};
|
|
Cartographic bottomRightCart{
|
|
bottomLeftCart.longitude + glm::radians(1.0),
|
|
bottomLeftCart.latitude,
|
|
0.0};
|
|
Cartographic centerCart{
|
|
(bottomLeftCart.longitude + topRightCart.longitude) / 2.0,
|
|
(bottomLeftCart.latitude + topRightCart.latitude) / 2.0,
|
|
0.0};
|
|
glm::dvec3 center = ellipsoid.cartographicToCartesian(centerCart);
|
|
std::vector<glm::vec3> positions{
|
|
static_cast<glm::vec3>(
|
|
ellipsoid.cartographicToCartesian(bottomLeftCart) - center),
|
|
static_cast<glm::vec3>(
|
|
ellipsoid.cartographicToCartesian(topLeftCart) - center),
|
|
static_cast<glm::vec3>(
|
|
ellipsoid.cartographicToCartesian(topRightCart) - center),
|
|
static_cast<glm::vec3>(
|
|
ellipsoid.cartographicToCartesian(bottomRightCart) - center),
|
|
};
|
|
std::vector<glm::vec2> uvs{
|
|
glm::vec2{0.0, 0.0},
|
|
glm::vec2{0.0, 1.0},
|
|
glm::vec2{1.0, 0.0},
|
|
glm::vec2{1.0, 1.0}};
|
|
std::vector<uint8_t> indices{0, 2, 1, 3};
|
|
uint32_t positionsBufferSize =
|
|
static_cast<uint32_t>(positions.size() * sizeof(glm::vec3));
|
|
uint32_t uvsBufferSize =
|
|
static_cast<uint32_t>(uvs.size() * sizeof(glm::vec2));
|
|
uint32_t indicesBufferSize =
|
|
static_cast<uint32_t>(indices.size() * sizeof(uint8_t));
|
|
|
|
Model model;
|
|
|
|
// create buffer
|
|
model.buffers.emplace_back();
|
|
Buffer& buffer = model.buffers.back();
|
|
buffer.cesium.data.resize(
|
|
positionsBufferSize + uvsBufferSize + indicesBufferSize);
|
|
std::memcpy(buffer.cesium.data.data(), positions.data(), positionsBufferSize);
|
|
std::memcpy(
|
|
buffer.cesium.data.data() + positionsBufferSize,
|
|
uvs.data(),
|
|
uvsBufferSize);
|
|
std::memcpy(
|
|
buffer.cesium.data.data() + positionsBufferSize + uvsBufferSize,
|
|
indices.data(),
|
|
indicesBufferSize);
|
|
|
|
// create position
|
|
model.bufferViews.emplace_back();
|
|
BufferView& positionBufferView = model.bufferViews.emplace_back();
|
|
positionBufferView.buffer = static_cast<int>(model.buffers.size() - 1);
|
|
positionBufferView.byteOffset = 0;
|
|
positionBufferView.byteLength = positionsBufferSize;
|
|
|
|
model.accessors.emplace_back();
|
|
Accessor& positionAccessor = model.accessors.back();
|
|
positionAccessor.bufferView =
|
|
static_cast<int32_t>(model.bufferViews.size() - 1);
|
|
positionAccessor.byteOffset = 0;
|
|
positionAccessor.count = static_cast<int64_t>(positions.size());
|
|
positionAccessor.componentType = Accessor::ComponentType::FLOAT;
|
|
positionAccessor.type = Accessor::Type::VEC3;
|
|
|
|
int32_t positionAccessorIdx =
|
|
static_cast<int32_t>(model.accessors.size() - 1);
|
|
|
|
// create uv
|
|
model.bufferViews.emplace_back();
|
|
BufferView& uvBufferView = model.bufferViews.emplace_back();
|
|
uvBufferView.buffer = static_cast<int32_t>(model.buffers.size() - 1);
|
|
uvBufferView.byteOffset = positionsBufferSize;
|
|
uvBufferView.byteLength = uvsBufferSize;
|
|
|
|
model.accessors.emplace_back();
|
|
Accessor& uvAccessor = model.accessors.back();
|
|
uvAccessor.bufferView = static_cast<int32_t>(model.bufferViews.size() - 1);
|
|
uvAccessor.byteOffset = 0;
|
|
uvAccessor.count = static_cast<int64_t>(uvs.size());
|
|
uvAccessor.componentType = Accessor::ComponentType::FLOAT;
|
|
uvAccessor.type = Accessor::Type::VEC2;
|
|
|
|
int32_t uvAccessorIdx = static_cast<int32_t>(model.accessors.size() - 1);
|
|
|
|
// create indices
|
|
model.bufferViews.emplace_back();
|
|
BufferView& indicesBufferView = model.bufferViews.emplace_back();
|
|
indicesBufferView.buffer = static_cast<int>(model.buffers.size() - 1);
|
|
indicesBufferView.byteOffset = positionsBufferSize + uvsBufferSize;
|
|
indicesBufferView.byteLength = indicesBufferSize;
|
|
|
|
model.accessors.emplace_back();
|
|
Accessor& indicesAccessor = model.accessors.back();
|
|
indicesAccessor.bufferView = static_cast<int>(model.bufferViews.size() - 1);
|
|
indicesAccessor.byteOffset = 0;
|
|
indicesAccessor.count = static_cast<int64_t>(indices.size());
|
|
indicesAccessor.componentType = Accessor::ComponentType::UNSIGNED_BYTE;
|
|
indicesAccessor.type = Accessor::Type::SCALAR;
|
|
|
|
int indicesAccessorIdx = static_cast<int>(model.accessors.size() - 1);
|
|
|
|
model.meshes.emplace_back();
|
|
Mesh& mesh = model.meshes.back();
|
|
mesh.primitives.emplace_back();
|
|
|
|
MeshPrimitive& primitive = mesh.primitives.back();
|
|
primitive.mode = MeshPrimitive::Mode::TRIANGLE_STRIP;
|
|
primitive.attributes["_CESIUMOVERLAY_0"] = uvAccessorIdx;
|
|
primitive.attributes["POSITION"] = positionAccessorIdx;
|
|
primitive.indices = indicesAccessorIdx;
|
|
|
|
// create node and update bounding volume
|
|
model.nodes.emplace_back();
|
|
Node& node = model.nodes[0];
|
|
node.mesh = static_cast<int>(model.meshes.size() - 1);
|
|
node.matrix = {
|
|
1.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
-1.0,
|
|
0.0,
|
|
0.0,
|
|
1.0,
|
|
0.0,
|
|
0.0,
|
|
center.x,
|
|
center.z,
|
|
-center.y,
|
|
1.0};
|
|
|
|
CesiumGeometry::UpsampledQuadtreeNode lowerLeft{
|
|
CesiumGeometry::QuadtreeTileID(1, 0, 0)};
|
|
|
|
Model upsampledModel = *RasterOverlayUtilities::upsampleGltfForRasterOverlays(
|
|
model,
|
|
lowerLeft,
|
|
false);
|
|
|
|
REQUIRE(upsampledModel.meshes.size() == 1);
|
|
const Mesh& upsampledMesh = upsampledModel.meshes.back();
|
|
|
|
REQUIRE(upsampledMesh.primitives.size() == 1);
|
|
const MeshPrimitive& upsampledPrimitive = upsampledMesh.primitives.back();
|
|
|
|
REQUIRE(upsampledPrimitive.indices >= 0);
|
|
REQUIRE(
|
|
upsampledPrimitive.attributes.find("POSITION") !=
|
|
upsampledPrimitive.attributes.end());
|
|
AccessorView<glm::vec3> upsampledPosition(
|
|
upsampledModel,
|
|
upsampledPrimitive.attributes.at("POSITION"));
|
|
AccessorView<uint32_t> upsampledIndices(
|
|
upsampledModel,
|
|
upsampledPrimitive.indices);
|
|
|
|
glm::vec3 p0 = upsampledPosition[0];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p0,
|
|
positions[0],
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p1 = upsampledPosition[1];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p1,
|
|
(positions[0] + positions[2]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p2 = upsampledPosition[2];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p2,
|
|
(upsampledPosition[1] + positions[1]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p3 = upsampledPosition[3];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p3,
|
|
(positions[0] + positions[1]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p4 = upsampledPosition[4];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p4,
|
|
(positions[0] + positions[2]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p5 = upsampledPosition[5];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p5,
|
|
(positions[1] + positions[2]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p6 = upsampledPosition[6];
|
|
REQUIRE(
|
|
glm::epsilonEqual(
|
|
p6,
|
|
(upsampledPosition[4] + positions[1]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
}
|
|
|
|
TEST_CASE("upsampleGltfForRasterOverlay with a triangle fan primitive") {
|
|
const Ellipsoid& ellipsoid = CesiumGeospatial::Ellipsoid::WGS84;
|
|
Cartographic bottomLeftCart{glm::radians(110.0), glm::radians(32.0), 0.0};
|
|
Cartographic topLeftCart{
|
|
bottomLeftCart.longitude,
|
|
bottomLeftCart.latitude + glm::radians(1.0),
|
|
0.0};
|
|
Cartographic topRightCart{
|
|
bottomLeftCart.longitude + glm::radians(1.0),
|
|
bottomLeftCart.latitude + glm::radians(1.0),
|
|
0.0};
|
|
Cartographic bottomRightCart{
|
|
bottomLeftCart.longitude + glm::radians(1.0),
|
|
bottomLeftCart.latitude,
|
|
0.0};
|
|
Cartographic centerCart{
|
|
(bottomLeftCart.longitude + topRightCart.longitude) / 2.0,
|
|
(bottomLeftCart.latitude + topRightCart.latitude) / 2.0,
|
|
0.0};
|
|
glm::dvec3 center = ellipsoid.cartographicToCartesian(centerCart);
|
|
std::vector<glm::vec3> positions{
|
|
static_cast<glm::vec3>(
|
|
ellipsoid.cartographicToCartesian(bottomLeftCart) - center),
|
|
static_cast<glm::vec3>(
|
|
ellipsoid.cartographicToCartesian(topLeftCart) - center),
|
|
static_cast<glm::vec3>(
|
|
ellipsoid.cartographicToCartesian(topRightCart) - center),
|
|
static_cast<glm::vec3>(
|
|
ellipsoid.cartographicToCartesian(bottomRightCart) - center),
|
|
};
|
|
std::vector<glm::vec2> uvs{
|
|
glm::vec2{0.0, 0.0},
|
|
glm::vec2{0.0, 1.0},
|
|
glm::vec2{1.0, 0.0},
|
|
glm::vec2{1.0, 1.0}};
|
|
std::vector<uint8_t> indices{0, 3, 2, 1};
|
|
uint32_t positionsBufferSize =
|
|
static_cast<uint32_t>(positions.size() * sizeof(glm::vec3));
|
|
uint32_t uvsBufferSize =
|
|
static_cast<uint32_t>(uvs.size() * sizeof(glm::vec2));
|
|
uint32_t indicesBufferSize =
|
|
static_cast<uint32_t>(indices.size() * sizeof(uint8_t));
|
|
|
|
Model model;
|
|
|
|
// create buffer
|
|
model.buffers.emplace_back();
|
|
Buffer& buffer = model.buffers.back();
|
|
buffer.cesium.data.resize(
|
|
positionsBufferSize + uvsBufferSize + indicesBufferSize);
|
|
std::memcpy(buffer.cesium.data.data(), positions.data(), positionsBufferSize);
|
|
std::memcpy(
|
|
buffer.cesium.data.data() + positionsBufferSize,
|
|
uvs.data(),
|
|
uvsBufferSize);
|
|
std::memcpy(
|
|
buffer.cesium.data.data() + positionsBufferSize + uvsBufferSize,
|
|
indices.data(),
|
|
indicesBufferSize);
|
|
|
|
// create position
|
|
model.bufferViews.emplace_back();
|
|
BufferView& positionBufferView = model.bufferViews.emplace_back();
|
|
positionBufferView.buffer = static_cast<int>(model.buffers.size() - 1);
|
|
positionBufferView.byteOffset = 0;
|
|
positionBufferView.byteLength = positionsBufferSize;
|
|
|
|
model.accessors.emplace_back();
|
|
Accessor& positionAccessor = model.accessors.back();
|
|
positionAccessor.bufferView =
|
|
static_cast<int32_t>(model.bufferViews.size() - 1);
|
|
positionAccessor.byteOffset = 0;
|
|
positionAccessor.count = static_cast<int64_t>(positions.size());
|
|
positionAccessor.componentType = Accessor::ComponentType::FLOAT;
|
|
positionAccessor.type = Accessor::Type::VEC3;
|
|
|
|
int32_t positionAccessorIdx =
|
|
static_cast<int32_t>(model.accessors.size() - 1);
|
|
|
|
// create uv
|
|
model.bufferViews.emplace_back();
|
|
BufferView& uvBufferView = model.bufferViews.emplace_back();
|
|
uvBufferView.buffer = static_cast<int32_t>(model.buffers.size() - 1);
|
|
uvBufferView.byteOffset = positionsBufferSize;
|
|
uvBufferView.byteLength = uvsBufferSize;
|
|
|
|
model.accessors.emplace_back();
|
|
Accessor& uvAccessor = model.accessors.back();
|
|
uvAccessor.bufferView = static_cast<int32_t>(model.bufferViews.size() - 1);
|
|
uvAccessor.byteOffset = 0;
|
|
uvAccessor.count = static_cast<int64_t>(uvs.size());
|
|
uvAccessor.componentType = Accessor::ComponentType::FLOAT;
|
|
uvAccessor.type = Accessor::Type::VEC2;
|
|
|
|
int32_t uvAccessorIdx = static_cast<int32_t>(model.accessors.size() - 1);
|
|
|
|
// create indices
|
|
model.bufferViews.emplace_back();
|
|
BufferView& indicesBufferView = model.bufferViews.emplace_back();
|
|
indicesBufferView.buffer = static_cast<int>(model.buffers.size() - 1);
|
|
indicesBufferView.byteOffset = positionsBufferSize + uvsBufferSize;
|
|
indicesBufferView.byteLength = indicesBufferSize;
|
|
|
|
model.accessors.emplace_back();
|
|
Accessor& indicesAccessor = model.accessors.back();
|
|
indicesAccessor.bufferView = static_cast<int>(model.bufferViews.size() - 1);
|
|
indicesAccessor.byteOffset = 0;
|
|
indicesAccessor.count = static_cast<int64_t>(indices.size());
|
|
indicesAccessor.componentType = Accessor::ComponentType::UNSIGNED_BYTE;
|
|
indicesAccessor.type = Accessor::Type::SCALAR;
|
|
|
|
int indicesAccessorIdx = static_cast<int>(model.accessors.size() - 1);
|
|
|
|
model.meshes.emplace_back();
|
|
Mesh& mesh = model.meshes.back();
|
|
mesh.primitives.emplace_back();
|
|
|
|
MeshPrimitive& primitive = mesh.primitives.back();
|
|
primitive.mode = MeshPrimitive::Mode::TRIANGLE_FAN;
|
|
primitive.attributes["_CESIUMOVERLAY_0"] = uvAccessorIdx;
|
|
primitive.attributes["POSITION"] = positionAccessorIdx;
|
|
primitive.indices = indicesAccessorIdx;
|
|
|
|
// create node and update bounding volume
|
|
model.nodes.emplace_back();
|
|
Node& node = model.nodes[0];
|
|
node.mesh = static_cast<int>(model.meshes.size() - 1);
|
|
node.matrix = {
|
|
1.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
-1.0,
|
|
0.0,
|
|
0.0,
|
|
1.0,
|
|
0.0,
|
|
0.0,
|
|
center.x,
|
|
center.z,
|
|
-center.y,
|
|
1.0};
|
|
|
|
CesiumGeometry::UpsampledQuadtreeNode lowerLeft{
|
|
CesiumGeometry::QuadtreeTileID(1, 0, 0)};
|
|
|
|
Model upsampledModel = *RasterOverlayUtilities::upsampleGltfForRasterOverlays(
|
|
model,
|
|
lowerLeft,
|
|
false);
|
|
|
|
REQUIRE(upsampledModel.meshes.size() == 1);
|
|
const Mesh& upsampledMesh = upsampledModel.meshes.back();
|
|
|
|
REQUIRE(upsampledMesh.primitives.size() == 1);
|
|
const MeshPrimitive& upsampledPrimitive = upsampledMesh.primitives.back();
|
|
|
|
REQUIRE(upsampledPrimitive.indices >= 0);
|
|
REQUIRE(
|
|
upsampledPrimitive.attributes.find("POSITION") !=
|
|
upsampledPrimitive.attributes.end());
|
|
AccessorView<glm::vec3> upsampledPosition(
|
|
upsampledModel,
|
|
upsampledPrimitive.attributes.at("POSITION"));
|
|
AccessorView<uint32_t> upsampledIndices(
|
|
upsampledModel,
|
|
upsampledPrimitive.indices);
|
|
|
|
glm::vec3 p0 = upsampledPosition[0];
|
|
CHECK(
|
|
glm::epsilonEqual(
|
|
p0,
|
|
positions[0],
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p1 = upsampledPosition[1];
|
|
CHECK(
|
|
glm::epsilonEqual(
|
|
p1,
|
|
(positions[0] + positions[3]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p2 = upsampledPosition[2];
|
|
CHECK(
|
|
glm::epsilonEqual(
|
|
p2,
|
|
(positions[0] + positions[2]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p3 = upsampledPosition[3];
|
|
CHECK(
|
|
glm::epsilonEqual(
|
|
p3,
|
|
(positions[0] + positions[2]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p4 = upsampledPosition[4];
|
|
CHECK(
|
|
glm::epsilonEqual(
|
|
p4,
|
|
(upsampledPosition[3] + positions[1]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p5 = upsampledPosition[5];
|
|
CHECK(
|
|
glm::epsilonEqual(
|
|
p5,
|
|
(positions[0] + positions[1]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
|
|
glm::vec3 p6 = upsampledPosition[6];
|
|
CHECK(
|
|
glm::epsilonEqual(
|
|
p6,
|
|
(positions[0] + positions[2]) * 0.5f,
|
|
glm::vec3(static_cast<float>(Math::Epsilon7))) == glm::bvec3(true));
|
|
}
|
|
|
|
TEST_CASE("upsampleGltfForRasterOverlay with a point primitive") {
|
|
const Ellipsoid& ellipsoid = CesiumGeospatial::Ellipsoid::WGS84;
|
|
Cartographic bottomLeftCart{glm::radians(110.0), glm::radians(32.0), 0.0};
|
|
Cartographic topLeftCart{
|
|
bottomLeftCart.longitude,
|
|
bottomLeftCart.latitude + glm::radians(1.0),
|
|
0.0};
|
|
Cartographic topRightCart{
|
|
bottomLeftCart.longitude + glm::radians(1.0),
|
|
bottomLeftCart.latitude + glm::radians(1.0),
|
|
0.0};
|
|
Cartographic bottomRightCart{
|
|
bottomLeftCart.longitude + glm::radians(1.0),
|
|
bottomLeftCart.latitude,
|
|
0.0};
|
|
Cartographic centerCart{
|
|
(bottomLeftCart.longitude + topRightCart.longitude) / 2.0,
|
|
(bottomLeftCart.latitude + topRightCart.latitude) / 2.0,
|
|
0.0};
|
|
glm::dvec3 center = ellipsoid.cartographicToCartesian(centerCart);
|
|
std::vector<glm::vec3> positions{
|
|
static_cast<glm::vec3>(
|
|
ellipsoid.cartographicToCartesian(bottomLeftCart) - center),
|
|
static_cast<glm::vec3>(
|
|
ellipsoid.cartographicToCartesian(topLeftCart) - center),
|
|
static_cast<glm::vec3>(
|
|
ellipsoid.cartographicToCartesian(topRightCart) - center),
|
|
static_cast<glm::vec3>(
|
|
ellipsoid.cartographicToCartesian(bottomRightCart) - center),
|
|
};
|
|
std::vector<glm::vec2> uvs{
|
|
glm::vec2{0.0, 0.0},
|
|
glm::vec2{0.0, 1.0},
|
|
glm::vec2{1.0, 0.0},
|
|
glm::vec2{1.0, 1.0}};
|
|
std::vector<glm::vec4> miscData{
|
|
glm::vec4(0.5, 0.1, 0.283, 0.12),
|
|
glm::vec4(0.1, 0.12, 3.41, 0.31),
|
|
glm::vec4(-10.4, 1.20, 0.3, 0.12),
|
|
glm::vec4(1.02, 10.2, 0.5, 0.43)};
|
|
uint32_t positionsBufferSize =
|
|
static_cast<uint32_t>(positions.size() * sizeof(glm::vec3));
|
|
uint32_t uvsBufferSize =
|
|
static_cast<uint32_t>(uvs.size() * sizeof(glm::vec2));
|
|
uint32_t miscBufferSize =
|
|
static_cast<uint32_t>(miscData.size() * sizeof(glm::vec4));
|
|
|
|
Model model;
|
|
|
|
// create buffer
|
|
model.buffers.emplace_back();
|
|
Buffer& buffer = model.buffers.back();
|
|
buffer.cesium.data.resize(
|
|
positionsBufferSize + uvsBufferSize + miscBufferSize);
|
|
std::memcpy(buffer.cesium.data.data(), positions.data(), positionsBufferSize);
|
|
std::memcpy(
|
|
buffer.cesium.data.data() + positionsBufferSize,
|
|
uvs.data(),
|
|
uvsBufferSize);
|
|
std::memcpy(
|
|
buffer.cesium.data.data() + positionsBufferSize + uvsBufferSize,
|
|
miscData.data(),
|
|
miscBufferSize);
|
|
|
|
// create position
|
|
model.bufferViews.emplace_back();
|
|
BufferView& positionBufferView = model.bufferViews.emplace_back();
|
|
positionBufferView.buffer = static_cast<int>(model.buffers.size() - 1);
|
|
positionBufferView.byteOffset = 0;
|
|
positionBufferView.byteLength = positionsBufferSize;
|
|
|
|
model.accessors.emplace_back();
|
|
Accessor& positionAccessor = model.accessors.back();
|
|
positionAccessor.bufferView =
|
|
static_cast<int32_t>(model.bufferViews.size() - 1);
|
|
positionAccessor.byteOffset = 0;
|
|
positionAccessor.count = static_cast<int64_t>(positions.size());
|
|
positionAccessor.componentType = Accessor::ComponentType::FLOAT;
|
|
positionAccessor.type = Accessor::Type::VEC3;
|
|
|
|
int32_t positionAccessorIdx =
|
|
static_cast<int32_t>(model.accessors.size() - 1);
|
|
|
|
// create uv
|
|
model.bufferViews.emplace_back();
|
|
BufferView& uvBufferView = model.bufferViews.emplace_back();
|
|
uvBufferView.buffer = static_cast<int32_t>(model.buffers.size() - 1);
|
|
uvBufferView.byteOffset = positionsBufferSize;
|
|
uvBufferView.byteLength = uvsBufferSize;
|
|
|
|
model.accessors.emplace_back();
|
|
Accessor& uvAccessor = model.accessors.back();
|
|
uvAccessor.bufferView = static_cast<int32_t>(model.bufferViews.size() - 1);
|
|
uvAccessor.byteOffset = 0;
|
|
uvAccessor.count = static_cast<int64_t>(uvs.size());
|
|
uvAccessor.componentType = Accessor::ComponentType::FLOAT;
|
|
uvAccessor.type = Accessor::Type::VEC2;
|
|
|
|
int32_t uvAccessorIdx = static_cast<int32_t>(model.accessors.size() - 1);
|
|
|
|
// create indices
|
|
model.bufferViews.emplace_back();
|
|
BufferView& miscBufferView = model.bufferViews.emplace_back();
|
|
miscBufferView.buffer = static_cast<int>(model.buffers.size() - 1);
|
|
miscBufferView.byteOffset = positionsBufferSize + uvsBufferSize;
|
|
miscBufferView.byteLength = miscBufferSize;
|
|
|
|
model.accessors.emplace_back();
|
|
Accessor& miscAccessor = model.accessors.back();
|
|
miscAccessor.bufferView = static_cast<int>(model.bufferViews.size() - 1);
|
|
miscAccessor.byteOffset = 0;
|
|
miscAccessor.count = static_cast<int64_t>(miscData.size());
|
|
miscAccessor.componentType = Accessor::ComponentType::FLOAT;
|
|
miscAccessor.type = Accessor::Type::VEC4;
|
|
|
|
int miscAccessorIdx = static_cast<int>(model.accessors.size() - 1);
|
|
|
|
model.meshes.emplace_back();
|
|
Mesh& mesh = model.meshes.back();
|
|
mesh.primitives.emplace_back();
|
|
|
|
MeshPrimitive& primitive = mesh.primitives.back();
|
|
primitive.mode = MeshPrimitive::Mode::POINTS;
|
|
primitive.attributes["_CESIUMOVERLAY_0"] = uvAccessorIdx;
|
|
primitive.attributes["POSITION"] = positionAccessorIdx;
|
|
primitive.attributes["MISC_DATA"] = miscAccessorIdx;
|
|
|
|
// create node and update bounding volume
|
|
model.nodes.emplace_back();
|
|
Node& node = model.nodes[0];
|
|
node.mesh = static_cast<int>(model.meshes.size() - 1);
|
|
node.matrix = {
|
|
1.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
-1.0,
|
|
0.0,
|
|
0.0,
|
|
1.0,
|
|
0.0,
|
|
0.0,
|
|
center.x,
|
|
center.z,
|
|
-center.y,
|
|
1.0};
|
|
|
|
CesiumGeometry::UpsampledQuadtreeNode lowerLeft{
|
|
CesiumGeometry::QuadtreeTileID(1, 0, 0)};
|
|
|
|
Model upsampledModel = *RasterOverlayUtilities::upsampleGltfForRasterOverlays(
|
|
model,
|
|
lowerLeft,
|
|
false);
|
|
|
|
REQUIRE(upsampledModel.meshes.size() == 1);
|
|
const Mesh& upsampledMesh = upsampledModel.meshes.back();
|
|
|
|
REQUIRE(upsampledMesh.primitives.size() == 1);
|
|
const MeshPrimitive& upsampledPrimitive = upsampledMesh.primitives.back();
|
|
|
|
REQUIRE(
|
|
upsampledPrimitive.attributes.find("POSITION") !=
|
|
upsampledPrimitive.attributes.end());
|
|
AccessorView<glm::vec3> upsampledPosition(
|
|
upsampledModel,
|
|
upsampledPrimitive.attributes.at("POSITION"));
|
|
AccessorView<glm::vec4> upsampledMiscData(
|
|
upsampledModel,
|
|
upsampledPrimitive.attributes.at("MISC_DATA"));
|
|
|
|
REQUIRE(upsampledPosition.size() == 1);
|
|
REQUIRE(upsampledPosition[0] == positions[0]);
|
|
REQUIRE(upsampledMiscData[0] == miscData[0]);
|
|
}
|
|
|
|
constexpr size_t POINT_COUNT = 1000;
|
|
|
|
TEST_CASE("upsampleGltfForRasterOverlay with random points") {
|
|
const Ellipsoid& ellipsoid = CesiumGeospatial::Ellipsoid::WGS84;
|
|
Cartographic bottomLeftCart{glm::radians(110.0), glm::radians(32.0), 0.0};
|
|
Cartographic topLeftCart{
|
|
bottomLeftCart.longitude,
|
|
bottomLeftCart.latitude + glm::radians(1.0),
|
|
0.0};
|
|
Cartographic topRightCart{
|
|
bottomLeftCart.longitude + glm::radians(1.0),
|
|
bottomLeftCart.latitude + glm::radians(1.0),
|
|
0.0};
|
|
Cartographic bottomRightCart{
|
|
bottomLeftCart.longitude + glm::radians(1.0),
|
|
bottomLeftCart.latitude,
|
|
0.0};
|
|
Cartographic centerCart{
|
|
(bottomLeftCart.longitude + topRightCart.longitude) / 2.0,
|
|
(bottomLeftCart.latitude + topRightCart.latitude) / 2.0,
|
|
0.0};
|
|
glm::dvec3 center = ellipsoid.cartographicToCartesian(centerCart);
|
|
std::vector<glm::vec3> positions;
|
|
positions.reserve(POINT_COUNT);
|
|
std::vector<glm::vec2> uvs;
|
|
uvs.reserve(POINT_COUNT);
|
|
std::vector<glm::vec4> miscData;
|
|
miscData.reserve(POINT_COUNT);
|
|
|
|
std::random_device rd;
|
|
std::mt19937 mt(rd());
|
|
std::uniform_real_distribution<float> dist(0.0, 0.5);
|
|
std::vector<std::vector<size_t>> pointsPerQuadrant;
|
|
for (size_t i = 0; i < 4; i++) {
|
|
const float x = (float)(i % 2) / 2.0f;
|
|
const float y = std::floor((float)i / 2) / 2.0f;
|
|
std::vector<size_t> pointIndices;
|
|
pointIndices.reserve(POINT_COUNT / 4);
|
|
for (size_t j = 0; j < POINT_COUNT / 4; j++) {
|
|
const glm::vec2 uv(x + dist(mt), y + dist(mt));
|
|
const Cartographic posCart(
|
|
bottomLeftCart.longitude +
|
|
(bottomRightCart.longitude - bottomLeftCart.longitude) * uv.x,
|
|
bottomLeftCart.latitude +
|
|
(topLeftCart.latitude - bottomLeftCart.latitude) * uv.y,
|
|
0.0);
|
|
const glm::vec3 pos =
|
|
glm::vec3(ellipsoid.cartographicToCartesian(posCart) - center);
|
|
pointIndices.emplace_back(positions.size());
|
|
positions.emplace_back(pos);
|
|
uvs.emplace_back(uv);
|
|
miscData.emplace_back(
|
|
dist(mt) * 2.0f,
|
|
dist(mt) * 2.0f,
|
|
dist(mt) * 2.0f,
|
|
dist(mt) * 2.0f);
|
|
}
|
|
pointsPerQuadrant.emplace_back(pointIndices);
|
|
}
|
|
|
|
uint32_t positionsBufferSize =
|
|
static_cast<uint32_t>(positions.size() * sizeof(glm::vec3));
|
|
uint32_t uvsBufferSize =
|
|
static_cast<uint32_t>(uvs.size() * sizeof(glm::vec2));
|
|
uint32_t miscBufferSize =
|
|
static_cast<uint32_t>(miscData.size() * sizeof(glm::vec4));
|
|
|
|
Model model;
|
|
|
|
// create buffer
|
|
model.buffers.emplace_back();
|
|
Buffer& buffer = model.buffers.back();
|
|
buffer.cesium.data.resize(
|
|
positionsBufferSize + uvsBufferSize + miscBufferSize);
|
|
std::memcpy(buffer.cesium.data.data(), positions.data(), positionsBufferSize);
|
|
std::memcpy(
|
|
buffer.cesium.data.data() + positionsBufferSize,
|
|
uvs.data(),
|
|
uvsBufferSize);
|
|
std::memcpy(
|
|
buffer.cesium.data.data() + positionsBufferSize + uvsBufferSize,
|
|
miscData.data(),
|
|
miscBufferSize);
|
|
|
|
// create position
|
|
model.bufferViews.emplace_back();
|
|
BufferView& positionBufferView = model.bufferViews.emplace_back();
|
|
positionBufferView.buffer = static_cast<int>(model.buffers.size() - 1);
|
|
positionBufferView.byteOffset = 0;
|
|
positionBufferView.byteLength = positionsBufferSize;
|
|
|
|
model.accessors.emplace_back();
|
|
Accessor& positionAccessor = model.accessors.back();
|
|
positionAccessor.bufferView =
|
|
static_cast<int32_t>(model.bufferViews.size() - 1);
|
|
positionAccessor.byteOffset = 0;
|
|
positionAccessor.count = static_cast<int64_t>(positions.size());
|
|
positionAccessor.componentType = Accessor::ComponentType::FLOAT;
|
|
positionAccessor.type = Accessor::Type::VEC3;
|
|
|
|
int32_t positionAccessorIdx =
|
|
static_cast<int32_t>(model.accessors.size() - 1);
|
|
|
|
// create uv
|
|
model.bufferViews.emplace_back();
|
|
BufferView& uvBufferView = model.bufferViews.emplace_back();
|
|
uvBufferView.buffer = static_cast<int32_t>(model.buffers.size() - 1);
|
|
uvBufferView.byteOffset = positionsBufferSize;
|
|
uvBufferView.byteLength = uvsBufferSize;
|
|
|
|
model.accessors.emplace_back();
|
|
Accessor& uvAccessor = model.accessors.back();
|
|
uvAccessor.bufferView = static_cast<int32_t>(model.bufferViews.size() - 1);
|
|
uvAccessor.byteOffset = 0;
|
|
uvAccessor.count = static_cast<int64_t>(uvs.size());
|
|
uvAccessor.componentType = Accessor::ComponentType::FLOAT;
|
|
uvAccessor.type = Accessor::Type::VEC2;
|
|
|
|
int32_t uvAccessorIdx = static_cast<int32_t>(model.accessors.size() - 1);
|
|
|
|
// create indices
|
|
model.bufferViews.emplace_back();
|
|
BufferView& miscBufferView = model.bufferViews.emplace_back();
|
|
miscBufferView.buffer = static_cast<int>(model.buffers.size() - 1);
|
|
miscBufferView.byteOffset = positionsBufferSize + uvsBufferSize;
|
|
miscBufferView.byteLength = miscBufferSize;
|
|
|
|
model.accessors.emplace_back();
|
|
Accessor& miscAccessor = model.accessors.back();
|
|
miscAccessor.bufferView = static_cast<int>(model.bufferViews.size() - 1);
|
|
miscAccessor.byteOffset = 0;
|
|
miscAccessor.count = static_cast<int64_t>(miscData.size());
|
|
miscAccessor.componentType = Accessor::ComponentType::FLOAT;
|
|
miscAccessor.type = Accessor::Type::VEC4;
|
|
|
|
int miscAccessorIdx = static_cast<int>(model.accessors.size() - 1);
|
|
|
|
model.meshes.emplace_back();
|
|
Mesh& mesh = model.meshes.back();
|
|
mesh.primitives.emplace_back();
|
|
|
|
MeshPrimitive& primitive = mesh.primitives.back();
|
|
primitive.mode = MeshPrimitive::Mode::POINTS;
|
|
primitive.attributes["_CESIUMOVERLAY_0"] = uvAccessorIdx;
|
|
primitive.attributes["POSITION"] = positionAccessorIdx;
|
|
primitive.attributes["MISC_DATA"] = miscAccessorIdx;
|
|
|
|
// create node and update bounding volume
|
|
model.nodes.emplace_back();
|
|
Node& node = model.nodes[0];
|
|
node.mesh = static_cast<int>(model.meshes.size() - 1);
|
|
node.matrix = {
|
|
1.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
0.0,
|
|
-1.0,
|
|
0.0,
|
|
0.0,
|
|
1.0,
|
|
0.0,
|
|
0.0,
|
|
center.x,
|
|
center.z,
|
|
-center.y,
|
|
1.0};
|
|
|
|
for (uint32_t i = 0; i < 4; i++) {
|
|
CesiumGeometry::UpsampledQuadtreeNode quadrant{
|
|
CesiumGeometry::QuadtreeTileID(1, i % 2, (uint32_t)std::floor(i / 2))};
|
|
|
|
Model upsampledModel =
|
|
*RasterOverlayUtilities::upsampleGltfForRasterOverlays(
|
|
model,
|
|
quadrant,
|
|
false);
|
|
|
|
REQUIRE(upsampledModel.meshes.size() == 1);
|
|
const Mesh& upsampledMesh = upsampledModel.meshes.back();
|
|
|
|
REQUIRE(upsampledMesh.primitives.size() == 1);
|
|
const MeshPrimitive& upsampledPrimitive = upsampledMesh.primitives.back();
|
|
|
|
REQUIRE(
|
|
upsampledPrimitive.attributes.find("POSITION") !=
|
|
upsampledPrimitive.attributes.end());
|
|
AccessorView<glm::vec3> upsampledPosition(
|
|
upsampledModel,
|
|
upsampledPrimitive.attributes.at("POSITION"));
|
|
AccessorView<glm::vec4> upsampledMiscData(
|
|
upsampledModel,
|
|
upsampledPrimitive.attributes.at("MISC_DATA"));
|
|
|
|
const std::vector<size_t>& quadrantIndices = pointsPerQuadrant[i];
|
|
REQUIRE(upsampledPosition.size() == quadrantIndices.size());
|
|
for (int64_t j = 0; j < upsampledPosition.size(); j++) {
|
|
CHECK(upsampledPosition[j] == positions[quadrantIndices[(size_t)j]]);
|
|
CHECK(upsampledMiscData[j] == miscData[quadrantIndices[(size_t)j]]);
|
|
}
|
|
}
|
|
}
|