diff --git a/.gitmodules b/.gitmodules index ebdaee0..338898b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "glm"] path = glm url = https://github.com/g-truc/glm.git +[submodule "picojson"] + path = picojson + url = https://github.com/kazuho/picojson.git diff --git a/.vim.local/ycm_extra_conf.py b/.vim.local/ycm_extra_conf.py index 66ccfe3..00f328a 100644 --- a/.vim.local/ycm_extra_conf.py +++ b/.vim.local/ycm_extra_conf.py @@ -47,6 +47,7 @@ flags = [ '-I','.', '-I','imgui', '-I','glm', + '-I','picojson', '-I','/usr/include/SDL2' , '-D','GLM_ENABLE_EXPERIMENTAL', ] diff --git a/Makefile b/Makefile index 54940d5..c480409 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,8 @@ OUTDIR = output CXXFLAGS += $(shell sdl2-config --cflags) -std=c++14 -Wall -g CFLAGS += $(shell sdl2-config --cflags) -CPPFLAGS += -I. -I$(OUTDIR) -Iimgui -Iglm \ +CPPFLAGS += -I. -I$(OUTDIR) \ + -Iimgui -Iglm -Ipicojson \ -DREAL_BUILD -DGLM_ENABLE_EXPERIMENTAL LIBS += $(shell sdl2-config --libs) -lGL -lGLEW -ldl diff --git a/externals.hh b/externals.hh index b85f018..7944d70 100644 --- a/externals.hh +++ b/externals.hh @@ -31,6 +31,9 @@ #include #include +// PicoJSON +#include + // Silly decoration macros I use everywhere #define __rd__ #define __wr__ diff --git a/picojson b/picojson new file mode 160000 index 0000000..1ebfc7b --- /dev/null +++ b/picojson @@ -0,0 +1 @@ +Subproject commit 1ebfc7b998fed25c48b4c14c0907bda74ead42c5 diff --git a/utilities.cc b/utilities.cc index aa7c9fa..4cc285c 100644 --- a/utilities.cc +++ b/utilities.cc @@ -39,3 +39,93 @@ void anglesToMatrix( matrix[7] = s[0]*c[1]; matrix[8] = c[0]*c[1]; } + + +/*= JSON =====================================================================*/ + +template void jsonAdd< double >( + __rw__ picojson::value::object& object , + __rd__ std::string const& key , + __rd__ double const& v ); +template void jsonAdd< picojson::value::object >( + __rw__ picojson::value::object& object , + __rd__ std::string const& key , + __rd__ picojson::value::object const& v ); +template void jsonAdd< picojson::value::array >( + __rw__ picojson::value::object& object , + __rd__ std::string const& key , + __rd__ picojson::value::array const& v ); +template void jsonAdd< std::string >( + __rw__ picojson::value::object& object , + __rd__ std::string const& key , + __rd__ std::string const& v ); + +template< > +void jsonAdd< picojson::value >( + __rw__ picojson::value::object& object , + __rd__ std::string const& key , + __rd__ picojson::value const& v ) +{ + using T_Entry = std::pair< std::string , picojson::value >; + object.insert( T_Entry( key , v ) ); +} + +template< > +void jsonAdd< int >( + __rw__ picojson::value::object& object , + __rd__ std::string const& key , + __rd__ int const& v ) +{ + using T_Entry = std::pair< std::string , picojson::value >; + object.insert( T_Entry( key , picojson::value( double( v ) ) ) ); +} + +template< > +void jsonAdd< unsigned >( + __rw__ picojson::value::object& object , + __rd__ std::string const& key , + __rd__ unsigned const& v ) +{ + using T_Entry = std::pair< std::string , picojson::value >; + object.insert( T_Entry( key , picojson::value( double( v ) ) ) ); +} + +/*----------------------------------------------------------------------------*/ + +picojson::value jsonVector( float const* vector , const int nComponents ) +{ + using namespace picojson; + value::array a; + for ( auto i = 0 ; i < nComponents ; i ++ ) { + a.push_back( value( vector[ i ] ) ); + } + return value( a ); +} + +/*----------------------------------------------------------------------------*/ + +void jsonGetVectorOpt( + __wr__ float* vector , + __rd__ const size_t size , + __rd__ picojson::value::object const& object , + __rd__ char const* key ) +{ + using namespace picojson; + auto const* ptr( jsonGetOpt< array >( object , key ) ); + if ( !ptr ) { + return; + } + + auto const& a( *ptr ); + if ( a.size( ) != size ) { + throw X_JsonGetFailed( ); + } + + int i = 0; + for ( auto const& av : a ) { + if ( !av.is< double >( ) ) { + throw X_JsonGetFailed( ); + } + vector[ i ++ ] = float( av.get< double >( ) ); + } +} diff --git a/utilities.hh b/utilities.hh index a113218..d646e25 100644 --- a/utilities.hh +++ b/utilities.hh @@ -58,3 +58,178 @@ inline auto find( { return std::find( collection.begin( ) , collection.end( ) , item ); } + + +/*= JSON utilities ===========================================================*/ + +// Convert a vector to a JSON array +picojson::value jsonVector( + __rd__ float const* vector , + __rd__ const int nComponents ); + +// Convert a GLM vec3 to a JSON array +inline picojson::value jsonVector( + __rd__ glm::vec3 const& vector ) +{ + return jsonVector( &vector.x , 3 ); +} + +// Add an entry to a JSON object +template< typename T > +inline void jsonAdd( + __rw__ picojson::value::object& object , + __rd__ std::string const& key , + __rd__ T const& v ) +{ + using T_Entry = std::pair< std::string , picojson::value >; + object.insert( T_Entry( key , picojson::value( v ) ) ); +} + +extern template void jsonAdd< double >( + __rw__ picojson::value::object& object , + __rd__ std::string const& key , + __rd__ double const& v ); +extern template void jsonAdd< picojson::value::object >( + __rw__ picojson::value::object& 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__ std::string const& key , + __rd__ picojson::value::array const& v ); +extern template void jsonAdd< std::string >( + __rw__ picojson::value::object& object , + __rd__ std::string const& key , + __rd__ std::string const& v ); + +template< > +void jsonAdd< picojson::value >( + __rw__ picojson::value::object& object , + __rd__ std::string const& key , + __rd__ picojson::value const& v ); +template< > +void jsonAdd< int >( + __rw__ picojson::value::object& object , + __rd__ std::string const& key , + __rd__ int const& v ); +template< > +void jsonAdd< unsigned >( + __rw__ picojson::value::object& object , + __rd__ std::string const& key , + __rd__ unsigned const& v ); + +/*----------------------------------------------------------------------------*/ + +// JSON read type error +class X_JsonGetFailed : public std::exception { }; + +// 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__ char const* const key ) +{ + const std::string k( key ); + if ( object.find( k ) == object.end( ) ) { + return nullptr; + } + + picojson::value const& v( object.at( k ) ); + if ( !v.is< T >( ) ) { + throw X_JsonGetFailed( ); + } + return &v.get< T >( ); +} + +// Read a value from a JSON object, if the value actually exists. Fails with +// X_JsonGetFailed if the type is wrong +template< typename T > +inline bool jsonSetFromOpt( + __wr__ T& out , + __rd__ picojson::value::object const& object , + __rd__ char const* const key ) +{ + T const* ptr( jsonGetOpt< T >( object , key ) ); + if ( ptr ) { + out = *ptr; + } + return ptr; +} + +// Integer specialization of the above +template< > +inline bool jsonSetFromOpt< int >( + __wr__ int& out , + __rd__ picojson::value::object const& object , + __rd__ char const* const key ) +{ + double const* ptr( jsonGetOpt< double >( object , key ) ); + if ( ptr ) { + out = int( *ptr ); + } + return ptr; +} + +// Unsigned integer specialization of the above +template< > +inline bool jsonSetFromOpt< unsigned >( + __wr__ unsigned& out , + __rd__ picojson::value::object const& object , + __rd__ char const* const key ) +{ + double const* ptr( jsonGetOpt< double >( object , key ) ); + if ( ptr ) { + out = unsigned( *ptr ); + } + return ptr; +} + +// 32-bit float specialization of the above +template< > +inline bool jsonSetFromOpt< float >( + __wr__ float& out , + __rd__ picojson::value::object const& object , + __rd__ char const* const key ) +{ + double const* ptr( jsonGetOpt< double >( object , key ) ); + if ( ptr ) { + out = float( *ptr ); + } + return ptr; +} + +// Read a vector from a JSON array of numbers +void jsonGetVectorOpt( + __wr__ float* vector , + __rd__ const size_t size , + __rd__ picojson::value::object 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__ char const* key ) +{ + jsonGetVectorOpt( &vector.x , 3 , object , key ); +} + +// Try to load some object using its "bool load( jsonobject )" method. +template< typename T > +inline bool jsonLoadOpt( + __rw__ T& out , + __rd__ picojson::value::object const& object , + __rd__ char const* key ) +{ + using T_Obj = picojson::value::object; + try { + auto const* sub( jsonGetOpt< T_Obj >( object , key ) ); + if ( sub ) { + return out.load( *sub ); + } + return true; + } catch ( X_JsonGetFailed const& ) { + return false; + } +}