1#include "OGC3DTilesUtils.h"
3#include "OGC3DTilesTileset.h"
5#include "Foundation/Logging/Logger.h"
6#include "Foundation/Geometry/GeodeticUtils.h"
17OGC3DTilesUtils::isInsideBox(
const glm::dvec3& point,
const Cogs::Geometry::DBoundingBox& box)
19 return point.x > box.min.x && point.x < box.max.x &&
20 point.y > box.min.y && point.y < box.max.y &&
21 point.z > box.min.z && point.z < box.max.z;
25OGC3DTilesUtils::distanceFromBBox(
const glm::dvec3 point,
const Cogs::Geometry::DBoundingBox& bbox)
27 if (OGC3DTilesUtils::isInsideBox(point, bbox)) {
32 const double dx = std::max({ bbox.min.x - point.x, 0.0, point.x - bbox.max.x });
33 const double dy = std::max({ bbox.min.y - point.y, 0.0, point.y - bbox.max.y });
34 const double dz = std::max({ bbox.min.z - point.z, 0.0, point.z - bbox.max.z });
35 return glm::length(glm::dvec3(dx, dy, dz));
39OGC3DTilesUtils::printBBox(
const Cogs::Geometry::BoundingBox& bbox,
const std::string& prefix)
41 printf(
"%s: bbox=<%.2f, %.2f, %.2f> | <%.2f, %.2f, %.2f> (w=%.2f, h=%.2f, d=%.2f)\n",
43 bbox.min.x, bbox.min.y, bbox.min.z,
44 bbox.max.x, bbox.max.y, bbox.max.z,
45 float(bbox.max.x - bbox.min.x),
float(bbox.max.y - bbox.min.y),
float(bbox.max.z - bbox.min.z));
49OGC3DTilesUtils::printBBox(
const Cogs::Geometry::DBoundingBox& bbox,
const std::string& prefix)
51 printf(
"%s: bbox=<%.2f, %.2f, %.2f> | <%.2f, %.2f, %.2f> (w=%.2f, h=%.2f, d=%.2f)\n",
53 bbox.min.x, bbox.min.y, bbox.min.z,
54 bbox.max.x, bbox.max.y, bbox.max.z,
55 float(bbox.max.x - bbox.min.x),
float(bbox.max.y - bbox.min.y),
float(bbox.max.z - bbox.min.z));
59OGC3DTilesUtils::transformBBox(Cogs::Geometry::DBoundingBox& bbox,
const glm::mat4& mat)
61 glm::dvec3 points[2] = { bbox.min, bbox.max };
63 static constexpr double mx = std::numeric_limits<double>::max();
64 static constexpr double mn = -mx;
65 bbox.min = glm::dvec3(mx, mx, mx);
66 bbox.max = glm::dvec3(mn, mn, mn);
68 for (
int i = 0; i < 8; i++) {
69 glm::dvec4 corner = glm::dvec4(points[(i & 4) >> 2].x, points[(i & 2) >> 1].y, points[i & 1].z, 1.0);
70 glm::dvec4 xfmd = mat * corner;
71 bbox += glm::dvec3(xfmd.x, xfmd.y, xfmd.z);
76OGC3DTilesUtils::translateBBox(Cogs::Geometry::DBoundingBox& bbox,
const glm::dvec3& offset)
83OGC3DTilesUtils::bboxInsideViewFrustum(
const Cogs::Geometry::DBoundingBox& bbox,
const glm::mat4& viewProjectionMatrix)
86 glm::dvec3 corners[8] = {
88 glm::dvec3(bbox.min.x, bbox.min.y, bbox.max.z),
89 glm::dvec3(bbox.min.x, bbox.max.y, bbox.min.z),
90 glm::dvec3(bbox.min.x, bbox.max.y, bbox.max.z),
91 glm::dvec3(bbox.max.x, bbox.min.y, bbox.min.z),
92 glm::dvec3(bbox.max.x, bbox.min.y, bbox.max.z),
93 glm::dvec3(bbox.max.x, bbox.max.y, bbox.min.z),
97 uint32_t allInsideMask = 0b111111;
99 uint32_t anyInsideMask = 0u;
100 for (
size_t i = 0; i < 8; i++) {
101 glm::dvec4 p = viewProjectionMatrix * glm::dvec4(corners[i], 1.f);
102 const uint32_t cornerMask =
103 (p.x <= p.w ? 1 : 0) |
104 (p.y <= p.w ? 2 : 0) |
105 (p.z <= p.w ? 4 : 0) |
106 (-p.w <= p.x ? 8 : 0) |
107 (-p.w <= p.y ? 16 : 0) |
108 (-p.w <= p.z ? 32 : 0);
109 anyInsideMask = anyInsideMask | cornerMask;
110 allInsideMask = allInsideMask & cornerMask;
113 if (anyInsideMask != 0b111111) {
128latLngAltToVec3(
double latitudeRad,
double longitudeRad,
double altitudeMeter)
130 constexpr double semimajorAxis = 6378137.0;
131 constexpr double semiminorAxis = 6356752.314245;
132 const double sinLat = std::sin(latitudeRad);
133 const double cosLat = std::cos(latitudeRad);
134 const double sinLong = std::sin(longitudeRad);
135 const double cosLong = std::cos(longitudeRad);
136 constexpr double ellipsoidFlatness = ((semimajorAxis - semiminorAxis) / semimajorAxis);
137 constexpr double firstEccentricitySquared = (ellipsoidFlatness * (2.0 - ellipsoidFlatness));
138 const double n = semimajorAxis / std::sqrt(1.0 - firstEccentricitySquared * sinLat * sinLat);
140 return glm::dvec3((altitudeMeter + n) * cosLat * cosLong,
141 (altitudeMeter + n) * cosLat * sinLong,
142 (altitudeMeter + (1.0 - firstEccentricitySquared) * n) * sinLat);
146Cogs::Geometry::DBoundingBox
149 Cogs::Geometry::DBoundingBox bbox = Cogs::Geometry::makeEmptyBoundingBox<Cogs::Geometry::DBoundingBox>();
151 if (bv.type == OGC3DTiles::BoundingVolumeType::BOX) {
155 glm::vec3 center = glm::vec3(bv.values[0], bv.values[1], bv.values[2]);
157 glm::vec3 dx = glm::abs(glm::vec3(bv.values[3], bv.values[4], bv.values[5]));
158 glm::vec3 dy = glm::abs(glm::vec3(bv.values[6], bv.values[7], bv.values[8]));
159 glm::vec3 dz = glm::abs(glm::vec3(bv.values[9], bv.values[10], bv.values[11]));
169 else if (bv.type == OGC3DTiles::BoundingVolumeType::REGION) {
172 const double west = bv.values[0];
173 const double south = bv.values[1];
174 const double east = bv.values[2];
175 const double north = bv.values[3];
176 const double minHeight = bv.values[4];
177 const double maxHeight = bv.values[5];
183 const double latRange = std::abs(east - west);
184 const double lngRange = std::abs(north - south);
186 if (latRange > glm::pi<double>()) {
187 latSteps = int(latRange / (glm::pi<double>() / 3.0));
188 assert(latSteps >= 1);
192 if (lngRange > glm::pi<double>()) {
193 lngSteps = int(lngRange / (glm::pi<double>() / 3.0));
194 assert(lngSteps >= 1);
197 bbox += latLngAltToVec3(north, west, minHeight);
199 for (
int i = 1; i <= latSteps; ++i) {
200 for (
int j = 1; j <= lngSteps; ++j) {
201 bbox += latLngAltToVec3((south + j * (lngRange / lngSteps)), (west + i * (latRange / latSteps)), minHeight);
202 bbox += latLngAltToVec3((south + j * (lngRange / lngSteps)), (west + i * (latRange / latSteps)), maxHeight);
206 bbox += latLngAltToVec3(south, east, maxHeight);
209 assert(std::abs(bbox.min.x - bbox.max.x) > 0.1 &&
210 std::abs(bbox.min.y - bbox.max.y) > 0.1 &&
211 std::abs(bbox.min.z - bbox.max.z) > 0.1 &&
"BBox is flat!");
217 else if (bv.type == OGC3DTiles::BoundingVolumeType::SPHERE) {
218 glm::vec3 center = glm::vec3(bv.values[0], bv.values[1], bv.values[2]);
222 glm::vec3 dx = center + glm::vec3(bv.values[3], 0, 0);
223 glm::vec3 dy = center + glm::vec3(0, bv.values[3], 0);
224 glm::vec3 dz = center + glm::vec3(0, 0, bv.values[3]);
233 LOG_ERROR(logger,
"Invalid BoundingVolume: No valid candidates.");
234 assert(
false &&
"Invalid BoundingVolume: No valid candidates.");
237 OGC3DTilesUtils::transformBBox(bbox, transform);
245Cogs::Geometry::DBoundingBox
246Cogs::Core::OGC3DTilesUtils::getBBoxFromCoord(
const OGC3DTiles::Coord& tileCoord,
const Cogs::Geometry::DBoundingBox& parentBBox)
248 const double width = parentBBox.max.x > parentBBox.min.x ? parentBBox.max.x - parentBBox.min.x : parentBBox.min.x - parentBBox.max.x;
249 const double height = parentBBox.max.y > parentBBox.min.y ? parentBBox.max.y - parentBBox.min.y : parentBBox.min.y - parentBBox.max.y;
250 const double depth = parentBBox.max.z > parentBBox.min.z ? parentBBox.max.z - parentBBox.min.z : parentBBox.min.z - parentBBox.max.z;
252 assert(width >= 0 &&
"Invalid bbox width: Cannot be negative.");
253 assert(height >= 0 &&
"Invalid bbox height: Cannot be negative.");
254 assert(depth >= 0 &&
"Invalid bbox depth: Cannot be negative.");
256 const bool isOctTree = tileCoord.z >= 0;
257 const double range =
static_cast<double>(std::pow(2, tileCoord.level));
259 const double dx = width / range;
260 const double dy = height / range;
261 const double dz = depth / range;
263 glm::dvec3 min = glm::dvec3(parentBBox.min.x + tileCoord.x * dx,
264 parentBBox.min.y + tileCoord.y * dy,
265 isOctTree ? (parentBBox.min.z + tileCoord.z * dz) : parentBBox.min.z);
267 glm::dvec3 max = glm::dvec3(parentBBox.min.x + (tileCoord.x + 1) * dx,
268 parentBBox.min.y + (tileCoord.y + 1) * dy,
269 isOctTree ? (parentBBox.min.z + (tileCoord.z + 1) * dz) : parentBBox.max.z);
271 Cogs::Geometry::DBoundingBox bbox = Cogs::Geometry::makeEmptyBoundingBox<Cogs::Geometry::DBoundingBox>();
283 const double range =
static_cast<float>(std::pow(2, tileCoord.level));
284 const bool isOctTree = tileCoord.z >= 0;
286 if (globalBoundingVolume.type == OGC3DTiles::BoundingVolumeType::BOX) {
287 glm::dvec3 center = glm::dvec3(globalBoundingVolume.values[0], globalBoundingVolume.values[1], globalBoundingVolume.values[2]);
288 glm::dvec3 xa = glm::abs(glm::dvec3(globalBoundingVolume.values[3], globalBoundingVolume.values[4], globalBoundingVolume.values[5]));
289 glm::dvec3 ya = glm::abs(glm::dvec3(globalBoundingVolume.values[6], globalBoundingVolume.values[7], globalBoundingVolume.values[8]));
290 glm::dvec3 za = glm::abs(glm::dvec3(globalBoundingVolume.values[9], globalBoundingVolume.values[10], globalBoundingVolume.values[11]));
294 glm::dvec3 xan = glm::normalize(xa);
295 glm::dvec3 yan = glm::normalize(ya);
296 glm::dvec3 zan = glm::normalize(za);
298 const double dx = glm::length(xa) / range;
299 const double dy = glm::length(ya) / range;
300 const double dz = glm::length(za) / range;
302 const glm::dvec3 corner = center - xa - ya - za;
303 const glm::dvec3 newcenter = corner + xan * (dx * (tileCoord.x * 2 + 1)) + yan * (dy * (tileCoord.y * 2 + 1)) + zan * (dz * (tileCoord.z * 2 + 1));
305 bv.values[0] = newcenter.x;
306 bv.values[1] = newcenter.y;
308 const glm::dvec3 newxa = xa / range;
309 const glm::dvec3 newya = ya / range;
311 bv.values[3] = newxa.x;
312 bv.values[4] = newxa.y;
313 bv.values[5] = newxa.z;
315 bv.values[6] = newya.x;
316 bv.values[7] = newya.y;
317 bv.values[8] = newya.z;
320 bv.values[2] = newcenter.z;
321 const glm::dvec3 newza = za / range;
322 bv.values[9] = newza.x;
323 bv.values[10] = newza.y;
324 bv.values[11] = newza.z;
327 bv.values[2] = globalBoundingVolume.values[2];
328 bv.values[9] = globalBoundingVolume.values[9];
329 bv.values[10] = globalBoundingVolume.values[10];
330 bv.values[11] = globalBoundingVolume.values[11];
333 else if (globalBoundingVolume.type == OGC3DTiles::BoundingVolumeType::REGION) {
335 const double width = globalBoundingVolume.values[2] > globalBoundingVolume.values[0] ? globalBoundingVolume.values[2] - globalBoundingVolume.values[0] : globalBoundingVolume.values[0] - globalBoundingVolume.values[2];
337 const double height = globalBoundingVolume.values[3] > globalBoundingVolume.values[1] ? globalBoundingVolume.values[3] - globalBoundingVolume.values[1] : globalBoundingVolume.values[1] - globalBoundingVolume.values[3];
339 const double depth = globalBoundingVolume.values[5] > globalBoundingVolume.values[4] ? globalBoundingVolume.values[5] - globalBoundingVolume.values[4] : globalBoundingVolume.values[4] - globalBoundingVolume.values[5];
341 const double dx = width / range;
342 const double dy = height / range;
343 const double dz = depth / range;
345 bv.values[0] = globalBoundingVolume.values[0] + tileCoord.x * dx;
346 bv.values[1] = globalBoundingVolume.values[1] + tileCoord.y * dy;
347 bv.values[2] = globalBoundingVolume.values[0] + (tileCoord.x + 1) * dx;
348 bv.values[3] = globalBoundingVolume.values[1] + (tileCoord.y + 1) * dy;
351 bv.values[4] = globalBoundingVolume.values[4] + tileCoord.z * dz;
352 bv.values[5] = globalBoundingVolume.values[4] + (tileCoord.z + 1) * dz;
355 bv.values[4] = globalBoundingVolume.values[4];
356 bv.values[5] = globalBoundingVolume.values[5];
360 printf(
"%s: %s (range=%f, w=%f, h=%f, d=%f, dx=%f, dy=%f, dz=%f)\n (global: %s)\n", tileCoord.toString().c_str(), bv.toString().c_str(),
361 range, width, height, depth, dx, dy, dz, globalBoundingVolume.toString().c_str());
364 else if (globalBoundingVolume.type == OGC3DTiles::BoundingVolumeType::SPHERE) {
365 const double delta = globalBoundingVolume.values[3] / range;
366 const glm::dvec3 newcenter = glm::dvec3(delta * tileCoord.x, delta * tileCoord.y, delta * tileCoord.z);
367 bv.values[0] = newcenter.x;
368 bv.values[1] = newcenter.y;
369 bv.values[2] = newcenter.z;
370 bv.values[3] = delta;
373 LOG_WARNING(logger,
"getBoundingVolumeFromCoord(): Division of sphere-based boundingvolumes not tested yet.");
376 LOG_ERROR(logger,
"Invalid BoundingVolume: No valid candidates.");
377 assert(
false &&
"Invalid BoundingVolume: No valid candidates.");
Log implementation class.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
constexpr Log getLogger(const char(&name)[LEN]) noexcept