Skip to content

Instantly share code, notes, and snippets.

@Ninja-Koala
Last active May 6, 2024 14:53
Show Gist options
  • Save Ninja-Koala/74fa7652fb4de248949ce1e27b989c14 to your computer and use it in GitHub Desktop.
Save Ninja-Koala/74fa7652fb4de248949ce1e27b989c14 to your computer and use it in GitHub Desktop.
Convert a svg path to a shadertoy shader
#!/usr/bin/python3
import os
import sys
import numpy as np
def read_value(string, index):
#go to the beginning of the value
while svg_content[index] not in numerals:
index+=1
#read in the value
number_string=""
while svg_content[index] in numerals:
number_string+=svg_content[index]
index+=1
value=float(number_string)
return (value,index)
def read_coordinate(string, index):
(x_coordinate,index)=read_value(string,index)
(y_coordinate,index)=read_value(string,index)
point=np.array([x_coordinate,y_coordinate])
return (point,index)
if len(sys.argv) not in (2,3):
print("Please give input svg file and optionally shader filename")
exit()
svg_file=open(sys.argv[1])
svg_content=svg_file.read()
if svg_content.find("<svg") == -1:
print("Doesn't look like a svg file")
exit()
if len(sys.argv) == 3:
shaderfilename=sys.argv[2]
else:
shaderfilename=os.path.splitext(sys.argv[1])[0]+".glsl"
shaderfile = open(shaderfilename, 'w')
index=0
numerals="0123456789-.eE"
path_index=0
path_strings=""
path_dis_strings=""
point_lists=[]
segment_lists=[]
q_bezier_lists=[]
c_bezier_lists=[]
while svg_content.find("<path",index) != -1:
index=svg_content.find("<path",index)
index=svg_content.find(" d=\"",index)
index+=4
initial_point=-1
cur_point=-1
point_list=[]
segment_list=[]
q_bezier_list=[]
c_bezier_list=[]
relative=False
while index < len(svg_content):
#m or M means move current point to given coordinate
if svg_content[index] in "mM":
if svg_content[index] == "m":
relative=True
else:
relative=False
first_iteration=True
index+=1
while svg_content[index] == " ":
index+=1
while svg_content[index] in numerals:
(coord,index)=read_coordinate(svg_content,index)
if relative and cur_point != -1:
coord+=point_list[cur_point]
point_list+=[coord]
p1=len(point_list)-1
if first_iteration:
initial_point=p1
else:
segment_list+=[(cur_point,p1)]
cur_point=p1
while svg_content[index] == " ":
index+=1
first_iteration=False
cur_point=len(point_list)-1
if initial_point == -1:
initial_point=cur_point
index-=1
#l or L means line
if svg_content[index] in "lL":
if svg_content[index] == "l":
relative=True
else:
relative=False
index+=1
while svg_content[index] == " ":
index+=1
while svg_content[index] in numerals:
p0=cur_point
(coord,index)=read_coordinate(svg_content,index)
if relative and cur_point != -1:
coord+=point_list[cur_point]
point_list+=[coord]
p1=len(point_list)-1
segment_list+=[(p0,p1)]
cur_point=p1
while svg_content[index] == " ":
index+=1
index-=1
#h or H means horizontal line
if svg_content[index] in "hH":
if svg_content[index] == "h":
relative=True
else:
relative=False
index+=1
while svg_content[index] == " ":
index+=1
while svg_content[index] in numerals:
p0=cur_point
(value,index)=read_value(svg_content,index)
coord=np.array([value,0])
if True or cur_point!=-1:
if relative:
coord+=point_list[cur_point]
else:
coord[1]=point_list[cur_point][1]
point_list+=[coord]
p1=len(point_list)-1
segment_list+=[(p0,p1)]
cur_point=p1
while svg_content[index] == " ":
index+=1
index-=1
#v or V means vertical line
if svg_content[index] in "vV":
if svg_content[index] == "v":
relative=True
else:
relative=False
index+=1
while svg_content[index] == " ":
index+=1
while svg_content[index] in numerals:
p0=cur_point
(value,index)=read_value(svg_content,index)
coord=np.array([0,value])
if cur_point!=-1:
if relative:
coord+=point_list[cur_point]
else:
coord[0]=point_list[cur_point][0]
point_list+=[coord]
p1=len(point_list)-1
segment_list+=[(p0,p1)]
cur_point=p1
while svg_content[index] == " ":
index+=1
index-=1
#q or Q means quadratic bezier curve(s)
if svg_content[index] in "qQ":
if svg_content[index] == "q":
relative=True
else:
relative=False
index+=1
while svg_content[index] == " ":
index+=1
while svg_content[index] in numerals:
p0=cur_point
(coord,index)=read_coordinate(svg_content,index)
if relative and cur_point != -1:
coord+=point_list[cur_point]
point_list+=[coord]
p1=len(point_list)-1
(coord,index)=read_coordinate(svg_content,index)
if relative and cur_point != -1:
coord+=point_list[cur_point]
point_list+=[coord]
p2=len(point_list)-1
q_bezier_list+=[(p0,p1,p2)]
cur_point=p2
while svg_content[index] == " ":
index+=1
index-=1
#t or T means smooth quadratic bezier curve(s)
if svg_content[index] in "tT":
if svg_content[index] == "t":
relative=True
else:
relative=False
index+=1
while svg_content[index] == " ":
index+=1
while svg_content[index] in numerals:
p0=cur_point
if q_bezier_list[-1][-1] == cur_point:
last_control_point=point_list[q_bezier_list[-1][1]]
cur_coord=point_list[cur_point]
coord=2*cur_coord-last_control_point
point_list+=[coord]
p1=len(point_list)-1
else:
p1=cur_point
(coord,index)=read_coordinate(svg_content,index)
if relative and cur_point != -1:
coord+=point_list[cur_point]
point_list+=[coord]
p2=len(point_list)-1
q_bezier_list+=[(p0,p1,p2)]
cur_point=p2
while svg_content[index] == " ":
index+=1
index-=1
#c or C means cubic bezier curve(s)
if svg_content[index] in "cC":
if svg_content[index] == "c":
relative=True
else:
relative=False
index+=1
while svg_content[index] == " ":
index+=1
while svg_content[index] in numerals:
p0=cur_point
(coord,index)=read_coordinate(svg_content,index)
if relative and cur_point != -1:
coord+=point_list[cur_point]
point_list+=[coord]
p1=len(point_list)-1
(coord,index)=read_coordinate(svg_content,index)
if relative and cur_point != -1:
coord+=point_list[cur_point]
point_list+=[coord]
p2=len(point_list)-1
(coord,index)=read_coordinate(svg_content,index)
if relative and cur_point != -1:
coord+=point_list[cur_point]
point_list+=[coord]
p3=len(point_list)-1
c_bezier_list+=[(p0,p1,p2,p3)]
cur_point=p3
while svg_content[index] == " ":
index+=1
index-=1
#s or S means smooth cubic bezier curve(s)
if svg_content[index] in "sS":
if svg_content[index] == "s":
relative=True
else:
relative=False
index+=1
while svg_content[index] == " ":
index+=1
while svg_content[index] in numerals:
p0=cur_point
if c_bezier_list[-1][-1] == cur_point:
last_control_point=point_list[c_bezier_list[-1][2]]
cur_coord=point_list[cur_point]
coord=2*cur_coord-last_control_point
point_list+=[coord]
p1=len(point_list)-1
else:
p1=cur_point
(coord,index)=read_coordinate(svg_content,index)
if relative and cur_point != -1:
coord+=point_list[cur_point]
point_list+=[coord]
p2=len(point_list)-1
(coord,index)=read_coordinate(svg_content,index)
if relative and cur_point != -1:
coord+=point_list[cur_point]
point_list+=[coord]
p3=len(point_list)-1
c_bezier_list+=[(p0,p1,p2,p3)]
cur_point=p3
while svg_content[index] == " ":
index+=1
index-=1
if svg_content[index] in "zZ":
if np.linalg.norm(point_list[cur_point]-point_list[initial_point]) < .001:
if len(segment_list)>0 and segment_list[-1][-1] == cur_point:
tmp=list(segment_list[-1])
tmp[-1]=initial_point
segment_list[-1]=tuple(tmp)
elif len(q_bezier_list)>0 and q_bezier_list[-1][-1] == cur_point:
tmp=list(q_bezier_list[-1])
tmp[-1]=initial_point
q_bezier_list[-1]=tuple(tmp)
elif len(c_bezier_list)>0 and c_bezier_list[-1][-1] == cur_point:
tmp=list(c_bezier_list[-1])
tmp[-1]=initial_point
c_bezier_list[-1]=tuple(tmp)
else:
print("Something weird happened…")
point_list=point_list[:-1]
else:
segment_list+=[(cur_point,initial_point)]
cur_point=initial_point
if svg_content[index] == "\"":
break
index+=1
point_lists+=[point_list]
segment_lists+=[segment_list]
q_bezier_lists+=[q_bezier_list]
c_bezier_lists+=[c_bezier_list]
min_x=float("inf")
max_x=float("-inf")
min_y=float("inf")
max_y=float("-inf")
path_min_x=[]
path_max_x=[]
path_min_y=[]
path_max_y=[]
if len(point_lists)==0:
print("No path found in svg file")
exit()
for path_index in range(len(point_lists)):
#TODO: compute more accurate bounding box, see https://www.shadertoy.com/view/XdVBWd and https://www.shadertoy.com/view/lsyfWc
path_min_x+=[min(min_x,min(map(lambda x: x[0], point_lists[path_index])))]
path_max_x+=[max(max_x,max(map(lambda x: x[0], point_lists[path_index])))]
path_min_y+=[min(min_y,min(map(lambda x: x[1], point_lists[path_index])))]
path_max_y+=[max(max_y,max(map(lambda x: x[1], point_lists[path_index])))]
min_x=min(path_min_x)
max_x=max(path_max_x)
min_y=min(path_min_y)
max_y=max(path_max_y)
zoom=min(0.4/(max_y-min_y),0.8/(max_x-min_x))
offset=np.array([(min_x+max_x)/2,(min_y+max_y)/2])
max_x=(max_x-offset[0])*zoom
min_x=(min_x-offset[0])*zoom
max_y=-(max_y-offset[1])*zoom
min_y=-(min_y-offset[1])*zoom
#swap max_x and max_y because we changed sign
tmp=max_y
max_y=min_y
min_y=tmp
for path_index in range(len(point_lists)):
for i in range(len(point_lists[path_index])):
point_lists[path_index][i]=(point_lists[path_index][i]-offset)*zoom
point_lists[path_index][i]=np.array([point_lists[path_index][i][0],-point_lists[path_index][i][1]])
path_max_x[path_index]=(path_max_x[path_index]-offset[0])*zoom
path_min_x[path_index]=(path_min_x[path_index]-offset[0])*zoom
path_max_y[path_index]=-(path_max_y[path_index]-offset[1])*zoom
path_min_y[path_index]=-(path_min_y[path_index]-offset[1])*zoom
#swap max_x and max_y because we changed sign
tmp=path_max_y[path_index]
path_max_y[path_index]=path_min_y[path_index]
path_min_y[path_index]=tmp
shader_template="""#define ZERO min(0,iFrame)
float border;
// Modified from http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c
// Credits to Doublefresh for hinting there
int solve_quadric(vec2 coeffs, inout vec2 roots){{
// normal form: x^2 + px + q = 0
float p = coeffs[1] / 2.;
float q = coeffs[0];
float D = p * p - q;
if (D < 0.){{
return 0;
}}
else{{
roots[0] = -sqrt(D) - p;
roots[1] = sqrt(D) - p;
return 2;
}}
}}
//From Trisomie21
//But instead of his cancellation fix i'm using a newton iteration
int solve_cubic(vec3 coeffs, inout vec3 r){{
float a = coeffs[2];
float b = coeffs[1];
float c = coeffs[0];
float p = b - a*a / 3.0;
float q = a * (2.0*a*a - 9.0*b) / 27.0 + c;
float p3 = p*p*p;
float d = q*q + 4.0*p3 / 27.0;
float offset = -a / 3.0;
if(d >= 0.0) {{ // Single solution
float z = sqrt(d);
float u = (-q + z) / 2.0;
float v = (-q - z) / 2.0;
u = sign(u)*pow(abs(u),1.0/3.0);
v = sign(v)*pow(abs(v),1.0/3.0);
r[0] = offset + u + v;
//Single newton iteration to account for cancellation
float f = ((r[0] + a) * r[0] + b) * r[0] + c;
float f1 = (3. * r[0] + 2. * a) * r[0] + b;
r[0] -= f / f1;
return 1;
}}
float u = sqrt(-p / 3.0);
float v = acos(-sqrt( -27.0 / p3) * q / 2.0) / 3.0;
float m = cos(v), n = sin(v)*1.732050808;
//Single newton iteration to account for cancellation
//(once for every root)
r[0] = offset + u * (m + m);
r[1] = offset - u * (n + m);
r[2] = offset + u * (n - m);
vec3 f = ((r + a) * r + b) * r + c;
vec3 f1 = (3. * r + 2. * a) * r + b;
r -= f / f1;
return 3;
}}
float quadratic_bezier_normal_iteration(float t, vec2 a0, vec2 a1, vec2 a2){{
//horner's method
vec2 a_1=a1+t*a2;
vec2 uv_to_p=a0+t*a_1;
vec2 tang=a_1+t*a2;
float l_tang=dot(tang,tang);
return t-dot(tang,uv_to_p)/l_tang;
}}
float quadratic_bezier_dis_approx_sq(vec2 uv, vec2 p0, vec2 p1, vec2 p2){{
vec2 a2 = p0 - 2. * p1 + p2;
vec2 a1 = -2. * p0 + 2. * p1;
vec2 a0 = p0 - uv;
float d0 = 1e38;
float t;
vec3 params=vec3(0,.5,1);
if(all(lessThan(uv,max(max(p0,p1),p2)+border)) && all(greaterThan(uv,min(min(p0,p1),p2)-border))){{
for(int i=ZERO;i<3;i++){{
t=params[i];
for(int j=ZERO;j<3;j++){{
t=quadratic_bezier_normal_iteration(t,a0,a1,a2);
}}
t=clamp(t,0.,1.);
vec2 uv_to_p=(a2*t+a1)*t+a0;
d0=min(d0,dot(uv_to_p,uv_to_p));
}}
}}
return d0;
}}
float cubic_bezier_normal_iteration(float t, vec2 a0, vec2 a1, vec2 a2, vec2 a3){{
//horner's method
vec2 a_2=a2+t*a3;
vec2 a_1=a1+t*a_2;
vec2 b_2=a_2+t*a3;
vec2 uv_to_p=a0+t*a_1;
vec2 tang=a_1+t*b_2;
float l_tang=dot(tang,tang);
return t-dot(tang,uv_to_p)/l_tang;
}}
float cubic_bezier_dis_approx_sq(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3){{
vec2 a3 = (-p0 + 3. * p1 - 3. * p2 + p3);
vec2 a2 = (3. * p0 - 6. * p1 + 3. * p2);
vec2 a1 = (-3. * p0 + 3. * p1);
vec2 a0 = p0 - uv;
float d0 = 1e38;
float t;
vec3 params=vec3(0,.5,1);
if(all(lessThan(uv,max(max(p0,p1),max(p2,p3))+border)) && all(greaterThan(uv,min(min(p0,p1),min(p2,p3))-border))){{
for(int i=ZERO;i<3;i++){{
t=params[i];
for(int j=ZERO;j<3;j++){{
t=cubic_bezier_normal_iteration(t,a0,a1,a2,a3);
}}
t=clamp(t,0.,1.);
vec2 uv_to_p=((a3*t+a2)*t+a1)*t+a0;
d0=min(d0,dot(uv_to_p,uv_to_p));
}}
}}
return d0;
}}
//segment_dis_sq by iq
float length2( vec2 v ) {{ return dot(v,v); }}
float segment_dis_sq( vec2 p, vec2 a, vec2 b ){{
vec2 pa = p-a, ba = b-a;
float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 );
return length2( pa - ba*h );
}}
int segment_int_test(vec2 uv, vec2 p0, vec2 p1){{
p0-=uv;
p1-=uv;
int ret;
if(p0.y*p1.y<0.){{
vec2 nor=p0-p1;
nor=vec2(nor.y,-nor.x);
float sgn;
if(p0.y>p1.y){{
sgn=1.;
}}
else{{
sgn=-1.;
}}
if(dot(nor,p0)*sgn<0.){{
ret=0;
}}
else{{
ret=1;
}}
}}
else{{
ret=0;
}}
return ret;
}}
int quadratic_bezier_int_test(vec2 uv, vec2 p0, vec2 p1, vec2 p2){{
float qu = (p0.y - 2. * p1.y + p2.y);
float li = (-2. * p0.y + 2. * p1.y);
float co = p0.y - uv.y;
vec2 roots = vec2(1e38);
int n_roots = solve_quadric(vec2(co/qu,li/qu),roots);
int n_ints = 0;
for(int i=ZERO;i<n_roots;i++){{
if(roots[i] >= 0. && roots[i] <= 1.){{
float x_pos = p0.x - 2. * p1.x + p2.x;
x_pos = x_pos * roots[i] + -2. * p0.x + 2. * p1.x;
x_pos = x_pos * roots[i] + p0.x;
if(x_pos > uv.x){{
n_ints++;
}}
}}
}}
return n_ints;
}}
int cubic_bezier_int_test(vec2 uv, vec2 p0, vec2 p1, vec2 p2, vec2 p3){{
float cu = (-p0.y + 3. * p1.y - 3. * p2.y + p3.y);
float qu = (3. * p0.y - 6. * p1.y + 3. * p2.y);
float li = (-3. * p0.y + 3. * p1.y);
float co = p0.y - uv.y;
vec3 roots = vec3(1e38);
int n_roots;
int n_ints=0;
if(uv.x<min(min(p0.x,p1.x),min(p2.x,p3.x))){{
if(uv.y>=min(p0.y,p3.y) && uv.y<=max(p0.y,p3.y)){{
n_ints=1;
}}
}}
else{{
if(abs(cu) < .0001){{
n_roots = solve_quadric(vec2(co/qu,li/qu),roots.xy);
}}
else{{
n_roots = solve_cubic(vec3(co/cu,li/cu,qu/cu),roots);
}}
for(int i=ZERO;i<n_roots;i++){{
if(roots[i] >= 0. && roots[i] <= 1.){{
float x_pos = -p0.x + 3. * p1.x - 3. * p2.x + p3.x;
x_pos = x_pos * roots[i] + 3. * p0.x - 6. * p1.x + 3. * p2.x;
x_pos = x_pos * roots[i] + -3. * p0.x + 3. * p1.x;
x_pos = x_pos * roots[i] + p0.x;
if(x_pos > uv.x){{
n_ints++;
}}
}}
}}
}}
return n_ints;
}}
{}void mainImage(out vec4 fragColor, in vec2 fragCoord){{
border=1./iResolution.x;
vec2 uv=fragCoord.xy/iResolution.xy;
uv-=.5;
uv.y*=iResolution.y/iResolution.x;
vec2 mouse=vec2(0);
if(iMouse.x>0.0){{
mouse = iMouse.xy / iResolution.y;
mouse.x -= .5 * iResolution.x / iResolution.y;
mouse.y -= .75;
}}
const float pi=3.14159265358979;
float t0=smoothstep(0.,5.,iTime);
float t1=t0*6.*pi;
mat2 rot=mat2(cos(t1),sin(t1),-sin(t1),cos(t1));
border*=exp(4.*mouse.y)*exp(1.-1.*t0);
uv*=exp(4.*mouse.y)*exp(1.-1.*t0);
uv*=rot;
uv.x+=mouse.x;
float dis_sq=1e38;
if(all(lessThan(uv,vec2({:.6},{:.6})+border)) && all(greaterThan(uv,vec2({:.6},{:.6})-border))){{
{} }}
float dis=sign(dis_sq)*sqrt(abs(dis_sq));
fragColor=vec4(smoothstep(-border, border, dis));
}}"""
path_template="""float path{}_dis_sq(vec2 uv){{
float dis_sq=1e38;
int num_its=0;
{}
if(all(lessThan(uv,vec2({:.6},{:.6})+border)) && all(greaterThan(uv,vec2({:.6},{:.6})-border))){{
{} }}
float sgn=1.;
if(num_its%2==1){{
sgn=-1.;
}}
return sgn*dis_sq;
}}
"""
path_dis_template="\t\tdis_sq=min(dis_sq,path{}_dis_sq(uv));\n"
segment_dis_template="\t\t\tdis_sq=min(dis_sq,segment_dis_sq(uv,{}));\n"
q_bezier_dis_template="\t\t\tdis_sq=min(dis_sq,quadratic_bezier_dis_approx_sq(uv,{}));\n"
c_bezier_dis_template="\t\t\tdis_sq=min(dis_sq,cubic_bezier_dis_approx_sq(uv,{}));\n"
segment_int_template="\t\t\tnum_its+=segment_int_test(uv,{});\n"
q_bezier_int_template="\t\t\tnum_its+=quadratic_bezier_int_test(uv,{});\n"
c_bezier_int_template="\t\t\tnum_its+=cubic_bezier_int_test(uv,{});\n"
vector_template="vec2({:.6},{:.6})"
ivec2_template="ivec2({},{})"
ivec3_template="ivec3({},{},{})"
ivec4_template="ivec4({},{},{},{})"
points_template="\tvec2[{}] p=vec2[]("
segments_template="\tivec2[{}] seg=ivec2[]("
q_beziers_template="\tivec3[{}] q_bez=ivec3[]("
c_beziers_template="\tivec4[{}] c_bez=ivec4[]("
segment_points_template="p[seg[i][{}]],"
q_bezier_points_template="p[q_bez[i][{}]],"
c_bezier_points_template="p[c_bez[i][{}]],"
loop_template="""\t\tfor(int i=ZERO;i<{};i++){{\n{}\t\t}}\n"""
data_string=""
loops=""
points=""
points=points_template.format(len(point_lists[path_index]))
separator=",\n\t"+(len(points)-1)*" "
for (i,point) in enumerate(point_lists[path_index]):
points+=vector_template.format(point[0],point[1])+separator
points=points[:-len(separator)]+");\n"
data_string+=points
if len(segment_lists[path_index])>0:
segments=segments_template.format(len(segment_lists[path_index]))
separator=",\n\t"+(len(segments)-1)*" "
for (i,curve) in enumerate(segment_lists[path_index]):
segments+=ivec2_template.format(curve[0],curve[1])+separator
segments=segments[:-len(separator)]+");\n"
data_string+="\n"+segments
segment_points=""
for i in range(2):
segment_points+=segment_points_template.format(i)
segment_points=segment_points[:-1]
loop_body=segment_dis_template.format(segment_points)
loop_body+=segment_int_template.format(segment_points)
loops+=loop_template.format(len(segment_lists[path_index]),loop_body)
if len(q_bezier_lists[path_index])>0:
q_beziers=q_beziers_template.format(len(q_bezier_lists[path_index]))
separator=",\n\t"+(len(q_beziers)-1)*" "
for (i,curve) in enumerate(q_bezier_lists[path_index]):
q_beziers+=ivec3_template.format(curve[0],curve[1],curve[2])+separator
q_beziers=q_beziers[:-len(separator)]+");\n"
data_string+="\n"+q_beziers
q_bezier_points=""
for i in range(3):
q_bezier_points+=q_bezier_points_template.format(i)
q_bezier_points=q_bezier_points[:-1]
loop_body=q_bezier_dis_template.format(q_bezier_points)
loop_body+=q_bezier_int_template.format(q_bezier_points)
loops+=loop_template.format(len(q_bezier_lists[path_index]),loop_body)
if len(c_bezier_lists[path_index])>0:
c_beziers=c_beziers_template.format(len(c_bezier_lists[path_index]))
separator=",\n\t"+(len(c_beziers)-1)*" "
for (i,curve) in enumerate(c_bezier_lists[path_index]):
c_beziers+=ivec4_template.format(curve[0],curve[1],curve[2],curve[3])+separator
c_beziers=c_beziers[:-len(separator)]+");\n"
data_string+="\n"+c_beziers
c_bezier_points=""
for i in range(4):
c_bezier_points+=c_bezier_points_template.format(i)
c_bezier_points=c_bezier_points[:-1]
loop_body=c_bezier_dis_template.format(c_bezier_points)
loop_body+=c_bezier_int_template.format(c_bezier_points)
loops+=loop_template.format(len(c_bezier_lists[path_index]),loop_body)
p_max_x=path_max_x[path_index]
p_max_y=path_max_y[path_index]
p_min_x=path_min_x[path_index]
p_min_y=path_min_y[path_index]
path_strings+=path_template.format(path_index,data_string,p_max_x,p_max_y,p_min_x,p_min_y,loops)
path_dis_strings+=path_dis_template.format(path_index)
shader_string=shader_template.format(path_strings,max_x,max_y,min_x,min_y,path_dis_strings)
shaderfile.write(shader_string)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment