]> git.tdb.fi Git - libs/gl.git/blob - blender/io_mspgl/export_object.py
Make resource handling in the Blender exporter more flexible
[libs/gl.git] / blender / io_mspgl / export_object.py
1 import os
2 import mathutils
3
4 def linear_to_srgb(l):
5         if l<0.0031308:
6                 return 12.92*l
7         else:
8                 return 1.055*(l**(1/2.4))-0.055
9
10 def get_colormap(srgb):
11         if srgb:
12                 return linear_to_srgb
13         else:
14                 return lambda x: x
15
16 def image_name(i):
17         fp = i.filepath
18         if fp:
19                 return os.path.split(fp)[1]
20         else:
21                 return i.name
22
23
24 class ObjectExporter:
25         def __init__(self):
26                 self.show_progress = True
27                 self.use_strips = True
28                 self.use_degen_tris = False
29                 self.textures = "REF"
30                 self.separate_mesh = False
31                 self.separate_tech = False
32                 self.shared_resources = True
33                 self.export_lods = True
34
35         def compute_bounding_sphere(self, obj):
36                 p1 = max(((v.co, v.co.length) for v in obj.data.vertices), key=lambda x:x[1])[0]
37                 p2 = max(((v.co, (v.co-p1).length) for v in obj.data.vertices), key=lambda x:x[1])[0]
38                 center = (p1+p2)/2
39                 radius = (p1-p2).length/2
40                 for v in obj.data.vertices:
41                         d = v.co-center
42                         if d.length>radius:
43                                 center += d*(1-radius/d.length)/2
44                                 radius = (radius+d.length)/2
45
46                 return center, radius
47
48         def collect_object_lods(self, obj):
49                 lods = [obj]
50                 if self.export_lods:
51                         lods += sorted([c for c in obj.children if c.lod_for_parent], key=(lambda l: l.lod_index))
52                         for i, l in enumerate(lods):
53                                 if i>0 and l.lod_index!=i:
54                                         raise Exception("Inconsistent LOD indices")
55
56                 return lods
57
58         def create_mesh_exporter(self):
59                 from .export_mesh import MeshExporter
60                 mesh_export = MeshExporter()
61                 mesh_export.use_strips = self.use_strips
62                 mesh_export.use_degen_tris = self.use_degen_tris
63                 return mesh_export
64
65         def export_to_file(self, context, out_fn):
66                 obj = context.active_object
67
68                 from .util import Progress
69                 progress = Progress(self.show_progress and context)
70
71                 path, base = os.path.split(out_fn)
72                 base = os.path.splitext(base)[0]
73
74                 resources = {}
75                 self.export_object_resources(context, obj, resources, progress)
76
77                 obj_res = self.export_object(context, obj, progress, resources=resources)
78                 refs = obj_res.collect_references()
79                 if not self.shared_resources:
80                         numbers = {}
81                         for r in refs:
82                                 ext = os.path.splitext(r.name)[1]
83                                 n = numbers.get(ext, 0)
84                                 if n>0:
85                                         r.name = "{}_{}{}".format(base, n, ext)
86                                 else:
87                                         r.name = base+ext
88                                 numbers[ext] = n+1
89
90                 for r in refs:
91                         with open(os.path.join(path, r.name), "w") as out_file:
92                                 for s in r.statements:
93                                         s.write_to_file(out_file)
94
95                 with open(out_fn, "w") as out_file:
96                         for s in obj_res.statements:
97                                 s.write_to_file(out_file)
98
99         def export_object_resources(self, context, obj, resources, progress):
100                 lods = self.collect_object_lods(obj)
101
102                 from .mesh import create_mesh_from_object
103                 mesh_export = self.create_mesh_exporter()
104
105                 for i, l in enumerate(lods):
106                         lod_index = l.lod_index if l.lod_for_parent else 0
107                         progress.push_task_slice("LOD {}".format(lod_index), i, len(lods))
108
109                         mesh_name = l.data.name+".mesh"
110                         if mesh_name not in resources:
111                                 mesh = create_mesh_from_object(context, l, progress)
112                                 mesh_res = mesh_export.export_mesh(context, mesh, progress)
113                                 resources[mesh_name] = mesh_res
114
115                         material = None
116                         if l.material_slots and l.material_slots[0].material:
117                                 material = l.material_slots[0].material
118                                 mat_name = material.name+".mat"
119                                 if mat_name not in resources:
120                                         resources[mat_name] = self.export_material(material)
121
122                         tech_name = (material.name if material else l.name)+".tech"
123                         if tech_name not in resources:
124                                 resources[tech_name] = self.export_object_technique(l, resources=resources)
125
126                         progress.pop_task()
127
128         def export_object(self, context, obj, progress, *, resources=None):
129                 if resources is None:
130                         resources = {}
131                         self.export_object_resources(context, obj, resources, progress)
132
133                 lods = self.collect_object_lods(obj)
134
135                 from .datafile import Resource, Statement
136                 obj_res = Resource(obj.name+".object")
137                 statements = obj_res.statements
138
139                 center, radius = self.compute_bounding_sphere(obj)
140                 statements.append(Statement("bounding_sphere_hint", *center, radius))
141
142                 prev_mesh = None
143                 prev_tech = (None, None)
144                 for i, l in enumerate(lods):
145                         lod_st = []
146
147                         if l.data.name!=prev_mesh:
148                                 mesh_res = resources[l.data.name+".mesh"]
149                                 if self.separate_mesh:
150                                         lod_st.append(obj_res.create_reference_statement("mesh", mesh_res))
151                                 else:
152                                         lod_st.append(obj_res.create_embed_statement("mesh", mesh_res))
153
154                                 prev_mesh = l.data.name
155
156                         mat_name = None
157                         if l.material_slots and l.material_slots[0].material:
158                                 mat_name = l.material_slots[0].material.name
159
160                         tech = (l.technique, mat_name)
161                         if tech!=prev_tech:
162                                 tech_res = resources[(mat_name or l.name)+".tech"]
163                                 if l.technique:
164                                         if l.inherit_tech:
165                                                 lod_st.append(obj_res.create_embed_statement("technique", tech_res))
166                                         else:
167                                                 lod_st.append(Statement("technique", l.technique))
168                                 elif self.separate_tech:
169                                         lod_st.append(obj_res.create_reference_statement("technique", tech_res))
170                                 else:
171                                         lod_st.append(obj_res.create_embed_statement("technique", tech_res))
172
173                                 prev_tech = tech
174
175                         if i>0:
176                                 st = Statement("level_of_detail", i)
177                                 st.sub = lod_st
178                                 statements.append(st)
179                         else:
180                                 statements += lod_st
181
182                 progress.set_progress(1.0)
183
184                 return obj_res
185
186         def export_object_technique(self, obj, *, resources):
187                 material = None
188                 if obj.material_slots:
189                         material = obj.material_slots[0].material
190
191                 from .datafile import Resource, Statement, Token
192                 tech_res = Resource((material.name if material else obj.name)+".tech")
193
194                 mat_res = None
195                 if material:
196                         mat_res = resources[material.name+".mat"]
197
198                 if obj.technique:
199                         if not obj.inherit_tech:
200                                 return tech_res
201
202                         st = Statement("inherit", obj.technique)
203                         if material:
204                                 for slot in material.texture_slots:
205                                         if slot and slot.texture.type=="IMAGE":
206                                                 name = image_name(slot.texture.image)
207                                                 if slot.use_map_color_diffuse:
208                                                         st.sub.append(Statement("texture", "diffuse_map", name))
209                                                 elif slot.use_map_normal:
210                                                         st.sub.append(Statement("texture", "normal_map", name))
211                                 if obj.override_material:
212                                         st.sub.append(tech_res.create_reference_statement("material", "surface", mat_res))
213                         tech_res.statements.append(st)
214
215                         return tech_res
216
217                 pass_st = Statement("pass", "")
218                 if material:
219                         pass_st.sub.append(tech_res.create_embed_statement("material", mat_res))
220
221                         if self.textures!="NONE":
222                                 diffuse_tex = None
223                                 for slot in material.texture_slots:
224                                         if slot and slot.texture.type=="IMAGE" and slot.use_map_color_diffuse:
225                                                 diffuse_tex = slot.texture
226                                                 break
227
228                                 if diffuse_tex:
229                                         st = Statement("texunit", 0)
230                                         if self.textures=="INLINE":
231                                                 ss = Statement("texture2d")
232                                                 ss.sub.append(Statement("min_filter", Token("LINEAR")))
233                                                 ss.sub.append(Statement("storage", Token("RGBA"), tex.image.size[0], tex.image.size[1]))
234                                                 texdata = ""
235                                                 for p in tex.image.pixels:
236                                                         texdata += "\\x%02X"%int(p*255)
237                                                 ss.sub.append(Statement("raw_data", texdata))
238                                                 st.sub.append(ss)
239                                         elif tex.image:
240                                                 st.sub.append(Statement("texture", image_name(tex.image)))
241                                         pass_st.sub.append(st)
242                 tech_res.statements.append(pass_st)
243
244                 return tech_res
245
246         def export_material(self, material):
247                 from .datafile import Resource, Statement
248                 mat_res = Resource(material.name+".mat")
249                 statements = mat_res.statements
250
251                 cm = get_colormap(material.srgb_colors)
252                 if any(s.use_map_color_diffuse for s in material.texture_slots if s):
253                         statements.append(Statement("diffuse", 1.0, 1.0, 1.0, 1.0))
254                         amb = cm(material.ambient)
255                         statements.append(Statement("ambient", amb, amb, amb, 1.0))
256                 else:
257                         diff = material.diffuse_color*material.diffuse_intensity
258                         statements.append(Statement("diffuse", cm(diff.r), cm(diff.g), cm(diff.b), 1.0))
259                         amb = diff*material.ambient
260                         statements.append(Statement("ambient", cm(amb.r), cm(amb.g), cm(amb.b), 1.0))
261                 spec = material.specular_color*material.specular_intensity
262                 statements.append(Statement("specular", cm(spec.r), cm(spec.g), cm(spec.g), 1.0))
263                 statements.append(Statement("shininess", material.specular_hardness))
264
265                 return mat_res