@@ -362,11 +362,10 @@ static SCP_string opengl_shader_get_header(shader_type type_id, int flags, shade
362362 * @param flags integer variable holding a combination of SDR_* flags
363363 * @return C-string holding the complete shader source code
364364 */
365- static SCP_string opengl_load_shader (const char *filename)
366- {
365+ static SCP_string opengl_load_shader (const char * filename) {
367366 SCP_string content;
368367 if (Enable_external_shaders) {
369- CFILE * cf_shader = cfopen (filename, " rt" , CFILE_NORMAL , CF_TYPE_EFFECTS );
368+ CFILE * cf_shader = cfopen (filename, " rt" , CFILE_NORMAL , CF_TYPE_EFFECTS );
370369
371370 if (cf_shader != NULL ) {
372371 int len = cfilelength (cf_shader);
@@ -387,11 +386,86 @@ static SCP_string opengl_load_shader(const char *filename)
387386 return content;
388387}
389388
389+ static void handle_includes_impl (SCP_vector<SCP_string>& include_stack,
390+ SCP_stringstream& output,
391+ int & include_counter,
392+ const SCP_string& filename,
393+ const SCP_string& original) {
394+ include_stack.emplace_back (filename);
395+ auto current_source_number = include_counter + 1 ;
396+
397+ const char * INCLUDE_STRING = " #include" ;
398+ SCP_stringstream input (original);
399+
400+ int line_num = 1 ;
401+ for (SCP_string line; std::getline (input, line);) {
402+ auto include_start = line.find (INCLUDE_STRING );
403+ if (include_start != SCP_string::npos) {
404+ auto first_quote = line.find (' "' , include_start + strlen (INCLUDE_STRING ));
405+ auto second_quote = line.find (' "' , first_quote + 1 );
406+
407+ if (first_quote == SCP_string::npos || second_quote == SCP_string::npos) {
408+ Error (LOCATION ,
409+ " Shader %s:%d: Malformed include line. Could not find both quote charaters." ,
410+ filename.c_str (),
411+ line_num);
412+ }
413+
414+ auto file_name = line.substr (first_quote + 1 , second_quote - first_quote - 1 );
415+ auto existing_name = std::find_if (include_stack.begin (), include_stack.end (), [&file_name](const SCP_string& str) {
416+ return str == file_name;
417+ });
418+ if (existing_name != include_stack.end ()) {
419+ SCP_stringstream stack_string;
420+ for (auto & name : include_stack) {
421+ stack_string << " \t " << name << " \n " ;
422+ }
423+
424+ Error (LOCATION ,
425+ " Shader %s:%d: Detected cyclic include! Previous includes (top level file first):\n %s" ,
426+ filename.c_str (),
427+ line_num,
428+ stack_string.str ().c_str ());
429+ }
430+
431+ ++include_counter;
432+ // The second parameter defines which source string we are currently working with. We keep track of how many
433+ // excludes have been in the file so far to specify this
434+ output << " #line 1 " << include_counter + 1 << " \n " ;
435+
436+ handle_includes_impl (include_stack,
437+ output,
438+ include_counter,
439+ file_name,
440+ opengl_load_shader (file_name.c_str ()));
441+
442+ // We are done with the include file so now we can return to the original file
443+ output << " #line " << line_num + 1 << " " << current_source_number << " \n " ;
444+ } else {
445+ output << line << " \n " ;
446+ }
447+
448+ ++line_num;
449+ }
450+
451+ include_stack.pop_back ();
452+ }
453+
454+ static SCP_string handle_includes (const char * filename, const SCP_string& original) {
455+ SCP_stringstream output;
456+ SCP_vector<SCP_string> include_stack;
457+ auto include_counter = 0 ;
458+
459+ handle_includes_impl (include_stack, output, include_counter, filename, original);
460+
461+ return output.str ();
462+ }
463+
390464static SCP_vector<SCP_string> opengl_get_shader_content (shader_type type_id, const char * filename, int flags, shader_stage stage) {
391465 SCP_vector<SCP_string> parts;
392466 parts.push_back (opengl_shader_get_header (type_id, flags, stage));
393467
394- parts.push_back (opengl_load_shader (filename));
468+ parts.push_back (handle_includes (filename, opengl_load_shader (filename) ));
395469
396470 return parts;
397471}
0 commit comments