diff --git a/curves.json b/curves.json new file mode 100644 index 0000000..7aab9df --- /dev/null +++ b/curves.json @@ -0,0 +1,23 @@ +{ + "dof:sharp-distance" : [ + { + "type" : "smooth" , + "values" : [ 35 , 100 , 35 ] , + "durations" : [ 300 , 300 ] + } + ] , + "dof:sharp-range" : [ + { + "type" : "smooth" , + "values" : [ 20 , 2 ] , + "durations" : [ 900 ] + } + ] , + "dof:falloff" : [ + { + "type" : "linear" , + "values" : [ 2 , 2 ] , + "durations" : [ 1 ] + } + ] +} diff --git a/demo.cc b/demo.cc index 880d9bb..1ab049d 100644 --- a/demo.cc +++ b/demo.cc @@ -24,40 +24,12 @@ bool T_Demo::initialise( ) combine->output( ) ); Globals::Sync( ).clearInputs( ); + // XXX should come from program Globals::Sync( ).addInput( "dof:sharp-distance" , 15 ); Globals::Sync( ).addInput( "dof:sharp-range" , 5 ); Globals::Sync( ).addInput( "dof:falloff" , 10 ); Globals::Sync( ).addInput( "dof:max-blur" , 16 ); Globals::Sync( ).addInput( "dof:samples" , 16 ); - - // XXX test curve - { - T_SyncCurve curve; - curve.name = "dof:sharp-distance"; - { - T_SyncSegment seg; - seg.nPoints = 4; - seg.type = T_SyncSegment::SMOOTH; - seg.durations.push_back( 300 ); - seg.durations.push_back( 300 ); - seg.durations.push_back( 300 ); - seg.values.push_back( 15 ); - seg.values.push_back( 30 ); - seg.values.push_back( 15 ); - seg.values.push_back( 15 ); - curve.segments.emplace_back( std::move( seg ) ); - } - { - T_SyncSegment seg; - seg.nPoints = 2; - seg.type = T_SyncSegment::LINEAR; - seg.durations.push_back( 600 ); - seg.values.push_back( 100 ); - seg.values.push_back( 20 ); - curve.segments.emplace_back( std::move( seg ) ); - } - Globals::Sync( ).setCurve( std::move( curve ) ); - } Globals::Sync( ).updateCurveCaches( ); return true; diff --git a/filewatcher.hh b/filewatcher.hh index 51b4219..11d7861 100644 --- a/filewatcher.hh +++ b/filewatcher.hh @@ -52,4 +52,4 @@ struct T_WatchedFiles bool triggered; std::vector< int > identifiers; }; - +using P_WatchedFiles = std::unique_ptr< T_WatchedFiles >; diff --git a/main.cc b/main.cc index 20a3a53..21a1608 100644 --- a/main.cc +++ b/main.cc @@ -71,6 +71,7 @@ void T_Main::mainLoop( ) Globals::Watcher( ).check( ); Globals::Shaders( ).update( ); + Globals::Sync( ).checkCurveFile( ); glFinish( ); p.startFrame( ); diff --git a/sync.cc b/sync.cc index a27cec3..e220d2b 100644 --- a/sync.cc +++ b/sync.cc @@ -1,5 +1,18 @@ #include "externals.hh" #include "sync.hh" +#include "globals.hh" + +namespace { + +const std::map< std::string , T_SyncSegment::E_SegmentType > SegmentTypes_( ([] { + std::map< std::string , T_SyncSegment::E_SegmentType > t; + t.emplace( "linear" , T_SyncSegment::LINEAR ); + t.emplace( "ramp" , T_SyncSegment::RAMP ); + t.emplace( "smooth" , T_SyncSegment::SMOOTH ); + return t; +})()); + +} /*= T_SyncTime ===============================================================*/ @@ -228,6 +241,23 @@ void T_SyncManager::setTime( /*----------------------------------------------------------------------------*/ +void T_SyncManager::checkCurveFile( ) +{ + if ( watcher_ ) { + return; + } + + printf( "CURVE INIT\n" ); + bool missing; + if ( loadCurves_( missing ) || !missing ) { + watcher_ = std::make_unique< T_WatchedFiles >( + Globals::Watcher( ) , + [this] { curvesChanged_( ); } ); + watcher_->watch( "curves.json" ); + } + printf( "INIT MISSING IS %c\n" , missing ? 'Y' : 'N' ); +} + void T_SyncManager::clearCurves( ) { curves_.clear( ); @@ -241,6 +271,174 @@ void T_SyncManager::setCurve( updateCurveCaches( ); } +void T_SyncManager::curvesChanged_( ) +{ + bool missing; + if ( !loadCurves_( missing ) && missing ) { + watcher_.reset( ); + } else { + // XXX temp bullshit to work around the deletion problem + watcher_->clear( ); + watcher_->watch( "curves.json" ); + } +} + +bool T_SyncManager::loadCurves_( + __wr__ bool& missing ) +{ + using T_STI_ = std::istream_iterator< char >; + std::ifstream file( "curves.json" ); + picojson::value root; + try { + missing = !file.is_open( ); + if ( missing ) { + return false; + } + + std::string errors; + picojson::parse( root , T_STI_( file ) , T_STI_( ) , &errors ); + if ( !errors.empty( ) ) { + printf( "Failed to parse 'curves.json':\n%s\n" , errors.c_str( ) ); + return false; + } + } catch ( std::ios_base::failure const& e ) { + printf( "I/O error while reading 'curves.json'\n%s\n" , e.what( ) ); + missing = false; + return false; + } + + T_SyncCurves nCurves; + if ( !loadCurvesData_( nCurves , root ) ) { + return false; + } + curves_ = std::move( nCurves ); + updateCurveCaches( ); + return true; +} + +bool T_SyncManager::loadCurvesData_( + __wr__ T_SyncCurves& curves , + __rd__ picojson::value const& root ) +{ + if ( !root.is< T_JSONObject >( ) ) { + printf( "Curves data: root is not a JSON object\n" ); + return false; + } + + auto const& r( root.get< T_JSONObject >( ) ); + bool ok( true ); + for ( auto const& item : r ) { + if ( curves.indexOf( item.first ) != -1 ) { + printf( "Curves data: duplicate curve '%s'\n" , + item.first.c_str( ) ); + ok = false; + continue; + } + if ( !item.second.is< T_JSONArray >( ) ) { + printf( "Curves data: entry for curve '%s' is not an array\n" , + item.first.c_str( ) ); + ok = false; + continue; + } + + T_SyncCurve nsc; + nsc.name = item.first; + + bool segsOk( true ); + for ( auto const& v : item.second.get< T_JSONArray >( ) ) { + if ( !v.is< T_JSONObject >( ) ) { + printf( "Curves data: curve '%s': invalid segment\n" , + item.first.c_str( ) ); + segsOk = false; + continue; + } + + T_SyncSegment segment; + try { + if ( !loadSegmentData_( segment , nsc.name , + v.get< T_JSONObject >( ) ) ) { + segsOk = false; + continue; + } + } catch ( X_JsonGetFailed const& ) { + printf( "Curves data: curve '%s': could not parse segment data\n" , + item.first.c_str( ) ); + segsOk = false; + continue; + } + nsc.segments.emplace_back( std::move( segment ) ); + } + + ok = ok && segsOk; + if ( nsc.segments.empty( ) && segsOk ) { + printf( "Curves data: curve '%s': no segments\n" , + item.first.c_str( ) ); + ok = false; + } + curves.setCurve( std::move( nsc ) ); + } + + return ok; +} + +bool T_SyncManager::loadSegmentData_( + __wr__ T_SyncSegment& segment , + __rd__ std::string const& curve , + __rd__ T_JSONObject const& sd ) +{ + auto const& sType( jsonGet< std::string >( sd , "type" ) ); + + const auto p( SegmentTypes_.find( sType ) ); + if ( p == SegmentTypes_.end( ) ) { + printf( "Curves data: curve '%s': invalid segment type '%s'\n" , + curve.c_str( ) , sType.c_str( ) ); + return false; + } + segment.type = p->second; + + auto const& vList( jsonGet< T_JSONArray >( sd , "values" ) ); + auto const& dList( jsonGet< T_JSONArray >( sd , "durations" ) ); + + if ( vList.size( ) < 2 ) { + printf( "Curves data: curve '%s': segment doesn't have enough values\n" , + curve.c_str( ) ); + return false; + } + if ( vList.size( ) != dList.size( ) + 1 ) { + printf( "Curves data: curve '%s': segment values / durations count mismatch\n" , + curve.c_str( ) ); + return false; + } + + for ( auto const& vv : vList ) { + if ( !vv.is< double >( ) ) { + printf( "Curves data: curve '%s': non-numeric entry in segment values\n" , + curve.c_str( ) ); + return false; + } + segment.values.push_back( float( vv.get< double >( ) ) ); + } + + for ( auto const& dv : dList ) { + if ( !dv.is< double >( ) ) { + printf( "Curves data: curve '%s': non-numeric entry in segment durations\n" , + curve.c_str( ) ); + return false; + } + + const double dvn( dv.get< double >( ) ); + if ( fmod( dvn , 1.0 ) != 0.0 || dvn <= 0 ) { + printf( "Curves data: curve '%s': invalid segment duration %f\n" , + curve.c_str( ) , dvn ); + return false; + } + segment.durations.push_back( uint32_t( dvn ) ); + } + + segment.nPoints = segment.values.size( ); + return true; +} + /*----------------------------------------------------------------------------*/ void T_SyncManager::updateCurveCaches( ) diff --git a/sync.hh b/sync.hh index 6e4c76b..92315e1 100644 --- a/sync.hh +++ b/sync.hh @@ -1,8 +1,6 @@ #pragma once -#ifndef REAL_BUILD -# include "externals.hh" -#endif - +#include "filewatcher.hh" +#include "utilities.hh" // Duration and current playing time struct T_SyncTime @@ -174,15 +172,30 @@ struct T_SyncManager // --------------------------------------------------------------------- // Curves + void checkCurveFile( ); void clearCurves( ); void setCurve( __rd__ T_SyncCurve curve ); - // --------------------------------------------------------------------- + private: + void curvesChanged_( ); + bool loadCurves_( __wr__ bool& missing ); + bool loadCurvesData_( + __wr__ T_SyncCurves& curves , + __rd__ picojson::value const& root ); + bool loadSegmentData_( + __wr__ T_SyncSegment& segment , + __rd__ std::string const& curve , + __rd__ T_JSONObject const& sd ); + // --------------------------------------------------------------------- + // Update + + public: void updateCurveCaches( ); void updateValues( ); private: + P_WatchedFiles watcher_; T_SyncTime time_; T_SyncValues values_; T_SyncCurves curves_; diff --git a/utilities.cc b/utilities.cc index 4cc285c..2ab17c4 100644 --- a/utilities.cc +++ b/utilities.cc @@ -44,25 +44,25 @@ void anglesToMatrix( /*= JSON =====================================================================*/ template void jsonAdd< double >( - __rw__ picojson::value::object& object , + __rw__ T_JSONObject& object , __rd__ std::string const& key , __rd__ double const& v ); -template void jsonAdd< picojson::value::object >( - __rw__ picojson::value::object& object , +template void jsonAdd< T_JSONObject >( + __rw__ T_JSONObject& object , __rd__ std::string const& key , - __rd__ picojson::value::object const& v ); -template void jsonAdd< picojson::value::array >( - __rw__ picojson::value::object& object , + __rd__ T_JSONObject const& v ); +template void jsonAdd< T_JSONArray >( + __rw__ T_JSONObject& object , __rd__ std::string const& key , - __rd__ picojson::value::array const& v ); + __rd__ T_JSONArray const& v ); template void jsonAdd< std::string >( - __rw__ picojson::value::object& object , + __rw__ T_JSONObject& object , __rd__ std::string const& key , __rd__ std::string const& v ); template< > void jsonAdd< picojson::value >( - __rw__ picojson::value::object& object , + __rw__ T_JSONObject& object , __rd__ std::string const& key , __rd__ picojson::value const& v ) { @@ -72,7 +72,7 @@ void jsonAdd< picojson::value >( template< > void jsonAdd< int >( - __rw__ picojson::value::object& object , + __rw__ T_JSONObject& object , __rd__ std::string const& key , __rd__ int const& v ) { @@ -82,7 +82,7 @@ void jsonAdd< int >( template< > void jsonAdd< unsigned >( - __rw__ picojson::value::object& object , + __rw__ T_JSONObject& object , __rd__ std::string const& key , __rd__ unsigned const& v ) { @@ -107,7 +107,7 @@ picojson::value jsonVector( float const* vector , const int nComponents ) void jsonGetVectorOpt( __wr__ float* vector , __rd__ const size_t size , - __rd__ picojson::value::object const& object , + __rd__ T_JSONObject const& object , __rd__ char const* key ) { using namespace picojson; diff --git a/utilities.hh b/utilities.hh index d646e25..0918e43 100644 --- a/utilities.hh +++ b/utilities.hh @@ -62,6 +62,9 @@ inline auto find( /*= JSON utilities ===========================================================*/ +using T_JSONObject = picojson::value::object; +using T_JSONArray = picojson::value::array; + // Convert a vector to a JSON array picojson::value jsonVector( __rd__ float const* vector , @@ -77,7 +80,7 @@ inline picojson::value jsonVector( // Add an entry to a JSON object template< typename T > inline void jsonAdd( - __rw__ picojson::value::object& object , + __rw__ T_JSONObject& object , __rd__ std::string const& key , __rd__ T const& v ) { @@ -86,35 +89,35 @@ inline void jsonAdd( } extern template void jsonAdd< double >( - __rw__ picojson::value::object& object , + __rw__ T_JSONObject& object , __rd__ std::string const& key , __rd__ double const& v ); -extern template void jsonAdd< picojson::value::object >( - __rw__ picojson::value::object& object , +extern template void jsonAdd< T_JSONObject >( + __rw__ T_JSONObject& object , __rd__ std::string const& key , - __rd__ picojson::value::object const& v ); -extern template void jsonAdd< picojson::value::array >( - __rw__ picojson::value::object& object , + __rd__ T_JSONObject const& v ); +extern template void jsonAdd< T_JSONArray >( + __rw__ T_JSONObject& object , __rd__ std::string const& key , - __rd__ picojson::value::array const& v ); + __rd__ T_JSONArray const& v ); extern template void jsonAdd< std::string >( - __rw__ picojson::value::object& object , + __rw__ T_JSONObject& object , __rd__ std::string const& key , __rd__ std::string const& v ); template< > void jsonAdd< picojson::value >( - __rw__ picojson::value::object& object , + __rw__ T_JSONObject& object , __rd__ std::string const& key , __rd__ picojson::value const& v ); template< > void jsonAdd< int >( - __rw__ picojson::value::object& object , + __rw__ T_JSONObject& object , __rd__ std::string const& key , __rd__ int const& v ); template< > void jsonAdd< unsigned >( - __rw__ picojson::value::object& object , + __rw__ T_JSONObject& object , __rd__ std::string const& key , __rd__ unsigned const& v ); @@ -123,11 +126,30 @@ void jsonAdd< unsigned >( // JSON read type error class X_JsonGetFailed : public std::exception { }; +// Get a JSON object field; throws X_JsonGetFailed if the type is incorrect or +// if the field is missing. +template< typename T > +inline T const& jsonGet( + __rd__ T_JSONObject const& object , + __rd__ char const* const key ) +{ + const std::string k( key ); + if ( object.find( k ) == object.end( ) ) { + throw X_JsonGetFailed( ); + } + + picojson::value const& v( object.at( k ) ); + if ( !v.is< T >( ) ) { + throw X_JsonGetFailed( ); + } + return v.get< T >( ); +} + // Get a pointer to a JSON object field, or nullptr if the field doesn't exist. // Throws X_JsonGetFailed if the type is incorrect. template< typename T > inline T const* jsonGetOpt( - __rd__ picojson::value::object const& object , + __rd__ T_JSONObject const& object , __rd__ char const* const key ) { const std::string k( key ); @@ -147,7 +169,7 @@ inline T const* jsonGetOpt( template< typename T > inline bool jsonSetFromOpt( __wr__ T& out , - __rd__ picojson::value::object const& object , + __rd__ T_JSONObject const& object , __rd__ char const* const key ) { T const* ptr( jsonGetOpt< T >( object , key ) ); @@ -161,7 +183,7 @@ inline bool jsonSetFromOpt( template< > inline bool jsonSetFromOpt< int >( __wr__ int& out , - __rd__ picojson::value::object const& object , + __rd__ T_JSONObject const& object , __rd__ char const* const key ) { double const* ptr( jsonGetOpt< double >( object , key ) ); @@ -175,7 +197,7 @@ inline bool jsonSetFromOpt< int >( template< > inline bool jsonSetFromOpt< unsigned >( __wr__ unsigned& out , - __rd__ picojson::value::object const& object , + __rd__ T_JSONObject const& object , __rd__ char const* const key ) { double const* ptr( jsonGetOpt< double >( object , key ) ); @@ -189,7 +211,7 @@ inline bool jsonSetFromOpt< unsigned >( template< > inline bool jsonSetFromOpt< float >( __wr__ float& out , - __rd__ picojson::value::object const& object , + __rd__ T_JSONObject const& object , __rd__ char const* const key ) { double const* ptr( jsonGetOpt< double >( object , key ) ); @@ -203,13 +225,13 @@ inline bool jsonSetFromOpt< float >( void jsonGetVectorOpt( __wr__ float* vector , __rd__ const size_t size , - __rd__ picojson::value::object const& object , + __rd__ T_JSONObject const& object , __rd__ char const* key ); // Read a GLM vec3 from a JSON array of numbers inline void jsonGetVectorOpt( __wr__ glm::vec3& vector , - __rd__ picojson::value::object const& object , + __rd__ T_JSONObject const& object , __rd__ char const* key ) { jsonGetVectorOpt( &vector.x , 3 , object , key ); @@ -219,12 +241,11 @@ inline void jsonGetVectorOpt( template< typename T > inline bool jsonLoadOpt( __rw__ T& out , - __rd__ picojson::value::object const& object , + __rd__ T_JSONObject const& object , __rd__ char const* key ) { - using T_Obj = picojson::value::object; try { - auto const* sub( jsonGetOpt< T_Obj >( object , key ) ); + auto const* sub( jsonGetOpt< T_JSONObject >( object , key ) ); if ( sub ) { return out.load( *sub ); }