Skip to content

Commit

Permalink
Merge pull request #18 from Holzhaus/rg-use-opengl-node-and-add-shade…
Browse files Browse the repository at this point in the history
…rs-python

rendergraph: Port perl script to Python and add pre-commit hook
  • Loading branch information
m0dB authored Dec 22, 2024
2 parents a3f76ce + 547c7a4 commit b80d02b
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 73 deletions.
7 changes: 7 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,10 @@ repos:
additional_dependencies:
- lxml==5.3.0
files: ^(res/translations/.*\.ts)$
- id: update-rendergraph-shaders
name: update-rendergraph-shaders
description: "Regenerate GL shaders for rendergraph"
entry: python tools/rg_generate_shaders_gl.py --cmake-output src/rendergraph/shaders/generated_shaders_gl.cmake src/rendergraph/shaders/
pass_filenames: false
language: python
files: ^src/rendergraph/shaders/.*\.(vert|frag|gl)$
68 changes: 0 additions & 68 deletions src/rendergraph/shaders/generate_shaders_gl.pl

This file was deleted.

10 changes: 5 additions & 5 deletions src/rendergraph/shaders/generated_shaders_gl.cmake
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
set(
generated_shaders_gl
pattern.vert.gl
rgb.vert.gl
rgba.vert.gl
texture.vert.gl
unicolor.vert.gl
pattern.frag.gl
pattern.vert.gl
rgb.frag.gl
rgb.vert.gl
rgba.frag.gl
rgba.vert.gl
texture.frag.gl
texture.vert.gl
unicolor.frag.gl
unicolor.vert.gl
)
159 changes: 159 additions & 0 deletions tools/rg_generate_shaders_gl.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
#!/usr/bin/env python3
"""
Converts a fragment or vertex shader file into a GL shader file.
You can use it like this:
$ ./rg_generate_shaders_gl.py --cmake-output \\
../src/rendergraph/shaders/generated_shaders_gl.cmake \\
../src/rendergraph/shaders/*.{vert,frag}
"""
import argparse
import logging
import os
import pathlib
import re
import shutil
import subprocess
import tempfile
import typing


def find_executable(
executable_name: str, additional_paths: list[str] | None = None
) -> pathlib.Path:
"""Find an executable by name in $PATH and in the additional paths."""
if executable_path := shutil.which(executable_name):
return pathlib.Path(executable_path)

if additional_paths:
if executable_path := shutil.which(
executable_name, path=os.pathsep.join(additional_paths)
):
return pathlib.Path(executable_path)

raise OSError(f"Executable {executable_name!r} not found!")


QSB_EXECUTABLE = find_executable(
"qsb",
additional_paths=[
"/usr/lib/qt6/bin",
"/lib/qt6/bin",
"/usr/local/lib/qt6/bin",
],
)


def parse_shader(input_filepath: pathlib.Path) -> typing.Iterator[str]:
"""Parse a Fragment/Vertex shader file and yield lines for a GL file."""
with tempfile.NamedTemporaryFile() as fp:
subprocess.check_call(
[
QSB_EXECUTABLE,
"--glsl",
"120",
"--output",
fp.name,
input_filepath,
]
)
output = subprocess.check_output(
[QSB_EXECUTABLE, "--dump", fp.name],
encoding="utf-8",
universal_newlines=True,
)

comment_added = False
ok = False
in_shader_block = 0
buffered_blank_line = False
for line in output.splitlines():
if in_shader_block == 2:
if re.match(r"^\*\*", line):
ok = True
else:
if not comment_added and not re.match(r"^#", line):
yield "//// GENERATED - EDITS WILL BE OVERWRITTEN"
comment_added = True
if line:
if buffered_blank_line:
yield ""
buffered_blank_line = False
yield line
else:
buffered_blank_line = True
elif in_shader_block == 1:
if line.rstrip() == "Contents:":
in_shader_block = 2
else:
if line.rstrip() == "Shader 1: GLSL 120 [Standard]":
in_shader_block = 1
if not ok:
raise EOFError("end of file reached before end marker reached")


def get_paths(paths: list[pathlib.Path]) -> typing.Iterator[pathlib.Path]:
for path in paths:
if path.is_dir():
yield from path.glob("*.vert")
yield from path.glob("*.frag")
else:
yield path


def main(argv: list[str] | None = None) -> int:
logging.basicConfig(level=logging.DEBUG, format="%(message)s")

logger = logging.getLogger(__name__)

description, _, epilog = __doc__.strip().partition("\n\n")
parser = argparse.ArgumentParser(
description=description,
epilog=epilog,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument(
"file",
nargs="+",
type=pathlib.Path,
help="Input files (.vert, .frag) or directory",
)
parser.add_argument(
"--cmake-output",
type=argparse.FileType("w"),
required=True,
help="CMake Output files (.cmake)",
)
args = parser.parse_args(argv)

generated_shaders: list[pathlib.Path] = []

for file in sorted(get_paths(args.file)):
logger.info("Reading file: %s", file)
try:
lines = list(parse_shader(file))
except EOFError as err:
logger.error("Failed to parse %s: %s", file, err)
continue

output_file = file.with_suffix(f"{file.suffix}.gl")
logger.info("Writing file: %s", output_file)
with output_file.open("w") as fp:
for line in lines:
fp.write(f"{line}\n")

generated_shaders.append(output_file)

args.cmake_output.write("set(\n")
args.cmake_output.write(" generated_shaders_gl\n")
for generated_file in generated_shaders:
args.cmake_output.write(f" {generated_file.name}\n")
args.cmake_output.write(")\n")
logger.info("Generated %d shader files.", len(generated_shaders))

return 0


if __name__ == "__main__":
main()

0 comments on commit b80d02b

Please sign in to comment.