]> git.tdb.fi Git - libs/gl.git/blob - blender/io_mspgl/material.py
Force the usage of normalmap as RGB data
[libs/gl.git] / blender / io_mspgl / material.py
1 import os
2
3 def get_linked_node_and_socket(node_tree, socket):
4         for l in node_tree.links:
5                 if socket==l.to_socket:
6                         return (l.from_node, l.from_socket)
7                 elif socket==l.from_socket:
8                         return (l.to_node, l.to_socket)
9         return (None, None)
10
11 class MaterialProperty:
12         def __init__(self, keyword, tex_keyword, value):
13                 self.keyword = keyword
14                 self.tex_keyword = tex_keyword
15                 self.value = value
16                 self.texture = None
17                 self.tex_usage = None
18
19         def set_from_input(self, node_tree, input_socket, alpha_socket=None):
20                 if self.keyword:
21                         if type(self.value)==tuple:
22                                 if alpha_socket:
23                                         self.value = input_socket.default_value[:len(self.value)-1]+(alpha_socket.default_value,)
24                                 else:
25                                         self.value = input_socket.default_value[:len(self.value)]
26                         else:
27                                 self.value = input_socket.default_value
28
29                 if self.tex_keyword:
30                         from_node, _ = get_linked_node_and_socket(node_tree, input_socket)
31                         alpha_from = None
32                         if from_node:
33                                 usage = None
34                                 if from_node.type=='NORMAL_MAP':
35                                         from_node, _ = get_linked_node_and_socket(node_tree, from_node.inputs["Color"])
36                                         usage = 'RGB'
37                                 elif from_node.type=='RGBTOBW':
38                                         from_node, _ = get_linked_node_and_socket(node_tree, from_node.inputs["Color"])
39
40                                 if alpha_socket:
41                                         alpha_from, _ = get_linked_node_and_socket(node_tree, alpha_socket)
42                                         if alpha_from and alpha_from!=from_node:
43                                                 raise Exception("Separate textures for color and alpha are not supported")
44
45                                 if from_node.type=='TEX_IMAGE':
46                                         self.texture = from_node
47                                         if usage:
48                                                 self.tex_usage = usage
49                                         elif alpha_from:
50                                                 self.tex_usage = 'RGBA'
51                                         elif type(self.value)==tuple:
52                                                 self.tex_usage = 'RGB'
53                                         else:
54                                                 self.tex_usage = 'GRAY'
55                                 else:
56                                         raise Exception("Unsupported property input node type "+from_node.type)
57
58 class Material:
59         def __init__(self, material):
60                 self.name = material.name
61                 self.type = None
62                 self.properties = []
63
64                 self.render_mode = material.render_mode
65                 self.technique = material.technique
66                 self.shader = material.shader
67
68                 if self.render_mode=='EXTERNAL' and not self.technique:
69                         raise Exception("Missing technique with external rendering mode")
70                 elif self.render_mode=='CUSTOM' and not self.shader:
71                         raise Exception("Missing shader with custom rendering mode")
72
73                 out_node = None
74                 for n in material.node_tree.nodes:
75                         if n.type=='OUTPUT_MATERIAL':
76                                 out_node = n
77                                 break
78
79                 if not out_node:
80                         raise Exception("No material output node found")
81
82                 surface_node, _ = get_linked_node_and_socket(material.node_tree, out_node.inputs["Surface"])
83                 if not surface_node:
84                         if self.render_mode=='BUILTIN':
85                                 raise Exception("Empty material can't use builtin rendering mode")
86                         return
87                 elif surface_node.type=='BSDF_PRINCIPLED':
88                         self.type = "pbr"
89
90                         base_color = self.create_property("base_color", (0.8, 0.8, 0.8, 1.0))
91                         metalness = self.create_property("metalness", 0.0)
92                         roughness = self.create_property("roughness", 0.5)
93                         normal = self.create_property("normal_map")
94                         emission = self.create_property("emission", (0.0, 0.0, 0.0))
95
96                         base_color.set_from_input(material.node_tree, surface_node.inputs["Base Color"], surface_node.inputs["Alpha"])
97                         metalness.set_from_input(material.node_tree, surface_node.inputs["Metallic"])
98                         roughness.set_from_input(material.node_tree, surface_node.inputs["Roughness"])
99                         normal.set_from_input(material.node_tree, surface_node.inputs["Normal"])
100                         emission.set_from_input(material.node_tree, surface_node.inputs["Emission"])
101                 elif surface_node.type=='EMISSION':
102                         self.type = "unlit"
103
104                         color = self.create_property("color", "texture", (1.0, 1.0, 1.0, 1.0))
105
106                         color.set_from_input(material.node_tree, surface_node.inputs["Color"])
107                 else:
108                         raise Exception("Unsupported surface node type "+surface_node.type)
109
110                 sampler_settings = None
111                 for p in self.properties:
112                         if p.texture:
113                                 settings = (p.texture.default_filter, p.texture.interpolation, p.texture.use_mipmap, p.texture.max_anisotropy)
114                                 if sampler_settings is None:
115                                         sampler_settings = settings
116                                 elif settings!=sampler_settings:
117                                         raise Exception("Conflicting sampler settings in material textures")
118
119         def create_property(self, *args):
120                 prop = None
121                 if len(args)==1:
122                         prop = MaterialProperty(None, args[0], None)
123                 elif len(args)==2:
124                         prop = MaterialProperty(args[0], args[0]+"_map", args[1])
125                 else:
126                         prop = MaterialProperty(*args)
127                 self.properties.append(prop)
128                 return prop
129
130
131 class MaterialAtlas:
132         def __init__(self, materials):
133                 self.render_mode = materials[0].render_mode
134                 if self.render_mode=='EXTERNAL':
135                         raise Exception("Material atlas with external render mode does not make sense")
136
137                 self.shader = materials[0].shader
138                 if self.shader:
139                         self.name = "material_atlas_"+os.path.splitext(self.shader)[0]
140                 else:
141                         self.name = "material_atlas"
142                 self.materials = materials
143                 self.material_names = [m.name for m in self.materials]
144                 for m in self.materials:
145                         if m.render_mode!=self.render_mode:
146                                 raise Exception("Conflicting render modes in MaterialAtlas constructor")
147                         if self.render_mode=='CUSTOM' and m.shader!=self.shader:
148                                 raise Exception("Conflicting shaders in MaterialAtlas constructor")
149
150                 count = len(self.materials)
151                 size = 1
152                 while size*size*2<count:
153                         size *= 2
154                 if size*size>=count:
155                         self.size = (size, size)
156                 else:
157                         self.size = (size*2, size)
158
159                 from .util import get_colormap
160
161                 cm = get_colormap(True)
162                 self.base_color_data = ""
163                 for m in map(Material, self.materials):
164                         if any(p.texture for p in m.properties):
165                                 raise Exception("Texturing is incompatible with material atlas")
166                         base_color = [int(cm(c)*255) for c in m.base_color.value]
167                         self.base_color_data += "\\x{:02X}\\x{:02X}\\x{:02X}\\xFF".format(*base_color)
168                 self.base_color_data += "\\x00\\x00\\x00\\x00"*(self.size[0]*self.size[1]-count)
169
170         def get_material_uv(self, material):
171                 index = self.material_names.index(material.name)
172                 x = index%self.size[0]
173                 y = index//self.size[0]
174                 return ((x+0.5)/self.size[0], (y+0.5)/self.size[1])
175
176 def create_material_atlas(context, material):
177         if not material.material_atlas:
178                 raise Exception("Material is not part of a material atlas")
179
180         shader = material.shader
181         materials = []
182         for m in context.blend_data.materials:
183                 if m.material_atlas and m.shader==shader:
184                         materials.append(m)
185
186         mat_map = MaterialAtlas(materials)
187
188         return mat_map