Browse Source

Add support for #pragma once directives in shader include files

master
C. J. Howard 1 year ago
parent
commit
9297744a04
1 changed files with 72 additions and 24 deletions
  1. +72
    -24
      src/resources/shader-loader.cpp

+ 72
- 24
src/resources/shader-loader.cpp View File

@ -24,17 +24,43 @@
#include "gl/shader-object.hpp" #include "gl/shader-object.hpp"
#include "gl/shader-program.hpp" #include "gl/shader-program.hpp"
#include <sstream> #include <sstream>
#include <unordered_set>
/**
* Scans a text file for the presence of a `#pragma once` directive.
*
* @param source Text file to scan.
*
* @return `true` if the file contains a `#pragma once` directive, `false` otherwise.
*/
static bool has_pragma_once(const text_file& source)
{
for (const auto& line: source)
{
std::istringstream line_stream(line);
std::string token;
// If line contains a `#pragma once` directive
if (line_stream >> token && token == "#pragma" &&
line_stream >> token && token == "once")
{
return true;
}
}
return false;
}
/** /**
* Handles `#pragma include` directives by loading the specified text files and inserting them in place. * Handles `#pragma include` directives by loading the specified text files and inserting them in place.
*/ */
static void handle_includes(text_file* source, resource_manager* resource_manager)
static void handle_includes(text_file& source, std::unordered_set<const text_file*>& include_once, resource_manager* resource_manager)
{ {
// For each line in the source // For each line in the source
for (std::size_t i = 0; i < source->size(); ++i)
for (std::size_t i = 0; i < source.size(); ++i)
{ {
std::string token; std::string token;
std::istringstream line_stream((*source)[i]);
std::istringstream line_stream(source[i]);
// If line contains a `#pragma include` directive // If line contains a `#pragma include` directive
if (line_stream >> token && token == "#pragma" && if (line_stream >> token && token == "#pragma" &&
@ -49,27 +75,39 @@ static void handle_includes(text_file* source, resource_manager* resource_manage
std::string path = token.substr(1, token.length() - 2); std::string path = token.substr(1, token.length() - 2);
// Load include file // Load include file
if (!resource_manager->load<text_file>(path))
text_file* include_file = resource_manager->load<text_file>(path);
if (!include_file)
{ {
(*source)[i] = "#error file not found (" + path + ")";
source[i] = "#error file not found (" + path + ")";
} }
else else
{ {
// Create copy of include file
text_file include_file = *(resource_manager->load<text_file>(path));
// Handle `#pragma include` directives inside include file
handle_includes(&include_file, resource_manager);
// Replace #pragma include directive with include file contents
source->erase(source->begin() + i);
source->insert(source->begin() + i, include_file.begin(), include_file.end());
i += include_file.size() - 1;
// If file has not been included or has no `#pragma once` directive
if (!include_once.contains(include_file))
{
// If file has `#pragma once` directive
if (has_pragma_once(*include_file))
{
// Add file to set of files to include once
include_once.insert(include_file);
}
// Create a copy of the include file
text_file include_file_copy = *include_file;
// Handle `#pragma include` directives inside include file
handle_includes(include_file_copy, include_once, resource_manager);
// Replace #pragma include directive with include file contents
source.erase(source.begin() + i);
source.insert(source.begin() + i, include_file_copy.begin(), include_file_copy.end());
i += include_file_copy.size() - 1;
}
} }
} }
else else
{ {
(*source)[i] = "#error malformed include directive (" + (*source)[i] + ")";
source[i] = "#error malformed include directive (" + source[i] + ")";
} }
} }
} }
@ -78,15 +116,20 @@ static void handle_includes(text_file* source, resource_manager* resource_manage
template <> template <>
gl::shader_program* resource_loader<gl::shader_program>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) gl::shader_program* resource_loader<gl::shader_program>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path)
{ {
// Load shader template source
text_file source_lines = *resource_loader<text_file>::load(resource_manager, file, path);
// Load shader source file
text_file* source_file = resource_loader<text_file>::load(resource_manager, file, path);
// Make a copy of the shader source file
text_file source_file_copy = *source_file;
// Handle `#pragma include` directives // Handle `#pragma include` directives
handle_includes(&source_lines, resource_manager);
std::unordered_set<const text_file*> include_once;
include_once.insert(source_file);
handle_includes(source_file_copy, include_once, resource_manager);
// Join vector of source lines into single string // Join vector of source lines into single string
std::ostringstream stream; std::ostringstream stream;
std::copy(source_lines.begin(), source_lines.end(), std::ostream_iterator<std::string>(stream, "\n"));
std::copy(source_file_copy.begin(), source_file_copy.end(), std::ostream_iterator<std::string>(stream, "\n"));
// Create shader template // Create shader template
render::shader_template* shader = new render::shader_template(stream.str()); render::shader_template* shader = new render::shader_template(stream.str());
@ -109,15 +152,20 @@ gl::shader_program* resource_loader::load(resource_manager*
template <> template <>
render::shader_template* resource_loader<render::shader_template>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path) render::shader_template* resource_loader<render::shader_template>::load(resource_manager* resource_manager, PHYSFS_File* file, const std::filesystem::path& path)
{ {
// Load shader template source
text_file source_lines = *resource_loader<text_file>::load(resource_manager, file, path);
// Load shader template source file
text_file* source_file = resource_loader<text_file>::load(resource_manager, file, path);
// Make a copy of the shader template source file
text_file source_file_copy = *source_file;
// Handle `#pragma include` directives // Handle `#pragma include` directives
handle_includes(&source_lines, resource_manager);
std::unordered_set<const text_file*> include_once;
include_once.insert(source_file);
handle_includes(source_file_copy, include_once, resource_manager);
// Join vector of source lines into single string // Join vector of source lines into single string
std::ostringstream stream; std::ostringstream stream;
std::copy(source_lines.begin(), source_lines.end(), std::ostream_iterator<std::string>(stream, "\n"));
std::copy(source_file_copy.begin(), source_file_copy.end(), std::ostream_iterator<std::string>(stream, "\n"));
// Create shader template // Create shader template
render::shader_template* shader = new render::shader_template(stream.str()); render::shader_template* shader = new render::shader_template(stream.str());

Loading…
Cancel
Save