///////////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2020- Equinor ASA // // ResInsight is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY // WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. // // See the GNU General Public License at // for more details. // ///////////////////////////////////////////////////////////////////////////////// #include "RivSurfacePartMgr.h" #include "RiaPreferences.h" #include "ProjectDataModel/RimCase.h" #include "RigSurface.h" #include "Rim3dView.h" #include "RimRegularLegendConfig.h" #include "RimSurface.h" #include "RimSurfaceInView.h" #include "RimSurfaceResultDefinition.h" #include "RivIntersectionResultsColoringTools.h" #include "RivMeshLinesSourceInfo.h" #include "RivPartPriority.h" #include "RivReservoirSurfaceIntersectionSourceInfo.h" #include "RivScalarMapperUtils.h" #include "RivSurfaceIntersectionGeometryGenerator.h" #include "cafEffectGenerator.h" #include "cvfDrawableGeo.h" #include "cvfModelBasicList.h" #include "cvfPart.h" #include "cvfPrimitiveSetIndexedUInt.h" #include //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- RivSurfacePartMgr::RivSurfacePartMgr( RimSurfaceInView* surface ) : m_surfaceInView( surface ) { CVF_ASSERT( surface ); cvf::ref hexGrid = m_surfaceInView->createHexGridInterface(); m_intersectionGenerator = new RivSurfaceIntersectionGeometryGenerator( m_surfaceInView, hexGrid.p() ); m_intersectionFacesTextureCoords = new cvf::Vec2fArray; m_nativeTrianglesTextureCoords = new cvf::Vec2fArray; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RivSurfacePartMgr::appendNativeGeometryPartsToModel( cvf::ModelBasicList* model, cvf::Transform* scaleTransform ) { if ( m_nativeTrianglesPart.isNull() || m_surfaceInView->surface()->surfaceData() != m_usedSurfaceData.p() ) { generateNativePartGeometry(); } if ( m_nativeTrianglesPart.notNull() ) { m_nativeTrianglesPart->setTransform( scaleTransform ); this->updateNativeSurfaceColors(); model->addPart( m_nativeTrianglesPart.p() ); if ( m_nativeMeshLinesPart.notNull() ) { m_nativeMeshLinesPart->setTransform( scaleTransform ); model->addPart( m_nativeMeshLinesPart.p() ); } } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RivSurfacePartMgr::updateCellResultColor( size_t timeStepIndex ) { if ( m_intersectionFaces.notNull() ) { RivIntersectionResultsColoringTools::calculateIntersectionResultColors( timeStepIndex, true, m_surfaceInView, m_intersectionGenerator.p(), nullptr, nullptr, m_intersectionFaces.p(), m_intersectionFacesTextureCoords.p() ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RivSurfacePartMgr::appendIntersectionGeometryPartsToModel( cvf::ModelBasicList* model, cvf::Transform* scaleTransform ) { if ( !m_surfaceInView->surfaceResultDefinition()->isChecked() ) { if ( m_intersectionFaces.isNull() ) { generatePartGeometry(); } if ( m_intersectionFaces.notNull() ) { m_intersectionFaces->setTransform( scaleTransform ); model->addPart( m_intersectionFaces.p() ); } // Mesh Lines if ( m_intersectionGridLines.isNull() ) { generatePartGeometry(); } if ( m_intersectionGridLines.notNull() ) { m_intersectionGridLines->setTransform( scaleTransform ); model->addPart( m_intersectionGridLines.p() ); } if ( m_intersectionFaultGridLines.notNull() ) { m_intersectionFaultGridLines->setTransform( scaleTransform ); model->addPart( m_intersectionFaultGridLines.p() ); } } appendNativeGeometryPartsToModel( model, scaleTransform ); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RivSurfacePartMgr::updateNativeSurfaceColors() { if ( m_surfaceInView->surfaceResultDefinition()->isChecked() ) { if ( m_usedSurfaceData.isNull() ) generateNativePartGeometry(); auto mapper = m_surfaceInView->surfaceResultDefinition()->legendConfig()->scalarMapper(); if ( m_usedSurfaceData.notNull() ) { QString propertyName = m_surfaceInView->surfaceResultDefinition()->propertyName(); auto values = m_usedSurfaceData->propertyValues( propertyName ); const std::vector& vertices = m_usedSurfaceData->vertices(); m_nativeTrianglesTextureCoords->resize( vertices.size() ); m_nativeTrianglesTextureCoords->setAll( cvf::Vec2f( 0.5f, 1.0f ) ); for ( size_t i = 0; i < values.size(); i++ ) { const double val = values[i]; if ( val < std::numeric_limits::infinity() && val == val ) { m_nativeTrianglesTextureCoords->set( i, mapper->mapToTextureCoord( val ) ); } } float effectiveOpacityLevel = 1.0; bool disableLighting = true; // always disable lighting for now, as it doesn't look good RivScalarMapperUtils::applyTextureResultsToPart( m_nativeTrianglesPart.p(), m_nativeTrianglesTextureCoords.p(), mapper, effectiveOpacityLevel, caf::FC_NONE, disableLighting ); } } else { caf::SurfaceEffectGenerator surfaceGenBehind( cvf::Color4f( m_surfaceInView->surface()->color() ), caf::PO_POS_LARGE ); cvf::ref effBehind = surfaceGenBehind.generateCachedEffect(); if ( m_nativeTrianglesPart.notNull() ) { m_nativeTrianglesPart->setEffect( effBehind.p() ); } } if ( m_intersectionFaces.notNull() ) { caf::SurfaceEffectGenerator surfaceGen( cvf::Color4f( m_surfaceInView->surface()->color() ), caf::PO_1 ); cvf::ref eff = surfaceGen.generateCachedEffect(); m_intersectionFaces->setEffect( eff.p() ); } // Update mesh colors as well, in case of change RiaPreferences* prefs = RiaPreferences::current(); if ( m_intersectionGridLines.notNull() ) { cvf::ref eff; caf::MeshEffectGenerator CrossSectionEffGen( prefs->defaultGridLineColors() ); eff = CrossSectionEffGen.generateCachedEffect(); m_intersectionGridLines->setEffect( eff.p() ); } if ( m_intersectionFaultGridLines.notNull() ) { cvf::ref eff; caf::MeshEffectGenerator CrossSectionEffGen( prefs->defaultFaultGridLineColors() ); eff = CrossSectionEffGen.generateCachedEffect(); m_intersectionFaultGridLines->setEffect( eff.p() ); } } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- QString RivSurfacePartMgr::resultInfoText( Rim3dView* view, uint hitPart, cvf::Vec3d hitPoint ) { QString retval; if ( m_surfaceInView->surfaceResultDefinition()->isChecked() ) { const auto& values = m_usedSurfaceData->propertyValues( m_surfaceInView->surfaceResultDefinition()->propertyName() ); if ( values.empty() ) return ""; const auto& ind = m_usedSurfaceData->triangleIndices(); const auto& vert = m_usedSurfaceData->vertices(); size_t indIndex = ( size_t )( hitPart * 3 ); // find closest triangle corner point to hit point and show that value if ( ind.size() > ( indIndex + 2 ) ) { uint vertIndex1 = ind[indIndex]; uint vertIndex2 = ind[indIndex + 1]; uint vertIndex3 = ind[indIndex + 2]; double dist1 = vert[vertIndex1].pointDistance( hitPoint ); double dist2 = vert[vertIndex2].pointDistance( hitPoint ); double dist3 = vert[vertIndex3].pointDistance( hitPoint ); double resultValue = -1.0; if ( vertIndex1 < values.size() ) resultValue = values[vertIndex1]; if ( dist2 < dist1 && vertIndex2 < values.size() ) resultValue = values[vertIndex2]; if ( ( dist3 < dist1 ) && ( dist3 < dist2 ) && vertIndex3 < values.size() ) resultValue = values[vertIndex3]; retval += QString( "%1 : %2\n\n" ).arg( m_surfaceInView->surfaceResultDefinition()->propertyName() ).arg( resultValue ); } } return retval; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- const RivIntersectionGeometryGeneratorIF* RivSurfacePartMgr::intersectionGeometryGenerator() const { if ( m_intersectionGenerator.notNull() ) return m_intersectionGenerator.p(); return nullptr; } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RivSurfacePartMgr::generatePartGeometry() { if ( m_intersectionGenerator.isNull() ) return; bool useBufferObjects = true; // Surface geometry { cvf::ref geo = m_intersectionGenerator->generateSurface(); if ( geo.notNull() ) { geo->computeNormals(); if ( useBufferObjects ) { geo->setRenderMode( cvf::DrawableGeo::BUFFER_OBJECT ); } cvf::ref part = new cvf::Part; part->setName( "Intersected Reservoir Surface" ); part->setDrawable( geo.p() ); // Set mapping from triangle face index to cell index cvf::ref si = new RivReservoirSurfaceIntersectionSourceInfo( m_intersectionGenerator.p() ); part->setSourceInfo( si.p() ); part->updateBoundingBox(); part->setEnableMask( intersectionCellFaceBit ); part->setPriority( RivPartPriority::PartType::Intersection ); m_intersectionFaces = part; } } // Cell Mesh geometry { cvf::ref geoMesh = m_intersectionGenerator->createMeshDrawable(); if ( geoMesh.notNull() ) { if ( useBufferObjects ) { geoMesh->setRenderMode( cvf::DrawableGeo::BUFFER_OBJECT ); } cvf::ref part = new cvf::Part; part->setName( "Surface intersection mesh" ); part->setDrawable( geoMesh.p() ); part->updateBoundingBox(); part->setEnableMask( intersectionCellMeshBit ); part->setPriority( RivPartPriority::PartType::MeshLines ); part->setSourceInfo( new RivMeshLinesSourceInfo( m_surfaceInView ) ); m_intersectionGridLines = part; } } // Fault Mesh geometry { cvf::ref geoMesh = m_intersectionGenerator->createFaultMeshDrawable(); if ( geoMesh.notNull() ) { if ( useBufferObjects ) { geoMesh->setRenderMode( cvf::DrawableGeo::BUFFER_OBJECT ); } cvf::ref part = new cvf::Part; part->setName( "Surface fault mesh" ); part->setDrawable( geoMesh.p() ); part->updateBoundingBox(); part->setEnableMask( intersectionFaultMeshBit ); part->setPriority( RivPartPriority::PartType::FaultMeshLines ); part->setSourceInfo( new RivMeshLinesSourceInfo( m_surfaceInView ) ); m_intersectionFaultGridLines = part; } } updateNativeSurfaceColors(); } //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- void RivSurfacePartMgr::generateNativePartGeometry() { RimCase* ownerCase; m_surfaceInView->firstAncestorOrThisOfTypeAsserted( ownerCase ); cvf::Vec3d displayModOffsett = ownerCase->displayModelOffset(); m_usedSurfaceData = m_surfaceInView->surface()->surfaceData(); if ( m_usedSurfaceData.isNull() ) return; const std::vector& vertices = m_usedSurfaceData->vertices(); cvf::ref cvfVertices = new cvf::Vec3fArray( vertices.size() ); for ( size_t i = 0; i < vertices.size(); ++i ) { ( *cvfVertices )[i] = cvf::Vec3f( vertices[i] - displayModOffsett ); } const std::vector& triangleIndices = m_usedSurfaceData->triangleIndices(); cvf::ref cvfIndices = new cvf::UIntArray( triangleIndices ); cvf::ref indexSet = new cvf::PrimitiveSetIndexedUInt( cvf::PT_TRIANGLES ); indexSet->setIndices( cvfIndices.p() ); cvf::ref drawGeo = new cvf::DrawableGeo; drawGeo->addPrimitiveSet( indexSet.p() ); drawGeo->setVertexArray( cvfVertices.p() ); drawGeo->computeNormals(); cvf::ref objectSourceInfo = new RivObjectSourceInfo( m_surfaceInView ); m_nativeTrianglesPart = new cvf::Part(); m_nativeTrianglesPart->setName( "Native Reservoir Surface" ); m_nativeTrianglesPart->setDrawable( drawGeo.p() ); m_nativeTrianglesPart->setSourceInfo( objectSourceInfo.p() ); }