123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865 |
- import { OrbitControls } from '../examples/resource/js/OrbitControls.js';
- import { CameraRig, FreeMovementControls } from '../examples/resource/js/three-story-controls.js' //'../../examples/resource/js/three-story-controls.js'
- import Stats from '../examples/resource/js/stats.module.js';
- import StatsWidget from '../examples/resource/js/stats-widget.js' //'@probe.gl/stats-widget';
- import { load } from '@loaders.gl/core';
- import { CesiumIonLoader, Tiles3DLoader } from '@loaders.gl/3d-tiles';
- import { Tileset3D, TILE_TYPE, TILE_CONTENT_STATE } from '@loaders.gl/tiles';
- import { CullingVolume, Plane } from '@math.gl/culling';
- import { _PerspectiveFrustum as PerspectiveFrustum} from '@math.gl/culling';
- import { Matrix4 as MathGLMatrix4 } from '@math.gl/core';
- import * as Util from './util';
- import {
- Scene,
- Clock,
- sRGBEncoding,
- Object3D,
- Group,
- Matrix4,
- Vector3,
- Vector2,
- Mesh,
- BufferGeometry,
- MeshStandardMaterial,
- LineSegments,
- Material,
- Float32BufferAttribute,
- ShaderMaterial,
- MeshBasicMaterial,
- Uint8BufferAttribute,
- BufferAttribute,
- Points,
- Camera,
- PerspectiveCamera,
- WebGLRenderer,
- Texture,
- Euler
- } from '../examples/resource/js/three.js';
- import { GLTFLoader } from '../examples/resource/js/GLTFLoader.js';
- import { DRACOLoader } from '../examples/resource/js/DRACOLoader.js';
- import { KTX2Loader } from '../examples/resource/js/KTX2Loader.js';
- import { Gradients } from './gradients';
- import { PointCloudFS, PointCloudVS } from './shaders';
- import { LoaderProps, LoaderOptions, Runtime, PointCloudColoring, Shading, GeoCoord, GeoTransform } from './types';
- const gradient = Gradients.RAINBOW;
- const gradientTexture = typeof document != 'undefined' ? Util.generateGradientTexture(gradient) : null;
- const grayscale = Gradients.GRAYSCALE;
- const grayscaleTexture = typeof document != 'undefined' ? Util.generateGradientTexture(grayscale) : null;
- const defaultOptions: LoaderOptions = {
- throttleRequests: true,
- maxRequests: 64,
- updateInterval: 0.1,
- maxConcurrency: 1,
- maximumScreenSpaceError: 16,
- maximumMemoryUsage: 32,
- viewDistanceScale: 1.0,
- skipLevelOfDetail: false,
- updateTransforms: true,
- shading: Shading.FlatTexture,
- transparent: false,
- pointCloudColoring: PointCloudColoring.White,
- pointSize: 1.0,
- worker: true,
- wireframe: false,
- debug: false,
- basisTranscoderPath: null,
- dracoDecoderPath: null,
- material: null,
- computeNormals: false,
- shaderCallback: null,
- geoTransform: GeoTransform.Reset,
- preloadTilesCount: null
- };
- /** 3D Tiles Loader */
- class Loader3DTiles {
- /**
- * Loads a tileset of 3D Tiles according to the given {@link LoaderProps}
- * @public
- *
- * @param props - Properties for this load call {@link LoaderProps}.
- * @returns An object containing the 3D Model to be added to the scene
- * and a runtime engine to be updated every frame.
- */
- public static async load (props: LoaderProps): Promise<{ model: Object3D; runtime: Runtime }> {
- const options = { ...defaultOptions, ...props.options };
- const { url } = props;
- const UPDATE_INTERVAL = options.updateInterval;
- const MAX_DEPTH_FOR_ORIENTATION = 5;
- const loadersGLOptions: {[key: string]: unknown} = {};
- if (options.cesiumIONToken) {
- loadersGLOptions['cesium-ion'] = {
- accessToken: options.cesiumIONToken,
- };
- const metadata = await CesiumIonLoader.preload(url, loadersGLOptions);
- loadersGLOptions['fetch'] = { headers: metadata.headers };
- }
- if (props.loadingManager) {
- props.loadingManager.itemStart(url);
- }
- const tilesetJson = await load(url, Tiles3DLoader, {
- ...loadersGLOptions,
- });
- const renderMap = {};
- const boxMap = {};
- const unloadQueue = [];
- const root = new Group();
- const tileBoxes = new Group();
- if (!options.debug) {
- tileBoxes.visible = false;
- } else {
- // TODO: Need to have a parent root with no transform and then a conent root with transform
- //root.add(tileBoxes)
- }
- const pointcloudUniforms = {
- pointSize: { type: 'f', value: options.pointSize },
- gradient: { type: 't', value: gradientTexture },
- grayscale: { type: 't', value: grayscaleTexture },
- rootCenter: { type: 'vec3', value: new Vector3() },
- rootNormal: { type: 'vec3', value: new Vector3() },
- coloring: { type: 'i', value: options.pointCloudColoring },
- hideGround: { type: 'b', value: true },
- elevationRange: { type: 'vec2', value: new Vector2(0, 400) },
- maxIntensity: { type: 'f', value: 1.0 },
- intensityContrast: { type: 'f', value: 1.0 },
- alpha: { type: 'f', value: 1.0 },
- };
- const pointcloudMaterial = new ShaderMaterial({
- uniforms: pointcloudUniforms,
- vertexShader: PointCloudVS,
- fragmentShader: PointCloudFS,
- transparent: options.transparent,
- vertexColors: true
- });
-
- let cameraReference = null;
- let rendererReference = null;
- const gltfLoader = new GLTFLoader();
- let ktx2Loader = undefined;
- let dracoLoader = undefined;
- if (options.basisTranscoderPath) {
- ktx2Loader = new KTX2Loader();
- ktx2Loader.detectSupport(props.renderer);
- ktx2Loader.setTranscoderPath(options.basisTranscoderPath + '/');
- ktx2Loader.setWorkerLimit(1);
- gltfLoader.setKTX2Loader(ktx2Loader);
- }
- if (options.dracoDecoderPath) {
- dracoLoader = new DRACOLoader();
- dracoLoader.setDecoderPath(options.dracoDecoderPath + '/');
- dracoLoader.setWorkerLimit(options.maxConcurrency);
- gltfLoader.setDRACOLoader(dracoLoader);
- }
- const unlitMaterial = new MeshBasicMaterial({transparent: options.transparent});
- const tileOptions = {
- maximumMemoryUsage: options.maximumMemoryUsage,
- maximumScreenSpaceError: options.maximumScreenSpaceError,
- viewDistanceScale: options.viewDistanceScale,
- skipLevelOfDetail: options.skipLevelOfDetail,
- updateTransforms: options.updateTransforms,
- throttleRequests: options.throttleRequests,
- maxRequests: options.maxRequests,
- contentLoader: async (tile) => {
- let tileContent = null;
- switch (tile.type) {
- case TILE_TYPE.POINTCLOUD: {
- tileContent = createPointNodes(tile, pointcloudMaterial, options, rootTransformInverse);
- break;
- }
- case TILE_TYPE.SCENEGRAPH:
- case TILE_TYPE.MESH: {
- tileContent = await createGLTFNodes(gltfLoader, tile, unlitMaterial, options, rootTransformInverse);
- break;
- }
- default:
- break;
- }
- if (tileContent) {
- tileContent.visible = false;
- renderMap[tile.id] = tileContent;
- root.add(renderMap[tile.id]);
- if (options.debug) {
- const box = Util.loadersBoundingBoxToMesh(tile);
- tileBoxes.add(box);
- boxMap[tile.id] = box;
- }
- }
- },
- onTileLoad: async (tile) => {
- if (tileset) {
- if (!orientationDetected && tile?.depth <= MAX_DEPTH_FOR_ORIENTATION) {
- detectOrientation(tile);
- }
- tileset._frameNumber++;
- tilesetUpdate(tileset, renderMap, rendererReference, cameraReference);
- }
- },
- onTileUnload: (tile) => {
- unloadQueue.push(tile);
- },
- onTileError: (tile, message) => {
- console.error('Tile error', tile.id, message);
- },
- };
- const tileset = new Tileset3D(tilesetJson, {
- ...tileOptions,
- loadOptions: {
- ...loadersGLOptions,
- maxConcurrency: options.maxConcurrency,
- worker: options.worker,
- gltf: {
- loadImages: false,
- },
- '3d-tiles': {
- loadGLTF: false
- },
- },
- });
- //
- // transformations
- const threeMat = new Matrix4();
- const tileTrasnform = new Matrix4();
- const rootCenter = new Vector3();
- let orientationDetected = false;
- if (tileset.root.boundingVolume) {
- if (tileset.root.header.boundingVolume.region) {
- // TODO: Handle region type bounding volumes
- // https://github.com/visgl/loaders.gl/issues/1994
- console.warn("Cannot apply a model matrix to bounding volumes of type region. Tileset stays in original geo-coordinates.")
- options.geoTransform = GeoTransform.WGS84Cartesian;
- }
- tileTrasnform.setPosition(
- tileset.root.boundingVolume.center[0],
- tileset.root.boundingVolume.center[1],
- tileset.root.boundingVolume.center[2]
- )
- } else {
- console.warn("Bounding volume not found, no transformations applied")
- }
- if (options.debug) {
- const box = Util.loadersBoundingBoxToMesh(tileset.root);
- tileBoxes.add(box);
- boxMap[tileset.root.id] = box;
- }
- let disposeFlag = false;
- let loadingEnded = false;
- pointcloudUniforms.rootCenter.value.copy(rootCenter);
- pointcloudUniforms.rootNormal.value.copy(new Vector3(0, 0, 1).normalize());
- // Extra stats
- tileset.stats.get('Loader concurrency').count = options.maxConcurrency
- tileset.stats.get('Maximum SSE').count = options.maximumScreenSpaceError;
- tileset.stats.get('Maximum mem usage').count = options.maximumMemoryUsage;
- let timer = 0;
- let lastCameraTransform: Matrix4 = null;
- let lastCameraAspect = null;
- const lastCameraPosition = new Vector3(Infinity, Infinity, Infinity);
- let sseDenominator = null;
- root.updateMatrixWorld(true);
- const lastRootTransform:Matrix4 = new Matrix4().copy(root.matrixWorld)
- const rootTransformInverse = new Matrix4().copy(lastRootTransform).invert();
- detectOrientation(tileset.root);
- updateResetTransform();
- if (options.debug) {
- boxMap[tileset.root.id].applyMatrix4(threeMat);
- tileBoxes.matrixWorld.copy(root.matrixWorld);
- }
- if (options.geoTransform == GeoTransform.Mercator) {
- const coords = Util.datumsToSpherical(
- tileset.cartographicCenter[1],
- tileset.cartographicCenter[0]
- )
- rootCenter.set(
- coords.x,
- 0,
- -coords.y
- );
- root.position.copy(rootCenter);
- root.rotation.set(-Math.PI / 2, 0, 0);
- root.updateMatrixWorld(true);
- } else if (options.geoTransform == GeoTransform.WGS84Cartesian) {
- root.applyMatrix4(tileTrasnform);
- root.updateMatrixWorld(true);
- rootCenter.copy(root.position);
- }
- function detectOrientation(tile) {
- if (!tile.boundingVolume.halfAxes) {
- return;
- }
- const halfAxes = tile.boundingVolume.halfAxes;
- const orientationMatrix = new Matrix4()
- .extractRotation(Util.getMatrix4FromHalfAxes(halfAxes))
- .premultiply(new Matrix4().extractRotation(rootTransformInverse));
- const rotation = new Euler().setFromRotationMatrix(orientationMatrix);
- if (!rotation.equals(new Euler())) {
- orientationDetected = true;
- const pos = new Vector3(
- tileTrasnform.elements[12],
- tileTrasnform.elements[13],
- tileTrasnform.elements[14])
- ;
- tileTrasnform.extractRotation(orientationMatrix);
- tileTrasnform.setPosition(pos);
- updateResetTransform();
- }
- }
- function updateResetTransform() {
- if (options.geoTransform != GeoTransform.WGS84Cartesian) {
- // Reset the current model matrix and apply our own transformation
- threeMat.copy(tileTrasnform).invert();
- threeMat.premultiply(lastRootTransform);
-
- threeMat.copy(lastRootTransform).multiply(new Matrix4().copy(tileTrasnform).invert());
- tileset.modelMatrix = new MathGLMatrix4(threeMat.toArray());
- }
- }
- function tilesetUpdate(tileset, renderMap, renderer, camera) {
- if (disposeFlag) {
- return;
- }
- // Assumes camera fov, near and far are not changing
- if (!sseDenominator || camera.aspect != lastCameraAspect) {
- const loadersFrustum = new PerspectiveFrustum({
- fov: (camera.fov / 180) * Math.PI,
- aspectRatio: camera.aspect,
- near: camera.near,
- far: camera.far,
- });
- sseDenominator = loadersFrustum.sseDenominator;
- lastCameraAspect = camera.aspect;
- if (options.debug) {
- console.log('Updated sse denonimator:', sseDenominator);
- }
- }
- const frustum = Util.getCameraFrustum(camera);
- const planes = frustum.planes.map((plane) => new Plane(plane.normal.toArray(), plane.constant));
- const cullingVolume = new CullingVolume(planes);
- const rendererSize = new Vector2();
- renderer.getSize(rendererSize);
- const frameState = {
- camera: {
- position: lastCameraPosition.toArray(),
- },
- height: rendererSize.y,
- frameNumber: tileset._frameNumber,
- sseDenominator: sseDenominator,
- cullingVolume: cullingVolume,
- viewport: {
- id: 0,
- },
- };
- tileset._cache.reset();
- tileset._traverser.traverse(tileset.root, frameState, tileset.options);
- for (const tile of tileset.tiles) {
- if (tile.selected) {
- if (!renderMap[tile.id]) {
- console.error('TILE SELECTED BUT NOT LOADED!!', tile.id);
- } else {
- // Make sure it's visible
- renderMap[tile.id].visible = true;
- }
- } else {
- if (renderMap[tile.id]) {
- renderMap[tile.id].visible = false;
- }
- }
- }
- while (unloadQueue.length > 0) {
- const tile = unloadQueue.pop();
- if (renderMap[tile.id] && tile.contentState == TILE_CONTENT_STATE.UNLOADED) {
- root.remove(renderMap[tile.id]);
- disposeNode(renderMap[tile.id]);
- delete renderMap[tile.id];
- }
- if (boxMap[tile.id]) {
- disposeNode(boxMap[tile.id]);
- tileBoxes.remove(boxMap[tile.id]);
- delete boxMap[tile.id];
- }
- }
- const tilesLoaded = tileset.stats.get('Tiles Loaded').count;
- const tilesLoading = tileset.stats.get('Tiles Loading').count;
- if (props.onProgress) {
- props.onProgress(
- tilesLoaded,
- tilesLoaded + tilesLoading
- );
- }
- if (props.loadingManager && !loadingEnded) {
- if (tilesLoading == 0 &&
- (
- options.preloadTilesCount == null ||
- tilesLoaded >= options.preloadTilesCount)
- ) {
- loadingEnded = true;
- props.loadingManager.itemEnd(props.url);
- }
- }
- return frameState;
- }
- return {
- model: root,
- runtime: {
- getTileset: () => {
- return tileset;
- },
- getStats: () => {
- return tileset.stats;
- },
- showTiles: (visible) => {
- tileBoxes.visible = visible;
- },
- setWireframe: (wireframe) => {
- options.wireframe = wireframe;
- root.traverse((object) => {
- if (object instanceof Mesh) {
- object.material.wireframe = wireframe;
- }
- });
- },
- setDebug: (debug) => {
- options.debug = debug;
- tileBoxes.visible = debug;
- },
- setShading: (shading) => {
- options.shading = shading;
- },
- getTileBoxes: () => {
- return tileBoxes;
- },
- setViewDistanceScale: (scale) => {
- tileset.options.viewDistanceScale = scale;
- tileset._frameNumber++;
- tilesetUpdate(tileset, renderMap, rendererReference, cameraReference);
- },
- setHideGround: (enabled) => {
- pointcloudUniforms.hideGround.value = enabled;
- },
- setPointCloudColoring: (selection) => {
- pointcloudUniforms.coloring.value = selection;
- },
- setElevationRange: (range) => {
- pointcloudUniforms.elevationRange.value.set(range[0], range[1]);
- },
- setMaxIntensity: (intensity) => {
- pointcloudUniforms.maxIntensity.value = intensity;
- },
- setIntensityContrast: (contrast) => {
- pointcloudUniforms.intensityContrast.value = contrast;
- },
- setPointAlpha: (alpha) => {
- pointcloudUniforms.alpha.value = alpha;
- },
- getLatLongHeightFromPosition: (position) => {
- const cartographicPosition = tileset.ellipsoid.cartesianToCartographic(
- new Vector3().copy(position).applyMatrix4(new Matrix4().copy(threeMat).invert()).toArray(),
- );
- return {
- lat: cartographicPosition[1],
- long: cartographicPosition[0],
- height: cartographicPosition[2],
- };
- },
- getPositionFromLatLongHeight: (coord) => {
- const cartesianPosition = tileset.ellipsoid.cartographicToCartesian([coord.long, coord.lat, coord.height]);
- return new Vector3(...cartesianPosition).applyMatrix4(threeMat);
- },
- getCameraFrustum: (camera: Camera) => {
- const frustum = Util.getCameraFrustum(camera);
- const meshes = frustum.planes
- .map((plane) => new Plane(plane.normal.toArray(), plane.constant))
- .map((loadersPlane) => Util.loadersPlaneToMesh(loadersPlane));
- const model = new Group();
- for (const mesh of meshes) model.add(mesh);
- return model;
- },
- update: function (dt: number, renderer: WebGLRenderer, camera: Camera) {
- cameraReference = camera;
- rendererReference = renderer;
- timer += dt;
- if (tileset && timer >= UPDATE_INTERVAL) {
- if (!lastRootTransform.equals(root.matrixWorld)) {
- timer = 0;
- lastRootTransform.copy(root.matrixWorld);
- updateResetTransform();
- const rootCenter = new Vector3().setFromMatrixPosition(lastRootTransform);
- pointcloudUniforms.rootCenter.value.copy(rootCenter);
- pointcloudUniforms.rootNormal.value.copy(new Vector3(0, 0, 1).applyMatrix4(lastRootTransform).normalize());
- rootTransformInverse.copy(lastRootTransform).invert();
- if (options.debug) {
- boxMap[tileset.root.id].matrixWorld.copy(threeMat);
- boxMap[tileset.root.id].applyMatrix4(lastRootTransform);
- }
- }
- if (lastCameraTransform == null) {
- lastCameraTransform = new Matrix4().copy(camera.matrixWorld);
- } else {
- const cameraChanged: boolean =
- !camera.matrixWorld.equals(lastCameraTransform) ||
- !((<PerspectiveCamera>camera).aspect == lastCameraAspect);
- if (cameraChanged) {
- timer = 0;
- tileset._frameNumber++;
- camera.getWorldPosition(lastCameraPosition);
- lastCameraTransform.copy(camera.matrixWorld);
- tilesetUpdate(tileset, renderMap, renderer, camera);
- }
- }
- }
- },
- dispose: function () {
- disposeFlag = true;
- tileset._destroy();
- while (root.children.length > 0) {
- const obj = root.children[0];
- disposeNode(obj);
- root.remove(obj);
- }
- while (tileBoxes.children.length > 0) {
- const obj = tileBoxes.children[0] as LineSegments;
- tileBoxes.remove(obj);
- obj.geometry.dispose();
- (<Material>obj.material).dispose();
- }
- if (ktx2Loader) {
- ktx2Loader.dispose();
- }
- if (dracoLoader) {
- dracoLoader.dispose();
- }
- },
- },
- };
- }
- }
- async function createGLTFNodes(gltfLoader, tile, unlitMaterial, options, rootTransformInverse): Promise<Object3D> {
- return new Promise((resolve, reject) => {
- const rotateX = new Matrix4().makeRotationAxis(new Vector3(1, 0, 0), Math.PI / 2);
- const shouldRotate = tile.tileset.asset?.gltfUpAxis !== "Z";
- // The computed trasnform already contains the root's transform, so we have to invert it
- const contentTransform = new Matrix4().fromArray(tile.computedTransform).premultiply(rootTransformInverse);
- if (shouldRotate) {
- contentTransform.multiply(rotateX); // convert from GLTF Y-up to Z-up
- }
- gltfLoader.parse(
- tile.content.gltfArrayBuffer,
- tile.contentUrl ? tile.contentUrl.substr(0,tile.contentUrl.lastIndexOf('/') + 1) : '',
- (gltf) => {
- const tileContent = gltf.scenes[0] as Group;
- tileContent.applyMatrix4(contentTransform);
- tileContent.traverse((object) => {
- if (object.type == "Mesh") {
- const mesh = object as Mesh;
- const originalMaterial = (mesh.material as MeshStandardMaterial);
- const originalMap = originalMaterial.map;
- if (options.material) {
- mesh.material = options.material.clone();
- originalMaterial.dispose();
- } else if (options.shading == Shading.FlatTexture) {
- mesh.material = unlitMaterial.clone();
- originalMaterial.dispose();
- }
- if (options.shading != Shading.ShadedNoTexture) {
- if ((mesh.material as Material).type == "ShaderMaterial") {
- (mesh.material as ShaderMaterial).uniforms.map = { value: originalMap };
- } else {
- (mesh.material as MeshStandardMaterial).map = originalMap;
- }
- } else {
- if (originalMap) {
- originalMap.dispose();
- }
- (mesh.material as MeshStandardMaterial).map = null;
- }
- if (options.shaderCallback) {
- mesh.onBeforeRender = options.shaderCallback;
- }
- (mesh.material as MeshStandardMaterial | MeshBasicMaterial).wireframe = options.wireframe;
- if (options.computeNormals) {
- mesh.geometry.computeVertexNormals();
- }
- }
- });
- resolve(tileContent);
- },
- (e) => {
- reject(new Error(`error parsing gltf in tile ${tile.id}: ${e}`));
- },
- );
- });
- }
- function createPointNodes(tile, pointcloudMaterial, options, rootTransformInverse) {
- const d = {
- rtc_center: tile.content.rtcCenter, // eslint-disable-line camelcase
- points: tile.content.attributes.positions,
- intensities: tile.content.attributes.intensity,
- classifications: tile.content.attributes.classification,
- rgb: null,
- rgba: null,
- };
- const { colors } = tile.content.attributes;
- if (colors && colors.size === 3) {
- d.rgb = colors.value;
- }
- if (colors && colors.size === 4) {
- d.rgba = colors.value;
- }
- const geometry = new BufferGeometry();
- geometry.setAttribute('position', new Float32BufferAttribute(d.points, 3));
- const contentTransform = new Matrix4().fromArray(tile.computedTransform).premultiply(rootTransformInverse);
- if (d.rgba) {
- geometry.setAttribute('color', new Float32BufferAttribute(d.rgba, 4));
- } else if (d.rgb) {
- geometry.setAttribute('color', new Uint8BufferAttribute(d.rgb, 3, true));
- }
- if (d.intensities) {
- geometry.setAttribute(
- 'intensity',
- // Handles both 16bit or 8bit intensity values
- new BufferAttribute(d.intensities, 1, true)
- );
- }
- if (d.classifications) {
- geometry.setAttribute('classification', new Uint8BufferAttribute(d.classifications, 1, false));
- }
- const tileContent = new Points(geometry, options.material || pointcloudMaterial);
- if (d.rtc_center) {
- const c = d.rtc_center;
- contentTransform.multiply(new Matrix4().makeTranslation(c[0], c[1], c[2]));
- }
- tileContent.applyMatrix4(contentTransform);
- return tileContent;
- }
- function disposeMaterial(material) {
- if ((material as ShaderMaterial)?.uniforms?.map) {
- ((material as ShaderMaterial)?.uniforms?.map.value as Texture)?.dispose();
- }
- else if (material.map) {
- (material.map as Texture)?.dispose();
- }
- material.dispose();
- }
- function disposeNode(node) {
- node.traverse((object) => {
- if (object.isMesh) {
- object.geometry.dispose();
- if (object.material.isMaterial) {
- disposeMaterial(object.material);
- } else {
- // an array of materials
- for (const material of object.material) {
- disposeMaterial(material);
- }
- }
- }
- });
- for (let i = node.children.length - 1; i >= 0; i--) {
- const obj = node.children[i];
- node.remove(obj);
- }
- }
- export { Loader3DTiles, PointCloudColoring, Shading, Runtime, GeoCoord, GeoTransform, LoaderOptions, LoaderProps };
- //------------------------------------------------------test
- const queryParams = new URLSearchParams(document.location.search);
- const canvasParent = document.querySelector('#canvas-parent') as HTMLElement;
- const statsParent = document.querySelector('#stats-widget') as HTMLElement
- const scene = new Scene();
- const camera = new PerspectiveCamera(
- 35,
- 1,
- 0.01,
- 1000,
- );
- const renderer = new WebGLRenderer();
- renderer.outputEncoding = sRGBEncoding;
- const clock = new Clock()
- const rig = new CameraRig(camera, scene)
-
- let controls = undefined;
- if (queryParams.get('orbit')) {
- controls = new OrbitControls( camera, canvasParent);
- controls.listenToKeyEvents( document.body );
- camera.position.set(0,200,0);
- controls.update();
- } else {
- controls = new FreeMovementControls(rig, {
- panDegreeFactor: 2,
- wheelScaleFactor: 0.01,
- keyboardScaleFactor: 0.015,
- keyboardDampFactor: 0
- });
- controls.enable();
- }
-
- canvasParent.appendChild(renderer.domElement);
- const threeJsStats = new Stats();
- threeJsStats.domElement.style.position = 'absolute';
- threeJsStats.domElement.style.top = '10px';
- threeJsStats.domElement.style.left = '10px';
- canvasParent.appendChild( threeJsStats.domElement );
- let tilesRuntime = undefined;
- let statsRuntime = undefined;
- if (queryParams.get('tilesetUrl')) {
- (document.querySelector('#example-desc') as HTMLElement).style.display = 'none';
- }
- loadTileset();
- async function loadTileset() {
- const result = await Loader3DTiles.load(
- {
- url:
- queryParams.get('tilesetUrl') ??
- //'https://int.nyt.com/data/3dscenes/ONA360/TILESET/0731_FREEMAN_ALLEY_10M_A_36x8K__10K-PN_50P_DB/tileset_tileset.json',
- 'https://testgis.4dage.com/LVBADUI_qp/tileset.json',
- renderer: renderer,
- options: {
- dracoDecoderPath: 'https://cdn.jsdelivr.net/npm/three@0.137.0/examples/js/libs/draco',
- basisTranscoderPath: 'https://cdn.jsdelivr.net/npm/three@0.137.0/examples/js/libs/basis',
- maximumScreenSpaceError: 48 //queryParams.get('sse') ?? 48,
- }
- }
- );
- const {model, runtime} = result;
- model.rotation.set(-Math.PI / 2, 0, Math.PI / 2);
- if (!queryParams.get('tilesetUrl')) {
- model.position.set(-1, 4, -16);
- }
- tilesRuntime = runtime;
- scene.add(model);
- statsRuntime = new StatsWidget(runtime.getStats(), {container: statsParent });
- statsParent.style.visibility = 'visible';
- }
- function render(t) {
- const dt = clock.getDelta()
- controls.update(t);
- if (tilesRuntime) {
- tilesRuntime.update(dt, renderer, camera);
- }
- if (statsRuntime) {
- statsRuntime.update();
- }
- renderer.render(scene, camera);
- threeJsStats.update();
- window.requestAnimationFrame(render);
- }
- onWindowResize();
- function onWindowResize() {
- renderer.setSize(canvasParent.clientWidth, canvasParent.clientHeight);
- camera.aspect = canvasParent.clientWidth / canvasParent.clientHeight;
- camera.updateProjectionMatrix();
- }
- window.addEventListener('resize', onWindowResize)
- render(undefined);
|