11import * as THREE from 'three' ;
22import GeometryLayer from 'Layer/GeometryLayer' ;
3- import PointsMaterial , { PNTS_MODE } from 'Renderer/PointsMaterial' ;
3+ import PointsMaterial , { PNTS_MODE , PNTS_SIZE_MODE } from 'Renderer/PointsMaterial' ;
44import Picking from 'Core/Picking' ;
55
66import type PointCloudNode from 'Core/PointCloudNode' ;
@@ -506,6 +506,37 @@ abstract class PointCloudLayer<S extends PointCloudSource = PointCloudSource>
506506 this . dispatchEvent ( { type : 'dispose-model' , scene : obj , tile : obj . userData . node } ) ;
507507 }
508508 }
509+
510+ // @ts -expect-error PointsMaterial is not typed yet
511+ if ( this . material . sizeMode === PNTS_SIZE_MODE . ADAPTIVE ) {
512+ const visibilityTextureData = this . computeVisibilityTextureData ( this . group . children ) ;
513+
514+ // @ts -expect-error PointsMaterial is not typed yet
515+ const vnt = this . material . visibleNodes ;
516+ const data = vnt . image . data ;
517+ data . set ( visibilityTextureData . data ) ;
518+ vnt . needsUpdate = true ;
519+
520+ const octreeSize = this . root ! . voxelOBB . box3D . getSize ( new THREE . Vector3 ( ) ) . x ;
521+
522+ for ( const pts of this . group . children ) {
523+ const node = pts . userData . node ;
524+ const depth = node . depth ;
525+ const nodeStartOffset = visibilityTextureData . offsets . get ( node ) ;
526+ const octreeSpacing = node . source . spacing ;
527+
528+ pts . onBeforeRender = ( _renderer , _scene , _camera , _geometry , material ) => {
529+ // @ts -expect-error Material is not typed yet
530+ material . uniforms . nodeStartOffset . value = nodeStartOffset ;
531+ // @ts -expect-error Material is not typed yet
532+ material . uniforms . octreeSize . value = octreeSize ;
533+ // @ts -expect-error Material is not typed yet
534+ material . uniforms . octreeSpacing . value = octreeSpacing ;
535+ // @ts -expect-error Material is not typed yet
536+ material . uniforms . nodeDepth . value = depth ;
537+ } ;
538+ }
539+ }
509540 }
510541
511542 // @ts -expect-error Layer and Picking are not typed yet
@@ -529,6 +560,77 @@ abstract class PointCloudLayer<S extends PointCloudSource = PointCloudSource>
529560 }
530561 }
531562 }
563+
564+ // Encoding the octree hierarchy in bread-first order
565+ // into a texture for adaptive point size rendering
566+ // Explanation p36: https://www.cg.tuwien.ac.at/research/publications/2016/SCHUETZ-2016-POT/SCHUETZ-2016-POT-thesis.pdf
567+ computeVisibilityTextureData ( children : THREE . Object3D [ ] ) {
568+ // copy array
569+ const orderedChildren = children . slice ( ) ;
570+
571+ // Helper function to get octree child index from node
572+ const getChildIndex = ( node : PointCloudNode ) : number => {
573+ if ( ! node . parent ) {
574+ return 0 ;
575+ }
576+ const parent = node . parent ;
577+ // @ts -expect-error PointCloudNode has x properties
578+ const dx = node . x - parent . x * 2 ;
579+ // @ts -expect-error PointCloudNode has y properties
580+ const dy = node . y - parent . y * 2 ;
581+ // @ts -expect-error PointCloudNode has z properties
582+ const dz = node . z - parent . z * 2 ;
583+ // Octree child index (Potree convention): 4*x + 2*y + z
584+ return 4 * dx + 2 * dy + dz ;
585+ } ;
586+
587+ // sort by level and hierarchy order
588+ const sort = function sortNodes ( a : THREE . Object3D , b : THREE . Object3D ) {
589+ const na = a . userData . node ;
590+ const nb = b . userData . node ;
591+ if ( na . depth !== nb . depth ) { return na . depth - nb . depth ; }
592+ if ( na . z !== nb . z ) { return na . z - nb . z ; }
593+ if ( na . y !== nb . y ) { return na . y - nb . y ; }
594+ return na . x - nb . x ;
595+ } ;
596+ orderedChildren . sort ( sort ) ;
597+
598+ const data = new Uint8Array ( orderedChildren . length * 4 ) ;
599+ const visibleNodeTextureOffsets = new Map ( ) ;
600+ const offsetsToChild : number [ ] = new Array ( orderedChildren . length ) . fill ( Infinity ) ;
601+
602+ for ( let nodeIndex = 0 ; nodeIndex < orderedChildren . length ; nodeIndex ++ ) {
603+ const node = orderedChildren [ nodeIndex ] . userData . node ;
604+ visibleNodeTextureOffsets . set ( node , nodeIndex ) ;
605+
606+ if ( node . parent ) {
607+ const childIndex = getChildIndex ( node ) ;
608+ const parentIndex = visibleNodeTextureOffsets . get ( node . parent ) ;
609+
610+ const parentOffsetToChild = nodeIndex - parentIndex ;
611+ const offsetToFirstChild =
612+ Math . min ( offsetsToChild [ parentIndex ] , parentOffsetToChild ) ;
613+ offsetsToChild [ parentIndex ] = offsetToFirstChild ;
614+
615+ // The 8 bits of the red value indicate
616+ // which of the children are visible
617+ data [ parentIndex * 4 ] = data [ parentIndex * 4 ] | ( 1 << childIndex ) ;
618+ // Offset to child is stored on 2 bytes,
619+ // so it can support up to 65536 nodes per subtree.
620+ // The green channel contains the relative offset
621+ // to the node’s first child (8 most significant bits)
622+ data [ parentIndex * 4 + 1 ] = offsetToFirstChild >> 8 ;
623+ // The blue channel contains the relative offset
624+ // to the node’s first child (8 least significant bits)
625+ data [ parentIndex * 4 + 2 ] = offsetToFirstChild % 256 ;
626+ }
627+ }
628+
629+ return {
630+ data,
631+ offsets : visibleNodeTextureOffsets ,
632+ } ;
633+ }
532634}
533635
534636export default PointCloudLayer ;
0 commit comments