const coral = @import("../coral.zig"); const std = @import("std"); pub const Version = enum { core_430, }; pub fn generate(source: coral.bytes.Writable, root: coral.shaders.Root, version: Version) coral.shaders.GenerationError!void { try coral.bytes.writeFormatted(source, "#version {version_name}\n", .{ .version_name = switch (version) { .core_430 => "430 core", }, }); for (root.uniforms.slice()) |uniform| { try coral.bytes.writeFormatted(source, "\nlayout (binding = {binding}) uniform {semantic} {{\n", .{ .binding = coral.utf8.cDec(uniform.binding), .semantic = uniform.semantic, }); var has_field = uniform.has_field; while (has_field) |field| : (has_field = field.has_next) { try coral.bytes.writeAll(source, "\t"); try generateType(source, field.type); try coral.bytes.writeFormatted(source, " {identifier};\n", .{ .identifier = field.identifier }); } try coral.bytes.writeFormatted(source, "}} {identifier};\n", .{ .identifier = uniform.identifier, }); } for (root.textures.slice()) |texture| { try coral.bytes.writeFormatted(source, "\nlayout (binding = {binding}) uniform {layout} {identifier};\n", .{ .binding = coral.utf8.cDec(texture.binding), .identifier = texture.identifier, .layout = switch (texture.layout) { .dimensions_2 => "sampler2D", }, }); } for (root.inputs.slice()) |input| { try coral.bytes.writeFormatted(source, "\nlayout (location = {location}) in ", .{ .location = coral.utf8.cDec(input.location), }); try generateType(source, input.type); try coral.bytes.writeFormatted(source, " {identifier};\n", .{ .identifier = input.identifier }); } for (root.outputs.slice()) |output| { try coral.bytes.writeFormatted(source, "\nlayout (location = {location}) out ", .{ .location = coral.utf8.cDec(output.location), }); try generateType(source, output.type); try coral.bytes.writeFormatted(source, " {identifier};\n", .{ .identifier = output.identifier }); } for (root.functions.slice()) |function| { if (function.has_return_type) |return_type| { try coral.bytes.writeAll(source, "\n"); try generateType(source, return_type); } else { try coral.bytes.writeAll(source, "\nvoid"); } try coral.bytes.writeFormatted(source, " {identifier}(", .{ .identifier = function.identifier }); var has_parameter = function.has_parameter; if (has_parameter) |first_parameter| { try generateType(source, first_parameter.type); try coral.bytes.writeFormatted(source, " {identifier}", .{ .identifier = first_parameter.identifier }); has_parameter = first_parameter.has_next; while (has_parameter) |parameter| : (has_parameter = parameter.has_next) { try coral.bytes.writeAll(source, ", "); try generateType(source, parameter.type); try coral.bytes.writeFormatted(source, " {identifier}", .{ .identifier = parameter.identifier }); } } try coral.bytes.writeAll(source, ") "); try generateBlock(source, function.block); } } fn generateArguments(source: coral.bytes.Writable, first_argument: *const coral.shaders.Argument) coral.bytes.ReadWriteError!void { try generateExpression(source, first_argument.expression); var has_next_argument = first_argument.has_next; while (has_next_argument) |next_argument| : (has_next_argument = next_argument.has_next) { try coral.bytes.writeAll(source, ", "); try generateExpression(source, next_argument.expression); } } fn generateBlock(source: coral.bytes.Writable, block: *const coral.shaders.Block) coral.bytes.ReadWriteError!void { try coral.bytes.writeAll(source, "{\n"); var has_next_statement = block.has_statement; while (has_next_statement) |statement| { try coral.bytes.writeN(source, "\t", block.depth); switch (statement.*) { .return_expression => |return_expression| { try coral.bytes.writeAll(source, "return "); if (return_expression) |expression| { try generateExpression(source, expression); } try coral.bytes.writeAll(source, ";\n"); has_next_statement = null; }, .declare_local => |declare_local| { try generateType(source, declare_local.local.type); try coral.bytes.writeFormatted(source, " {identifier} = ", .{ .identifier = declare_local.local.identifier }); try generateExpression(source, declare_local.local.expression); try coral.bytes.writeAll(source, ";\n"); has_next_statement = declare_local.has_next; }, .mutate_local => |mutate_local| { try coral.bytes.writeFormatted(source, "{identifier} = ", .{ .identifier = mutate_local.local.identifier }); try generateExpression(source, mutate_local.expression); try coral.bytes.writeAll(source, ";\n"); has_next_statement = mutate_local.has_next; }, .mutate_output => |mutate_output| { try coral.bytes.writeFormatted(source, "{identifier} = ", .{ .identifier = mutate_output.output.identifier }); try generateExpression(source, mutate_output.expression); try coral.bytes.writeAll(source, ";\n"); has_next_statement = mutate_output.has_next; }, } } try coral.bytes.writeAll(source, "}\n"); } fn generateExpression(source: coral.bytes.Writable, expression: *const coral.shaders.Expression) coral.bytes.ReadWriteError!void { switch (expression.*) { .float => |float| { try coral.bytes.writeFormatted(source, "{whole}.{decimal}", float); }, .int => |int| { try coral.bytes.writeAll(source, int.literal); }, .group_expression => |group_expression| { try coral.bytes.writeAll(source, "("); try generateExpression(source, group_expression); try coral.bytes.writeAll(source, ")"); }, .add => |add| { try generateExpression(source, add.lhs_expression); try coral.bytes.writeAll(source, " + "); try generateExpression(source, add.rhs_expression); }, .subtract => |subtract| { try generateExpression(source, subtract.lhs_expression); try coral.bytes.writeAll(source, " - "); try generateExpression(source, subtract.rhs_expression); }, .multiply => |multiply| { try generateExpression(source, multiply.lhs_expression); try coral.bytes.writeAll(source, " * "); try generateExpression(source, multiply.rhs_expression); }, .divide => |divide| { try generateExpression(source, divide.lhs_expression); try coral.bytes.writeAll(source, " / "); try generateExpression(source, divide.rhs_expression); }, .equal => |equal| { try generateExpression(source, equal.lhs_expression); try coral.bytes.writeAll(source, " == "); try generateExpression(source, equal.rhs_expression); }, .greater_than => |greater_than| { try generateExpression(source, greater_than.lhs_expression); try coral.bytes.writeAll(source, " > "); try generateExpression(source, greater_than.rhs_expression); }, .greater_equal => |greater_equal| { try generateExpression(source, greater_equal.lhs_expression); try coral.bytes.writeAll(source, " >= "); try generateExpression(source, greater_equal.rhs_expression); }, .lesser_than => |lesser_than| { try generateExpression(source, lesser_than.lhs_expression); try coral.bytes.writeAll(source, " < "); try generateExpression(source, lesser_than.rhs_expression); }, .lesser_equal => |lesser_equal| { try generateExpression(source, lesser_equal.lhs_expression); try coral.bytes.writeAll(source, " <= "); try generateExpression(source, lesser_equal.rhs_expression); }, .negate_expression => |negate_expression| { try coral.bytes.writeAll(source, "-"); try generateExpression(source, negate_expression); }, .get_local => |local| { try coral.bytes.writeAll(source, local.identifier); }, .mutate_local => |mutate_local| { try coral.bytes.writeAll(source, mutate_local.local.identifier); try coral.bytes.writeAll(source, " = "); try generateExpression(source, mutate_local.expression); }, .get_object => |get_object| { try generateExpression(source, get_object.object_expression); try coral.bytes.writeAll(source, "."); try coral.bytes.writeAll(source, get_object.field.identifier); }, .get_uniform => |get_uniform| { try coral.bytes.writeFormatted(source, "{identifier}.", .{ .identifier = get_uniform.uniform.identifier }); try coral.bytes.writeAll(source, get_uniform.field.identifier); }, .get_texture => |texture| { try coral.bytes.writeAll(source, texture.identifier); }, .get_parameter => |parameter| { try coral.bytes.writeAll(source, parameter.identifier); }, .get_output => |output| { try coral.bytes.writeAll(source, output.identifier); }, .mutate_output => |mutate_output| { try coral.bytes.writeAll(source, mutate_output.output.identifier); try coral.bytes.writeAll(source, " = "); try generateExpression(source, mutate_output.expression); }, .get_input => |input| { try coral.bytes.writeAll(source, input.identifier); }, .invoke => |invoke| { try coral.bytes.writeFormatted(source, "{name}(", .{ .name = invoke.function.identifier }); if (invoke.has_argument) |first_argument| { try generateArguments(source, first_argument); } try coral.bytes.writeAll(source, ")"); }, .convert => |convert| { try generateType(source, convert.target_type); try coral.bytes.writeAll(source, "("); try generateArguments(source, convert.first_argument); try coral.bytes.writeAll(source, ")"); }, .pow => |pow| { try coral.bytes.writeAll(source, "pow("); try generateArguments(source, pow.first_argument); try coral.bytes.writeAll(source, ")"); }, .abs => |abs| { try coral.bytes.writeAll(source, "abs("); try generateArguments(source, abs.first_argument); try coral.bytes.writeAll(source, ")"); }, .sin => |sin| { try coral.bytes.writeAll(source, "sin("); try generateArguments(source, sin.first_argument); try coral.bytes.writeAll(source, ")"); }, .sample => |sample| { try coral.bytes.writeAll(source, "texture("); if (sample.has_argument) |first_argument| { try generateArguments(source, first_argument); } try coral.bytes.writeAll(source, ")"); }, } } fn generateType(source: coral.bytes.Writable, @"type": *const coral.shaders.Type) coral.bytes.ReadWriteError!void { if (@"type" == coral.shaders.Type.float2) { return try coral.bytes.writeAll(source, "vec2"); } if (@"type" == coral.shaders.Type.float3) { return try coral.bytes.writeAll(source, "vec3"); } if (@"type" == coral.shaders.Type.float4) { return try coral.bytes.writeAll(source, "vec4"); } if (@"type" == coral.shaders.Type.float4x4) { return try coral.bytes.writeAll(source, "mat4"); } try coral.bytes.writeAll(source, @"type".identifier); }