forked from CrossGL/crosstl
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
704ed0f
commit 0afb91f
Showing
30 changed files
with
5,521 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
include README.md | ||
include LICENSE | ||
include src/backend/DirectX/*.py | ||
include src/backend/Metal/*.py | ||
include src/backend/Opengl/*.py | ||
include src/translator/*.py | ||
include src/translator/codegen/*.py | ||
include crosstl.py | ||
include crosstl | ||
include corsstl/src/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from ._crosstl import * |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
from .src import translator | ||
from .src.translator.lexer import Lexer | ||
from .src.translator.parser import Parser | ||
from .src.translator.codegen import directx_codegen, metal_codegen, opengl_codegen | ||
from .src.translator.ast import ASTNode | ||
from .src.backend.DirectX import * | ||
from .src.backend.Metal import * | ||
from .src.backend.Opengl import * | ||
|
||
def translate(file_path: str, backend: str = "crossgl") -> str: | ||
backend = backend.lower() | ||
|
||
with open(file_path, 'r') as file: | ||
shader_code = file.read() | ||
|
||
# Determine the input shader type based on the file extension | ||
if file_path.endswith(".cgl"): | ||
lexer = Lexer(shader_code) | ||
parser = Parser(lexer.tokens) | ||
elif file_path.endswith(".hlsl"): | ||
lexer = HLSLLexer(shader_code) | ||
parser = HLSLParser(lexer.tokens) | ||
elif file_path.endswith(".metal"): | ||
lexer = MetalLexer(shader_code) | ||
parser = MetalParser(lexer.tokens) | ||
elif file_path.endswith(".glsl"): | ||
lexer = GLSLLexer(shader_code) | ||
parser = GLSLParser(lexer.tokens) | ||
else: | ||
raise ValueError(f"Unsupported shader file type: {file_path}") | ||
|
||
ast = parser.parse() | ||
|
||
if file_path.endswith(".cgl"): | ||
if backend == "metal": | ||
codegen = metal_codegen.MetalCodeGen() | ||
return codegen.generate(ast) | ||
elif backend == "directx": | ||
codegen = directx_codegen.HLSLCodeGen() | ||
return codegen.generate(ast) | ||
elif backend == "opengl": | ||
codegen = opengl_codegen.GLSLCodeGen() | ||
return codegen.generate(ast) | ||
else: | ||
raise ValueError(f"Unsupported backend for CrossGL file: {backend}") | ||
else: | ||
if backend == "cgl": | ||
if file_path.endswith(".hlsl"): | ||
codegen = HLSLToCrossGLConverter() | ||
elif file_path.endswith(".metal"): | ||
codegen = MetalToCrossGLConverter() | ||
elif file_path.endswith(".glsl"): | ||
codegen = GLSLToCrossGLConverter() | ||
else: | ||
raise ValueError(f"Reverse translation not supported for: {file_path}") | ||
return codegen.generate(ast) | ||
else: | ||
raise ValueError(f"Unsupported translation scenario: {file_path} to {backend}") | ||
|
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
class ASTNode: | ||
pass | ||
|
||
|
||
class ShaderNode: | ||
def __init__(self, input_struct, output_struct, functions): | ||
self.input_struct = input_struct | ||
self.output_struct = output_struct | ||
self.functions = functions | ||
|
||
def __repr__(self): | ||
return f"ShaderNode(input_struct={self.input_struct}, output_struct={self.output_struct}, functions={self.functions})" | ||
|
||
|
||
class StructNode: | ||
def __init__(self, name, members): | ||
self.name = name | ||
self.members = members | ||
|
||
def __repr__(self): | ||
return f"StructNode(name={self.name}, members={self.members})" | ||
|
||
|
||
class FunctionNode(ASTNode): | ||
def __init__(self, return_type, name, params, body): | ||
self.return_type = return_type | ||
self.name = name | ||
self.params = params | ||
self.body = body | ||
|
||
def __repr__(self): | ||
return f"FunctionNode(return_type={self.return_type}, name={self.name}, params={self.params}, body={self.body})" | ||
|
||
|
||
class VariableNode(ASTNode): | ||
def __init__(self, vtype, name, semantic=None): | ||
self.vtype = vtype | ||
self.name = name | ||
self.semantic = semantic | ||
|
||
def __repr__(self): | ||
return f"VariableNode(vtype='{self.vtype}', name='{self.name}', semantic={self.semantic})" | ||
|
||
|
||
class AssignmentNode(ASTNode): | ||
def __init__(self, left, right, operator="="): | ||
self.left = left | ||
self.right = right | ||
self.operator = operator | ||
|
||
def __repr__(self): | ||
return f"AssignmentNode(left={self.left}, operator='{self.operator}', right={self.right})" | ||
|
||
|
||
class IfNode(ASTNode): | ||
def __init__(self, condition, if_body, else_body=None): | ||
self.condition = condition | ||
self.if_body = if_body | ||
self.else_body = else_body | ||
|
||
def __repr__(self): | ||
return f"IfNode(condition={self.condition}, if_body={self.if_body}, else_body={self.else_body})" | ||
|
||
|
||
class ForNode(ASTNode): | ||
def __init__(self, init, condition, update, body): | ||
self.init = init | ||
self.condition = condition | ||
self.update = update | ||
self.body = body | ||
|
||
def __repr__(self): | ||
return f"ForNode(init={self.init}, condition={self.condition}, update={self.update}, body={self.body})" | ||
|
||
|
||
class ReturnNode(ASTNode): | ||
def __init__(self, value): | ||
self.value = value | ||
|
||
def __repr__(self): | ||
return f"ReturnNode(value={self.value})" | ||
|
||
|
||
class FunctionCallNode(ASTNode): | ||
def __init__(self, name, args): | ||
self.name = name | ||
self.args = args | ||
|
||
def __repr__(self): | ||
return f"FunctionCallNode(name={self.name}, args={self.args})" | ||
|
||
|
||
class BinaryOpNode(ASTNode): | ||
def __init__(self, left, op, right): | ||
self.left = left | ||
self.op = op | ||
self.right = right | ||
|
||
def __repr__(self): | ||
return f"BinaryOpNode(left={self.left}, operator={self.op}, right={self.right})" | ||
|
||
|
||
class MemberAccessNode(ASTNode): | ||
def __init__(self, object, member): | ||
self.object = object | ||
self.member = member | ||
|
||
def __repr__(self): | ||
return f"MemberAccessNode(object={self.object}, member={self.member})" | ||
|
||
|
||
class VectorConstructorNode: | ||
def __init__(self, type_name, args): | ||
self.type_name = type_name | ||
self.args = args | ||
|
||
def __repr__(self): | ||
return f"VectorConstructorNode(type_name={self.type_name}, args={self.args})" | ||
|
||
|
||
class UnaryOpNode(ASTNode): | ||
def __init__(self, op, operand): | ||
self.op = op | ||
self.operand = operand | ||
|
||
def __repr__(self): | ||
return f"UnaryOpNode(operator={self.op}, operand={self.operand})" | ||
|
||
def __str__(self): | ||
return f"({self.op}{self.operand})" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
from .DirectxAst import * | ||
from .DirectxParser import * | ||
from .DirectxLexer import * | ||
|
||
|
||
class HLSLToCrossGLConverter: | ||
def __init__(self): | ||
self.vertex_inputs = [] | ||
self.vertex_outputs = [] | ||
self.fragment_inputs = [] | ||
self.fragment_outputs = [] | ||
self.type_map = { | ||
"float": "float", | ||
"float2": "vec2", | ||
"float3": "vec3", | ||
"float4": "vec4", | ||
"int": "int", | ||
} | ||
|
||
def convert(self, ast): | ||
self.process_structs(ast) | ||
|
||
code = "shader main {\n" | ||
|
||
# Generate vertex shader | ||
code += " // Vertex Shader\n" | ||
code += " vertex {\n" | ||
code += self.generate_io_declarations("vertex") | ||
code += "\n" | ||
code += self.generate_vertex_main( | ||
next(f for f in ast.functions if f.name == "VSMain") | ||
) | ||
code += " }\n\n" | ||
|
||
# Generate custom functions | ||
for func in ast.functions: | ||
if func.name not in ["VSMain", "PSMain"]: | ||
code += self.generate_function(func) | ||
|
||
# Generate fragment shader | ||
code += " // Fragment Shader\n" | ||
code += " fragment {\n" | ||
code += self.generate_io_declarations("fragment") | ||
code += "\n" | ||
code += self.generate_fragment_main( | ||
next(f for f in ast.functions if f.name == "PSMain") | ||
) | ||
code += " }\n" | ||
|
||
code += "}\n" | ||
return code | ||
|
||
def process_structs(self, ast): | ||
if ast.input_struct and ast.input_struct.name == "Vertex_INPUT": | ||
for member in ast.input_struct.members: | ||
self.vertex_inputs.append((member.vtype, member.name)) | ||
if ast.output_struct and ast.output_struct.name == "Vertex_OUTPUT": | ||
for member in ast.output_struct.members: | ||
if member.name != "position": | ||
self.vertex_outputs.append((member.vtype, member.name)) | ||
self.fragment_inputs.append((member.vtype, member.name)) | ||
if ast.output_struct and ast.output_struct.name == "Fragment_OUTPUT": | ||
for member in ast.output_struct.members: | ||
self.fragment_outputs.append((member.vtype, member.name)) | ||
|
||
def generate_io_declarations(self, shader_type): | ||
code = "" | ||
if shader_type == "vertex": | ||
for type, name in self.vertex_inputs: | ||
code += f" input {self.map_type(type)} {name};\n" | ||
for type, name in self.vertex_outputs: | ||
code += f" output {self.map_type(type)} {name};\n" | ||
elif shader_type == "fragment": | ||
for type, name in self.fragment_inputs: | ||
code += f" input {self.map_type(type)} {name};\n" | ||
for type, name in self.fragment_outputs: | ||
code += f" output {self.map_type(type)} {name};\n" | ||
return code.rstrip() | ||
|
||
def generate_function(self, func): | ||
params = ", ".join(f"{self.map_type(p.vtype)} {p.name}" for p in func.params) | ||
code = f" {self.map_type(func.return_type)} {func.name}({params}) {{\n" | ||
code += self.generate_function_body(func.body, indent=2) | ||
code += " }\n\n" | ||
return code | ||
|
||
def generate_vertex_main(self, func): | ||
code = " void main() {\n" | ||
code += self.generate_function_body(func.body, indent=3, is_main=True) | ||
code += " }\n" | ||
return code | ||
|
||
def generate_fragment_main(self, func): | ||
code = " void main() {\n" | ||
code += self.generate_function_body(func.body, indent=3, is_main=True) | ||
code += " }\n" | ||
return code | ||
|
||
def generate_function_body(self, body, indent=0, is_main=False): | ||
code = "" | ||
for stmt in body: | ||
code += " " * indent | ||
if isinstance(stmt, VariableNode): | ||
if not is_main: | ||
code += f"{self.map_type(stmt.vtype)} {stmt.name};\n" | ||
elif isinstance(stmt, AssignmentNode): | ||
code += self.generate_assignment(stmt, is_main) + ";\n" | ||
elif isinstance(stmt, ReturnNode): | ||
if not is_main: | ||
code += f"return {self.generate_expression(stmt.value, is_main)};\n" | ||
return code | ||
|
||
def generate_assignment(self, node, is_main): | ||
lhs = self.generate_expression(node.left, is_main) | ||
rhs = self.generate_expression(node.right, is_main) | ||
if ( | ||
is_main | ||
and isinstance(node.left, MemberAccessNode) | ||
and node.left.object == "output" | ||
): | ||
if node.left.member == "position": | ||
return f"gl_Position = {rhs}" | ||
return f"{node.left.member} = {rhs}" | ||
return f"{lhs} = {rhs}" | ||
|
||
def generate_expression(self, expr, is_main=False): | ||
if isinstance(expr, str): | ||
return expr | ||
elif isinstance(expr, VariableNode): | ||
return f"{expr.vtype} {expr.name}" | ||
elif isinstance(expr, BinaryOpNode): | ||
left = self.generate_expression(expr.left, is_main) | ||
right = self.generate_expression(expr.right, is_main) | ||
return f"({left} {expr.op} {right})" | ||
elif isinstance(expr, UnaryOpNode): | ||
operand = self.generate_expression(expr.operand, is_main) | ||
return f"({expr.operator}{operand})" | ||
elif isinstance(expr, FunctionCallNode): | ||
args = ", ".join( | ||
self.generate_expression(arg, is_main) for arg in expr.args | ||
) | ||
return f"{expr.name}({args})" | ||
elif isinstance(expr, MemberAccessNode): | ||
obj = self.generate_expression(expr.object) | ||
if obj == "output" or obj == "input": | ||
return expr.member | ||
return f"{obj}.{expr.member}" | ||
elif isinstance(expr, VectorConstructorNode): | ||
args = ", ".join( | ||
self.generate_expression(arg, is_main) for arg in expr.args | ||
) | ||
return f"{self.map_type(expr.type_name)}({args})" | ||
else: | ||
return str(expr) | ||
|
||
def map_type(self, hlsl_type): | ||
return self.type_map.get(hlsl_type, hlsl_type) |
Oops, something went wrong.