Emmanuel BENOîT
68d01ca42e
Shaders are no longer found under /shaders in the project; they can be anywhere. In addition, paths for both (program) instructions in the script and include directives in shader source code are relative to the file which contains them.
195 lines
4.3 KiB
C++
195 lines
4.3 KiB
C++
#include "externals.hh"
|
|
#include "common.hh"
|
|
#include "c-project.hh"
|
|
#include "c-shaders.hh"
|
|
|
|
|
|
namespace {
|
|
|
|
const std::map< std::string , E_ShaderInput > InputTypes_( ([] {
|
|
std::map< std::string , E_ShaderInput > t;
|
|
t.emplace( "chunk" , E_ShaderInput::CHUNK );
|
|
t.emplace( "library" , E_ShaderInput::LIBRARY );
|
|
t.emplace( "lib" , E_ShaderInput::LIBRARY );
|
|
t.emplace( "vertex" , E_ShaderInput::VERTEX );
|
|
t.emplace( "fragment" , E_ShaderInput::FRAGMENT );
|
|
t.emplace( "compute" , E_ShaderInput::COMPUTE );
|
|
t.emplace( "geo" , E_ShaderInput::GEOMETRY );
|
|
t.emplace( "geometry" , E_ShaderInput::GEOMETRY );
|
|
return t;
|
|
})());
|
|
|
|
const std::regex PreprocDirective_( "^\\s*//!\\s*([a-z]+(\\s+([^\\s]+))*)\\s*$" );
|
|
|
|
} // namespace <anon>
|
|
|
|
|
|
/*============================================================================*/
|
|
namespace {
|
|
|
|
// Input reader state and functions, used when loading a source file
|
|
struct T_InputReader_
|
|
{
|
|
using T_Tokens_ = std::vector< std::string >;
|
|
|
|
const T_FSPath path;
|
|
FILE* const file;
|
|
T_ShaderInput& input;
|
|
uint32_t line{ 0 };
|
|
|
|
char* buffer{ nullptr };
|
|
ssize_t readCount;
|
|
|
|
std::string accumulator{ };
|
|
uint32_t accumLines{ 0 };
|
|
|
|
T_InputReader_( T_FSPath const& path ,
|
|
FILE* const file ,
|
|
T_ShaderInput& input )
|
|
: path( path ) , file( file ) , input( input )
|
|
{ }
|
|
~T_InputReader_( );
|
|
|
|
void read( );
|
|
|
|
void handleDirective( T_Tokens_ const& tokens );
|
|
|
|
void error( T_String const& err );
|
|
void nl( );
|
|
void addAccumulated( );
|
|
};
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
T_InputReader_::~T_InputReader_( )
|
|
{
|
|
if ( buffer ) {
|
|
free( buffer );
|
|
}
|
|
fclose( file );
|
|
}
|
|
|
|
void T_InputReader_::read( )
|
|
{
|
|
size_t bsz( 0 );
|
|
while ( ( readCount = getline( &buffer , &bsz , file ) ) != -1 ) {
|
|
line ++;
|
|
std::cmatch match;
|
|
if ( std::regex_match( buffer , match , PreprocDirective_ ) ) {
|
|
const T_Tokens_ tokens( ([](std::string const& a) {
|
|
using stri = std::istream_iterator< std::string >;
|
|
std::istringstream iss( a );
|
|
return T_Tokens_{ stri( iss ) , stri( ) };
|
|
})( match[ 1 ].str( ) ) );
|
|
assert( tokens.size( ) >= 1 );
|
|
handleDirective( tokens );
|
|
} else {
|
|
accumulator += buffer;
|
|
accumLines ++;
|
|
}
|
|
}
|
|
addAccumulated( );
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
void T_InputReader_::handleDirective(
|
|
T_Tokens_ const& tokens )
|
|
{
|
|
auto const& directive( tokens[ 0 ] );
|
|
|
|
if ( directive == "include" ) {
|
|
if ( tokens.size( ) != 2 ) {
|
|
nl( );
|
|
error( "invalid arguments" );
|
|
return;
|
|
}
|
|
|
|
T_FSPath inclPath{ tokens[ 1 ].c_str( ) };
|
|
if ( !inclPath.isValid( ) ) {
|
|
nl( );
|
|
error( "invalid path" );
|
|
return;
|
|
}
|
|
if ( inclPath.isAbsolute( ) ) {
|
|
inclPath = inclPath.makeRelative( inclPath.root( ) );
|
|
inclPath = ( Common::Project( ).basePath( ) + inclPath ).canonical( );
|
|
} else {
|
|
inclPath = ( path.parent( ) + inclPath ).canonical( );
|
|
}
|
|
if ( !inclPath.isUnder( Common::Project( ).basePath( ) ) ) {
|
|
nl( );
|
|
error( "path is outside of project" );
|
|
return;
|
|
}
|
|
|
|
addAccumulated( );
|
|
auto& ck( input.chunks );
|
|
ck.addNew( inclPath , 1 );
|
|
|
|
} else if ( directive == "type" ) {
|
|
nl( );
|
|
if ( tokens.size( ) != 2 ) {
|
|
error( "invalid arguments" );
|
|
return;
|
|
}
|
|
auto pos( InputTypes_.find( tokens[ 1 ] ) );
|
|
if ( pos == InputTypes_.end( ) ) {
|
|
error( "unknown type" );
|
|
} else {
|
|
input.type = pos->second;
|
|
}
|
|
|
|
} else {
|
|
nl( );
|
|
error( "unknown directive" );
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
|
|
void T_InputReader_::error(
|
|
T_String const& err )
|
|
{
|
|
input.errors.addNew( line , err );
|
|
}
|
|
|
|
void T_InputReader_::nl( )
|
|
{
|
|
accumLines ++;
|
|
accumulator += '\n';
|
|
}
|
|
|
|
void T_InputReader_::addAccumulated( )
|
|
{
|
|
if ( accumLines ) {
|
|
auto& ck( input.chunks );
|
|
ck.addNew( accumulator.c_str( ) , accumLines );
|
|
accumulator = {};
|
|
accumLines = 0;
|
|
}
|
|
}
|
|
|
|
} // namespace <anon>
|
|
|
|
|
|
/*= T_ShaderInput ============================================================*/
|
|
|
|
bool T_ShaderInput::load(
|
|
T_FSPath const& path )
|
|
{
|
|
type = E_ShaderInput::CHUNK;
|
|
chunks.clear( );
|
|
errors.clear( );
|
|
|
|
const auto osPath{ path.toString( ).toOSString( ) };
|
|
FILE* const file{ fopen( (char const*) osPath.data( ) , "r" ) };
|
|
if ( !file ) {
|
|
return false;
|
|
}
|
|
|
|
T_InputReader_ reader{ path , file , *this };
|
|
reader.read( );
|
|
|
|
return true;
|
|
}
|