547 lines
13 KiB
C++
547 lines
13 KiB
C++
#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 ===============================================================*/
|
|
|
|
void T_SyncTime::setDuration(
|
|
const float uDuration ,
|
|
const uint32_t iDuration )
|
|
{
|
|
this->uDuration = std::max( 1e-3f , uDuration );
|
|
this->iDuration = std::max( 1u , iDuration );
|
|
time = std::min( time , duration( ) );
|
|
}
|
|
|
|
|
|
/*= T_SyncCurves =============================================================*/
|
|
|
|
void T_SyncCurves::clear( )
|
|
{
|
|
curves.clear( );
|
|
}
|
|
|
|
void T_SyncCurves::setCurve(
|
|
T_SyncCurve curve )
|
|
{
|
|
curves.set( std::move( curve ) );
|
|
}
|
|
|
|
int32_t T_SyncCurves::indexOf(
|
|
T_String const& name ) noexcept
|
|
{
|
|
const auto idx( curves.indexOf( name ) );
|
|
return idx == ebcl::T_HashIndex::INVALID_INDEX ? -1 : int32_t( idx );
|
|
}
|
|
|
|
|
|
/*= T_SyncCurveCache =========================================================*/
|
|
|
|
T_SyncCurveCache::T_SyncCurveCache(
|
|
T_SyncTime const& time ,
|
|
T_SyncCurves const& curves ,
|
|
const uint32_t curve ) noexcept
|
|
: curve( curve ) , curPos( 0 )
|
|
{
|
|
auto const& c( curves.curves[ curve ] );
|
|
const auto ns( c.segments.size( ) );
|
|
assert( ns > 0 );
|
|
|
|
uint32_t s = 0;
|
|
for ( auto i = 0u ; i < ns ; i ++ ) {
|
|
auto const& v( c.segments[ i ] );
|
|
assert( v.nPoints >= 2 );
|
|
assert( v.durations.size( ) == v.nPoints - 1 );
|
|
|
|
const auto nd( v.nPoints - 1 );
|
|
for ( auto j = 0u ; j < nd ; j ++ ) {
|
|
const auto sStart( s * time.uDuration );
|
|
if ( time.time >= sStart ) {
|
|
curPos = segStarts.size( );
|
|
}
|
|
segStarts.add( sStart );
|
|
segRefs.add( std::make_pair( i , j ) );
|
|
s += v.durations[ j ];
|
|
segEnds.add( std::min( s , time.iDuration ) * time.uDuration );
|
|
if ( s > time.iDuration ) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
uint32_t T_SyncCurveCache::findSegment(
|
|
const float time ) const noexcept
|
|
{
|
|
const auto ns( segStarts.size( ) );
|
|
for ( auto i = 0u ; i < ns ; i ++ ) {
|
|
if ( segStarts[ i ] <= time && segEnds[ i ] > time ) {
|
|
return i;
|
|
}
|
|
}
|
|
return ns;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
float T_SyncCurveCache::valueAt(
|
|
T_SyncTime const& time ,
|
|
T_SyncCurves const& curves ,
|
|
const float position ) const noexcept
|
|
{
|
|
return segmentValue( time.time , findSegment( position ) ,
|
|
curves.curves[ curve ].segments );
|
|
}
|
|
|
|
float T_SyncCurveCache::value(
|
|
T_SyncTime const& time ,
|
|
T_SyncCurves const& curves ) noexcept
|
|
{
|
|
const auto t( time.time );
|
|
|
|
// Check / update curPos
|
|
const float ss0( curPos == segStarts.size( )
|
|
? time.duration( )
|
|
: segStarts[ curPos ] );
|
|
if ( ss0 > t ) {
|
|
curPos = findSegment( t );
|
|
} else {
|
|
while ( curPos < segStarts.size( ) && t >= segEnds[ curPos ] ) {
|
|
curPos ++;
|
|
}
|
|
}
|
|
|
|
// We got the actual index in curPos, now compute the value.
|
|
return segmentValue( t , curPos , curves.curves[ curve ].segments );
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
float T_SyncCurveCache::segmentValue(
|
|
float time ,
|
|
uint32_t segIndex ,
|
|
T_Array< T_SyncSegment > const& segments ) const noexcept
|
|
{
|
|
const auto sss( segStarts.size( ) );
|
|
if ( segIndex >= sss ) {
|
|
assert( sss != 0 );
|
|
segIndex = sss - 1;
|
|
time = segEnds[ segIndex ];
|
|
}
|
|
|
|
auto const& idxp( segRefs[ segIndex ] );
|
|
auto const& seg( segments[ idxp.first ] );
|
|
|
|
// Interpolation factor
|
|
const float st( segStarts[ segIndex ] );
|
|
const float et( segEnds[ segIndex ] );
|
|
const float v0 = ( time - st ) / ( et - st );
|
|
float v = v0;
|
|
if ( seg.type != T_SyncSegment::LINEAR ) {
|
|
v *= v0;
|
|
if ( seg.type == T_SyncSegment::SMOOTH ) {
|
|
v *= 3 - 2 * v0;
|
|
}
|
|
}
|
|
|
|
const auto pid( idxp.second );
|
|
const float sv( seg.values[ pid ] );
|
|
const float ev( seg.values[ pid + 1 ] );
|
|
#if 0
|
|
printf( "[%.2f] gidx %d - seg %d idx %d - %f\n" , time , segIndex ,
|
|
idxp.first , idxp.second , v * ( ev - sv ) + sv );
|
|
#endif
|
|
return v * ( ev - sv ) + sv;
|
|
}
|
|
|
|
|
|
/*= T_SyncValues =============================================================*/
|
|
|
|
T_SyncValues::T_SyncValues( )
|
|
{
|
|
values.add( 0 );
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
void T_SyncValues::clear( )
|
|
{
|
|
index.clear( );
|
|
identifiers.clear( );
|
|
values.clear( );
|
|
overriden.clear( );
|
|
values.add( 0 );
|
|
}
|
|
|
|
bool T_SyncValues::addValue(
|
|
T_String const& name ,
|
|
const float initial ) noexcept
|
|
{
|
|
const uint32_t hash{ ComputeHash( name ) };
|
|
uint32_t existing{ index.first( hash ) };
|
|
while ( existing != T_HashIndex::INVALID_INDEX ) {
|
|
if ( name == identifiers[ existing ] ) {
|
|
return false;
|
|
}
|
|
existing = index.next( existing );
|
|
}
|
|
|
|
const auto li( values.size( ) );
|
|
index.add( hash );
|
|
identifiers.add( name );
|
|
values.add( initial );
|
|
std::swap( values[ li ] , values[ li - 1 ] );
|
|
overriden.add( false );
|
|
return true;
|
|
}
|
|
|
|
uint32_t T_SyncValues::indexOf(
|
|
T_String const& name ) const noexcept
|
|
{
|
|
const uint32_t hash{ ComputeHash( name ) };
|
|
uint32_t existing{ index.first( hash ) };
|
|
while ( existing != T_HashIndex::INVALID_INDEX ) {
|
|
if ( name == identifiers[ existing ] ) {
|
|
return existing;
|
|
}
|
|
existing = index.next( existing );
|
|
}
|
|
return values.size( ) - 1;
|
|
}
|
|
|
|
|
|
/*= T_SyncManager ============================================================*/
|
|
|
|
void T_SyncManager::setDuration(
|
|
const float uDuration ,
|
|
const uint32_t iDuration )
|
|
{
|
|
time_.setDuration( uDuration , iDuration );
|
|
updateCurveCaches( );
|
|
}
|
|
|
|
void T_SyncManager::setTime(
|
|
const float time )
|
|
{
|
|
time_.setTime( time );
|
|
updateValues( );
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
void T_SyncManager::checkCurveFile( )
|
|
{
|
|
if ( watcher_ ) {
|
|
return;
|
|
}
|
|
|
|
printf( "CURVE INIT\n" );
|
|
bool missing;
|
|
if ( loadCurves_( missing ) || !missing ) {
|
|
watcher_ = NewOwned< 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( );
|
|
updateCurveCaches( );
|
|
}
|
|
|
|
void T_SyncManager::setCurve(
|
|
T_SyncCurve curve )
|
|
{
|
|
curves_.setCurve( curve );
|
|
updateCurveCaches( );
|
|
}
|
|
|
|
void T_SyncManager::curvesChanged_( )
|
|
{
|
|
bool missing;
|
|
if ( !loadCurves_( missing ) && missing ) {
|
|
watcher_.clear( );
|
|
}
|
|
}
|
|
|
|
bool T_SyncManager::loadCurves_(
|
|
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_(
|
|
T_SyncCurves& curves ,
|
|
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.c_str( ) ) != -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{ item.first.c_str( ) };
|
|
|
|
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 , (char*) nsc.name.toOSString( ).data( ) ,
|
|
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.add( std::move( segment ) );
|
|
}
|
|
|
|
ok = ok && segsOk;
|
|
if ( nsc.segments.size( ) == 0 && 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_(
|
|
T_SyncSegment& segment ,
|
|
T_String const& curve ,
|
|
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.toOSString( ).data( ) , 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.toOSString( ).data( ) );
|
|
return false;
|
|
}
|
|
if ( vList.size( ) != dList.size( ) + 1 ) {
|
|
printf( "Curves data: curve '%s': segment values / durations count mismatch\n" ,
|
|
curve.toOSString( ).data( ) );
|
|
return false;
|
|
}
|
|
|
|
for ( auto const& vv : vList ) {
|
|
if ( !vv.is< double >( ) ) {
|
|
printf( "Curves data: curve '%s': non-numeric entry in segment values\n" ,
|
|
curve.toOSString( ).data( ) );
|
|
return false;
|
|
}
|
|
segment.values.add( 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.toOSString( ).data( ) );
|
|
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.toOSString( ).data( ) , dvn );
|
|
return false;
|
|
}
|
|
segment.durations.add( dvn );
|
|
}
|
|
|
|
segment.nPoints = segment.values.size( );
|
|
return true;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
void T_SyncManager::updateCurveCaches( )
|
|
{
|
|
curveCaches_.clear( );
|
|
const uint32_t nv( values_.identifiers.size( ) );
|
|
for ( auto i = 0u ; i < nv ; i ++ ) {
|
|
auto const& id( values_.identifiers[ i ] );
|
|
const auto cp( curves_.indexOf( id ) );
|
|
if ( cp < 0 ) {
|
|
curveCaches_.addNew( );
|
|
} else {
|
|
curveCaches_.add( NewOwned< T_SyncCurveCache >(
|
|
time_ , curves_ , cp
|
|
) );
|
|
}
|
|
}
|
|
updateValues( );
|
|
}
|
|
|
|
void T_SyncManager::updateValues( )
|
|
{
|
|
const auto nv( values_.identifiers.size( ) );
|
|
assert( nv == curveCaches_.size( ) );
|
|
for ( auto i = 0u ; i < nv ; i ++ ) {
|
|
auto const& cc( curveCaches_[ i ] );
|
|
if ( !cc || values_.overriden[ i ] ) {
|
|
continue;
|
|
}
|
|
values_.values[ i ] = cc->value( time_ , curves_ );
|
|
}
|
|
}
|
|
|
|
/*============================================================================*/
|
|
|
|
#if 0
|
|
void T_SyncManager::makeUI( )
|
|
{
|
|
auto const& dspSize( ImGui::GetIO( ).DisplaySize );
|
|
|
|
if ( wOverrides ) {
|
|
ImGui::SetNextWindowSize( ImVec2( 300 , dspSize.y - 300 ) ,
|
|
ImGuiSetCond_Once );
|
|
ImGui::SetNextWindowPos( ImVec2( 0 , 150 ) ,
|
|
ImGuiSetCond_Once );
|
|
ImGui::Begin( "Input overrides" );
|
|
displayOvSections( uiRoot , true );
|
|
ImGui::End( );
|
|
}
|
|
if ( wCurves ) {
|
|
ImGui::SetNextWindowSize( ImVec2( dspSize.x , 150 ) ,
|
|
ImGuiSetCond_Once );
|
|
ImGui::SetNextWindowPos( ImVec2( 0 , dspSize.y - 150 ) ,
|
|
ImGuiSetCond_Once );
|
|
ImGui::Begin( "Curve editor" );
|
|
// XXX contents
|
|
ImGui::End( );
|
|
}
|
|
}
|
|
|
|
void T_SyncManager::displayOvSections(
|
|
T_SyncUISections& sections ,
|
|
const bool topLevel )
|
|
{
|
|
for ( auto& s : sections ) {
|
|
const bool display( topLevel
|
|
? ImGui::CollapsingHeader( s->title.c_str( ) )
|
|
: ImGui::TreeNode( s->title.c_str( ) ) );
|
|
if ( !display ) {
|
|
continue;
|
|
}
|
|
displayOvSections( s->subsections );
|
|
if ( s->subsections.size( ) && s->overrides.size( ) ) {
|
|
ImGui::Separator( );
|
|
}
|
|
displayOvControls( s->overrides );
|
|
if ( !topLevel ) {
|
|
ImGui::TreePop( );
|
|
}
|
|
}
|
|
}
|
|
|
|
void T_SyncManager::displayOvControls(
|
|
T_SyncUIOverrides& overrides )
|
|
{
|
|
for ( auto& o : overrides ) {
|
|
// XXX enable override checkbox should be selected and disabled
|
|
// if there is no curve
|
|
const bool changed( ImGui::Checkbox( "" , &o->enabled ) );
|
|
if ( changed ) {
|
|
// XXX mark the inputs as coming from the UI / the curves
|
|
}
|
|
ImGui::SameLine( );
|
|
|
|
switch ( o->type ) {
|
|
case T_SyncUIOverride::FLOAT:
|
|
case T_SyncUIOverride::VEC2:
|
|
case T_SyncUIOverride::VEC3:
|
|
case T_SyncUIOverride::VEC4:
|
|
case T_SyncUIOverride::INT:
|
|
case T_SyncUIOverride::COLOR:
|
|
case T_SyncUIOverride::COLOR_GRADING:
|
|
case T_SyncUIOverride::CAMERA:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|