Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
26 changes: 23 additions & 3 deletions source/MRMesh/MRObjectVoxels.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,12 @@ void ObjectVoxels::construct( const SimpleVolume& volume, ProgressCallback cb )
indexer_ = VolumeIndexer( vdbVolume_.dims );
activeBox_ = Box3i( Vector3i(), vdbVolume_.dims );
reverseVoxelSize_ = { 1 / vdbVolume_.voxelSize.x,1 / vdbVolume_.voxelSize.y,1 / vdbVolume_.voxelSize.z };

volumeRenderActiveVoxels_.clear();

updateHistogram_( volume.min, volume.max );
if ( volumeRendering_ )
dirty_ |= ( DIRTY_PRIMITIVES | DIRTY_TEXTURE );
dirty_ |= ( DIRTY_PRIMITIVES | DIRTY_TEXTURE | DIRTY_SELECTION );
}

void ObjectVoxels::construct( const FloatGrid& grid, const Vector3f& voxelSize, ProgressCallback cb )
Expand All @@ -56,9 +59,11 @@ void ObjectVoxels::construct( const FloatGrid& grid, const Vector3f& voxelSize,
vdbVolume_.voxelSize = voxelSize;
reverseVoxelSize_ = { 1 / vdbVolume_.voxelSize.x,1 / vdbVolume_.voxelSize.y,1 / vdbVolume_.voxelSize.z };

volumeRenderActiveVoxels_.clear();

updateHistogramAndSurface( cb );
if ( volumeRendering_ )
dirty_ |= ( DIRTY_PRIMITIVES | DIRTY_TEXTURE );
dirty_ |= ( DIRTY_PRIMITIVES | DIRTY_TEXTURE | DIRTY_SELECTION );
}

void ObjectVoxels::construct( const VdbVolume& volume, ProgressCallback cb )
Expand Down Expand Up @@ -232,6 +237,10 @@ void ObjectVoxels::setActiveBounds( const Box3i& activeBox, ProgressCallback cb,
accessor.setActiveState( {x,y,z}, insideX && insideY && insideZ );
reportProgress( cb, [&]{ return cbModifier * float( counter ) / volume; }, ++counter, 256 );
}

volumeRenderActiveVoxels_.clear();
dirty_ |= DIRTY_SELECTION;

lastProgress = cbModifier;
if ( updateSurface )
{
Expand All @@ -250,6 +259,17 @@ void ObjectVoxels::setActiveBounds( const Box3i& activeBox, ProgressCallback cb,
}
}

void ObjectVoxels::setVolumeRenderActiveVoxels( const VoxelBitSet& activeVoxels )
{
auto box = activeBox_.size();
const bool valid = activeVoxels.size() == box.x * box.y * box.z;
assert( valid );
if ( !valid )
return;
volumeRenderActiveVoxels_ = activeVoxels;
dirty_ |= DIRTY_SELECTION;
}

VoxelId ObjectVoxels::getVoxelIdByCoordinate( const Vector3i& coord ) const
{
return indexer_.toVoxelId( coord );
Expand All @@ -275,7 +295,7 @@ bool ObjectVoxels::prepareDataForVolumeRendering( ProgressCallback cb /*= {} */
res.min = vdbVolume_.min;
res.voxelSize = vdbVolume_.voxelSize;
auto activeBox = getActiveBounds();
res.dims = activeBox.size() + Vector3i::diagonal( 1 );
res.dims = activeBox.size();
VolumeIndexer indexer( res.dims );
res.data.resize( indexer.size() );

Expand Down
6 changes: 6 additions & 0 deletions source/MRMesh/MRObjectVoxels.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,11 @@ class MRMESH_CLASS ObjectVoxels : public ObjectMeshHolder

const VoxelBitSet& getSelectedVoxels() const { return selectedVoxels_; }
void selectVoxels( const VoxelBitSet& selectedVoxels ) { selectedVoxels_ = selectedVoxels; }

/// get active (visible) voxels
const VoxelBitSet& getVolumeRenderActiveVoxels() const { return volumeRenderActiveVoxels_; }
/// set active (visible) voxels (using only in Volume Rendering mode)
MRMESH_API void setVolumeRenderActiveVoxels( const VoxelBitSet& activeVoxels );

/// VoxelId is numerical representation of voxel
/// Coordinate is {x,y,z} indices of voxels in box (base dimensions space, NOT active dimensions)
Expand Down Expand Up @@ -202,6 +207,7 @@ class MRMESH_CLASS ObjectVoxels : public ObjectMeshHolder

protected:
VoxelBitSet selectedVoxels_;
VoxelBitSet volumeRenderActiveVoxels_;

ObjectVoxels( const ObjectVoxels& other ) = default;
bool volumeRendering_{ false };
Expand Down
50 changes: 48 additions & 2 deletions source/MRViewer/MRRenderVolumeObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "MRGladGlfw.h"
#include "MRGLStaticHolder.h"
#include "MRMesh/MRFloatGrid.h"
#include "MRPch/MRTBB.h"

namespace MR
{
Expand Down Expand Up @@ -52,6 +53,41 @@ void RenderVolumeObject::forceBindAll()
bindVolume_( true );
}

RenderBufferRef<unsigned> RenderVolumeObject::loadActiveVoxelsTextureBuffer_()
{
auto& glBuffer = GLStaticHolder::getStaticGLBuffer();
if ( !( dirty_ & DIRTY_SELECTION ) || !objVoxels_->vdbVolume().data )
return glBuffer.prepareBuffer<unsigned>( activeVoxelsTextureSize_.x * activeVoxelsTextureSize_.y, false );

const auto& dims = objVoxels_->vdbVolume().dims;
auto numV = dims.x * dims.y * dims.z;

auto size = numV / 32 + 1;
activeVoxelsTextureSize_ = calcTextureRes( size, maxTexSize_ );
assert( activeVoxelsTextureSize_.x * activeVoxelsTextureSize_.y >= size );
auto buffer = glBuffer.prepareBuffer<unsigned>( activeVoxelsTextureSize_.x * activeVoxelsTextureSize_.y );


if ( objVoxels_->getVolumeRenderActiveVoxels().empty() )
{
tbb::parallel_for( tbb::blocked_range<int>( 0, ( int )buffer.size() ), [&] ( const tbb::blocked_range<int>& range )
{
for ( int r = range.begin(); r < range.end(); ++r )
buffer[r] = 0xFFFFFFFF;
} );
return buffer;
}
const auto& activeVoxels = objVoxels_->getVolumeRenderActiveVoxels().m_bits;
const unsigned* activeVoxelsData = ( unsigned* )activeVoxels.data();
tbb::parallel_for( tbb::blocked_range<int>( 0, ( int )buffer.size() ), [&] ( const tbb::blocked_range<int>& range )
{
for ( int r = range.begin(); r < range.end(); ++r )
buffer[r] = activeVoxelsData[r];
} );

return buffer;
}

void RenderVolumeObject::render_( const RenderParams& renderParams, unsigned geomId )
{
if ( !getViewerInstance().isGLInitialized() )
Expand Down Expand Up @@ -288,20 +324,30 @@ void RenderVolumeObject::bindVolume_( bool picker )
denseMap_.bind();
GL_EXEC( glUniform1i( glGetUniformLocation( shader, "denseMap" ), 1 ) );

// Active Voxels
auto activeVoxels = loadActiveVoxelsTextureBuffer_();
GL_EXEC( glActiveTexture( GL_TEXTURE2 ) );
activeVoxelsTex_.loadDataOpt( activeVoxels.dirty(),
{ .resolution = activeVoxelsTextureSize_, .internalFormat = GL_R32UI, .format = GL_RED_INTEGER, .type = GL_UNSIGNED_INT },
activeVoxels );
GL_EXEC( glUniform1i( glGetUniformLocation( shader, "activeVoxels" ), 2 ) );

GL_EXEC( glUniform1f( glGetUniformLocation( shader, "minValue" ), ( params.min - volume.min ) / ( volume.max - volume.min ) ) );
GL_EXEC( glUniform1f( glGetUniformLocation( shader, "maxValue" ), ( params.max - volume.min ) / ( volume.max - volume.min ) ) );
GL_EXEC( glUniform1i( glGetUniformLocation( shader, "shadingMode" ), int( params.shadingType ) ) );

dirty_ &= ~( DIRTY_PRIMITIVES | DIRTY_TEXTURE );
dirty_ &= ~( DIRTY_PRIMITIVES | DIRTY_TEXTURE | DIRTY_SELECTION );
}

void RenderVolumeObject::initBuffers_()
{
GL_EXEC( glGenVertexArrays( 1, &volumeArrayObjId_ ) );
GL_EXEC( glBindVertexArray( volumeArrayObjId_ ) );

dirty_ = DIRTY_PRIMITIVES | DIRTY_TEXTURE;
GL_EXEC( glGetIntegerv( GL_MAX_TEXTURE_SIZE, &maxTexSize_ ) );
assert( maxTexSize_ > 0 );

dirty_ = DIRTY_PRIMITIVES | DIRTY_TEXTURE | DIRTY_SELECTION;
}

void RenderVolumeObject::freeBuffers_()
Expand Down
9 changes: 8 additions & 1 deletion source/MRViewer/MRRenderVolumeObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace MR
{
class RenderVolumeObject : public IRenderObject
class MRVIEWER_CLASS RenderVolumeObject : public IRenderObject
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need export this class?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why we don't need this (unlike others object classes)?

{
public:
RenderVolumeObject( const VisualObject& visObj );
Expand All @@ -19,6 +19,7 @@ class RenderVolumeObject : public IRenderObject
virtual size_t heapBytes() const override;
virtual size_t glBytes() const override;
virtual void forceBindAll() override;

private:
const ObjectVoxels* objVoxels_{ nullptr };

Expand All @@ -30,6 +31,10 @@ class RenderVolumeObject : public IRenderObject
GlTexture3 volume_;
GlTexture2 denseMap_;

Vector2i activeVoxelsTextureSize_;
GlTexture2 activeVoxelsTex_;
int maxTexSize_{ 0 };

void render_( const RenderParams& params, unsigned geomId );
void bindVolume_( bool picker );

Expand All @@ -41,6 +46,8 @@ class RenderVolumeObject : public IRenderObject

void update_();

RenderBufferRef<unsigned> loadActiveVoxelsTextureBuffer_();

// Marks dirty buffers that need to be uploaded to OpenGL
uint32_t dirty_{ 0 };
};
Expand Down
28 changes: 28 additions & 0 deletions source/MRViewer/MRVolumeShader.cpp
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there can be uint overflow for big volumes voxel index it makes sanse to calculate voxel index directly

Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ std::string getVolumeFragmentShader()

uniform sampler3D volume;
uniform sampler2D denseMap;
uniform highp usampler2D activeVoxels; // (in from base) selection BitSet

uniform bool useClippingPlane; // (in from base) clip primitive by plane if true
uniform vec4 clippingPlane; // (in from base) clipping plane
Expand Down Expand Up @@ -163,6 +164,8 @@ std::string getVolumeFragmentShader()
vec3 rayStep = step * normRayDir;
rayStart = rayStart - rayStep * 0.5;
vec3 diagonal = maxPoint - minPoint;
uint dimsXY = uint( dims.y * dims.x );
uint dimsX = uint( dims.x );
Comment on lines +167 to +168
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
uint dimsXY = uint( dims.y * dims.x );
uint dimsX = uint( dims.x );
uint volumeSize = uint( dims.z *dims.y * dims.x );
uint dimsXY = uint( dims.y*dims.x );

Copy link
Contributor Author

@ABSitf ABSitf Sep 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see next

while ( outColor.a < 1.0 )
{
rayStart = rayStart + rayStep;
Expand All @@ -174,6 +177,17 @@ std::string getVolumeFragmentShader()
if (useClippingPlane && dot( vec3( model*vec4(rayStart,1.0)),vec3(clippingPlane))>clippingPlane.w)
continue;

{
ivec2 texSize = textureSize( activeVoxels, 0 );
// TODO fix potential ovewflow
uint voxelId = uint( textCoord.z * dims.z ) * dimsXY + uint( textCoord.y * dims.y ) * dimsX + uint( textCoord.x * dims.x );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
uint voxelId = uint( textCoord.z * dims.z ) * dimsXY + uint( textCoord.y * dims.y ) * dimsX + uint( textCoord.x * dims.x );
uint voxelId = uint( textCoord.z * volumeSize ) + uint( textCoord.y * dimsXY ) + uint( textCoord.x * dims.x );

Copy link
Contributor Author

@ABSitf ABSitf Sep 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

textCoord is not voxel center (textCoord = voxelCenter + smallShift)
so uint( textCoord.z * volumeSize ) have additional error

uint index = voxelId / 32u;
uint block = texelFetch( activeVoxels, ivec2( index % uint( texSize.x ), index / uint( texSize.x ) ), 0 ).r;
bool active = bool( block & uint( 1 << ( voxelId % 32u ) ) );
if ( !active )
continue;
}

float density = texture( volume, textCoord ).r;
if ( density < minValue || density > maxValue )
continue;
Expand Down Expand Up @@ -237,6 +251,7 @@ std::string getVolumePickerFragmentShader()
uniform sampler3D volume;
uniform sampler2D denseMap;

uniform highp usampler2D activeVoxels; // (in from base) selection BitSet
uniform bool useClippingPlane; // (in from base) clip primitive by plane if true
uniform vec4 clippingPlane; // (in from base) clipping plane

Expand Down Expand Up @@ -313,6 +328,8 @@ std::string getVolumePickerFragmentShader()
vec3 rayStep = step * normRayDir;
rayStart = rayStart - rayStep * 0.5;
vec3 diagonal = maxPoint - minPoint;
uint dimsXY = uint( dims.y * dims.x );
uint dimsX = uint( dims.x );
while ( !firstFound )
{
rayStart = rayStart + rayStep;
Expand All @@ -324,6 +341,17 @@ std::string getVolumePickerFragmentShader()
if (useClippingPlane && dot( vec3( model*vec4(rayStart,1.0)),vec3(clippingPlane))>clippingPlane.w)
continue;

{
ivec2 texSize = textureSize( activeVoxels, 0 );
// TODO fix potential ovewflow
uint voxelId = uint( textCoord.z * dims.z ) * dimsXY + uint( textCoord.y * dims.y ) * dimsX + uint( textCoord.x * dims.x );
uint index = voxelId / 32u;
uint block = texelFetch( activeVoxels, ivec2( index % uint( texSize.x ), index / uint( texSize.x ) ), 0 ).r;
bool active = bool( block & uint( 1 << ( voxelId % 32u ) ) );
if ( !active )
continue;
}

float density = texture( volume, textCoord ).r;
if ( density < minValue || density > maxValue )
continue;
Expand Down