4 def pixels_to_rgba(pixels):
5 return (int(p*255) for p in pixels)
7 def pixels_to_rgb(pixels):
8 for i in range(0, len(pixels), 4):
9 yield int(pixels[i]*255)
10 yield int(pixels[i+1]*255)
11 yield int(pixels[i+2]*255)
13 def pixels_to_rgb_invert(pixels, mask):
14 for i in range(0, len(pixels), 4):
15 r = int(pixels[i]*255)
16 yield 255-r if mask&1 else r
17 g = int(pixels[i+1]*255)
18 yield 255-g if mask&2 else g
19 b = int(pixels[i+2]*255)
20 yield 255-b if mask&4 else b
22 def pixels_to_gray(pixels):
23 for i in range(0, len(pixels), 4):
24 yield int((pixels[i]+pixels[i+1]+pixels[i+2])*255/3)
26 class TextureExporter:
27 def export_texture(self, tex_node, channels=['R', 'G', 'B']):
28 image = tex_node.image
29 from .datafile import RawData, Resource, Statement, Token
30 tex_res = Resource(image.name+".tex", "texture")
32 tex_res.statements.append(Statement("type", Token("\\2d")))
34 if tex_node.use_mipmap:
35 tex_res.statements.append(Statement("generate_mipmap", True))
37 colorspace = image.colorspace_settings.name
38 if len(channels)==1 and colorspace=='sRGB':
39 raise Exception("Unsupported configuration on texture {}: Grayscale with sRGB".format(image.name))
41 invert_mask = sum(1<<i for i, c in enumerate(channels) if c[0]=='~')
43 fn = bpy.path.basename(image.filepath)
44 if not invert_mask and fn:
45 if not tex_node.use_mipmap:
46 tex_res.statements.append(Statement("mipmap_levels", 1))
47 srgb = "_srgb" if colorspace=='sRGB' else ""
48 tex_res.statements.append(Statement("external_image"+srgb, fn))
51 fmt = 'SRGB8_ALPHA8' if colorspace=='sRGB' else 'RGBA8'
52 elif len(channels)==1:
55 fmt = 'SRGB8' if colorspace=='sRGB' else 'RGB8'
57 tex_res.statements.append(Statement("storage", Token(fmt), image.size[0], image.size[1]))
59 pixels = tuple(image.pixels)
62 texdata = pixels_to_rgba(pixels)
63 elif len(channels)==1:
64 texdata = pixels_to_gray(pixels)
66 texdata = pixels_to_rgb_invert(pixels, invert_mask)
68 texdata = pixels_to_rgb(pixels)
70 data = RawData(image.name+".mdr", bytes(texdata))
71 tex_res.statements.append(tex_res.create_reference_statement("external_data", data))
75 class SamplerExporter:
76 def export_sampler(self, tex_node):
77 from .datafile import Resource, Statement, Token
78 samp_res = Resource(self.get_sampler_name(tex_node), "sampler")
80 use_interpolation = tex_node.interpolation!='Closest'
82 if tex_node.use_mipmap:
83 samp_res.statements.append(Statement("filter", Token('LINEAR_MIPMAP_LINEAR')))
85 samp_res.statements.append(Statement("filter", Token('LINEAR')))
86 samp_res.statements.append(Statement("max_anisotropy", tex_node.max_anisotropy))
88 if tex_node.use_mipmap:
89 samp_res.statements.append(Statement("filter", Token('NEAREST_MIPMAP_NEAREST')))
91 samp_res.statements.append(Statement("filter", Token('NEAREST')))
93 if tex_node.extension=="REPEAT":
94 samp_res.statements.append(Statement("wrap", Token('REPEAT')))
95 elif tex_node.extension=="EXTEND":
96 samp_res.statements.append(Statement("wrap", Token('CLAMP_TO_EDGE')))
97 elif tex_node.extension=="CLIP":
98 samp_res.statements.append(Statement("wrap", Token('CLAMP_TO_BORDER')))
99 samp_res.statements.append(Statement("border_color", 0.0, 0.0, 0.0, 0.0))
103 def get_sampler_name(self, tex_node):
106 use_interpolation = tex_node.interpolation!='Closest'
107 name_parts.append("linear" if use_interpolation else "nearest")
108 if tex_node.use_mipmap:
109 name_parts.append("mip")
110 if use_interpolation and tex_node.max_anisotropy>1:
111 name_parts.append("aniso{:g}x".format(tex_node.max_anisotropy))
112 if tex_node.extension!="REPEAT":
113 name_parts.append("clip" if tex_node.extension=="CLIP" else "clamp")
115 return "_".join(name_parts)+".samp"