import { Box3 , Box3Helper , BoxGeometry , BufferGeometry , Color , Euler , Group , LineBasicMaterial , Material , Matrix4 , Mesh , ShaderMaterial , Vector2 , Vector3 , } from "three" ; import FusedChannelData from "./FusedChannelData" ; import { rayMarchingVertexShaderSrc , rayMarchingFragmentShaderSrc , rayMarchingShaderUniforms , } from "./constants/volumeRayMarchShader" ; import { Volume } from "." ; import Channel from "./Channel" ; import { ThreeJsPanel } from "./ThreeJsPanel" ; import { Bounds , FuseChannel } from "./types" ; export default class RayMarchedAtlasVolume { public volume : Volume ; public bounds : Bounds ; private cube : BoxGeometry ; private cubeMesh : Mesh < BufferGeometry , Material > ; private boxHelper : Box3Helper ; private cubeTransformNode : Group ; private uniforms : typeof rayMarchingShaderUniforms ; private channelData : FusedChannelData ; private scale : Vector3 ; constructor ( volume ) { this . volume = volume ; this . bounds = { bmin : new Vector3 ( - 0.5 , - 0.5 , - 0.5 ) , bmax : new Vector3 ( 0.5 , 0.5 , 0.5 ) , } ; this . scale = new Vector3 ( 1.0 , 1.0 , 1.0 ) ; this . cube = new BoxGeometry ( 1.0 , 1.0 , 1.0 ) ; this . cubeMesh = new Mesh ( this . cube ) ; this . cubeMesh . name = "Volume" ; this . boxHelper = new Box3Helper ( new Box3 ( this . bounds . bmin , this . bounds . bmax ) , new Color ( 0xffff00 ) ) ; this . boxHelper . updateMatrixWorld ( ) ; this . boxHelper . visible = false ; this . cubeTransformNode = new Group ( ) ; this . cubeTransformNode . name = "VolumeContainerNode" ; this . cubeTransformNode . add ( this . boxHelper ) ; this . cubeTransformNode . add ( this . cubeMesh ) ; this . uniforms = rayMarchingShaderUniforms ; const vtxsrc = rayMarchingVertexShaderSrc ; const fgmtsrc = rayMarchingFragmentShaderSrc ; const threeMaterial = new ShaderMaterial ( { uniforms : this . uniforms , vertexShader : vtxsrc , fragmentShader : fgmtsrc , transparent : true , depthTest : true , depthWrite : false , } ) ; this . cubeMesh . material = threeMaterial ; this . setUniform ( "ATLAS_X" , volume . imageInfo . cols ) ; this . setUniform ( "ATLAS_Y" , volume . imageInfo . rows ) ; this . setUniform ( "textureRes" , new Vector2 ( volume . imageInfo . atlas_width , volume . imageInfo . atlas_height ) ) ; this . setUniform ( "SLICES" , volume . z ) ; this . setScale ( this . scale ) ; this . channelData = new FusedChannelData ( volume . imageInfo . atlas_width , volume . imageInfo . atlas_height ) ; for ( let i = 0 ; i < this . volume . channels . length ; ++ i ) { if ( this . volume . getChannel ( i ) . loaded ) { this . channelData . onChannelLoaded ( [ i ] , this . volume . channels ) ; } } } public cleanup ( ) { this . cube . dispose ( ) ; this . cubeMesh . material . dispose ( ) ; this . channelData . cleanup ( ) ; } public setVisible ( isVisible ) { this . cubeMesh . visible = isVisible ; } public setShowBoundingBox ( showBoundingBox ) { this . boxHelper . visible = showBoundingBox ; } public setBoundingBoxColor ( color ) { ( this . boxHelper . material as LineBasicMaterial ) . color = new Color ( color [ 0 ] , color [ 1 ] , color [ 2 ] ) ; } public doRender ( canvas ) { if ( ! this . cubeMesh . visible ) { return ; } this . cubeTransformNode . updateMatrixWorld ( true ) ; const mvm = new Matrix4 ( ) ; mvm . multiplyMatrices ( canvas . camera . matrixWorldInverse , this . cubeMesh . matrixWorld ) ; const mi = new Matrix4 ( ) ; mi . copy ( mvm ) . invert ( ) ; this . setUniform ( "inverseModelViewMatrix" , mi ) ; } public get3dObject ( ) { return this . cubeTransformNode ; } public onChannelData ( batch ) { this . channelData . onChannelLoaded ( batch , this . volume . channels ) ; } public setScale ( scale ) { this . scale = scale ; this . cubeMesh . scale . copy ( new Vector3 ( scale . x , scale . y , scale . z ) ) ; this . setUniform ( "volumeScale" , scale ) ; this . boxHelper . box . set ( new Vector3 ( - 0.5 * scale . x , - 0.5 * scale . y , - 0.5 * scale . z ) , new Vector3 ( 0.5 * scale . x , 0.5 * scale . y , 0.5 * scale . z ) ) ; } public setRayStepSizes ( _primary , _secondary ) { } public setTranslation ( vec3xyz ) { this . cubeTransformNode . position . copy ( vec3xyz ) ; } public setRotation ( eulerXYZ ) { this . cubeTransformNode . rotation . copy ( eulerXYZ ) ; } public setOrthoScale ( value ) { this . setUniform ( "orthoScale" , value ) ; } public setResolution ( x , y ) { this . setUniform ( "iResolution" , new Vector2 ( x , y ) ) ; } public setPixelSamplingRate ( _value ) { } public setDensity ( density ) { this . setUniform ( "DENSITY" , density ) ; } public setBrightness ( brightness ) { this . setUniform ( "BRIGHTNESS" , brightness * 2.0 ) ; } public setIsOrtho ( isOrthoAxis ) { this . setUniform ( "isOrtho" , isOrthoAxis ? 1.0 : 0.0 ) ; if ( ! isOrthoAxis ) { this . setOrthoThickness ( 1.0 ) ; } } public viewpointMoved ( ) { } public setGamma ( gmin , glevel , gmax ) { this . setUniform ( "GAMMA_MIN" , gmin ) ; this . setUniform ( "GAMMA_MAX" , gmax ) ; this . setUniform ( "GAMMA_SCALE" , glevel ) ; } public setMaxProjectMode ( isMaxProject ) { this . setUniform ( "maxProject" , isMaxProject ? 1 : 0 ) ; } public setAxisClip ( axis , minval , maxval , isOrthoAxis ) { this . bounds . bmax [ axis ] = maxval ; this . bounds . bmin [ axis ] = minval ; if ( isOrthoAxis ) { const thicknessPct = maxval - minval ; this . setOrthoThickness ( thicknessPct ) ; } else { this . setOrthoThickness ( 1.0 ) ; } this . setUniform ( "AABB_CLIP_MIN" , this . bounds . bmin ) ; this . setUniform ( "AABB_CLIP_MAX" , this . bounds . bmax ) ; } public setFlipAxes ( flipX , flipY , flipZ ) { this . setUniform ( "flipVolume" , new Vector3 ( flipX , flipY , flipZ ) ) ; } public updateClipRegion ( xmin , xmax , ymin , ymax , zmin , zmax ) { this . bounds = { bmin : new Vector3 ( xmin - 0.5 , ymin - 0.5 , zmin - 0.5 ) , bmax : new Vector3 ( xmax - 0.5 , ymax - 0.5 , zmax - 0.5 ) , } ; this . setUniform ( "AABB_CLIP_MIN" , this . bounds . bmin ) ; this . setUniform ( "AABB_CLIP_MAX" , this . bounds . bmax ) ; } public setChannelAsMask ( channelIndex ) { if ( ! this . volume . channels [ channelIndex ] || ! this . volume . channels [ channelIndex ] . loaded ) { return false ; } return this . channelData . setChannelAsMask ( channelIndex , this . volume . channels [ channelIndex ] ) ; } public setMaskAlpha ( maskAlpha ) { this . setUniform ( "maskAlpha" , maskAlpha ) ; } public setOrthoThickness ( value ) { this . setUniform ( "orthoThickness" , value ) ; } private setUniform ( name , value ) { if ( ! this . uniforms [ name ] ) { return ; } this . uniforms [ name ] . value = value ; this . cubeMesh . material . needsUpdate = true ; } public fuse ( channelcolors , channeldata ) { const fusionType = "m" ; this . channelData . fuse ( channelcolors , fusionType , channeldata ) ; this . setUniform ( "textureAtlas" , this . channelData . fusedTexture ) ; this . setUniform ( "textureAtlasMask" , this . channelData . maskTexture ) ; } }