summaryrefslogtreecommitdiffstats
path: root/Scripts/generate_shader.py
blob: 15dc9b995777b087f109985d775e5a667ec65592 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#!/usr/bin/env python3

import argparse
import generate_utils
import os
import sys

# A single parameter looks like this:
#   _Letter_Row00_Col00_Byte0("_Letter_Row00_Col00_Byte0", float) = 0
def generateUnityParams(nbytes: int, nrows: int, ncols: int, prefix: str = "") -> str:
    lines = []
    lines.append(prefix + "// BEGIN GENERATED CODE BLOCK")
    for byte in range(0, nbytes):
        for row in range(0, nrows):
            for col in range(0, ncols):
                param_name = generate_utils.getShaderParamByRowColByte(row, col, byte)
                line = prefix + """{}("{}", float) = 0""".format(param_name, param_name)
                lines.append(line)
    lines.append(prefix + "// END GENERATED CODE BLOCK")
    return '\n'.join(lines)

# A single parameter looks like this:
#   float _Letter_Row00_Col00_Byte0;
def generateCgParams(nbytes: int, nrows: int, ncols: int, prefix: str = "") -> str:
    lines = []
    lines.append(prefix + "// BEGIN GENERATED CODE BLOCK")
    for byte in range(0, nbytes):
        for row in range(0, nrows):
            for col in range(0, ncols):
                param_name = generate_utils.getShaderParamByRowColByte(row, col, byte)
                line = prefix + """float {};""".format(param_name)
                lines.append(line)
    lines.append(prefix + "// END GENERATED CODE BLOCK")
    return '\n'.join(lines)

# Define 3 constants:
#   uniform int BYTES_PER_CHAR = $nbytes;
#   uniform int NROWS = $nrows;
#   uniform int NCOLS = $ncols;
def generateCgConstants(nbytes: int, nrows: int, ncols: int, prefix: str = "") -> str:
    lines = []
    lines.append(prefix + "// BEGIN GENERATED CODE BLOCK")
    lines.append(prefix + "#define BYTES_PER_CHAR {}".format(nbytes))
    lines.append(prefix + "#define NROWS {}".format(nrows))
    lines.append(prefix + "#define NCOLS {}".format(ncols))
    lines.append(prefix + "// END GENERATED CODE BLOCK")
    return '\n'.join(lines)

# This is the basic idea of what we're generating:
#      // Get the value of the parameter for the cell we're in.
#      uint GetLetterParameter(float2 uv)
#      {
#        float CHAR_COL = floor(uv.x * Cols);
#        float CHAR_ROW = floor(uv.y * Rows);
#        uint res = 0;
#
#          [forcecase] switch(CHAR_ROW) {
#            case n:
#            case n-1:
#            ...
#
#              [forcecase] switch (CHAR_COL) {
#              case 0:
#              case 1:
#              ...
#
#                res |= ((uint) round(_Letter_Row00_Col00_Byte0)) << (0 * 8);
#                res |= ((uint) round(_Letter_Row00_Col00_Byte1)) << (1 * 8);
#                continue;
#              }
#        }
#        return res;
#      }
# In English, this provides an accessor to the many (possibly thousands)
# float parameters which hold the text on the board.
def generateLetterAccessor(nbytes: int, nrows: int, ncols: int, prefix: str = "") -> str:
    lines = []
    lines.append(prefix + "// BEGIN GENERATED CODE BLOCK")
    lines.append(prefix + "[forcecase] switch (CHAR_ROW) {")
    for row in range(0, nrows):
        lines.append(prefix + "  case {}:".format(nrows - (row + 1)))
        lines.append(prefix + "    [forcecase] switch (CHAR_COL) {")
        for col in range(0, ncols):
            lines.append(prefix + "      case {}:".format(col))
            for byte in range(0, nbytes):
                param_name = generate_utils.getShaderParamByRowColByte(row, col, byte)
                lines.append(prefix + "        res |= ((uint) round({})) << ({} * 8);".format(param_name, byte))
            lines.append(prefix + "        return res;")
        lines.append(prefix + "      default:")
        lines.append(prefix + "        return 0;")
        lines.append(prefix + "    }")
    lines.append(prefix + "}")
    lines.append(prefix + "// END GENERATED CODE BLOCK")
    return '\n'.join(lines)

# Replace any line containing `macro` with `replacement`.
def applyLineMacro(old_path: str, new_path: str, macro: str, replacement: str) -> bool:
    new_lines = []
    times_applied = 0
    with open(old_path, 'r') as f:
        for line in f:
            if line[-1] == '\n':
                line = line[0:len(line)-1]
            if macro in line:
                new_lines.append(replacement)
                times_applied += 1
            else:
                new_lines.append(line)
    with open(new_path, 'w') as f:
        f.write('\n'.join(new_lines))
    return times_applied

if __name__ == "__main__":
    sys.stdout.reconfigure(encoding="utf-8")

    print("args: {}".format(" ".join(sys.argv)))

    parser = argparse.ArgumentParser()
    parser.add_argument("--bytes_per_char", type=str, help="The number of bytes to use to represent each character")
    parser.add_argument("--rows", type=str, help="The number of rows on the board")
    parser.add_argument("--cols", type=str, help="The number of columns on the board")
    parser.add_argument("--shader_template", type=str, help="The path to the shader template")
    parser.add_argument("--shader_path", type=str, help="The path where the generated shader will be written")
    args = parser.parse_args()

    if not args.bytes_per_char or not args.rows or not args.cols \
            or not args.shader_template or not args.shader_path:
        print("--bytes_per_char, --rows, --cols, --shader_template, --shader_path required", file=sys.stderr)
        sys.exit(1)

    nbytes = int(args.bytes_per_char)
    nrows = int(args.rows)
    ncols = int(args.cols)

    replacement = generateUnityParams(nbytes, nrows, ncols, prefix = "")
    #print(replacement)
    macro = "// %TEMPLATE__UNITY_ROW_COL_PARAMS%"
    applyLineMacro(args.shader_template, args.shader_path, macro, replacement)

    replacement = generateCgParams(nbytes, nrows, ncols, prefix = "  ")
    #print(replacement)
    macro = "// %TEMPLATE__CG_ROW_COL_PARAMS%"
    applyLineMacro(args.shader_path, args.shader_path, macro, replacement)

    replacement = generateCgConstants(nbytes, nrows, ncols, prefix = "  ")
    #print(replacement)
    macro = "// %TEMPLATE__CG_ROW_COL_CONSTANTS%"
    applyLineMacro(args.shader_path, args.shader_path, macro, replacement)

    replacement = generateLetterAccessor(nbytes, nrows, ncols, prefix = "      ")
    #print(replacement)
    macro = "// %TEMPLATE__CG_LETTER_ACCESSOR%"
    applyLineMacro(args.shader_path, args.shader_path, macro, replacement)