417 lines
9.9 KiB
C++
417 lines
9.9 KiB
C++
#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 );
|
|
}
|