Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(3d-tiles): implicit tiling v1.1 #2549

Merged
merged 6 commits into from
Jul 14, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 33 additions & 24 deletions modules/3d-tiles/src/lib/parsers/helpers/parse-3d-implicit-tiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {getS2CellIdFromToken, getS2ChildCellId, getS2TokenFromCellId} from '../.
import type {S2VolumeInfo} from '../../utils/obb/s2-corners-to-obb';
import {convertS2BoundingVolumetoOBB} from '../../utils/obb/s2-corners-to-obb';
import Long from 'long';
import {Tiles3DLoaderOptions} from '../../..';

const QUADTREE_DEVISION_COUNT = 4;
const OCTREE_DEVISION_COUNT = 8;
Expand Down Expand Up @@ -84,15 +85,16 @@ function getChildS2VolumeBox(
// eslint-disable-next-line max-statements
export async function parseImplicitTiles(params: {
subtree: Subtree;
options: any;
implicitOptions: any;
parentData?: {mortonIndex: number; x: number; y: number; z: number};
childIndex?: number;
level?: number;
globalData?: {level: number; mortonIndex: number; x: number; y: number; z: number};
s2VolumeBox?: S2VolumeBox;
loaderOptions: Tiles3DLoaderOptions;
}) {
const {
options,
implicitOptions,
parentData = {
mortonIndex: 0,
x: 0,
Expand All @@ -107,7 +109,8 @@ export async function parseImplicitTiles(params: {
y: 0,
z: 0
},
s2VolumeBox
s2VolumeBox,
loaderOptions
} = params;
let {subtree, level = 0} = params;
const {
Expand All @@ -117,44 +120,48 @@ export async function parseImplicitTiles(params: {
contentUrlTemplate,
subtreesUriTemplate,
basePath
} = options;

} = implicitOptions;
const tile = {children: [], lodMetricValue: 0, contentUrl: ''};

const lev = level + globalData.level;
if (lev > maximumLevel) {
return tile;
}

const childrenPerTile = SUBDIVISION_COUNT_MAP[subdivisionScheme];
const bitsPerTile = Math.log2(childrenPerTile);

// childIndex is in range [0, 7]
// childIndex is in range [0,4] for quadtrees and [0, 7] for octrees
const childX = childIndex & 0b01; // Get first bit for X
const childY = (childIndex >> 1) & 0b01; // Get second bit for Y
const childZ = (childIndex >> 2) & 0b01; // Get third bit for Z

const levelOffset = (childrenPerTile ** level - 1) / (childrenPerTile - 1);
let childTileMortonIndex = concatBits(parentData.mortonIndex, childIndex);
let childTileMortonIndex = concatBits(parentData.mortonIndex, childIndex, bitsPerTile);
let tileAvailabilityIndex = levelOffset + childTileMortonIndex;

// Local tile coordinates
let childTileX = concatBits(parentData.x, childX);
let childTileY = concatBits(parentData.y, childY);
let childTileZ = concatBits(parentData.z, childZ);
let childTileX = concatBits(parentData.x, childX, 1);
let childTileY = concatBits(parentData.y, childY, 1);
let childTileZ = concatBits(parentData.z, childZ, 1);

let isChildSubtreeAvailable = false;

if (level + 1 > subtreeLevels) {
if (level >= subtreeLevels) {
isChildSubtreeAvailable = getAvailabilityResult(
subtree.childSubtreeAvailability,
childTileMortonIndex
);
}

const x = concatBits(globalData.x, childTileX);
const y = concatBits(globalData.y, childTileY);
const z = concatBits(globalData.z, childTileZ);
const lev = level + globalData.level;
const x = concatBits(globalData.x, childTileX, level * bitsPerTile);
const y = concatBits(globalData.y, childTileY, level * bitsPerTile);
const z = concatBits(globalData.z, childTileZ, level * bitsPerTile);

if (isChildSubtreeAvailable) {
const subtreePath = `${basePath}/${subtreesUriTemplate}`;
const childSubtreeUrl = replaceContentUrlTemplate(subtreePath, lev, x, y, z);
const childSubtree = await load(childSubtreeUrl, Tile3DSubtreeLoader);
const childSubtree = await load(childSubtreeUrl, Tile3DSubtreeLoader, loaderOptions);

subtree = childSubtree;

Expand All @@ -174,7 +181,7 @@ export async function parseImplicitTiles(params: {

const isTileAvailable = getAvailabilityResult(subtree.tileAvailability, tileAvailabilityIndex);

if (!isTileAvailable || level > maximumLevel) {
if (!isTileAvailable) {
return tile;
}

Expand All @@ -200,11 +207,12 @@ export async function parseImplicitTiles(params: {
// Recursive calling...
const childTileParsed = await parseImplicitTiles({
subtree,
options,
implicitOptions,
loaderOptions,
parentData: pData,
childIndex: index,
level: childTileLevel,
globalData,
globalData: {...globalData},
s2VolumeBox: childS2VolumeBox
});

Expand All @@ -215,7 +223,7 @@ export async function parseImplicitTiles(params: {
childTileParsed,
globalLevel,
childCoordinates,
options,
implicitOptions,
s2VolumeBox
);
// @ts-ignore
Expand Down Expand Up @@ -363,11 +371,12 @@ function calculateBoundingVolumeForChildTile(

/**
* Do binary concatenation
* @param first
* @param second
* @param higher - number to put to higher part of result
* @param lower - number to put to lower part of result
* @param shift - number of bits to shift lower number
*/
function concatBits(first: number, second: number): number {
return parseInt(first.toString(2) + second.toString(2), 2);
function concatBits(higher: number, lower: number, shift: number): number {
return (higher << shift) + lower;
}

/**
Expand Down
11 changes: 7 additions & 4 deletions modules/3d-tiles/src/lib/parsers/parse-3d-tile-header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ export async function normalizeImplicitTileHeaders(
const {
subdivisionScheme,
maximumLevel,
availableLevels,
subtreeLevels,
subtrees: {uri: subtreesUriTemplate}
} = implicitTilingExtension;
Expand All @@ -184,7 +185,7 @@ export async function normalizeImplicitTileHeaders(
subtreesUriTemplate,
subdivisionScheme,
subtreeLevels,
maximumLevel,
maximumLevel: Number.isFinite(availableLevels) ? availableLevels - 1 : maximumLevel,
refine,
basePath,
lodMetricType: LOD_METRIC_TYPE.GEOMETRIC_ERROR,
Expand All @@ -194,7 +195,7 @@ export async function normalizeImplicitTileHeaders(
getRefine
};

return await normalizeImplicitTileData(tile, basePath, subtree, implicitOptions);
return await normalizeImplicitTileData(tile, basePath, subtree, implicitOptions, options);
}

/**
Expand All @@ -208,15 +209,17 @@ export async function normalizeImplicitTileData(
tile: Tiles3DTileJSON,
basePath: string,
rootSubtree: Subtree,
options: any
implicitOptions: any,
loaderOptions: Tiles3DLoaderOptions
): Promise<Tiles3DTileJSONPostprocessed | null> {
if (!tile) {
return null;
}

const {children, contentUrl} = await parseImplicitTiles({
subtree: rootSubtree,
options
implicitOptions,
loaderOptions
});

let tileContentUrl: string | undefined;
Expand Down
4 changes: 4 additions & 0 deletions modules/3d-tiles/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,10 @@ export type BufferView = {
* Spec - https://github.com/CesiumGS/3d-tiles/tree/main/extensions/3DTILES_implicit_tiling
*/
export type ImplicitTilingExensionData = ImplicitTilingData & {
/** This property is not part of the schema
* https://github.com/CesiumGS/3d-tiles/blob/main/extensions/3DTILES_implicit_tiling/schema/tile.3DTILES_implicit_tiling.schema.json
* But it can be seen in some test datasets. It is handled as substitute of `availableLevels`
*/
maximumLevel?: number;
};

Expand Down
2 changes: 1 addition & 1 deletion modules/3d-tiles/test/tiles-3d-loader.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ test('Tiles3DLoader#Implicit Octree Tileset with bitstream availability and subt
t.equal(tileset.root.children[0].children[0].children.length, 1);

// children level 3
t.equal(tileset.root.children[0].children[0].children[0].content.uri, 'content/3/2/0/1.pnts');
t.equal(tileset.root.children[0].children[0].children[0].content.uri, 'content/3/8/0/1.pnts');
t.equal(tileset.root.children[0].children[0].children[0].lodMetricValue, 625);
t.equal(tileset.root.children[0].children[0].children[0].refine, 1);
t.equal(tileset.root.children[0].children[0].children[0].type, 'pointcloud');
Expand Down