1#include "OGC3DTilesUtils.h"
3#include "OGC3DTilesTileset.h"
5#include "Foundation/Logging/Logger.h"
6#include "Foundation/Geometry/GeodeticUtils.h"
8#include <glm/gtc/type_ptr.hpp>
18OGC3DTilesUtils::isInsideBox(
const glm::dvec3& point,
const Cogs::Geometry::DBoundingBox& box)
20 return point.x > box.min.x && point.x < box.max.x &&
21 point.y > box.min.y && point.y < box.max.y &&
22 point.z > box.min.z && point.z < box.max.z;
26OGC3DTilesUtils::distanceFromBBox(
const glm::dvec3 point,
const Cogs::Geometry::DBoundingBox& bbox)
28 if (OGC3DTilesUtils::isInsideBox(point, bbox)) {
33 const double dx = std::max({ bbox.min.x - point.x, 0.0, point.x - bbox.max.x });
34 const double dy = std::max({ bbox.min.y - point.y, 0.0, point.y - bbox.max.y });
35 const double dz = std::max({ bbox.min.z - point.z, 0.0, point.z - bbox.max.z });
36 return glm::length(glm::dvec3(dx, dy, dz));
40OGC3DTilesUtils::printBBox(
const Cogs::Geometry::BoundingBox& bbox,
const std::string& prefix)
42 printf(
"%s: bbox=<%.2f, %.2f, %.2f> | <%.2f, %.2f, %.2f> (w=%.2f, h=%.2f, d=%.2f)\n",
44 bbox.min.x, bbox.min.y, bbox.min.z,
45 bbox.max.x, bbox.max.y, bbox.max.z,
46 float(bbox.max.x - bbox.min.x),
float(bbox.max.y - bbox.min.y),
float(bbox.max.z - bbox.min.z));
50OGC3DTilesUtils::printBBox(
const Cogs::Geometry::DBoundingBox& bbox,
const std::string& prefix)
52 printf(
"%s: bbox=<%.2f, %.2f, %.2f> | <%.2f, %.2f, %.2f> (w=%.2f, h=%.2f, d=%.2f)\n",
54 bbox.min.x, bbox.min.y, bbox.min.z,
55 bbox.max.x, bbox.max.y, bbox.max.z,
56 float(bbox.max.x - bbox.min.x),
float(bbox.max.y - bbox.min.y),
float(bbox.max.z - bbox.min.z));
60OGC3DTilesUtils::transformBBox(Cogs::Geometry::DBoundingBox& bbox,
const glm::mat4& mat)
62 const glm::dvec3 points[2] = { bbox.min, bbox.max };
64 static constexpr double mx = std::numeric_limits<double>::max();
65 static constexpr double mn = -mx;
66 bbox.min = glm::dvec3(mx);
67 bbox.max = glm::dvec3(mn);
69 for (
int i = 0; i < 8; i++) {
70 const glm::dvec4 corner = glm::dvec4(points[(i & 4) >> 2].x, points[(i & 2) >> 1].y, points[i & 1].z, 1.0);
71 const glm::dvec4 xfmd = mat * corner;
77OGC3DTilesUtils::translateBBox(Cogs::Geometry::DBoundingBox& bbox,
const glm::dvec3& offset)
84OGC3DTilesUtils::bboxInsideViewFrustum(
const Cogs::Geometry::DBoundingBox& bbox,
const glm::mat4& viewProjectionMatrix)
87 const glm::dvec3 corners[8] = {
89 glm::dvec3(bbox.min.x, bbox.min.y, bbox.max.z),
90 glm::dvec3(bbox.min.x, bbox.max.y, bbox.min.z),
91 glm::dvec3(bbox.min.x, bbox.max.y, bbox.max.z),
92 glm::dvec3(bbox.max.x, bbox.min.y, bbox.min.z),
93 glm::dvec3(bbox.max.x, bbox.min.y, bbox.max.z),
94 glm::dvec3(bbox.max.x, bbox.max.y, bbox.min.z),
98 uint32_t allInsideMask = 0b111111;
100 uint32_t anyInsideMask = 0u;
101 for (
size_t i = 0; i < 8; i++) {
102 const glm::dvec4 p = viewProjectionMatrix * glm::dvec4(corners[i], 1.f);
103 const uint32_t cornerMask =
104 (p.x <= p.w ? 1 : 0) |
105 (p.y <= p.w ? 2 : 0) |
106 (p.z <= p.w ? 4 : 0) |
107 (-p.w <= p.x ? 8 : 0) |
108 (-p.w <= p.y ? 16 : 0) |
109 (-p.w <= p.z ? 32 : 0);
110 anyInsideMask = anyInsideMask | cornerMask;
111 allInsideMask = allInsideMask & cornerMask;
114 if (anyInsideMask != 0b111111) {
129latLngAltToVec3(
double latitudeRad,
double longitudeRad,
double altitudeMeter)
131 constexpr double semimajorAxis = 6378137.0;
132 constexpr double semiminorAxis = 6356752.314245;
133 const double sinLat = std::sin(latitudeRad);
134 const double cosLat = std::cos(latitudeRad);
135 const double sinLong = std::sin(longitudeRad);
136 const double cosLong = std::cos(longitudeRad);
137 constexpr double ellipsoidFlatness = ((semimajorAxis - semiminorAxis) / semimajorAxis);
138 constexpr double firstEccentricitySquared = (ellipsoidFlatness * (2.0 - ellipsoidFlatness));
139 const double n = semimajorAxis / std::sqrt(1.0 - firstEccentricitySquared * sinLat * sinLat);
141 return glm::dvec3((altitudeMeter + n) * cosLat * cosLong,
142 (altitudeMeter + n) * cosLat * sinLong,
143 (altitudeMeter + (1.0 - firstEccentricitySquared) * n) * sinLat);
147Cogs::Geometry::DBoundingBox
150 Cogs::Geometry::DBoundingBox bbox = Cogs::Geometry::makeEmptyBoundingBox<Cogs::Geometry::DBoundingBox>();
152 if (bv.type == OGC3DTiles::BoundingVolumeType::BOX) {
156 glm::vec3 center = glm::make_vec3(&bv.values[0]);
158 glm::vec3 dx = glm::abs(glm::make_vec3(&bv.values[3]));
159 glm::vec3 dy = glm::abs(glm::make_vec3(&bv.values[6]));
160 glm::vec3 dz = glm::abs(glm::make_vec3(&bv.values[9]));
171 else if (bv.type == OGC3DTiles::BoundingVolumeType::REGION) {
174 const double west = bv.values[0];
175 const double south = bv.values[1];
176 const double east = bv.values[2];
177 const double north = bv.values[3];
178 const double minHeight = bv.values[4];
179 const double maxHeight = bv.values[5];
185 const double latRange = std::abs(east - west);
186 const double lngRange = std::abs(north - south);
188 if (latRange > glm::pi<double>()) {
189 latSteps = int(latRange / (glm::pi<double>() / 3.0));
190 assert(latSteps >= 1);
191 assert(latSteps >= 1);
195 if (lngRange > glm::pi<double>()) {
196 lngSteps = int(lngRange / (glm::pi<double>() / 3.0));
197 assert(lngSteps >= 1);
200 bbox += latLngAltToVec3(north, west, minHeight);
202 for (
int i = 1; i <= latSteps; ++i) {
203 for (
int j = 1; j <= lngSteps; ++j) {
204 bbox += latLngAltToVec3((south + j * (lngRange / lngSteps)), (west + i * (latRange / latSteps)), minHeight);
205 bbox += latLngAltToVec3((south + j * (lngRange / lngSteps)), (west + i * (latRange / latSteps)), maxHeight);
209 bbox += latLngAltToVec3(south, east, maxHeight);
212 assert(std::abs(bbox.min.x - bbox.max.x) > 0.1 &&
213 std::abs(bbox.min.y - bbox.max.y) > 0.1 &&
214 std::abs(bbox.min.z - bbox.max.z) > 0.1 &&
"BBox is flat!");
220 else if (bv.type == OGC3DTiles::BoundingVolumeType::SPHERE) {
221 glm::vec3 center = glm::vec3(bv.values[0], bv.values[1], bv.values[2]);
225 glm::vec3 dx = center + glm::vec3(bv.values[3], 0, 0);
226 glm::vec3 dy = center + glm::vec3(0, bv.values[3], 0);
227 glm::vec3 dz = center + glm::vec3(0, 0, bv.values[3]);
236 LOG_ERROR(logger,
"Invalid BoundingVolume: No valid candidates.");
237 assert(
false &&
"Invalid BoundingVolume: No valid candidates.");
240 OGC3DTilesUtils::transformBBox(bbox, transform);
248Cogs::Geometry::DBoundingBox
249Cogs::Core::OGC3DTilesUtils::getBBoxFromCoord(
const OGC3DTiles::Coord& tileCoord,
const Cogs::Geometry::DBoundingBox& parentBBox)
251 const double width = parentBBox.max.x > parentBBox.min.x ? parentBBox.max.x - parentBBox.min.x : parentBBox.min.x - parentBBox.max.x;
252 const double height = parentBBox.max.y > parentBBox.min.y ? parentBBox.max.y - parentBBox.min.y : parentBBox.min.y - parentBBox.max.y;
253 const double depth = parentBBox.max.z > parentBBox.min.z ? parentBBox.max.z - parentBBox.min.z : parentBBox.min.z - parentBBox.max.z;
255 assert(width >= 0 &&
"Invalid bbox width: Cannot be negative.");
256 assert(height >= 0 &&
"Invalid bbox height: Cannot be negative.");
257 assert(depth >= 0 &&
"Invalid bbox depth: Cannot be negative.");
259 const bool isOctTree = tileCoord.z >= 0;
260 const double range =
static_cast<const double>(fastPow2(tileCoord.level));
262 const double dx = width / range;
263 const double dy = height / range;
264 const double dz = depth / range;
266 const glm::dvec3 min = glm::dvec3(parentBBox.min.x + tileCoord.x * dx,
267 parentBBox.min.y + tileCoord.y * dy,
268 isOctTree ? (parentBBox.min.z + tileCoord.z * dz) : parentBBox.min.z);
270 const glm::dvec3 max = glm::dvec3(parentBBox.min.x + (tileCoord.x + 1) * dx,
271 parentBBox.min.y + (tileCoord.y + 1) * dy,
272 isOctTree ? (parentBBox.min.z + (tileCoord.z + 1) * dz) : parentBBox.max.z);
274 Cogs::Geometry::DBoundingBox bbox = Cogs::Geometry::makeEmptyBoundingBox<Cogs::Geometry::DBoundingBox>();
286 const double range =
static_cast<const double>(fastPow2(tileCoord.level));
287 const bool isOctTree = tileCoord.z >= 0;
289 if (globalBoundingVolume.type == OGC3DTiles::BoundingVolumeType::BOX) {
290 const glm::dvec3 center = glm::make_vec3(&globalBoundingVolume.values[0]);
291 const glm::dvec3 xa = glm::abs(glm::make_vec3(&globalBoundingVolume.values[3]));
292 const glm::dvec3 ya = glm::abs(glm::make_vec3(&globalBoundingVolume.values[6]));
293 const glm::dvec3 za = glm::abs(glm::make_vec3(&globalBoundingVolume.values[9]));
295 const glm::dvec3 xan = glm::normalize(xa);
296 const glm::dvec3 yan = glm::normalize(ya);
297 const glm::dvec3 zan = glm::normalize(za);
299 const double dx = glm::length(xa) / range;
300 const double dy = glm::length(ya) / range;
301 const double dz = glm::length(za) / range;
303 const glm::dvec3 corner = center - xa - ya - za;
304 const glm::dvec3 newcenter = corner + xan * (dx * (tileCoord.x * 2 + 1)) + yan * (dy * (tileCoord.y * 2 + 1)) + zan * (dz * (tileCoord.z * 2 + 1));
306 bv.values[0] = newcenter.x;
307 bv.values[1] = newcenter.y;
309 const glm::dvec3 newxa = xa / range;
310 const glm::dvec3 newya = ya / range;
312 bv.values[3] = newxa.x;
313 bv.values[4] = newxa.y;
314 bv.values[5] = newxa.z;
316 bv.values[6] = newya.x;
317 bv.values[7] = newya.y;
318 bv.values[8] = newya.z;
321 bv.values[2] = newcenter.z;
322 const glm::dvec3 newza = za / range;
323 bv.values[9] = newza.x;
324 bv.values[10] = newza.y;
325 bv.values[11] = newza.z;
328 bv.values[2] = globalBoundingVolume.values[2];
329 bv.values[9] = globalBoundingVolume.values[9];
330 bv.values[10] = globalBoundingVolume.values[10];
331 bv.values[11] = globalBoundingVolume.values[11];
334 else if (globalBoundingVolume.type == OGC3DTiles::BoundingVolumeType::REGION) {
336 const double width = globalBoundingVolume.values[2] > globalBoundingVolume.values[0] ?
337 globalBoundingVolume.values[2] - globalBoundingVolume.values[0] :
338 globalBoundingVolume.values[0] - globalBoundingVolume.values[2];
340 const double height = globalBoundingVolume.values[3] > globalBoundingVolume.values[1] ?
341 globalBoundingVolume.values[3] - globalBoundingVolume.values[1] :
342 globalBoundingVolume.values[1] - globalBoundingVolume.values[3];
344 const double depth = globalBoundingVolume.values[5] > globalBoundingVolume.values[4] ?
345 globalBoundingVolume.values[5] - globalBoundingVolume.values[4] :
346 globalBoundingVolume.values[4] - globalBoundingVolume.values[5];
348 const double dx = width / range;
349 const double dy = height / range;
350 const double dz = depth / range;
352 bv.values[0] = globalBoundingVolume.values[0] + tileCoord.x * dx;
353 bv.values[1] = globalBoundingVolume.values[1] + tileCoord.y * dy;
354 bv.values[2] = globalBoundingVolume.values[0] + (tileCoord.x + 1) * dx;
355 bv.values[3] = globalBoundingVolume.values[1] + (tileCoord.y + 1) * dy;
358 bv.values[4] = globalBoundingVolume.values[4] + tileCoord.z * dz;
359 bv.values[5] = globalBoundingVolume.values[4] + (tileCoord.z + 1) * dz;
362 bv.values[4] = globalBoundingVolume.values[4];
363 bv.values[5] = globalBoundingVolume.values[5];
367 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(),
368 range, width, height, depth, dx, dy, dz, globalBoundingVolume.toString().c_str());
371 else if (globalBoundingVolume.type == OGC3DTiles::BoundingVolumeType::SPHERE) {
372 const double delta = globalBoundingVolume.values[3] / range;
373 const glm::dvec3 newcenter = glm::dvec3(delta * tileCoord.x, delta * tileCoord.y, delta * tileCoord.z);
374 bv.values[0] = newcenter.x;
375 bv.values[1] = newcenter.y;
376 bv.values[2] = newcenter.z;
377 bv.values[3] = delta;
380 LOG_WARNING(logger,
"getBoundingVolumeFromCoord(): Division of sphere-based boundingvolumes not tested yet.");
383 LOG_ERROR(logger,
"Invalid BoundingVolume: No valid candidates.");
384 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