#include "externals.hh" #include "control.hh" #include "sync.hh" using namespace cops; /*= Command execution ========================================================*/ void cops::Execute( __rd__ T_Operations const& operations , __rw__ T_Context& context ) { for ( auto const& op : operations ) { op->execute( context ); } } T_Operations& T_Program::function( __rd__ std::string const& name , __rd__ const uint32_t args ) { const auto p( functions.find( name ) ); if ( p == functions.end( ) ) { return functions.emplace( name , T_Function{ name , args } ).first->second.operations; } assert( p->second.arguments == args ); return p->second.operations; } /*= X_OpFailure ==============================================================*/ X_OpFailure::X_OpFailure( __rd__ std::string const& source , __rd__ uint32_t line , __rd__ std::string const& error ) noexcept : std::exception( ) , source_( source ) , line_( line ) , error_( error ) { std::ostringstream oss; oss << "Program error (" << source << ", l. " << line << "): " << error; fullMessage_ = oss.str( ); } char const* X_OpFailure::what( ) const noexcept { return fullMessage_.c_str( ); } /*= T_Context ================================================================*/ T_Context& T_Context::store( __rd__ std::string const& name , __rd__ const float value ) { const auto vPos( varPos.find( name ) ); if ( vPos == varPos.end( ) ) { varPos.emplace( name , varValues.size( ) ); varValues.push_back( value ); } else { varValues[ vPos->second ] = value; } return *this; } /*= T_Op =====================================================================*/ T_Op::T_Op( __rd__ const E_Op op ) : op_( op ) { } T_Op::~T_Op( ) { } X_OpFailure T_Op::error( __rd__ std::string const& message ) const noexcept { return X_OpFailure( source , line , message ); } /*= OPLoadConstant ===========================================================*/ OPLoadConstant::OPLoadConstant( __rd__ const float constant ) : T_Op( OP_LOAD_CONSTANT ) , constant( constant ) { } void OPLoadConstant::execute( __rw__ T_Context& ctx ) const { ctx.opStack.push_back( constant ); } /*= OPLoadVariable ===========================================================*/ OPLoadVariable::OPLoadVariable( __rd__ std::string const& variable ) : T_Op( OP_LOAD_VARIABLE ) , variable( variable ) { } void OPLoadVariable::execute( __rw__ T_Context& ctx ) const { const auto vPos( ctx.varPos.find( variable ) ); if ( vPos == ctx.varPos.end( ) ) { throw error( "variable '" + variable + "' not found" ); } ctx.opStack.push_back( ctx.varValues[ vPos->second ] ); } /*= OPLoadInput ==============================================================*/ OPLoadInput::OPLoadInput( __rd__ std::string const& input ) : T_Op( OP_LOAD_VARIABLE ) , input( input ) { } void OPLoadInput::execute( __rw__ T_Context& ctx ) const { ctx.opStack.push_back( ctx.sync->valueOf( input , *( ctx.time ) ) ); } /*= OPStoreVariable ==========================================================*/ OPStoreVariable::OPStoreVariable( __rd__ std::string const& variable ) : T_Op( OP_LOAD_VARIABLE ) , variable( variable ) { } void OPStoreVariable::execute( __rw__ T_Context& ctx ) const { if ( ctx.opStack.empty( ) ) { throw error( "stack is empty" ); } ctx.store( variable , ctx.opStack.back( ) ); ctx.opStack.pop_back( ); } /*= Arithmetic operators =====================================================*/ void OPAdd::execute( __rw__ T_Context& ctx ) const { if ( ctx.opStack.size( ) < 2 ) { throw error( "missing operands in stack" ); } const float v( ctx.opStack.back( ) ); ctx.opStack.pop_back( ); ctx.opStack.back( ) += v; } void OPMul::execute( __rw__ T_Context& ctx ) const { if ( ctx.opStack.size( ) < 2 ) { throw error( "missing operands in stack" ); } const float v( ctx.opStack.back( ) ); ctx.opStack.pop_back( ); ctx.opStack.back( ) *= v; } void OPNeg::execute( __rw__ T_Context& ctx ) const { if ( ctx.opStack.empty( ) ) { throw error( "missing operand in stack" ); } ctx.opStack.back( ) = -ctx.opStack.back( ); } void OPInv::execute( __rw__ T_Context& ctx ) const { if ( ctx.opStack.empty( ) || ctx.opStack.back( ) == 0 ) { throw error( "missing operand in stack" ); } ctx.opStack.back( ) = 1.0f / ctx.opStack.back( ); } /*= Stack operations =========================================================*/ OPDup::OPDup( __rd__ const uint32_t stackIndex ) : T_Op( OP_DUP ) , stackIndex( stackIndex ) { } void OPDup::execute( __rw__ T_Context& ctx ) const { if ( ctx.opStack.size( ) <= stackIndex ) { std::ostringstream oss; oss << "stack does not have " << ( stackIndex + 1 ) << " items (only " << ctx.opStack.size( ) << ")"; throw error( oss.str( ) ); } ctx.opStack.push_back( *( ctx.opStack.end( ) - stackIndex - 1 ) ); } /*----------------------------------------------------------------------------*/ OPXchg::OPXchg( __rd__ const uint32_t stackIndex ) : T_Op( OP_XCHG ) , stackIndex( stackIndex ) { } void OPXchg::execute( __rw__ T_Context& ctx ) const { if ( ctx.opStack.size( ) <= stackIndex ) { std::string err( "stack does not have " ); err += stackIndex + 1; err += " items"; throw error( err ); } if ( stackIndex ) { std::swap( *( ctx.opStack.end( ) - 1 ) , *( ctx.opStack.end( ) - stackIndex - 1 ) ); } } /*= OPSetUniform =============================================================*/ // GENERAL FIXME // program identifier should be an entry in a table // or, you know, a program name OPSetUniform::OPSetUniform( __rd__ const uint32_t count , __rd__ const bool integer ) : T_Op( OP_SET_UNIFORM ) , count( count ) , integer( integer ) { } void OPSetUniform::execute( __rw__ T_Context& ctx ) const { if ( count == 0 || ctx.opStack.size( ) < count + 2 ) { std::string err( "stack does not have " ); err += count + 2; err += " items"; throw error( err ); } typedef void (*F_SetUniform_)( int , int , int , void* ); void const* const funcs[] = { &glProgramUniform1fv , &glProgramUniform2fv , &glProgramUniform3fv , &glProgramUniform4fv , &glProgramUniform1iv , &glProgramUniform2iv , &glProgramUniform3iv , &glProgramUniform4iv , }; const F_SetUniform_ func{ *(F_SetUniform_*) funcs[ count + ( integer ? 4 : 0 ) - 1 ] }; const GLuint program( ctx.opStack.back( ) ); ctx.opStack.pop_back( ); const GLuint uniform( ctx.opStack.back( ) ); ctx.opStack.pop_back( ); if ( integer ) { int values[ count ]; auto i = count; while ( i != 0 ) { values[ count - i ] = int( ctx.opStack.back( ) ); ctx.opStack.pop_back( ); i --; } func( program , uniform , 1 , values ); } else { float values[ count ]; auto i = count; while ( i != 0 ) { values[ count - i ] = ctx.opStack.back( ); ctx.opStack.pop_back( ); i --; } func( program , uniform , 1 , values ); } } /*= OPUsePipeline ============================================================*/ // GENERAL FIXME // pipeline identifier should be an entry in a table // or, you know, a program name OPUsePipeline::OPUsePipeline( __rd__ const uint32_t index ) : T_Op( OP_USE_PIPELINE ) , pipeline( index ) { } void OPUsePipeline::execute( __rw__ T_Context& ) const { glBindProgramPipeline( pipeline ); } /*= OPUseTexture =============================================================*/ // GENERAL FIXME // texture & sampler identifiers should be entries in a table OPUseTexture::OPUseTexture( __rd__ const uint32_t binding , __rd__ const uint32_t texture , __rd__ const uint32_t sampler ) : T_Op( OP_USE_TEXTURE ) , binding( binding ) , texture( texture ) , sampler( sampler ) { } void OPUseTexture::execute( __rw__ T_Context& ) const { glBindTextureUnit( binding , texture ); glBindSampler( binding , sampler ); } /*= OPUseFramebuffer =========================================================*/ // GENERAL FIXME // framebuffer identifier should be an entry in a table OPUseFramebuffer::OPUseFramebuffer( __rd__ const uint32_t framebuffer ) : T_Op( OP_USE_FRAMEBUFFER ) , framebuffer( framebuffer ) { } void OPUseFramebuffer::execute( __rw__ T_Context& ) const { glBindFramebuffer( GL_FRAMEBUFFER , framebuffer ); } /*= OPSetViewport ============================================================*/ void OPSetViewport::execute( __rw__ T_Context& ctx ) const { if ( ctx.opStack.size( ) < 4 ) { throw error( "stack does not have 4 items" ); } glViewport( *( ctx.opStack.end( ) - 1 ) , *( ctx.opStack.end( ) - 2 ) , *( ctx.opStack.end( ) - 3 ) , *( ctx.opStack.end( ) - 4 ) ); ctx.opStack.resize( ctx.opStack.size( ) - 4 ); } /*= Draw commands ============================================================*/ void OPClear::execute( __rw__ T_Context& ctx ) const { if ( ctx.opStack.size( ) < 4 ) { throw error( "stack does not have 4 items" ); } glClearColor( *( ctx.opStack.end( ) - 1 ) , *( ctx.opStack.end( ) - 2 ) , *( ctx.opStack.end( ) - 3 ) , *( ctx.opStack.end( ) - 4 ) ); ctx.opStack.resize( ctx.opStack.size( ) - 4 ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); } void OPFullscreen::execute( __rw__ T_Context& ) const { glBindVertexArray( 0 ); glDrawArrays( GL_TRIANGLE_STRIP , 0 , 4 ); } /*= Flow control =============================================================*/ OPCall::OPCall( __rd__ std::string const& function ) : T_Op( OP_CALL ) , function( function ) { } void OPCall::execute( __rw__ T_Context& ctx ) const { auto fp( ctx.program->functions.find( function ) ); if ( fp == ctx.program->functions.end( ) ) { throw error( "function '" + function + "' not found" ); } auto const& func( fp->second ); const auto ssz( ctx.opStack.size( ) ); if ( ssz < func.arguments ) { std::string err; err = "function '" + function + "' requires "; err += func.arguments; err += " argument"; err += ( func.arguments > 1 ? "s" : "" ); throw error( err ); } Execute( func.operations , ctx ); if ( ctx.opStack.size( ) < ssz ) { throw error( "function '" + function + "' ate the stack" ); } ctx.opStack.resize( ssz - func.arguments ); }