// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 #include "stdafx.h" #if defined( FIELD3D_AVAILABLE ) #include #include #pragma warning( push, 3 ) #pragma warning( disable : 4251 4244 4101 ) #include #include #include #include #include #pragma warning( pop ) #include namespace ember { namespace { template class field3d_field_impl : public field3d_field_interface { public: field3d_field_impl( typename FieldType::Ptr pField ); virtual Field3D::FieldRes& get_field() const; virtual Field3D::FieldRes::Ptr get_field_ptr() const; virtual std::size_t get_memory_usage() const; virtual bool evaluate_field( void* dest, const frantic::graphics::vector3f& pos ) const; virtual const frantic::channels::channel_map& get_channel_map() const; private: typedef typename InterpType::value_type value_type; typename FieldType::Ptr m_impl; InterpType m_interp; frantic::channels::channel_map m_map; }; template struct output_type { typedef float type; }; template struct output_type> { typedef frantic::graphics::vector3f type; }; template field3d_field_impl::field3d_field_impl( typename F::Ptr pField ) : m_impl( pField ) { m_map.define_channel::type>( frantic::strings::to_tstring( pField->attribute ) ); m_map.end_channel_definition(); } template Field3D::FieldRes& field3d_field_impl::get_field() const { return *m_impl; } template Field3D::FieldRes::Ptr field3d_field_impl::get_field_ptr() const { return m_impl; } template std::size_t field3d_field_impl::get_memory_usage() const { return static_cast( m_impl->memSize() ); } template struct output_cast { typedef float type; }; template struct output_cast> { typedef Imath::Vec3 type; }; template bool field3d_field_impl::evaluate_field( void* dest, const frantic::graphics::vector3f& pos ) const { Field3D::V3d vp; m_impl->mapping()->worldToVoxel( Field3D::V3d( pos.x, pos.y, pos.z ), vp ); *static_cast( dest ) = static_cast::type>( m_interp.sample( *m_impl, vp ) ); return true; } template const frantic::channels::channel_map& field3d_field_impl::get_channel_map() const { return m_map; } void dump_file( const Field3D::Field3DInputFile& file, const std::string& filePath ) { std::stringstream ss; std::vector partitionNames; std::vector scalarLayerNames, vectorLayerNames; file.getPartitionNames( partitionNames ); ss << "File: \"" << filePath << std::endl; for( std::vector::const_iterator it = partitionNames.begin(), itEnd = partitionNames.end(); it != itEnd; ++it ) { ss << "\tPartition: " << *it << std::endl; // Field3D::File::Partition::Ptr pPart = file.getPartition( *it ); // if( !pPart ) // continue; scalarLayerNames.clear(); vectorLayerNames.clear(); file.getScalarLayerNames( scalarLayerNames, *it ); file.getVectorLayerNames( vectorLayerNames, *it ); for( std::vector::const_iterator it = scalarLayerNames.begin(), itEnd = scalarLayerNames.end(); it != itEnd; ++it ) ss << "\t\tScalar layer: " << *it << std::endl; for( std::vector::const_iterator it = vectorLayerNames.begin(), itEnd = vectorLayerNames.end(); it != itEnd; ++it ) ss << "\t\tVector layer: " << *it << std::endl; } FF_LOG( debug ) << frantic::strings::to_tstring( ss.str() ); }; template boost::shared_ptr process_scalar_field( const typename Field3D::Field::Ptr& pField ) { if( Field3D::DenseField::Ptr pDense = Field3D::field_dynamic_cast>( pField ) ) return boost::make_shared, Field3D::DenseField::LinearInterp>>( pDense ); else return boost::make_shared, Field3D::LinearFieldInterp>>( pField ); } template void process_scalar_fields( Field3D::Field3DInputFile& file, concatenated_field& resultField, field3d_meta& outMeta ) { Field3D::Field::Vec scalarFields = file.readScalarLayers(); for( Field3D::Field::Vec::const_iterator it = scalarFields.begin(), itEnd = scalarFields.end(); it != itEnd; ++it ) { boost::shared_ptr pField = process_scalar_field( *it ); const Field3D::FieldRes& impl = pField->get_field(); Field3D::V3d wsMin, wsMax; impl.mapping()->voxelToWorld( static_cast( static_cast( impl.dataWindow().min ) + Field3D::V3d( 0.5 ) ), wsMin ); impl.mapping()->voxelToWorld( static_cast( static_cast( impl.dataWindow().max ) + Field3D::V3d( 0.5 ) ), wsMax ); outMeta.bounds += frantic::graphics::vector3f( static_cast( wsMin.x ), static_cast( wsMin.y ), static_cast( wsMin.z ) ); outMeta.bounds += frantic::graphics::vector3f( static_cast( wsMax.x ), static_cast( wsMax.y ), static_cast( wsMax.z ) ); outMeta.spacing = std::min( outMeta.spacing, static_cast( impl.mapping()->wsVoxelSize( impl.extents().min.x, impl.extents().min.y, impl.extents().min.z ).x ) ); outMeta.memoryUsage += static_cast( impl.memSize() ); outMeta.map.define_channel( frantic::strings::to_tstring( impl.attribute ) ); resultField.add_field( pField ); } } template boost::shared_ptr process_vector_field( const typename Field3D::Field>::Ptr& pField ) { if( Field3D::DenseField>::Ptr pDense = Field3D::field_dynamic_cast>>( pField ) ) return boost::make_shared< field3d_field_impl>, Field3D::DenseField>::LinearInterp>>( pDense ); else return boost::make_shared< field3d_field_impl>, Field3D::LinearFieldInterp>>>( pField ); } template void process_vector_fields( Field3D::Field3DInputFile& file, concatenated_field& resultField, field3d_meta& outMeta ) { Field3D::Field>::Vec vectorFields = file.readVectorLayers(); for( Field3D::Field>::Vec::const_iterator it = vectorFields.begin(), itEnd = vectorFields.end(); it != itEnd; ++it ) { boost::shared_ptr pField = process_vector_field( *it ); const Field3D::FieldRes& impl = pField->get_field(); Field3D::V3d wsMin, wsMax; impl.mapping()->voxelToWorld( static_cast( static_cast( impl.dataWindow().min ) + Field3D::V3d( 0.5 ) ), wsMin ); impl.mapping()->voxelToWorld( static_cast( static_cast( impl.dataWindow().max ) + Field3D::V3d( 0.5 ) ), wsMax ); outMeta.bounds += frantic::graphics::vector3f( static_cast( wsMin.x ), static_cast( wsMin.y ), static_cast( wsMin.z ) ); outMeta.bounds += frantic::graphics::vector3f( static_cast( wsMax.x ), static_cast( wsMax.y ), static_cast( wsMax.z ) ); outMeta.spacing = std::min( outMeta.spacing, static_cast( impl.mapping()->wsVoxelSize( impl.extents().min.x, impl.extents().min.y, impl.extents().min.z ).x ) ); outMeta.memoryUsage += static_cast( impl.memSize() ); outMeta.map.define_channel( frantic::strings::to_tstring( impl.attribute ) ); resultField.add_field( pField ); } } template struct convert { inline static TDest apply( const TSrc& src ) { return static_cast( src ); } }; template <> struct convert { inline static Field3D::V3f apply( const frantic::graphics::vector3f& src ) { return Field3D::V3f( src.x, src.y, src.z ); } }; template struct dense_grid_writer { static void write( Field3D::FieldRes& outField_, const void* srcData, const Field3D::V3i& pos ) { static_cast&>( outField_ ).fastLValue( pos.x, pos.y, pos.z ) = convert::apply( *static_cast( srcData ) ); } }; template struct generic_grid_writer { static void write( Field3D::FieldRes& outField_, const void* srcData, const Field3D::V3i& pos ) { static_cast&>( outField_ ).lvalue( pos.x, pos.y, pos.z ) = convert::apply( *static_cast( srcData ) ); } }; template typename Field3D::DenseField::Ptr convert_to_field3d( const frantic::volumetrics::field_interface& field, const Field3D::Box3i& extents, const Field3D::Box3i& dataWindow, float spacing ) { Field3D::DenseField::Ptr result( new Field3D::DenseField ); result->setSize( extents, dataWindow ); if( spacing != 1.f ) { Field3D::M44d tm; tm.setScale( Field3D::V3f( spacing * ( extents.max.x - extents.min.x + 1 ), spacing * ( extents.max.y - extents.min.y + 1 ), spacing * ( extents.max.z - extents.min.z + 1 ) ) ); Field3D::MatrixFieldMapping::Ptr map( new Field3D::MatrixFieldMapping ); map->setExtents( extents ); map->setLocalToWorld( tm ); result->setMapping( map ); } TSrc temp; for( Field3D::DenseField::iterator it = result->begin(), itEnd = result->end(); it != itEnd; ++it ) { Imath::V3d wp, vp( it.x, it.y, it.z ); result->mapping()->voxelToWorld( vp, wp ); if( field.evaluate_field( &temp, frantic::graphics::vector3f( static_cast( wp.x ), static_cast( wp.y ), static_cast( wp.z ) ) ) ) *it = static_cast( temp ); } return result; } struct scoped_redirect { std::ostream* pStream; std::streambuf* pOldBuf; scoped_redirect( std::ostream& stream, std::streambuf* newBuf ) : pStream( &stream ) { pStream->flush(); pOldBuf = pStream->rdbuf( newBuf ); } ~scoped_redirect() { pStream->rdbuf( pOldBuf ); } }; } // End of anonymous namespace boost::shared_ptr load_fields_from_file( const frantic::tstring& filePath ) { field3d_meta meta; return load_fields_from_file( filePath, meta ); } boost::shared_ptr load_fields_from_file( const frantic::tstring& filePath, field3d_meta& outMeta ) { std::stringstream ssout; scoped_redirect redir( std::cout, ssout.rdbuf() ); Field3D::Field3DInputFile file; boost::shared_ptr outField = boost::make_shared(); std::string realPath = frantic::strings::to_string( filePath ); bool result = file.open( realPath ); if( !result ) { FF_LOG( error ) << _T("Failed to load file: ") << filePath << std::endl; FF_LOG( error ) << _T("COUT:") << std::endl << frantic::strings::to_tstring( ssout.str() ) << std::endl; throw std::runtime_error( "load_fields_from_file() Failed to load file: \"" + realPath + "\"" ); } try { dump_file( file, realPath ); outMeta.bounds.set_to_empty(); outMeta.map.reset(); outMeta.memoryUsage = 0; outMeta.spacing = std::numeric_limits::max(); process_scalar_fields( file, *outField, outMeta ); process_scalar_fields( file, *outField, outMeta ); process_scalar_fields( file, *outField, outMeta ); process_vector_fields( file, *outField, outMeta ); process_vector_fields( file, *outField, outMeta ); process_vector_fields( file, *outField, outMeta ); outMeta.map.end_channel_definition(); if( outMeta.spacing == std::numeric_limits::max() ) outMeta.spacing = 0.f; result = file.close(); if( !result ) { FF_LOG( error ) << _T("Failed to close opened file: ") << filePath << std::endl; FF_LOG( error ) << _T("COUT:") << std::endl << frantic::strings::to_tstring( ssout.str() ) << std::endl; throw std::runtime_error( "load_fields_from_file() Failed to close file: \"" + realPath + "\" after load." ); } } catch( ... ) { file.close(); throw; } return outField; } void sample_to_field3d( const frantic::volumetrics::field_interface& field, std::vector& outFields, const Field3D::Box3i& extents, const Field3D::Box3i& dataWindow, float spacing ) { outFields.clear(); typedef std::pair accessor_type; std::vector accessors; Field3D::MatrixFieldMapping::Ptr pMap( new Field3D::MatrixFieldMapping ); Field3D::M44d tm; tm.setTranslation( Field3D::V3f( spacing * extents.min.x, spacing * extents.min.y, spacing * extents.min.z ) ); tm.scale( Field3D::V3f( spacing * ( extents.max.x - extents.min.x + 1 ), spacing * ( extents.max.y - extents.min.y + 1 ), spacing * ( extents.max.z - extents.min.z + 1 ) ) ); // Why is it like this?! pMap->setExtents( extents ); pMap->setLocalToWorld( tm ); const frantic::channels::channel_map& map = field.get_channel_map(); for( std::size_t i = 0, iEnd = map.channel_count(); i < iEnd; ++i ) { switch( map[i].data_type() ) { case frantic::channels::data_type_float32: if( map[i].arity() == 1 ) { Field3D::DenseFieldf::Ptr pF( new Field3D::DenseFieldf ); pF->name = "default"; pF->attribute = frantic::strings::to_string( map[i].name() ); pF->setMapping( pMap ); pF->setSize( extents, dataWindow ); outFields.push_back( pF ); accessors.push_back( accessor_type( map[i].offset(), &dense_grid_writer::write ) ); } else if( map[i].arity() == 3 ) { Field3D::DenseField3f::Ptr pF( new Field3D::DenseField3f ); pF->name = "default"; pF->attribute = frantic::strings::to_string( map[i].name() ); pF->setMapping( pMap ); pF->setSize( extents, dataWindow ); outFields.push_back( pF ); accessors.push_back( accessor_type( map[i].offset(), &dense_grid_writer::write ) ); } else { throw std::runtime_error( "Invalid type for sample_to_field3d(): " + frantic::strings::to_string( frantic::channels::channel_data_type_str( map[i].arity(), map[i].data_type() ) ) ); } break; default: throw std::runtime_error( "Invalid type for sample_to_field3d(): " + frantic::strings::to_string( frantic::channels::channel_data_type_str( map[i].arity(), map[i].data_type() ) ) ); } } Field3D::V3i p; void* buffer = alloca( map.structure_size() ); // TODO: Parallelize this. for( p = dataWindow.min; p.z <= dataWindow.max.z; ++p.z, p.y = dataWindow.min.y ) { for( ; p.y <= dataWindow.max.y; ++p.y, p.x = dataWindow.min.x ) { for( ; p.x <= dataWindow.max.x; ++p.x ) { Field3D::V3d wsP; pMap->voxelToWorld( Field3D::V3d( static_cast( p.x ) + 0.5, static_cast( p.y ) + 0.5, static_cast( p.z ) + 0.5 ), wsP ); if( field.evaluate_field( buffer, frantic::graphics::vector3f( static_cast( wsP.x ), static_cast( wsP.y ), static_cast( wsP.z ) ) ) ) { // Write to the grids. for( std::size_t i = 0, iEnd = outFields.size(); i < iEnd; ++i ) accessors[i].second( *outFields[i], static_cast( buffer ) + accessors[i].first, p ); } } } } } void write_fields_to_file( const frantic::tstring& filePath, const std::vector& fields ) { std::stringstream ssout; scoped_redirect redir( std::cout, ssout.rdbuf() ); Field3D::Field3DOutputFile file; std::string realPath = frantic::strings::to_string( filePath ); if( !file.create( realPath ) ) { FF_LOG( error ) << _T("Failed to create file: ") << filePath << std::endl; FF_LOG( error ) << _T("COUT:") << std::endl << frantic::strings::to_tstring( ssout.str() ) << std::endl; throw std::runtime_error( "write_fields_to_file() Failed to create file: \"" + realPath + "\"" ); } bool result = true; for( std::vector::const_iterator it = fields.begin(), itEnd = fields.end(); it != itEnd && result; ++it ) { if( Field3D::Field::Ptr pField = Field3D::field_dynamic_cast>( *it ) ) { result = file.writeScalarLayer( "default", pField->attribute, pField ); } else if( Field3D::Field::Ptr pField = Field3D::field_dynamic_cast>( *it ) ) { result = file.writeScalarLayer( "default", pField->attribute, pField ); } else if( Field3D::Field::Ptr pField = Field3D::field_dynamic_cast>( *it ) ) { result = file.writeScalarLayer( "default", pField->attribute, pField ); } else if( Field3D::Field::Ptr pField = Field3D::field_dynamic_cast>( *it ) ) { result = file.writeVectorLayer( "default", pField->attribute, pField ); } else if( Field3D::Field::Ptr pField = Field3D::field_dynamic_cast>( *it ) ) { result = file.writeVectorLayer( "default", pField->attribute, pField ); } else if( Field3D::Field::Ptr pField = Field3D::field_dynamic_cast>( *it ) ) { result = file.writeVectorLayer( "default", pField->attribute, pField ); } else { FF_LOG( warning ) << _T("Unknown Field3D type: ") << frantic::strings::to_tstring( ( *it )->classType() ) << std::endl; } } if( !result ) { FF_LOG( error ) << _T("Failed to write file: ") << filePath << std::endl; FF_LOG( error ) << _T("COUT:") << std::endl << frantic::strings::to_tstring( ssout.str() ) << std::endl; } else { result = file.close(); if( !result ) { FF_LOG( error ) << _T("Failed to close written file: ") << filePath << std::endl; FF_LOG( error ) << _T("COUT:") << std::endl << frantic::strings::to_tstring( ssout.str() ) << std::endl; } } } // void write_fields_to_file( const frantic::tstring& filePath, const std::vector< // boost::shared_ptr >& fields, Field3D::Box3i& extents, Field3D::Box3i& // dataWindow, float spacing ){ Field3D::Field3DOutputFile file; // // std::string realPath = frantic::strings::to_string( filePath ); // // file.create( realPath ); // // for( std::vector< boost::shared_ptr >::const_iterator it = //fields.begin(), itEnd = fields.end(); it != itEnd; ++it ){ if( boost::shared_ptr pField3D = //boost::dynamic_pointer_cast( *it ) ){ Field3D::FieldRes::Ptr pImpl = //pField3D->get_field_ptr(); // // std::string dtString = pImpl->dataTypeString(); // // // TODO: Should I be applying the extents & data window to these fields? // if( Field3D::Field::Ptr pField = Field3D::field_dynamic_cast< Field3D::Field >( pImpl //) ){ file.writeScalarLayer( pField->attribute, pField ); } else if( Field3D::Field::Ptr pField = //Field3D::field_dynamic_cast< Field3D::Field >( pImpl ) ){ file.writeScalarLayer( pField->attribute, pField //); } else if( Field3D::Field::Ptr pField = Field3D::field_dynamic_cast< Field3D::Field >( pImpl ) ){ // file.writeScalarLayer( pField->attribute, pField ); // } else if( Field3D::Field::Ptr pField = Field3D::field_dynamic_cast< //Field3D::Field >( pImpl ) ){ file.writeVectorLayer( pField->attribute, pField ); } else if( //Field3D::Field::Ptr pField = Field3D::field_dynamic_cast< Field3D::Field >( pImpl ) ){ // file.writeVectorLayer( pField->attribute, pField ); // } else if( Field3D::Field::Ptr pField = Field3D::field_dynamic_cast< //Field3D::Field >( pImpl ) ){ file.writeVectorLayer( pField->attribute, pField ); }else{ // FF_LOG(warning) << _T("Unknown Field3D type: ") << frantic::strings::to_tstring( //pImpl->classType() ) << std::endl; // } // // continue; // } // // // We need to sample our field into a Field3D dense grid. // // const frantic::channels::channel_map& map = (*it)->get_channel_map(); // if( map.channel_count() != 1 ) // throw std::runtime_error( "write_fields_to_file() does not support fields with multiple channels" //); // // switch( map[0].data_type() ){ // case frantic::channels::data_type_float32: // if( map[0].arity() == 1 ){ // Field3D::DenseField::Ptr pF = convert_to_field3d( **it, extents, //dataWindow, spacing ); // // pF->name = ""; // pF->attribute = frantic::strings::to_string( map[0].name() ); // // file.writeScalarLayer( pF->attribute, pF ); // }else if( map[0].arity() == 3 ){ // Field3D::DenseField::Ptr pF = convert_to_field3d( //**it, extents, dataWindow, spacing ); // // pF->name = ""; // pF->attribute = frantic::strings::to_string( map[0].name() ); // // file.writeVectorLayer( pF->attribute, pF ); // }else{ // throw std::runtime_error( "write_fields_to_file() does not support " + //frantic::strings::to_string( frantic::channels::channel_data_type_str( map[0].arity(), map[0].data_type() ) ) ); // } // // break; // default: // throw std::runtime_error( "write_fields_to_file() does not support " + frantic::strings::to_string( //frantic::channels::channel_data_type_str( map[0].arity(), map[0].data_type() ) ) ); // } // } // // file.close(); // } // // void write_fields_to_file( const frantic::tstring& filePath, const boost::shared_ptr& fields, // Field3D::Box3i& extents, Field3D::Box3i& dataWindow, float spacing ){ std::vector< //boost::shared_ptr > separateFields; // // for( std::size_t i = 0, iEnd = fields->get_num_fields(); i < iEnd; ++i ) // separateFields.push_back( fields->get_field(i) ); // // write_fields_to_file( filePath, separateFields, extents, dataWindow, spacing ); // } } // namespace ember #endif