added texture material for cycles render engine
[blender-addons-contrib.git] / io_scene_fpx / fpx_import.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20
21 ###############################################################################
22 #234567890123456789012345678901234567890123456789012345678901234567890123456789
23 #--------1---------2---------3---------4---------5---------6---------7---------
24
25
26 # ##### BEGIN COPYRIGHT BLOCK #####
27 #
28 # initial script copyright (c)2013 Alexander Nussbaumer
29 #
30 # ##### END COPYRIGHT BLOCK #####
31
32
33 #import python stuff
34 import io
35 from mathutils import (
36         Euler,
37         Vector,
38         Matrix,
39         )
40 from math import (
41         radians,
42         )
43 from os import (
44         path,
45         listdir,
46         rmdir,
47         remove,
48         )
49 from sys import (
50         exc_info,
51         )
52 from time import (
53         time,
54         )
55
56
57 # import io_scene_fpx stuff
58 if repr(globals()).find("bpy") != -1:
59     from io_scene_fpx.fpx_strings import (
60             fpx_str,
61             )
62     from io_scene_fpx.fpx_spec import (
63             Fpm_File_Reader,
64             Fpl_File_Reader,
65             Fpt_File_Reader,
66             Fpm_Model_Type,
67             Fpl_Library_Type,
68             FptElementType,
69             Fpt_PackedLibrary_Type,
70             )
71     from io_scene_fpx.fpx_ui import (
72             FpxUI,
73             )
74     from io_scene_fpx.fpx_utils import (
75             FpxUtilities,
76             )
77 else:
78     from fpx_strings import (
79             fpx_str,
80             )
81     from fpx_spec import (
82             Fpm_File_Reader,
83             Fpl_File_Reader,
84             Fpt_File_Reader,
85             Fpm_Model_Type,
86             Fpl_Library_Type,
87             FptElementType,
88             Fpt_PackedLibrary_Type,
89             )
90     from fpx_ui import (
91             FpxUI,
92             )
93     from fpx_utils import (
94             FpxUtilities,
95             )
96
97
98 #import blender stuff
99 from bpy import (
100         ops,
101         app,
102         #data,
103         )
104 import bmesh
105 from bpy_extras.image_utils import (
106         load_image,
107         )
108
109
110 ###############################################################################
111 FORMAT_SCENE = "{}.s"
112 FORMAT_GROUP = "{}.g"
113 FORMAT_IMAGE = "{}.i"
114 FORMAT_TEXTURE = "{}.tex"
115 # keep material name like it is (prevent name "snakes" on re-import)
116 #FORMAT_MATERIAL = "{}.mat"
117 FORMAT_MATERIAL = "{}"
118 FORMAT_ACTION = "{}.act"
119 FORMAT_MESH = "{}.m"
120 FORMAT_MESH_OBJECT = "{}.mo"
121 FORMAT_EMPTY_OBJECT = "{}.eo"
122 FORMAT_DUPLI_OBJECT = "{}.do"
123 FORMAT_CURVE = "{}.c"
124 FORMAT_CURVE_OBJECT = "{}.co"
125 FORMAT_ARMATURE = "{}.a"
126 FORMAT_ARMATURE_OBJECT = "{}.ao"
127 FORMAT_ARMATURE_NLA = "{}.an"
128
129 #FORMAT_RESOURCE = "{}\\{}"
130 FORMAT_RESOURCE = "{{{}}}.{}"
131
132 PREFIX_LOCAL = "1"
133 PREFIX_EMBEDDED = "0"
134
135 FORMAT_MODEL_SECONDARY = "{}.low"
136 FORMAT_MODEL_MASK = "{}.mask"
137 FORMAT_MODEL_MIRROR = "{}.mirror"
138 FORMAT_MODEL_START = "{}.start"
139 FORMAT_MODEL_END = "{}.end"
140 FORMAT_MODEL_CAP1 = "{}.cap1"
141 FORMAT_MODEL_CAP2 = "{}.cap2"
142 FORMAT_MODEL_RING = "{}.ring{:02d}"
143
144 TRANSLITE_OBJECT = 2
145 ###############################################################################
146 class FpmImporter():
147     """ Load a Future Pinball Model FPM File """
148     LAYERS_PRIMARY_MODEL = (
149             True, False, False, False, False,
150             False, False, False, False, False,
151             False, False, False, False, False,
152             False, False, False, False, False
153             )
154     LAYERS_SECONDARY_MODEL = (
155             False, True, False, False, False,
156             False, False, False, False, False,
157             False, False, False, False, False,
158             False, False, False, False, False
159             )
160     LAYERS_MASK_MODEL = (
161             False, False, True, False, False,
162             False, False, False, False, False,
163             False, False, False, False, False,
164             False, False, False, False, False
165             )
166     LAYERS_REFLECTION_MODEL = (
167             False, False, False, True, False,
168             False, False, False, False, False,
169             False, False, False, False, False,
170             False, False, False, False, False
171             )
172     LAYERS_COLLISION_MODEL = (
173             False, False, False, False, True,
174             False, False, False, False, False,
175             False, False, False, False, False,
176             False, False, False, False, False
177             )
178
179     def __init__(self,
180             report,
181             verbose=FpxUI.PROP_DEFAULT_VERBOSE,
182             keep_temp=FpxUI.PROP_DEFAULT_KEEP_TEMP,
183             use_all_models_of_folder=FpxUI.PROP_DEFAULT_ALL_MODELS,
184             use_scene_per_model=FpxUI.PROP_DEFAULT_SCENE,
185             name_extra=FpxUI.PROP_DEFAULT_NAME_EXTRA,
186             use_model_filter=FpxUI.PROP_DEFAULT_USE_MODEL_FILTER,
187             use_model_adjustment=FpxUI.PROP_DEFAULT_MODEL_ADJUST_FPM,
188             keep_name=False,
189             ):
190         self.report = report
191         self.verbose = verbose
192         self.keep_temp = keep_temp
193         self.use_all_models_of_folder = use_all_models_of_folder
194         self.use_scene_per_model = use_scene_per_model
195         self.name_extra = name_extra
196         self.use_model_filter = use_model_filter
197         self.use_model_adjustment = use_model_adjustment
198         self.keep_name = keep_name
199
200     def read(self, blender_context, filepath):
201         """ read fpm file and convert fpm content to bender content """
202         t1 = time()
203         t2 = None
204
205         fpx_reader = None
206
207         self.__context = blender_context
208         self.__blend_data = blender_context.blend_data
209
210         self.__table_width = 0.0
211         self.__table_length = 0.0
212         self.__translite_width = 0.0
213         self.__translite_length = 0.0
214
215         try:
216             self.folder_name, file_name = path.split(filepath)
217
218             debug_data = []
219
220             files = None
221             if self.use_all_models_of_folder:
222                 files = [path.join(self.folder_name, f) for f in listdir(self.folder_name) if f.endswith(".fpm")]
223             else:
224                 files = [filepath, ]
225
226             for file in files:
227                 self.folder_name, file_name = path.split(filepath)
228                 try:
229                     with io.FileIO(file, 'rb') as raw_io:
230                         # read and inject fpm data from disk to internal structure
231                         fpx_reader = Fpm_File_Reader(raw_io)
232                         fpx_reader.read_model()
233                         raw_io.close()
234                 finally:
235                     pass
236
237                 # if option is set, this time will enlarges the io time
238                 #if self.verbose and reader:
239                 #    fpx_reader.print_internal()
240                 t2 = time()
241
242                 if fpx_reader:
243                     temp_name = path.join(app.tempdir, "__grab__fpm__")
244                     dst_path, dst_sub_path_names = fpx_reader.grab_content(temp_name)
245
246                     model_name = fpx_reader.PinModel.get_value("name")
247                     model_name = FpxUtilities.toGoodName(model_name) ####
248                     #model_name = path.split(file)[1]
249                     #model_name = model_name.lower()
250                     #if model_name.endswith(".fpm"):
251                     #    model_name = model_name[:-4]
252
253                     if self.name_extra:
254                         model_name = FORMAT_RESOURCE.format(self.name_extra, model_name)
255
256
257                     model_filepath = dst_sub_path_names.get("modeldata")
258                     debug_data.append("type={}, model_filepath='{}'".format(dst_sub_path_names.get("type"), model_filepath))
259                     if model_filepath:
260                         self.read_ex(blender_context, dst_sub_path_names, model_name, debug_data)
261
262                     # cleanup
263                     if not self.keep_temp:
264                         try:
265                             rmdir(dst_path)
266                         except:
267                             pass
268
269             if self.verbose in FpxUI.VERBOSE_NORMAL:
270                 print()
271                 print("##########################################################")
272                 print("Import from FPM to Blender")
273                 for item in debug_data:
274                     print("#DEBUG", item)
275                 print("##########################################################")
276
277         except Exception as ex:
278             type, value, traceback = exc_info()
279             if self.verbose in FpxUI.VERBOSE_NORMAL:
280                 print("read fpm - exception in try block\n  type: '{0}'\n"
281                         "  value: '{1}'".format(type, value, traceback))
282
283             if t2 is None:
284                 t2 = time()
285
286             raise
287
288         else:
289             pass
290
291         finally:
292             self.__context = None
293             self.__blend_data = None
294
295         t3 = time()
296         if self.verbose in FpxUI.VERBOSE_NORMAL:
297             print(fpx_str['SUMMARY_IMPORT'].format(
298                     (t3 - t1), (t2 - t1), (t3 - t2)))
299
300         return {"FINISHED"}
301
302     ###########################################################################
303     def read_ex(self, blender_context, dst_sub_path_names, model_name, debug_data):
304         model_filepath = dst_sub_path_names.get("primary_model_data")
305         if model_filepath:
306             if self.use_scene_per_model:
307                 blender_scene = blender_context.blend_data.scenes.new(FORMAT_SCENE.format(model_name))
308                 blender_context.screen.scene = blender_scene
309             else:
310                 blender_scene = blender_context.scene
311                 # setup current Scene to default units
312                 FpxUtilities.set_scene_to_default(blender_scene)
313
314             blender_scene.layers = self.LAYERS_PRIMARY_MODEL
315             #{'FINISHED'}
316             #{'CANCELLED'}
317             if 'FINISHED' in ops.import_scene.ms3d(filepath=model_filepath, use_animation=True):
318                 name = blender_context.active_object.name
319                 src_ext = "ms3d"
320                 index = name.rfind(".{}.".format(src_ext))
321                 if index < 0:
322                     index = name.rfind(".")
323                     #if index < 0:
324                     #    return
325
326                 src_name = "{}.{}".format(name[:index], src_ext)
327
328                 remove_material(blender_context)
329                 if not self.keep_name:
330                     rename_active_ms3d(blender_context, src_name, model_name)
331
332                 if self.use_model_adjustment:
333                     adjust_position(blender_context, blender_scene, dst_sub_path_names)
334
335                 if FpxUI.USE_MODEL_FILTER_SECONDARY in self.use_model_filter:
336                     model_filepath = dst_sub_path_names.get("secondary_model_data")
337                     if model_filepath:
338                         blender_scene.layers = self.LAYERS_SECONDARY_MODEL
339                         if 'FINISHED' in ops.import_scene.ms3d(filepath=model_filepath, use_animation=False):
340                             remove_material(blender_context)
341                             if not self.keep_name:
342                                 rename_active_ms3d(blender_context, src_name, model_name, "secondary")
343
344                 if FpxUI.USE_MODEL_FILTER_MASK in self.use_model_filter:
345                     model_filepath = dst_sub_path_names.get("mask_model_data")
346                     if model_filepath:
347                         blender_scene.layers = self.LAYERS_MASK_MODEL
348                         if 'FINISHED' in ops.import_scene.ms3d(filepath=model_filepath, use_animation=False):
349                             remove_material(blender_context)
350                             if not self.keep_name:
351                                 rename_active_ms3d(blender_context, src_name, model_name, "mask")
352
353                 if FpxUI.USE_MODEL_FILTER_REFLECTION in self.use_model_filter:
354                     model_filepath = dst_sub_path_names.get("reflection_model_data")
355                     if model_filepath:
356                         blender_scene.layers = self.LAYERS_REFLECTION_MODEL
357                         if 'FINISHED' in ops.import_scene.ms3d(filepath=model_filepath, use_animation=False):
358                             remove_material(blender_context)
359                             if not self.keep_name:
360                                 rename_active_ms3d(blender_context, src_name, model_name, "reflection")
361
362                 if FpxUI.USE_MODEL_FILTER_COLLISION in self.use_model_filter:
363                     ## TODO
364                     pass
365
366                 blender_scene.layers = self.LAYERS_PRIMARY_MODEL
367
368         # setup all current 3d Views of the current scene to metric units
369         FpxUtilities.set_scene_to_metric(blender_context)
370
371         # cleanup
372         if not self.keep_temp:
373             for key, file in dst_sub_path_names.items():
374                 if key in {'type', 'sub_dir', }:
375                     continue
376                 try:
377                     remove(file)
378                 except:
379                     pass
380
381             sub_dir_path = dst_sub_path_names.get('sub_dir')
382             if sub_dir_path:
383                 try:
384                     rmdir(sub_dir_path)
385                 except:
386                     pass
387
388
389
390 ###############################################################################
391 class FplImporter():
392     """ Load a Future Pinball Library FPL File """
393     def __init__(self,
394             report,
395             verbose=FpxUI.PROP_DEFAULT_VERBOSE,
396             keep_temp=FpxUI.PROP_DEFAULT_KEEP_TEMP,
397             use_all_libraries_of_folder=FpxUI.PROP_DEFAULT_ALL_LIBRARIES,
398             use_library_filter=FpxUI.PROP_DEFAULT_USE_LIBRARY_FILTER,
399             use_model_filter=FpxUI.PROP_DEFAULT_USE_MODEL_FILTER,
400             use_model_adjustment=FpxUI.PROP_DEFAULT_MODEL_ADJUST_FPL,
401             keep_name=False,
402             ):
403         self.report = report
404         self.verbose = verbose
405         self.keep_temp = keep_temp
406         self.use_all_libraries_of_folder = use_all_libraries_of_folder
407         self.use_library_filter = use_library_filter
408         self.use_model_filter = use_model_filter
409         self.use_model_adjustment = use_model_adjustment
410         self.keep_name = keep_name
411
412     def read(self, blender_context, filepath):
413         """ read fpl file and convert fpm content to bender content """
414         t1 = time()
415         t2 = None
416
417         fpx_reader = None
418
419         self.__context = blender_context
420         self.__blend_data = blender_context.blend_data
421         active_scene = self.__context.screen.scene
422
423         try:
424             self.folder_name, file_name = path.split(filepath)
425
426             debug_data = []
427
428             if self.use_all_libraries_of_folder:
429                 files = [path.join(self.folder_name, f) for f in listdir(self.folder_name) if f.endswith(".fpl")]
430             else:
431                 files = [filepath, ]
432
433             for file in files:
434                 self.folder_name, file_name = path.split(file)
435                 try:
436                     with io.FileIO(file, 'rb') as raw_io:
437                         # read and inject fpl data from disk to internal structure
438                         fpx_reader = Fpl_File_Reader(raw_io)
439                         fpx_reader.read_library()
440                         raw_io.close()
441                 finally:
442                     pass
443
444                 # if option is set, this time will enlarges the io time
445                 #if self.verbose and reader:
446                 #    fpx_reader.print_internal()
447                 t2 = time()
448
449                 if fpx_reader:
450                     temp_name = path.join(app.tempdir, "__grab__fpl__")
451                     dst_path, dst_sub_path_names = fpx_reader.grab_content(temp_name)
452
453                     for key, item in dst_sub_path_names.items():
454                         if key is not None and key.startswith('type_'):
455                             type = item
456                             key_name = key[5:]
457                             #print("#DEBUG", key_name, type)
458
459                             if type not in self.use_library_filter:
460                                 continue
461
462                             item_path = dst_sub_path_names.get(key_name)
463
464                             if type == Fpl_Library_Type.TYPE_MODEL:
465                                 #print("#DEBUG", type, key_name)
466                                 FpmImporter(
467                                         report=self.report,
468                                         verbose=self.verbose,
469                                         keep_temp=self.keep_temp,
470                                         use_scene_per_model=True,
471                                         name_extra=file_name,
472                                         use_model_filter=self.use_model_filter,
473                                         use_model_adjustment=self.use_model_adjustment,
474                                     ).read(
475                                             blender_context=self.__context,
476                                             filepath=item_path,
477                                         )
478                                 if not self.keep_name:
479                                     rename_active_fpm(self.__context, FORMAT_RESOURCE.format(file_name, key_name))
480
481                             elif type == Fpl_Library_Type.TYPE_GRAPHIC:
482                                 #print("#DEBUG", type, key_name)
483                                 blend_image = self.__blend_data.images.load(item_path)
484                                 blend_image.name = FpxUtilities.toGoodName(FORMAT_RESOURCE.format(file_name, FORMAT_IMAGE.format(key_name)))
485                                 blend_image.pack()
486                                 blend_image.use_fake_user = True
487                                 item_dir, item_file = path.split(item_path)
488                                 blend_image.filepath_raw = "//unpacked_resource/{}".format(item_file)
489
490                         else:
491                             pass
492
493
494                     # cleanup
495                     if not self.keep_temp:
496                         cleanup_sub_dirs = []
497
498                         for key, file in dst_sub_path_names.items():
499                             if key is not None and key.startswith('sub_dir'):
500                                 cleanup_sub_dirs.append(file)
501                                 continue
502
503                             if key in {'type', None, } or key.startswith('type'):
504                                 continue
505
506                             try:
507                                 remove(file)
508                             except:
509                                 pass
510
511                         sub_dir_path = dst_sub_path_names.get('sub_dir')
512                         if sub_dir_path:
513                             try:
514                                 rmdir(sub_dir_path)
515                             except:
516                                 pass
517
518                         for sub_dir_path in cleanup_sub_dirs:
519                             try:
520                                 rmdir(sub_dir_path)
521                             except:
522                                 pass
523
524                         try:
525                             rmdir(dst_path)
526                         except:
527                             pass
528
529             if self.verbose in FpxUI.VERBOSE_NORMAL:
530                 print()
531                 print("##########################################################")
532                 print("Import from FPM to Blender")
533                 for item in debug_data:
534                     print("#DEBUG", item)
535                 print("##########################################################")
536
537         except Exception as ex:
538             type, value, traceback = exc_info()
539             if self.verbose in FpxUI.VERBOSE_NORMAL:
540                 print("read fpl - exception in try block\n  type: '{0}'\n"
541                         "  value: '{1}'".format(type, value, traceback))
542
543             if t2 is None:
544                 t2 = time()
545
546             raise
547
548         else:
549             pass
550
551         finally:
552             self.__context.screen.scene = active_scene
553             self.__context = None
554             self.__blend_data = None
555
556         t3 = time()
557         if self.verbose in FpxUI.VERBOSE_NORMAL:
558             print(fpx_str['SUMMARY_IMPORT'].format(
559                     (t3 - t1), (t2 - t1), (t3 - t2)))
560
561         return {"FINISHED"}
562
563     ###########################################################################
564
565
566 ###############################################################################
567 class FptImporter():
568     """ Load a Future Pinball Table FPT File """
569     LAYERS_WIRE_RING = (
570             True, True, False, False, True,
571             False, False, False, False, False,
572             False, False, True, False, False,
573             False, False, False, False, False
574             )
575     LAYERS_LIGHT_SPHERE = (
576             True, True, False, False, True,
577             False, False, False, False, False,
578             True, False, False, False, False,
579             False, False, False, False, False
580             )
581     BLENDER_OBJECT_NAME = 0
582
583     def __init__(self,
584             report,
585             verbose=FpxUI.PROP_DEFAULT_VERBOSE,
586             keep_temp=FpxUI.PROP_DEFAULT_KEEP_TEMP,
587             path_libraries=FpxUI.PROP_DEFAULT_LIBRARIES_PATH,
588             convert_to_mesh=FpxUI.PROP_DEFAULT_CONVERT_TO_MESH,
589             resolution_wire_bevel=FpxUI.PROP_DEFAULT_RESOLUTION_WIRE_BEVEL,
590             resolution_wire=FpxUI.PROP_DEFAULT_RESOLUTION_WIRE,
591             resolution_rubber_bevel=FpxUI.PROP_DEFAULT_RESOLUTION_RUBBER_BEVEL,
592             resolution_rubber=FpxUI.PROP_DEFAULT_RESOLUTION_RUBBER,
593             resolution_shape=FpxUI.PROP_DEFAULT_RESOLUTION_SHAPE,
594             use_hermite_handle=FpxUI.PROP_DEFAULT_USE_HERMITE_HANDLE,
595             use_library_filter=FpxUI.PROP_DEFAULT_USE_LIBRARY_FILTER,
596             use_model_filter=FpxUI.PROP_DEFAULT_USE_MODEL_FILTER,
597             use_model_adjustment=FpxUI.PROP_DEFAULT_MODEL_ADJUST_FPT,
598             keep_name=False,
599             ):
600         self.report = report
601         self.verbose = verbose
602         self.keep_temp = keep_temp
603         self.path_libraries = path_libraries
604         self.convert_to_mesh = convert_to_mesh
605         self.resolution_wire_bevel = resolution_wire_bevel
606         self.resolution_wire = resolution_wire
607         self.resolution_rubber_bevel = resolution_rubber_bevel
608         self.resolution_rubber = resolution_rubber
609         self.resolution_shape = resolution_shape
610         self.use_hermite_handle = use_hermite_handle
611         self.use_library_filter = use_library_filter
612         self.use_model_filter = use_model_filter
613         self.use_model_adjustment = use_model_adjustment
614         self.keep_name = keep_name
615
616         self.blend_resource_file = get_blend_resource_file_name()
617
618         self.debug_light_extrude = 0.2
619         self.debug_lightball_size = 2.0
620         self.debug_lightball_height = 4.5
621         self.debug_create_full_ramp_wires = False
622         self.debug_missing_resources = set()
623
624     ###########################################################################
625     # create empty blender fp_table
626     # read fpt file
627     # fill blender with fp_table content
628     def read(self, blender_context, filepath, ):
629         """ read fpt file and convert fpt content to bender content """
630         t1 = time()
631         t2 = None
632
633         fpx_reader = None
634
635         self.__context = blender_context
636         self.__blend_data = blender_context.blend_data
637
638         try:
639             try:
640                 with io.FileIO(filepath, 'rb') as raw_io:
641                     # read and inject fpt data from disk to internal structure
642                     fpx_reader = Fpt_File_Reader(raw_io)
643                     fpx_reader.read_table()
644                     raw_io.close()
645             finally:
646                 pass
647
648             # if option is set, this time will enlarges the io time
649             #if self.options.verbose and reader:
650             #    fpx_reader.print_internal()
651
652             t2 = time()
653             if fpx_reader:
654                 temp_name = path.join(app.tempdir, "__grab__fpt__")
655                 dst_path, dst_sub_path_names = fpx_reader.grab_content(temp_name)
656
657                 # setup current Scene to default units
658                 ##FpxUtilities.set_scene_to_default(self.__context.scene)
659
660                 self.folder_name, file_name = path.split(filepath)
661
662                 # search linked libraries
663                 self.fpx_images = {}
664                 self.GetLinked(fpx_reader.Image, self.fpx_images, Fpt_PackedLibrary_Type.TYPE_IMAGE, dst_sub_path_names)
665
666                 self.fpx_image_lists = {}
667                 for image_list in fpx_reader.ImageList.values():
668                     key = image_list.get_value("name")
669                     images = image_list.get_value("images")
670                     self.fpx_image_lists[key] = images
671
672                 self.fpx_pinmodels = {}
673                 self.GetLinked(fpx_reader.PinModel, self.fpx_pinmodels, Fpt_PackedLibrary_Type.TYPE_MODEL, dst_sub_path_names)
674
675                 for key, item in self.fpx_images.items():
676                     print("#DEBUG image:", key, item)
677
678                 for key, item in self.fpx_image_lists.items():
679                     print("#DEBUG image_list:", key, item)
680
681                 for key, item in self.fpx_pinmodels.items():
682                     print("#DEBUG pinmodel:", key, item)
683
684                 # build pincab
685                 self.CreatePinCab(fpx_reader.Table_Data)
686
687                 # handle table elements
688                 for key, fpx_item in fpx_reader.Table_Element.items():
689                     if fpx_item:
690                         object_appers_on = fpx_item.get_value("object_appers_on")
691                         if object_appers_on == TRANSLITE_OBJECT:
692                             continue
693                         #print("#DEBUG", object_appers_on, key, fpx_item)
694
695                         fpx_item_name = fpx_item.get_value("name")
696                         fpx_item_name = FpxUtilities.toGoodName(fpx_item_name) ####
697                         if not fpx_item_name:
698                             continue
699
700                         fpx_id = fpx_item.get_obj_value("id")
701
702                         ## get the height level (wall and/or surface) on what the item will be placed
703                         fpx_surface_name = fpx_item.get_value("surface")
704                         fpx_surface = None
705                         fpx_position_z = None
706                         fpx_position_zw = None
707                         if fpx_surface_name:
708                             fpx_wall = fpx_reader.Walls.get(FpxUtilities.toGoodName(fpx_surface_name))
709                             if fpx_wall:
710                                 fpx_position_zw = fpx_wall.get_value("height")
711                                 fpx_surface_name = fpx_wall.get_value("surface")
712                                 if fpx_surface_name:
713                                     fpx_surface = fpx_reader.Walls.get(FpxUtilities.toGoodName(fpx_surface_name))
714                             else:
715                                 fpx_surface = fpx_reader.Surfaces.get(FpxUtilities.toGoodName(fpx_surface_name))
716                             if fpx_surface:
717                                 fpx_position_z = fpx_surface.get_value("top_height")
718
719                         if fpx_position_zw is None:
720                             fpx_position_zw = 0.0
721                         if fpx_position_z is None:
722                             fpx_position_z = 0.0
723
724                         fpx_position_z += fpx_position_zw
725
726                         fpx_offset = fpx_item.get_value("offset")
727                         if fpx_offset:
728                             fpx_position_z += fpx_offset
729
730                         ## gather common information
731                         blender_object = None
732                         fpx_shape_points = fpx_item.get_value("shape_point")
733                         fpx_ramp_points =  fpx_item.get_value("ramp_point")
734                         fpx_position_xy = fpx_item.get_value("position")
735                         fpx_render_object = fpx_item.get_value("render_object")
736                         fpx_transparency = fpx_item.get_value("transparency")
737                         fpx_layer = fpx_item.get_value("layer")
738                         fpx_sphere_mapping = fpx_item.get_value("sphere_mapping")
739                         fpx_crystal = fpx_item.get_value("crystal")
740                         fpx_base = None # TODO:
741
742                         layers = self.FpxLayerToBlenderLayers(fpx_layer, fpx_id, fpx_render_object, fpx_transparency, fpx_sphere_mapping, fpx_crystal, fpx_base)
743
744                         # handle curve objects with shape_points
745                         if fpx_shape_points:
746                             if fpx_id == FptElementType.SURFACE:
747                                 blender_object = self.CreateSurface(fpx_item, fpx_item_name, layers, fpx_shape_points)
748                             elif fpx_id == FptElementType.LIGHT_SHAPEABLE:
749                                 blender_object = self.CreateLightShapeable(fpx_item, fpx_item_name, layers, fpx_shape_points, fpx_position_z)
750                             elif fpx_id == FptElementType.RUBBER_SHAPEABLE:
751                                 blender_object = self.CreateRubberShapeable(fpx_item, fpx_item_name, layers, fpx_shape_points, fpx_position_z)
752                             elif fpx_id == FptElementType.GUIDE_WALL:
753                                 blender_object = self.CreateGuideWall(fpx_item, fpx_item_name, layers, fpx_shape_points, fpx_position_z)
754                             elif fpx_id == FptElementType.GUIDE_WIRE:
755                                 blender_object = self.CreateGuideWire(fpx_item, fpx_item_name, layers, fpx_shape_points, fpx_position_z)
756                             else:
757                                 blender_object = None
758                         # handle curve objects with ramp_points
759                         elif fpx_ramp_points:
760                             if fpx_id == FptElementType.RAMP_WIRE:
761                                 blender_object = self.CreateWireRamp(fpx_item, fpx_item_name, layers, fpx_ramp_points, fpx_position_z, fpx_id)
762                             elif fpx_id == FptElementType.RAMP_RAMP:
763                                 blender_object = self.CreateRamp(fpx_item, fpx_item_name, layers, fpx_ramp_points, fpx_position_z)
764                             else:
765                                 blender_object = None
766                         else:
767                             if fpx_id == FptElementType.LIGHT_LIGHTIMAGE:
768                                 blender_object = self.CreateLightImage(fpx_item, fpx_item_name, layers, fpx_position_xy, fpx_position_z)
769                             else:
770                                 blender_object = None
771
772                         # put the just created object (curve) to its layer
773                         #if blender_object:
774                         #    blender_object.layers = layers
775
776                         if fpx_position_xy:
777                             fpx_rotation = fpx_item.get_value("rotation")
778                             if fpx_rotation:
779                                 blender_rotation = Euler((0.0, 0.0, radians(self.angle_correction(fpx_rotation))), 'XZY')
780                             else:
781                                 blender_rotation = Euler((0.0, 0.0, radians(self.angle_correction(0.0))), 'XZY')
782
783                             if fpx_id in {FptElementType.CONTROL_FLIPPER, FptElementType.CONTROL_DIVERTER, }:
784                                 fpx_start_angle = fpx_item.get_value("start_angle")
785                                 if fpx_start_angle is None:
786                                     fpx_start_angle = 0
787                                 m0 = blender_rotation.to_matrix()
788                                 m1 = Euler((0.0, 0.0, radians(self.angle_correction(fpx_start_angle) + 90)), 'XZY').to_matrix()
789                                 blender_rotation = (m0 * m1).to_euler('XZY')
790
791                             blender_position = self.geometry_correction((fpx_position_xy[0], fpx_position_xy[1], fpx_position_z))
792
793                             blender_empty_object = self.__blend_data.objects.new(FORMAT_EMPTY_OBJECT.format(fpx_item_name), None)
794                             blender_empty_object.location = blender_position
795                             blender_empty_object.rotation_mode = 'XZY'
796                             blender_empty_object.rotation_euler = blender_rotation
797                             blender_empty_object.empty_draw_type = 'ARROWS'
798                             blender_empty_object.empty_draw_size = 10.0
799                             self.__context.scene.objects.link(blender_empty_object)
800                             blender_empty_object.layers = layers
801
802                             blender_empty_object.fpt.name = fpx_item_name
803
804                             # handle model object (try to create an instance of existing group)
805                             fpx_model_name = fpx_item.get_value("model")
806                             if fpx_model_name:
807                                 fpx_model_beam_width = fpx_item.get_value("beam_width")
808                                 if fpx_model_beam_width:
809                                     offset = 3.75
810                                     self.attach_dupli_group(blender_empty_object, layers, fpx_model_name, "model", Vector((0.0 , ((fpx_model_beam_width / 2.0) + offset), 0.0)), -90)
811                                     self.attach_dupli_group(blender_empty_object, layers, fpx_model_name, "model", Vector((0.0 , -((fpx_model_beam_width / 2.0) + offset), 0.0)), 90)
812                                 else:
813                                     self.attach_dupli_group(blender_empty_object, layers, fpx_model_name, "model")
814
815                             fpx_model_name_cap = fpx_item.get_value("model_cap")
816                             if fpx_model_name_cap:
817                                 self.attach_dupli_group(blender_empty_object, layers, fpx_model_name_cap, "model_cap")
818
819                             fpx_model_name_base = fpx_item.get_value("model_base")
820                             if fpx_model_name_base:
821                                 self.attach_dupli_group(blender_empty_object, layers, fpx_model_name_base, "model_base")
822                                 self.attach_dupli_group(blender_empty_object, layers, "bumperring", 'LOCAL', Vector((0.0 , 0.0, 4.0)))
823                                 self.attach_dupli_group(blender_empty_object, layers, "bumperskirt", 'LOCAL', Vector((0.0 , 0.0, 3.5)))
824
825                             fpx_model_name_start = fpx_item.get_value("model_start")
826                             if fpx_model_name_start:
827                                 self.attach_dupli_group(blender_empty_object, layers, fpx_model_name_start, "model_start")
828
829                             fpx_model_name_end = fpx_item.get_value("model_end")
830                             if fpx_model_name_end:
831                                 self.attach_dupli_group(blender_empty_object, layers, fpx_model_name_end, "model_end")
832
833                             if fpx_id == FptElementType.RUBBER_ROUND:
834                                 blender_object = self.CreateRubberRound(fpx_item, fpx_item_name, layers, fpx_position_xy, fpx_position_z)
835
836                             if fpx_id:
837                                 blender_empty_object.fpt.id = FptElementType.VALUE_INT_TO_NAME.get(fpx_id)
838
839                             if fpx_id == FptElementType.LIGHT_ROUND:
840                                 blender_object = self.CreateLightRound(fpx_item, fpx_item_name, layers, fpx_position_xy, fpx_position_z)
841                                 if blender_object:
842                                     blender_object.layers = layers
843
844                             ## #DEBUG : light dummies
845                             if fpx_id in FptElementType.SET_LIGHT_OBJECTS: # and not blender_empty_object.children:
846                                 if ops.mesh.primitive_ico_sphere_add.poll():
847                                     blender_object = ops.mesh.primitive_ico_sphere_add(subdivisions=2, size=self.debug_lightball_size, location=blender_empty_object.location + Vector((0.0, 0.0, self.debug_lightball_height)), layers=FptImporter.LAYERS_LIGHT_SPHERE)
848
849                     # cleanup
850                     if not self.keep_temp:
851                         cleanup_sub_dirs = []
852
853                         for key, file in dst_sub_path_names.items():
854                             if key is not None and key.startswith('sub_dir'):
855                                 cleanup_sub_dirs.append(file)
856                                 continue
857
858                             if key in {'type', 'data', None, } or key.startswith('type') or key.startswith('data'):
859                                 continue
860
861                             try:
862                                 remove(file)
863                             except:
864                                 pass
865
866                         sub_dir_path = dst_sub_path_names.get('sub_dir')
867                         if sub_dir_path:
868                             try:
869                                 rmdir(sub_dir_path)
870                             except:
871                                 pass
872
873                         for sub_dir_path in cleanup_sub_dirs:
874                             try:
875                                 rmdir(sub_dir_path)
876                             except:
877                                 pass
878
879                         try:
880                             rmdir(dst_path)
881                         except:
882                             pass
883
884                 # setup all current 3d Views of the current scene to metric units
885                 FpxUtilities.set_scene_to_metric(self.__context)
886
887             if self.verbose in FpxUI.VERBOSE_NORMAL:
888                 print()
889                 print("##########################################################")
890                 print("Import from FPT to Blender")
891                 print("##########################################################")
892
893         except Exception as ex:
894             type, value, traceback = exc_info()
895             if self.verbose in FpxUI.VERBOSE_NORMAL:
896                 print("read fpt - exception in try block\n  type: '{0}'\n"
897                         "  value: '{1}'".format(type, value, traceback))
898
899             if t2 is None:
900                 t2 = time()
901
902             raise
903
904         else:
905             pass
906
907         finally:
908             self.__context = None
909             self.__blend_data = None
910
911         t3 = time()
912         if self.verbose in FpxUI.VERBOSE_NORMAL:
913             print(fpx_str['SUMMARY_IMPORT'].format(
914                     (t3 - t1), (t2 - t1), (t3 - t2)))
915
916         return {"FINISHED"}
917
918     def append_texture_material(self, blender_object, fpx_image_name, uv_layer=None):
919         fpx_image_object = self.fpx_images.get(fpx_image_name)
920         if fpx_image_object:
921             blender_image = self.__blend_data.images.get(fpx_image_object[self.BLENDER_OBJECT_NAME])
922             if blender_image:
923                 if uv_layer:
924                     bm_name = "uv_{}".format(FORMAT_MATERIAL.format(fpx_image_name))
925                 else:
926                     bm_name = "gen_{}".format(FORMAT_MATERIAL.format(fpx_image_name))
927                 blender_material = self.__blend_data.materials.get(bm_name)
928                 if not blender_material:
929                     print("#DEBUG create material", bm_name)
930                     blender_material = self.__blend_data.materials.new(bm_name)
931                     blender_material.use_transparency = True
932                     blender_material.use_raytrace = False
933                     blender_material.use_transparent_shadows = True
934                     blender_material.alpha = 0.0
935                     render_engine = self.__context.scene.render.engine
936
937                     #blender internal
938                     self.__context.scene.render.engine = 'BLENDER_RENDER'
939                     if uv_layer:
940                         bt_name = "uv_{}".format(FORMAT_TEXTURE.format(fpx_image_name))
941                     else:
942                         bt_name = "gen_{}".format(FORMAT_TEXTURE.format(fpx_image_name))
943                     blender_texture = self.__blend_data.textures.get(bt_name)
944                     if not blender_texture:
945                         print("#DEBUG create texture", bt_name)
946                         blender_texture = self.__blend_data.textures.new(bt_name, 'IMAGE')
947                         blender_texture.image = blender_image
948                     tex_slot = blender_material.texture_slots.create(0)
949                     tex_slot.texture = blender_texture
950                     tex_slot.use_map_alpha = True
951                     if uv_layer:
952                         tex_slot.texture_coords = 'UV'
953                         tex_slot.uv_layer = uv_layer
954                     #blender_material.use_shadeless = True #DEBUG
955
956                     # blender cycles
957                     self.__context.scene.render.engine = 'CYCLES'
958                     blender_material.use_nodes = True
959                     nodes = blender_material.node_tree.nodes
960                     links = blender_material.node_tree.links
961                     gap = 50.0
962                     nodes.clear()
963                     node0 = nodes.new('ShaderNodeOutputMaterial')
964                     node1 = nodes.new('ShaderNodeMixShader')
965                     node2 = nodes.new('ShaderNodeBsdfTranslucent')
966                     node2.inputs['Color'].default_value = (1.0, 1.0, 1.0, 1.0)
967                     node3 = nodes.new('ShaderNodeBsdfGlossy')
968                     node3.inputs['Roughness'].default_value = 0.25
969                     node4 = nodes.new('ShaderNodeTexImage')
970                     node4.image = blender_image
971                     node5 = nodes.new('ShaderNodeTexCoord')
972
973                     node5.location = (0.0, 0.0)
974                     node4.location = (node5.location.x + node5.width + gap, 0.0)
975                     node3.location = (node4.location.x + node4.width + gap, 0.0)
976                     node2.location = (node3.location.x + node3.width + gap, 0.0)
977                     node1.location = (node2.location.x + node2.width + gap, 0.0)
978                     node0.location = (node1.location.x + node1.width + gap, 0.0)
979
980                     link1_0 = links.new(node1.outputs['Shader'], node0.inputs['Surface'])
981                     link4_1a = links.new(node4.outputs['Alpha'], node1.inputs[0]) # Fac
982                     link2_1b = links.new(node2.outputs['BSDF'], node1.inputs[1]) # 2'nd Shader
983                     link3_1c = links.new(node3.outputs['BSDF'], node1.inputs[2]) # 1'st Shader
984                     link4_3 = links.new(node4.outputs['Color'], node3.inputs['Color'])
985                     if uv_layer:
986                         link5_4 = links.new(node5.outputs['UV'], node4.inputs['Vector'])
987                     else:
988                         link5_4 = links.new(node5.outputs['Generated'], node4.inputs['Vector'])
989                     if render_engine != 'CYCLES':
990                         blender_material.use_nodes = False
991
992                     """
993                     # blender game
994                     self.__context.scene.render.engine = 'BLENDER_GAME'
995                     #TODO
996                     """
997
998                     self.__context.scene.render.engine = render_engine
999
1000                 blender_object.data.materials.append(blender_material)
1001
1002     ###########################################################################
1003     def CreatePinCab(self, fpx_table_data):
1004         name = fpx_table_data.get_value("name")
1005         name = FpxUtilities.toGoodName(name) ####
1006
1007         dy = fpx_table_data.get_value("length")
1008         dx = fpx_table_data.get_value("width")
1009         z0 = fpx_table_data.get_value("glass_height_front")
1010         z1 = fpx_table_data.get_value("glass_height_rear")
1011         dtx = fpx_table_data.get_value("translite_height")
1012         dtz = fpx_table_data.get_value("translite_width")
1013         texture_table = fpx_table_data.get_value("playfield_texture")
1014         texture_translite = fpx_table_data.get_value("translite_image")
1015
1016         if not dtx:
1017             dtx = 676.0
1018         if not dtz:
1019             dtz = 676.0
1020
1021         self.__translite_width = dtx
1022         self.__translite_length = dtz
1023
1024         self.__table_width = dx
1025         self.__table_length = dy
1026
1027         mesh = self.__blend_data.meshes.new(FORMAT_MESH.format(name))
1028         obj = self.__blend_data.objects.new(FORMAT_MESH_OBJECT.format(name), mesh)
1029         self.__context.scene.objects.link(obj)
1030
1031         #inner playfield
1032         bm = bmesh.new()
1033         uv_layer = bm.loops.layers.uv.new("UVMap")
1034         tex_layer = bm.faces.layers.tex.new(uv_layer.name)
1035         bmv_list = []
1036         bmv = bm.verts.new(self.geometry_correction((0.0, 0.0, 0.0)))
1037         bmv_list.append(bmv)
1038         bmv = bm.verts.new(self.geometry_correction((0.0, dy, 0.0)))
1039         bmv_list.append(bmv)
1040         bmv = bm.verts.new(self.geometry_correction((dx, dy, 0.0)))
1041         bmv_list.append(bmv)
1042         bmv = bm.verts.new(self.geometry_correction((dx, 0.0, 0.0)))
1043         bmv_list.append(bmv)
1044         bmf = bm.faces.new(bmv_list)
1045         bmluv = bmf.loops[0][uv_layer]
1046         bmluv.uv = (0.0, 1.0)
1047         bmluv = bmf.loops[1][uv_layer]
1048         bmluv.uv = (0.0, 0.0)
1049         bmluv = bmf.loops[2][uv_layer]
1050         bmluv.uv = (1.0, 0.0)
1051         bmluv = bmf.loops[3][uv_layer]
1052         bmluv.uv = (1.0, 1.0)
1053         if texture_table:
1054             fpx_image_object = self.fpx_images.get(texture_table)
1055             if fpx_image_object:
1056                 bmf[tex_layer].image = self.__blend_data.images.get(fpx_image_object[self.BLENDER_OBJECT_NAME])
1057                 self.append_texture_material(obj, texture_table, uv_layer.name)
1058         bm.to_mesh(mesh)
1059         bm.free()
1060
1061         mesh_box = self.__blend_data.meshes.new(FORMAT_MESH.format("{}.playfield".format(name)))
1062         obj_box = self.__blend_data.objects.new(FORMAT_MESH_OBJECT.format("{}.playfield".format(name)), mesh_box)
1063         obj_box.parent = obj
1064         self.__context.scene.objects.link(obj_box)
1065
1066         bm = bmesh.new()
1067         #inner back
1068         bmv_list = []
1069         bmv = bm.verts.new(self.geometry_correction((0.0, 0.0, 0.0)))
1070         bmv_list.append(bmv)
1071         bmv = bm.verts.new(self.geometry_correction((dx, 0.0, 0.0)))
1072         bmv_list.append(bmv)
1073         bmv = bm.verts.new(self.geometry_correction((dx, 0.0, z1)))
1074         bmv_list.append(bmv)
1075         bmv = bm.verts.new(self.geometry_correction((0.0, 0.0, z1)))
1076         bmv_list.append(bmv)
1077         bmf = bm.faces.new(bmv_list)
1078
1079         #inner front
1080         bmv_list = []
1081         bmv = bm.verts.new(self.geometry_correction((0.0, dy, 0.0)))
1082         bmv_list.append(bmv)
1083         bmv = bm.verts.new(self.geometry_correction((0.0, dy, z0)))
1084         bmv_list.append(bmv)
1085         bmv = bm.verts.new(self.geometry_correction((dx, dy, z0)))
1086         bmv_list.append(bmv)
1087         bmv = bm.verts.new(self.geometry_correction((dx, dy, 0.0)))
1088         bmv_list.append(bmv)
1089         bmf = bm.faces.new(bmv_list)
1090
1091         #inner left
1092         bmv_list = []
1093         bmv = bm.verts.new(self.geometry_correction((0.0, 0.0, 0.0)))
1094         bmv_list.append(bmv)
1095         bmv = bm.verts.new(self.geometry_correction((0.0, 0.0, z1)))
1096         bmv_list.append(bmv)
1097         bmv = bm.verts.new(self.geometry_correction((0.0, dy, z0)))
1098         bmv_list.append(bmv)
1099         bmv = bm.verts.new(self.geometry_correction((0.0, dy, 0.0)))
1100         bmv_list.append(bmv)
1101         bmf = bm.faces.new(bmv_list)
1102
1103         #inner right
1104         bmv_list = []
1105         bmv = bm.verts.new(self.geometry_correction((dx, 0.0, 0.0)))
1106         bmv_list.append(bmv)
1107         bmv = bm.verts.new(self.geometry_correction((dx, dy, 0.0)))
1108         bmv_list.append(bmv)
1109         bmv = bm.verts.new(self.geometry_correction((dx, dy, z0)))
1110         bmv_list.append(bmv)
1111         bmv = bm.verts.new(self.geometry_correction((dx, 0.0, z1)))
1112         bmv_list.append(bmv)
1113         bmf = bm.faces.new(bmv_list)
1114
1115         bm.to_mesh(mesh_box)
1116         bm.free()
1117
1118         ##
1119         mesh_translite = self.__blend_data.meshes.new(FORMAT_MESH.format("{}.translite".format(name)))
1120         obj_translite = self.__blend_data.objects.new(FORMAT_MESH_OBJECT.format("{}.translite".format(name)), mesh_translite)
1121         obj_translite.parent = obj
1122         self.__context.scene.objects.link(obj_translite)
1123
1124         #inner translite
1125         bm = bmesh.new()
1126         uv_layer = bm.loops.layers.uv.new("UVMap")
1127         tex_layer = bm.faces.layers.tex.new(uv_layer.name)
1128         bmv_list = []
1129         bmv = bm.verts.new(self.geometry_correction(((dx - dtx) / 2.0, 0.0, z1 + 20.0)))
1130         bmv_list.append(bmv)
1131         bmv = bm.verts.new(self.geometry_correction(((dx + dtx) / 2.0, 0.0, z1 + 20.0)))
1132         bmv_list.append(bmv)
1133         bmv = bm.verts.new(self.geometry_correction(((dx + dtx) / 2.0, 0.0, z1 + 20.0 + dtz)))
1134         bmv_list.append(bmv)
1135         bmv = bm.verts.new(self.geometry_correction(((dx - dtx) / 2.0, 0.0, z1 + 20.0 + dtz)))
1136         bmv_list.append(bmv)
1137         bmf = bm.faces.new(bmv_list)
1138         bmluv = bmf.loops[0][uv_layer]
1139         bmluv.uv = (0.0, 0.0)
1140         bmluv = bmf.loops[1][uv_layer]
1141         bmluv.uv = (1.0, 0.0)
1142         bmluv = bmf.loops[2][uv_layer]
1143         bmluv.uv = (1.0, 1.0)
1144         bmluv = bmf.loops[3][uv_layer]
1145         bmluv.uv = (0.0, 1.0)
1146         if texture_translite:
1147             fpx_image_object = self.fpx_images.get(texture_translite)
1148             if fpx_image_object:
1149                 bmf[tex_layer].image = self.__blend_data.images.get(fpx_image_object[self.BLENDER_OBJECT_NAME])
1150                 self.append_texture_material(obj_translite, texture_translite, uv_layer.name)
1151         bm.to_mesh(mesh_translite)
1152         bm.free()
1153
1154         return obj
1155
1156     def CreateSurface(self, fpx_item, name, layers, fpx_points):
1157         top = fpx_item.get_value("top_height")
1158         bottom = fpx_item.get_value("bottom_height")
1159         cookie_cut = fpx_item.get_value("cookie_cut")
1160         texture = fpx_item.get_value("top_texture")
1161
1162         obj, cu, act_spline = self.create_curve(name, layers, self.resolution_shape)
1163         if texture:
1164             self.append_texture_material(obj, texture)
1165         if cookie_cut:
1166             cu.use_auto_texspace = False
1167             cu.texspace_location = Vector((self.__table_width / 2.0, self.__table_length / 2.0, 0))
1168             cu.texspace_size = Vector((self.__table_width / 2.0, self.__table_length / 2.0, 0))
1169
1170         modifier_edge_split = obj.modifiers.new("edge_split", type='EDGE_SPLIT')
1171
1172         if top is None:
1173             top = 0.0
1174         if bottom is None:
1175             bottom = 0.0
1176         cu.extrude = (top - bottom) / 2.0
1177         cu.dimensions = '2D'
1178
1179         act_spline.use_cyclic_u = True
1180         self.create_curve_points(act_spline, fpx_points)
1181
1182         obj.location = Vector((obj.location.x, obj.location.y, (top - cu.extrude)))
1183         return obj
1184
1185     def CreateRubberShapeable(self, fpx_item, name, layers, fpx_points, surface):
1186         obj, cu, act_spline = self.create_curve(name, layers, self.resolution_shape)
1187
1188         bevel_name = "__fpx_rubber_shapeable_bevel__"
1189         rubber_bevel = self.__blend_data.objects.get(bevel_name)
1190         if rubber_bevel is None:
1191             if ops.curve.primitive_bezier_circle_add.poll():
1192                 ops.curve.primitive_bezier_circle_add()
1193                 rubber_bevel = self.__context.active_object
1194                 rubber_bevel.name = bevel_name
1195                 rubber_bevel.data.dimensions = '2D'
1196                 rubber_bevel.data.resolution_u = self.resolution_rubber
1197                 rubber_bevel.data.splines[0].resolution_u = self.resolution_rubber
1198                 scale = 2.4
1199                 rubber_bevel.scale = Vector((scale,scale,scale))
1200         cu.bevel_object = rubber_bevel
1201
1202         offset = 2.5
1203         act_spline.use_cyclic_u = True
1204         self.create_curve_points(act_spline, fpx_points, (surface + offset))
1205
1206         return obj
1207
1208     def CreateRubberRound(self, fpx_item, name, layers, position_xy, surface):
1209         subtype = fpx_item.get_value("subtype")
1210
1211         #diameter = [44, 18.5, 13.5, 12, ]
1212         diameter = [13.5, 18.5, 12, 44, ]
1213
1214         bevel_name = "__fpx_guide_rubber_bevel__"
1215         wire_bevel = self.__blend_data.objects.get(bevel_name)
1216         if wire_bevel is None:
1217             cu0 = self.__blend_data.curves.new(bevel_name, 'CURVE')
1218             wire_bevel = self.__blend_data.objects.new(bevel_name, cu0)
1219             self.__context.scene.objects.link(wire_bevel)
1220             cu0.dimensions = '2D'
1221             cu0.resolution_u = self.resolution_rubber_bevel
1222
1223             h = 'AUTO'
1224             cu0.splines.new('BEZIER')
1225             p0 = Vector((0.0, 0.0, 0.0))
1226             s0 = 5.0 / 2.0
1227             spline0 = cu0.splines[-1]
1228             spline0.resolution_u = self.resolution_rubber_bevel
1229             spline0.use_cyclic_u = True
1230             spline0.bezier_points.add(3)
1231             spline0.bezier_points[0].co = p0 + Vector((0.0, -s0, 0.0))
1232             spline0.bezier_points[0].handle_left_type = h
1233             spline0.bezier_points[0].handle_right_type = h
1234             spline0.bezier_points[1].co = p0 + Vector((-s0, 0.0, 0.0))
1235             spline0.bezier_points[1].handle_left_type = h
1236             spline0.bezier_points[1].handle_right_type = h
1237             spline0.bezier_points[2].co = p0 + Vector((0.0, s0, 0.0))
1238             spline0.bezier_points[2].handle_left_type = h
1239             spline0.bezier_points[2].handle_right_type = h
1240             spline0.bezier_points[3].co = p0 + Vector((s0, 0.0, 0.0))
1241             spline0.bezier_points[3].handle_left_type = h
1242             spline0.bezier_points[3].handle_right_type = h
1243
1244         obj, cu1, spline1 = self.create_curve(name, layers, self.resolution_rubber)
1245
1246         h = 'AUTO'
1247         p1 = self.geometry_correction((position_xy[0], position_xy[1], surface + 2.5))
1248         s1 = (diameter[subtype] - 5.0) / 2.0
1249         spline1.use_cyclic_u = True
1250         spline1.resolution_u = self.resolution_rubber * 2
1251         spline1.bezier_points.add(3)
1252         spline1.bezier_points[0].co = p1 + Vector((0.0, -s1, 0.0))
1253         spline1.bezier_points[0].handle_left_type = h
1254         spline1.bezier_points[0].handle_right_type = h
1255         spline1.bezier_points[1].co = p1 + Vector((-s1, 0.0, 0.0))
1256         spline1.bezier_points[1].handle_left_type = h
1257         spline1.bezier_points[1].handle_right_type = h
1258         spline1.bezier_points[2].co = p1 + Vector((0.0, s1, 0.0))
1259         spline1.bezier_points[2].handle_left_type = h
1260         spline1.bezier_points[2].handle_right_type = h
1261         spline1.bezier_points[3].co = p1 + Vector((s1, 0.0, 0.0))
1262         spline1.bezier_points[3].handle_left_type = h
1263         spline1.bezier_points[3].handle_right_type = h
1264
1265         cu1.bevel_object = wire_bevel
1266
1267         return obj
1268
1269     def CreateGuideWire(self, fpx_item, name, layers, fpx_points, surface):
1270         height = fpx_item.get_value("height")
1271         width = fpx_item.get_value("width")
1272
1273         obj, cu, act_spline = self.create_curve(name, layers, self.resolution_wire)
1274
1275         if height is None:
1276             height = 0.0
1277         if width is not None:
1278             bevel_name = "__fpx_guide_wire_bevel_{}__".format(width)
1279             wire_bevel = self.__blend_data.objects.get(bevel_name)
1280             if wire_bevel is None:
1281                 if ops.curve.primitive_bezier_circle_add.poll():
1282                     ops.curve.primitive_bezier_circle_add()
1283                     wire_bevel = self.__context.active_object
1284                     wire_bevel.name = bevel_name
1285                     wire_bevel.data.dimensions = '2D'
1286                     wire_bevel.data.resolution_u = self.resolution_wire_bevel
1287                     wire_bevel.data.splines[0].resolution_u = self.resolution_wire_bevel
1288                     scale = width / 2.0
1289                     wire_bevel.scale = Vector((scale,scale,scale))
1290             cu.bevel_object = wire_bevel
1291             cu.use_fill_caps = True
1292         else:
1293             width = 0.0
1294
1295         act_spline.use_cyclic_u = False
1296         self.create_curve_points(act_spline, fpx_points, (surface + height + width / 2.0))
1297
1298         # create pole caps
1299         co1 = act_spline.bezier_points[0].co
1300         h_left1 = act_spline.bezier_points[0].handle_left
1301         h_right1 = act_spline.bezier_points[0].handle_right
1302         co2 = act_spline.bezier_points[-1].co
1303         h_left2 = act_spline.bezier_points[-1].handle_left
1304         h_right2 = act_spline.bezier_points[-1].handle_right
1305         self.create_wire_pole(cu.splines, co1, h_left1, h_right1, surface, width)
1306         self.create_wire_pole(cu.splines, co2, h_right2, h_left2, surface, width)
1307
1308         # merge wire curve with pole caps
1309         self.__context.scene.objects.active = obj
1310         self.merge_caps(cu.splines, width)
1311
1312         cu.splines[0].type = 'NURBS' # looks better for wires
1313         cu.twist_mode = 'MINIMUM'
1314
1315         return obj
1316
1317     def CreateGuideWall(self, fpx_item, name, layers, fpx_points, surface):
1318         height = fpx_item.get_value("height")
1319         width = fpx_item.get_value("width")
1320         cookie_cut = fpx_item.get_value("cookie_cut")
1321         texture = fpx_item.get_value("top_texture")
1322
1323         obj, cu, act_spline = self.create_curve(name, layers, self.resolution_shape)
1324         if texture:
1325             self.append_texture_material(obj, texture)
1326         if cookie_cut:
1327             cu.use_auto_texspace = False
1328             cu.texspace_location = Vector((self.__table_width / 2.0, self.__table_length / 2.0, 0))
1329             cu.texspace_size = Vector((self.__table_width / 2.0, self.__table_length / 2.0, 0))
1330
1331         modifier_solidify = obj.modifiers.new("width", type='SOLIDIFY')
1332         modifier_solidify.thickness = width
1333         modifier_solidify.offset = 0.0
1334         modifier_solidify.use_even_offset = True
1335
1336         modifier_edge_split = obj.modifiers.new("edge_split", type='EDGE_SPLIT')
1337
1338         if height is None:
1339             height = 0.0
1340         cu.extrude = height / 2.0
1341         cu.dimensions = '2D'
1342
1343         act_spline.use_cyclic_u = False
1344         self.create_curve_points(act_spline, fpx_points)
1345
1346         obj.location = Vector((obj.location.x, obj.location.y, (surface + cu.extrude)))
1347         return obj
1348
1349     def CreateLightRound(self, fpx_item, name, layers, position_xy, surface):
1350         diameter = fpx_item.get_value("diameter")
1351         cookie_cut = fpx_item.get_value("cookie_cut")
1352         texture = fpx_item.get_value("lens_texture")
1353
1354         obj, cu, spline = self.create_curve(name, layers, self.resolution_shape)
1355         if texture:
1356             self.append_texture_material(obj, texture)
1357         if cookie_cut:
1358             cu.use_auto_texspace = False
1359             cu.texspace_location = Vector((self.__table_width / 2.0, self.__table_length / 2.0, 0))
1360             cu.texspace_size = Vector((self.__table_width / 2.0, self.__table_length / 2.0, 0))
1361
1362         modifier_edge_split = obj.modifiers.new("edge_split", type='EDGE_SPLIT')
1363
1364         h = 'AUTO'
1365         p0 = self.geometry_correction((position_xy[0], position_xy[1], 0.0))
1366         d = diameter / 2.0
1367         spline.bezier_points.add(3)
1368         spline.bezier_points[0].co = p0 + Vector((0.0, -d, 0.0))
1369         spline.bezier_points[0].handle_left_type = h
1370         spline.bezier_points[0].handle_right_type = h
1371         spline.bezier_points[1].co = p0 + Vector((-d, 0.0, 0.0))
1372         spline.bezier_points[1].handle_left_type = h
1373         spline.bezier_points[1].handle_right_type = h
1374         spline.bezier_points[2].co = p0 + Vector((0.0, d, 0.0))
1375         spline.bezier_points[2].handle_left_type = h
1376         spline.bezier_points[2].handle_right_type = h
1377         spline.bezier_points[3].co = p0 + Vector((d, 0.0, 0.0))
1378         spline.bezier_points[3].handle_left_type = h
1379         spline.bezier_points[3].handle_right_type = h
1380         spline.use_cyclic_u = True
1381
1382         cu.extrude = self.debug_light_extrude
1383         cu.dimensions = '2D'
1384
1385         obj.location = Vector((obj.location.x, obj.location.y, surface))
1386         return obj
1387
1388     def CreateLightShapeable(self, fpx_item, name, layers, fpx_points, surface):
1389         cookie_cut = fpx_item.get_value("cookie_cut")
1390         texture = fpx_item.get_value("lens_texture")
1391
1392         obj, cu, act_spline = self.create_curve(name, layers, self.resolution_shape)
1393         if texture:
1394             self.append_texture_material(obj, texture)
1395         if cookie_cut:
1396             cu.use_auto_texspace = False
1397             cu.texspace_location = Vector((self.__table_width / 2.0, self.__table_length / 2.0, 0))
1398             cu.texspace_size = Vector((self.__table_width / 2.0, self.__table_length / 2.0, 0))
1399
1400         modifier_edge_split = obj.modifiers.new("edge_split", type='EDGE_SPLIT')
1401
1402         cu.extrude = self.debug_light_extrude
1403         cu.dimensions = '2D'
1404
1405         act_spline.use_cyclic_u = True
1406         self.create_curve_points(act_spline, fpx_points)
1407
1408         obj.location = Vector((obj.location.x, obj.location.y, surface))
1409         return obj
1410
1411     def CreateLightImage(self, fpx_item, name, layers, position_xy, surface):
1412         height = fpx_item.get_value("height")
1413         width = fpx_item.get_value("width")
1414         rotation = fpx_item.get_value("rotation")
1415         image_list = fpx_item.get_value("image_list")
1416
1417         mesh = self.__blend_data.meshes.new(FORMAT_MESH.format(name))
1418         obj = self.__blend_data.objects.new(FORMAT_MESH_OBJECT.format(name), mesh)
1419         self.__context.scene.objects.link(obj)
1420
1421         z = surface + self.debug_light_extrude
1422         bm = bmesh.new()
1423         uv_layer = bm.loops.layers.uv.new("UVMap")
1424         tex_layer = bm.faces.layers.tex.new(uv_layer.name)
1425         bmv_list = []
1426         bmv = bm.verts.new(self.geometry_correction((-width / 2.0, height / 2.0, 0.0)))
1427         bmv_list.append(bmv)
1428         bmv = bm.verts.new(self.geometry_correction((width / 2.0, height / 2.0, 0.0)))
1429         bmv_list.append(bmv)
1430         bmv = bm.verts.new(self.geometry_correction((width / 2.0, -height / 2.0, 0.0)))
1431         bmv_list.append(bmv)
1432         bmv = bm.verts.new(self.geometry_correction((-width / 2.0, -height / 2.0, 0.0)))
1433         bmv_list.append(bmv)
1434         bmf = bm.faces.new(bmv_list)
1435         bmluv = bmf.loops[0][uv_layer]
1436         bmluv.uv = (0.0, 1.0)
1437         bmluv = bmf.loops[1][uv_layer]
1438         bmluv.uv = (0.0, 0.0)
1439         bmluv = bmf.loops[2][uv_layer]
1440         bmluv.uv = (1.0, 0.0)
1441         bmluv = bmf.loops[3][uv_layer]
1442         bmluv.uv = (1.0, 1.0)
1443         if image_list:
1444             image_list_data = self.fpx_image_lists.get(image_list)
1445             if image_list_data:
1446                 fpx_image_name = image_list_data[0] # -1 for light on
1447                 fpx_image_object = self.fpx_images.get(fpx_image_name)
1448                 if fpx_image_object:
1449                     bmf[tex_layer].image = self.__blend_data.images.get(fpx_image_object[self.BLENDER_OBJECT_NAME])
1450                     self.append_texture_material(obj, fpx_image_name, uv_layer.name)
1451         bm.to_mesh(mesh)
1452         bm.free()
1453
1454         obj.location = self.geometry_correction((position_xy[0], position_xy[1], z))
1455         obj.rotation_mode = 'XZY'
1456         obj.rotation_euler = Euler((0.0, 0.0, radians(self.angle_correction(rotation))), obj.rotation_mode)
1457         return obj
1458
1459     def CreateWireRamp(self, fpx_item, name, layers, fpx_points, surface, fpx_id):
1460         start_height = fpx_item.get_value("start_height")
1461         end_height = fpx_item.get_value("end_height")
1462         model_name_start = fpx_item.get_value("model_start")
1463         model_name_end = fpx_item.get_value("model_end")
1464
1465         if start_height is None:
1466             start_height = 0.0
1467         if end_height is None:
1468             end_height = 0.0
1469
1470         wire_bevel = self.prepare_wire_ramp_bevel()
1471
1472         #"ramp_point"
1473         obj, cu, act_spline = self.create_curve(name, layers, self.resolution_wire)
1474
1475         cu.bevel_object = wire_bevel
1476         cu.use_fill_caps = True
1477
1478         act_spline.use_cyclic_u = False
1479         self.create_ramp_curve_points(act_spline, fpx_points, surface, start_height, end_height)
1480
1481         # ramp start
1482         if model_name_start:
1483             blender_empty_object = self.__blend_data.objects.new(FORMAT_EMPTY_OBJECT.format(FORMAT_MODEL_CAP1.format(name)), None)
1484             blender_empty_object.location = cu.splines[-1].bezier_points[0].co
1485             blender_empty_object.rotation_mode = 'XZY'
1486             v = (cu.splines[-1].bezier_points[0].handle_left - cu.splines[-1].bezier_points[0].co)
1487             blender_empty_object.rotation_euler = Euler((0, 0, Vector((v.x, v.y)).angle_signed(Vector((1.0, 0.0)))), 'XZY')
1488             blender_empty_object.empty_draw_type = 'ARROWS'
1489             blender_empty_object.empty_draw_size = 10.0
1490             self.__context.scene.objects.link(blender_empty_object)
1491             blender_empty_object.fpt.name = FORMAT_EMPTY_OBJECT.format(FORMAT_MODEL_START.format(name))
1492             if fpx_id:
1493                 blender_empty_object.fpt.id = FptElementType.VALUE_INT_TO_NAME.get(fpx_id)
1494             self.attach_dupli_group(blender_empty_object, FptImporter.LAYERS_WIRE_RING, model_name_start, "model_start")
1495             blender_empty_object.layers = FptImporter.LAYERS_WIRE_RING
1496
1497         # ramp end
1498         if model_name_end:
1499             blender_empty_object = self.__blend_data.objects.new(FORMAT_EMPTY_OBJECT.format(FORMAT_MODEL_CAP2.format(name)), None)
1500             blender_empty_object.location = cu.splines[-1].bezier_points[-1].co
1501             blender_empty_object.rotation_mode = 'XZY'
1502             v = (cu.splines[-1].bezier_points[-1].handle_right - cu.splines[-1].bezier_points[-1].co)
1503             blender_empty_object.rotation_euler = Euler((0, 0, Vector((v.x, v.y)).angle_signed(Vector((1.0, 0.0)))), 'XZY')
1504             blender_empty_object.empty_draw_type = 'ARROWS'
1505             blender_empty_object.empty_draw_size = 10.0
1506             self.__context.scene.objects.link(blender_empty_object)
1507             blender_empty_object.fpt.name = FORMAT_EMPTY_OBJECT.format(FORMAT_MODEL_END.format(name))
1508             if fpx_id:
1509                 blender_empty_object.fpt.id = FptElementType.VALUE_INT_TO_NAME.get(fpx_id)
1510             self.attach_dupli_group(blender_empty_object, FptImporter.LAYERS_WIRE_RING, model_name_end, "model_end")
1511             blender_empty_object.layers = FptImporter.LAYERS_WIRE_RING
1512
1513         # create rings
1514         wire_ring_model = [
1515                 None, # NoRing
1516                 'wirering01', # FullRing = 1
1517                 'wirering02', # OpenUp = 2
1518                 'wirering04', # OpenRight = 3
1519                 'wirering03', # OpenDown = 4
1520                 'wirering05', # OpenLeft = 5
1521                 'wirering06', # HalfLeft = 6
1522                 'wirering07', # HalfRight = 7
1523                 'wirering08', # Joiner = 8
1524                 'wirering09', # HalfDown = 9
1525                 'wirering10', # HalfUp = 10
1526                 'wirering14', # OpenUPLeft = 11
1527                 'wirering13', # OpenDownLeft = 12
1528                 'wirering12', # OpenDownRight = 13
1529                 'wirering11', # OpenUPRight = 14
1530                 'wirering15', # Split = 15
1531                 'wirering17', # QuarterRight = 16
1532                 'wirering16', # QuarterLeft = 17
1533                 'wirering19', # CresentRight = 18
1534                 'wirering18', # CresentLeft = 19
1535                 ]
1536
1537         left_wires_bevel = self.prepare_wire_ramp_side_bevel(2)
1538         right_wires_bevel = self.prepare_wire_ramp_side_bevel(3)
1539         left_upper_wires_bevel = self.prepare_wire_ramp_side_bevel(4)
1540         right_upper_wires_bevel = self.prepare_wire_ramp_side_bevel(5)
1541         top_wires_bevel = self.prepare_wire_ramp_side_bevel(6)
1542
1543         last_bezier_point = None
1544         last_fpx_point = None
1545
1546         last_left_wire = None
1547         last_right_wire = None
1548         last_left_upper_wire = None
1549         last_right_upper_wire = None
1550         last_top_wire = None
1551
1552         for index, fpx_point in enumerate(fpx_points):
1553             bezier_point = act_spline.bezier_points[index]
1554
1555             if self.debug_create_full_ramp_wires:
1556                 pass
1557             else:
1558                 """
1559                 there are problems, see [#36007] http://projects.blender.org/tracker/index.php?func=detail&aid=36007&group_id=9&atid=498
1560                 """
1561                 if index:
1562                     if last_fpx_point.get_value("left_guide"):
1563                         last_left_wire = self.create_wire_ramp_guide_piece(name, obj, layers, left_wires_bevel, 2, index, last_bezier_point, bezier_point, last_left_wire)
1564                     else:
1565                         last_left_wire = None
1566
1567                     if last_fpx_point.get_value("right_guide"):
1568                         last_right_wire = self.create_wire_ramp_guide_piece(name, obj, layers, right_wires_bevel, 3, index, last_bezier_point, bezier_point, last_right_wire)
1569                     else:
1570                         last_right_wire = None
1571
1572                     if last_fpx_point.get_value("left_upper_guide"):
1573                         last_left_upper_wire = self.create_wire_ramp_guide_piece(name, obj, layers, left_upper_wires_bevel, 4, index, last_bezier_point, bezier_point, last_left_upper_wire)
1574                     else:
1575                         last_left_upper_wire = None
1576
1577                     if last_fpx_point.get_value("right_upper_guide"):
1578                         last_right_upper_wire = self.create_wire_ramp_guide_piece(name, obj, layers, right_upper_wires_bevel, 5, index, last_bezier_point, bezier_point, last_right_upper_wire)
1579                     else:
1580                         last_right_upper_wire = None
1581
1582                     if last_fpx_point.get_value("top_wire"):
1583                         last_top_wire = self.create_wire_ramp_guide_piece(name, obj, layers, top_wires_bevel, 6, index, last_bezier_point, bezier_point, last_top_wire)
1584                     else:
1585                         last_top_wire = None
1586
1587
1588             last_bezier_point = bezier_point
1589             last_fpx_point = fpx_point
1590
1591             #fpx_point.get_value("mark_as_ramp_end_point")
1592
1593             type = fpx_point.get_value("ring_type")
1594             raw_model_name = wire_ring_model[type]
1595             if raw_model_name is None:
1596                 continue
1597
1598             blender_empty_object = self.__blend_data.objects.new(FORMAT_EMPTY_OBJECT.format(FORMAT_MODEL_RING.format(name, index)), None)
1599             blender_empty_object.location = bezier_point.co
1600             blender_empty_object.rotation_mode = 'XZY'
1601             v = (bezier_point.handle_right - bezier_point.co)
1602             blender_empty_object.rotation_euler = Euler((0, 0, Vector((v.x, v.y)).angle_signed(Vector((1.0, 0.0)))), 'XZY')
1603             blender_empty_object.empty_draw_type = 'ARROWS'
1604             blender_empty_object.empty_draw_size = 10.0
1605             self.__context.scene.objects.link(blender_empty_object)
1606             blender_empty_object.fpt.name = FORMAT_MODEL_RING.format(name, index)
1607             if fpx_id:
1608                 blender_empty_object.fpt.id = FptElementType.VALUE_INT_TO_NAME.get(fpx_id)
1609             self.attach_dupli_group(blender_empty_object, FptImporter.LAYERS_WIRE_RING, raw_model_name, 'LOCAL')
1610             blender_empty_object.layers = FptImporter.LAYERS_WIRE_RING
1611
1612         #cu.splines[0].type = 'NURBS' # looks better for wires
1613         #cu.twist_mode = 'MINIMUM'
1614         return obj
1615
1616     def create_wire_ramp_guide_piece(self, name, parent_obj, layers, wire_bevel, wire_index, point_index, last_bezier_point_template, bezier_point_template, last_object):
1617         if last_object:
1618             #reuse previouse curve
1619             spline = last_object.data.splines[0]
1620             spline.bezier_points.add(1)
1621             bezier_point = spline.bezier_points[-1]
1622             bezier_point.co = bezier_point_template.co
1623             bezier_point.radius = bezier_point_template.radius
1624             bezier_point.handle_left_type = bezier_point_template.handle_left_type
1625             bezier_point.handle_right_type = bezier_point_template.handle_right_type
1626             bezier_point.handle_left = bezier_point_template.handle_left
1627             bezier_point.handle_right = bezier_point_template.handle_right
1628             bezier_point.tilt = bezier_point_template.tilt
1629             obj = last_object
1630         else:
1631             #start to make a new curve
1632             sub_name = "{}_{}_{}".format(name, wire_index, point_index-1)
1633
1634             obj, cu, spline = self.create_curve(sub_name, layers, self.resolution_wire)
1635             obj.fpt.name = sub_name
1636             obj.parent = parent_obj
1637             cu.bevel_object = wire_bevel
1638             cu.use_fill_caps = True
1639             spline.use_cyclic_u = False
1640
1641             spline.bezier_points.add(1)
1642             bezier_point = spline.bezier_points[0]
1643             bezier_point.co = last_bezier_point_template.co
1644             bezier_point.radius = last_bezier_point_template.radius
1645             bezier_point.handle_left_type = last_bezier_point_template.handle_left_type
1646             bezier_point.handle_right_type = last_bezier_point_template.handle_right_type
1647             bezier_point.handle_left = last_bezier_point_template.handle_left
1648             bezier_point.handle_right = last_bezier_point_template.handle_right
1649             bezier_point.tilt = last_bezier_point_template.tilt
1650
1651             bezier_point = spline.bezier_points[1]
1652             bezier_point.co = bezier_point_template.co
1653             bezier_point.radius = bezier_point_template.radius
1654             bezier_point.handle_left_type = bezier_point_template.handle_left_type
1655             bezier_point.handle_right_type = bezier_point_template.handle_right_type
1656             bezier_point.handle_left = bezier_point_template.handle_left
1657             bezier_point.handle_right = bezier_point_template.handle_right
1658             bezier_point.tilt = bezier_point_template.tilt
1659
1660         return obj
1661
1662     def CreateRamp(self, fpx_item, name, layers, fpx_points, surface):
1663         start_height = fpx_item.get_value("start_height")
1664         end_height = fpx_item.get_value("end_height")
1665         start_width = fpx_item.get_value("start_width")
1666         end_width = fpx_item.get_value("end_width")
1667         height_left = fpx_item.get_value("left_side_height")
1668         height_right = fpx_item.get_value("right_side_height")
1669
1670         if start_width is None:
1671             start_width = 0.0
1672         if end_width is None:
1673             end_width = 0.0
1674
1675         if height_left is None:
1676             height_left = 0.0
1677         if height_right is None:
1678             height_right = 0.0
1679
1680         bevel_name = "__fpx_guide_ramp_wire_bevel_{}_{}_{}__".format(start_width, height_left, height_right, )
1681         wire_bevel = self.__blend_data.objects.get(bevel_name)
1682         if wire_bevel is None:
1683             cu = self.__blend_data.curves.new(bevel_name, 'CURVE')
1684             wire_bevel = self.__blend_data.objects.new(bevel_name, cu)
1685             self.__context.scene.objects.link(wire_bevel)
1686             cu.dimensions = '2D'
1687             cu.resolution_u = self.resolution_shape
1688
1689             h = 'VECTOR'
1690             cu.splines.new('BEZIER')
1691             spline0 = cu.splines[-1]
1692             spline0.resolution_u = self.resolution_shape
1693             p0 = Vector((0.0, 0.0, 0.0))
1694             spline0.use_cyclic_u = False
1695             spline0.bezier_points.add(3)
1696             spline0.bezier_points[0].co = p0 + Vector((-start_width / 2.0, height_left, 0.0))
1697             spline0.bezier_points[0].handle_left_type = h
1698             spline0.bezier_points[0].handle_right_type = h
1699             spline0.bezier_points[1].co = p0 + Vector((-start_width / 2.0, 0.0, 0.0))
1700             spline0.bezier_points[1].handle_left_type = h
1701             spline0.bezier_points[1].handle_right_type = h
1702             spline0.bezier_points[2].co = p0 + Vector((start_width / 2.0, 0.0, 0.0))
1703             spline0.bezier_points[2].handle_left_type = h
1704             spline0.bezier_points[2].handle_right_type = h
1705             spline0.bezier_points[3].co = p0 + Vector((start_width / 2.0, height_right, 0.0))
1706             spline0.bezier_points[3].handle_left_type = h
1707             spline0.bezier_points[3].handle_right_type = h
1708
1709         #"ramp_point"
1710         obj, cu, act_spline = self.create_curve(name, layers, self.resolution_wire)
1711
1712         modifier_solidify = obj.modifiers.new("solidify", type='SOLIDIFY')
1713         modifier_solidify.offset = 0.0
1714         modifier_solidify.thickness = 1.0
1715         modifier_edge_split = obj.modifiers.new("edge_split", type='EDGE_SPLIT')
1716
1717         cu.bevel_object = wire_bevel
1718         cu.use_fill_caps = False
1719
1720
1721         if start_height is None:
1722             start_height = 0.0
1723         if end_height is None:
1724             end_height = 0.0
1725
1726         self.create_ramp_curve_points(act_spline, fpx_points, surface, start_height, end_height, start_width, end_width)
1727
1728         return obj
1729
1730     def create_wire_pole(self, cu_splines, co, t, ti, surface, width):
1731         d = (t - co)
1732         dn = d.normalized()
1733         w = width / 2.0
1734         dw = dn * w
1735         co_dw = co + dw
1736
1737         cu_splines.new('BEZIER')
1738         act_spline = cu_splines[-1]
1739         act_spline.use_cyclic_u = False
1740
1741         point_0 = act_spline.bezier_points[-1]
1742         point_0.co = Vector((co_dw.x, co_dw.y, co.z - w))
1743         point_0.handle_left_type = 'ALIGNED'
1744         point_0.handle_right_type = 'ALIGNED'
1745         point_0.handle_left = Vector((point_0.co.x, point_0.co.y, point_0.co.z + w))
1746         point_0.handle_right = Vector((point_0.co.x, point_0.co.y, point_0.co.z - w))
1747
1748         act_spline.bezier_points.add()
1749         point_1 = act_spline.bezier_points[-1]
1750         point_1.co = Vector((co_dw.x, co_dw.y, surface))
1751         point_1.handle_left_type = 'ALIGNED'
1752         point_1.handle_right_type = 'ALIGNED'
1753         point_1.handle_left = Vector((point_1.co.x, point_1.co.y, point_1.co.z + (co.z - surface) / 4.0))
1754         point_1.handle_right = Vector((point_1.co.x, point_1.co.y, point_1.co.z - (co.z - surface) / 4.0))
1755
1756     def merge_caps(self, cu_splines, width):
1757         w = width / 2.0
1758
1759         # adjust endpoint of curve
1760         b_point = cu_splines[0].bezier_points[0]
1761         co = b_point.co.copy()
1762         h_left = b_point.handle_left.copy()
1763         h_right = b_point.handle_right.copy()
1764         b_point.handle_left_type = 'ALIGNED'
1765         b_point.handle_right_type = 'ALIGNED'
1766         b_point.handle_left = co + ((h_left - co).normalized() * w)
1767         b_point.handle_right = co + ((h_right - co).normalized() * w)
1768         # select endpoint of curve and start-point of pole-cap
1769         cu_splines[0].bezier_points[0].select_control_point = True
1770         cu_splines[1].bezier_points[0].select_control_point = True
1771         # merge curve an pole
1772         FpxUtilities.enable_edit_mode(True, self.__context)
1773         if ops.curve.make_segment.poll():
1774             ops.curve.make_segment()
1775         FpxUtilities.enable_edit_mode(False, self.__context)
1776
1777         # adjust endpoint of curve
1778         b_point = cu_splines[0].bezier_points[-1]
1779         co = b_point.co.copy()
1780         h_left = b_point.handle_left.copy()
1781         h_right = b_point.handle_right.copy()
1782         b_point.handle_left_type = 'ALIGNED'
1783         b_point.handle_right_type = 'ALIGNED'
1784         b_point.handle_left = co + ((h_left - co).normalized() * w)
1785         b_point.handle_right = co + ((h_right - co).normalized() * w)
1786         # select endpoint of curve and start-point of pole-cap
1787         cu_splines[0].bezier_points[-1].select_control_point = True
1788         cu_splines[1].bezier_points[0].select_control_point = True
1789         # merge curve an pole
1790         FpxUtilities.enable_edit_mode(True, self.__context)
1791         if ops.curve.make_segment.poll():
1792             ops.curve.make_segment()
1793         FpxUtilities.enable_edit_mode(False, self.__context)
1794
1795     def create_curve(self, name, layers, curve_resolution):
1796         cu = self.__blend_data.curves.new(FORMAT_CURVE.format(name), 'CURVE')
1797         obj = self.__blend_data.objects.new(FORMAT_CURVE_OBJECT.format(name), cu)
1798         self.__context.scene.objects.link(obj)
1799
1800         cu.dimensions = '3D'
1801         cu.twist_mode = 'Z_UP'
1802         cu.splines.new('BEZIER')
1803         spline = cu.splines[-1]
1804         spline.resolution_u = curve_resolution
1805         cu.resolution_u = curve_resolution
1806
1807         obj.layers = layers
1808         return obj, cu, spline
1809
1810     def create_ramp_curve_points(self, spline, fpx_points, z, z0, z1, w0=1.0, w1=1.0):
1811         ramp_length_sum = 0.0
1812         ramp_length = []
1813         last_point = None
1814         for fpx_point in fpx_points:
1815             fpx_position_xy = Vector(fpx_point.get_value("position"))
1816             if last_point:
1817                 length = (fpx_position_xy - last_point).length
1818                 ramp_length_sum += length
1819             ramp_length.append(ramp_length_sum)
1820             last_point = fpx_position_xy
1821
1822         # create_curve_points & radius
1823         spline.bezier_points.add(len(fpx_points) - 1)
1824
1825         for index, fpx_point in enumerate(fpx_points):
1826             fpx_position_xy = fpx_point.get_value("position")
1827             fpx_smooth = fpx_point.get_value("smooth")
1828
1829             factor = (ramp_length[index] / ramp_length_sum)
1830             offset = (z1 - z0) * factor
1831
1832             bezier_point = spline.bezier_points[index]
1833             bezier_point.co = self.geometry_correction((fpx_position_xy[0], fpx_position_xy[1], (z + z0 + offset)))
1834             bezier_point.radius = (w0 + ((w1 - w0) * factor)) / w0
1835
1836             if fpx_smooth:
1837                 handle_type = 'AUTO'
1838             else:
1839                 handle_type = 'VECTOR'
1840
1841             bezier_point.handle_left_type = handle_type
1842             bezier_point.handle_right_type = handle_type
1843
1844         if self.use_hermite_handle:
1845             self.set_catmull_rom_hermite_bezier_handle(spline)
1846
1847     def create_curve_points(self, spline, fpx_points, z=0.0):
1848         spline.bezier_points.add(len(fpx_points) - 1)
1849
1850         for index, fpx_point in enumerate(fpx_points):
1851             fpx_position_xy = fpx_point.get_value("position")
1852             fpx_smooth = fpx_point.get_value("smooth")
1853
1854             bezier_point = spline.bezier_points[index]
1855             bezier_point.co = self.geometry_correction((fpx_position_xy[0], fpx_position_xy[1], z))
1856
1857             if fpx_smooth:
1858                 handle_type = 'AUTO'
1859             else:
1860                 handle_type = 'VECTOR'
1861
1862             bezier_point.handle_left_type = handle_type
1863             bezier_point.handle_right_type = handle_type
1864
1865         if self.use_hermite_handle:
1866             self.set_catmull_rom_hermite_bezier_handle(spline)
1867
1868     def set_catmull_rom_hermite_bezier_handle(self, spline):
1869         if spline.type != 'BEZIER':
1870             return
1871         count_bezier_point = len(spline.bezier_points)
1872         max_index_bezier_point = count_bezier_point - 1
1873         min_index_bezier_point = 0
1874
1875         ## Catmull-Rom
1876         catmull_rom_vectors = []
1877         for index_bezier_point in range(count_bezier_point):
1878             if index_bezier_point > 0 and index_bezier_point < max_index_bezier_point:
1879                 point_prev = spline.bezier_points[index_bezier_point - 1].co
1880                 point_next = spline.bezier_points[index_bezier_point + 1].co
1881                 catmull_rom_vector = (point_next - point_prev) / 2.0
1882             elif not spline.use_cyclic_u and index_bezier_point == 0:
1883                 point_prev = spline.bezier_points[index_bezier_point].co
1884                 point_next = spline.bezier_points[index_bezier_point + 1].co
1885                 catmull_rom_vector = (point_next - point_prev) / 2.0
1886             elif not spline.use_cyclic_u and index_bezier_point == max_index_bezier_point:
1887                 point_prev = spline.bezier_points[index_bezier_point - 1].co
1888                 point_next = spline.bezier_points[index_bezier_point].co
1889                 catmull_rom_vector = (point_next - point_prev) / 2.0
1890             elif spline.use_cyclic_u and index_bezier_point == 0:
1891                 point_prev = spline.bezier_points[max_index_bezier_point].co
1892                 point_next = spline.bezier_points[index_bezier_point + 1].co
1893                 catmull_rom_vector = (point_next - point_prev) / 2.0
1894             elif spline.use_cyclic_u and index_bezier_point == max_index_bezier_point:
1895                 point_prev = spline.bezier_points[index_bezier_point - 1].co
1896                 point_next = spline.bezier_points[min_index_bezier_point].co
1897                 catmull_rom_vector = (point_next - point_prev) / 2.0
1898             else:
1899                 catmull_rom_vector = Vector()
1900             catmull_rom_vectors.append(catmull_rom_vector)
1901
1902         ## Hermite to Cubic Bezier right handles
1903         for index_bezier_point in range(count_bezier_point):
1904             bezier_point = spline.bezier_points[index_bezier_point]
1905             point = bezier_point.co
1906             if bezier_point.handle_right_type in {'VECTOR', }:
1907                 if index_bezier_point < max_index_bezier_point:
1908                     bezier_point_next = spline.bezier_points[index_bezier_point + 1]
1909                     point_next = bezier_point_next.co
1910                     catmull_rom_vector = (point_next - point) / 2.0
1911                 elif spline.use_cyclic_u and index_bezier_point == max_index_bezier_point:
1912                     bezier_point_next = spline.bezier_points[min_index_bezier_point]
1913                     point_next = bezier_point_next.co
1914                     catmull_rom_vector = (point_next - point) / 2.0
1915                 else:
1916                     bezier_point_prev = spline.bezier_points[index_bezier_point - 1]
1917                     point_prev = bezier_point_prev.co
1918                     catmull_rom_vector = (point - point_prev) / 2.0
1919             else:
1920                 catmull_rom_vector = catmull_rom_vectors[index_bezier_point]
1921             bezier_handle_point = point + (catmull_rom_vector / 3.0)
1922             bezier_point.handle_right_type = 'FREE'
1923             bezier_point.handle_right = bezier_handle_point
1924
1925         ## Hermite to Cubic Bezier left handles
1926         for index_bezier_point in range(count_bezier_point):
1927             bezier_point = spline.bezier_points[index_bezier_point]
1928             point = bezier_point.co
1929             if bezier_point.handle_left_type in {'VECTOR', }:
1930                 bezier_point.handle_left_type = 'FREE'
1931                 if index_bezier_point > 0:
1932                     bezier_point_prev = spline.bezier_points[index_bezier_point - 1]
1933                     point_prev = bezier_point_prev.co
1934                     catmull_rom_vector = (point - point_prev) / 2.0
1935                 elif spline.use_cyclic_u and index_bezier_point == min_index_bezier_point:
1936                     bezier_point_prev = spline.bezier_points[max_index_bezier_point]
1937                     point_prev = bezier_point_prev.co
1938                     catmull_rom_vector = (point - point_prev) / 2.0
1939                 else:
1940                     bezier_point_next = spline.bezier_points[index_bezier_point + 1]
1941                     point_next = bezier_point_next.co
1942                     catmull_rom_vector = (point_next - point) / 2.0
1943             else:
1944                 bezier_point.handle_left_type = 'ALIGNED'
1945                 bezier_point.handle_right_type = 'ALIGNED'
1946                 catmull_rom_vector = catmull_rom_vectors[index_bezier_point]
1947             bezier_handle_point = point - (catmull_rom_vector / 3.0)
1948             #bezier_point.handle_left_type = 'FREE'
1949             bezier_point.handle_left = bezier_handle_point
1950
1951     def create_wire_ramp_bevel(self, curve, wire_index):
1952         wire_diameter = [Vector((0.0, -2.0, 0.0)), Vector((-2.0, 0.0, 0.0)), Vector((0.0, 2.0, 0.0)), Vector((2.0, 0.0, 0.0))]
1953         wire_position = [Vector((-11.0, -2.0, 0.0)), Vector((11.0, -2.0, 0.0)), Vector((-17.0, 11, 0.0)), Vector((17.0, 11.0, 0.0)), Vector((-11.0, 24.0, 0.0)), Vector((11.0, 24.0, 0.0)), Vector((0.0, 33.0, 0.0))]
1954         w0 = Vector((0.0, 0.0, 0.0))
1955         handle = 'AUTO'
1956         curve.splines.new('BEZIER')
1957         p0 = wire_position[wire_index] + w0
1958         spline = curve.splines[-1]
1959         spline.resolution_u = self.resolution_wire_bevel
1960         spline.use_cyclic_u = True
1961         spline.bezier_points.add(3)
1962         spline.bezier_points[0].co = p0 + wire_diameter[0]
1963         spline.bezier_points[0].handle_left_type = handle
1964         spline.bezier_points[0].handle_right_type = handle
1965         spline.bezier_points[1].co = p0 + wire_diameter[1]
1966         spline.bezier_points[1].handle_left_type = handle
1967         spline.bezier_points[1].handle_right_type = handle
1968         spline.bezier_points[2].co = p0 + wire_diameter[2]
1969         spline.bezier_points[2].handle_left_type = handle
1970         spline.bezier_points[2].handle_right_type = handle
1971         spline.bezier_points[3].co = p0 + wire_diameter[3]
1972         spline.bezier_points[3].handle_left_type = handle
1973         spline.bezier_points[3].handle_right_type = handle
1974
1975     def prepare_wire_ramp_bevel(self):
1976         bevel_name = "__fpx_guide_ramp_wire_bevel__"
1977         wire_bevel = self.__blend_data.objects.get(bevel_name)
1978         if wire_bevel is None:
1979             cu = self.__blend_data.curves.new(bevel_name, 'CURVE')
1980             wire_bevel = self.__blend_data.objects.new(bevel_name, cu)
1981             self.__context.scene.objects.link(wire_bevel)
1982             cu.dimensions = '2D'
1983             cu.resolution_u = self.resolution_wire_bevel
1984
1985             self.create_wire_ramp_bevel(cu, 0) # base left inner
1986             self.create_wire_ramp_bevel(cu, 1) # base right inner
1987
1988             if self.debug_create_full_ramp_wires:
1989                 """
1990                 there are problems, see [#36007] http://projects.blender.org/tracker/index.php?func=detail&aid=36007&group_id=9&atid=498
1991                 """
1992                 self.create_wire_ramp_bevel(cu, 2) # left inner
1993                 self.create_wire_ramp_bevel(cu, 3) # right inner
1994                 self.create_wire_ramp_bevel(cu, 4) # upper left inner
1995                 self.create_wire_ramp_bevel(cu, 5) # upper right inner
1996                 self.create_wire_ramp_bevel(cu, 6) # top outer
1997             else:
1998                 pass
1999
2000         return wire_bevel
2001
2002     def prepare_wire_ramp_side_bevel(self, wire_index):
2003         bevel_name = "__fpx_guide_ramp_wire_bevel_{}__".format(wire_index)
2004         wire_bevel = self.__blend_data.objects.get(bevel_name)
2005         if wire_bevel is None:
2006             cu = self.__blend_data.curves.new(bevel_name, 'CURVE')
2007             wire_bevel = self.__blend_data.objects.new(bevel_name, cu)
2008             self.__context.scene.objects.link(wire_bevel)
2009             cu.dimensions = '2D'
2010             cu.resolution_u = self.resolution_wire_bevel
2011
2012             self.create_wire_ramp_bevel(cu, wire_index)
2013
2014         return wire_bevel
2015
2016     ###########################################################################
2017     def geometry_correction(self, value):
2018         return Vector((value[0], -value[1], value[2]))
2019
2020     def angle_correction(self, value):
2021         return -90 - value
2022
2023     def attach_dupli_group(self, blender_empty_object, layers, fpx_model_name, fpx_type_name, offset=Vector(), angle=0):
2024         fpx_model_name = FpxUtilities.toGoodName(fpx_model_name) ####
2025         if fpx_model_name in self.debug_missing_resources:
2026             return
2027
2028         if fpx_type_name == 'RAW':
2029             blender_group_name = FpxUtilities.toGoodName(FORMAT_GROUP.format(fpx_model_name))
2030             self.LoadObjectLite(blender_group_name, Fpt_PackedLibrary_Type.TYPE_MODEL)
2031         if fpx_type_name == 'LOCAL':
2032             blender_group_name = FpxUtilities.toGoodName(FORMAT_RESOURCE.format(PREFIX_LOCAL, FORMAT_GROUP.format(fpx_model_name)))
2033             self.LoadObjectLite(blender_group_name, Fpt_PackedLibrary_Type.TYPE_MODEL)
2034         else:
2035             fpx_model_object = self.fpx_pinmodels.get(fpx_model_name)
2036             if not fpx_model_object:
2037                 if self.verbose in FpxUI.VERBOSE_NORMAL:
2038                     print("#DEBUG attach_dupli_group, fpx_pinmodel not found!", fpx_model_name, fpx_type_name)
2039                     self.debug_missing_resources.add(fpx_model_name)
2040                 return
2041             blender_group_name = fpx_model_object[self.BLENDER_OBJECT_NAME]
2042
2043         if self.__blend_data.groups.get(blender_group_name):
2044             blender_empty_object_new = self.__blend_data.objects.new(FORMAT_DUPLI_OBJECT.format(blender_empty_object.name), None)
2045             blender_empty_object_new.location = offset
2046             blender_empty_object_new.rotation_mode = blender_empty_object.rotation_mode
2047             blender_empty_object_new.rotation_euler = Euler((0, 0, radians(angle)), blender_empty_object.rotation_mode)
2048             blender_empty_object_new.empty_draw_type = blender_empty_object.empty_draw_type
2049             blender_empty_object_new.empty_draw_size = blender_empty_object.empty_draw_size
2050             self.__context.scene.objects.link(blender_empty_object_new)
2051
2052             blender_empty_object_new.parent = blender_empty_object
2053
2054             blender_empty_object_new.dupli_type = 'GROUP'
2055             blender_empty_object_new.dupli_group = self.__blend_data.groups[blender_group_name]
2056
2057             blender_empty_object_new.layers = layers
2058         else:
2059             print("#DEBUG attach_dupli_group, blender_group not found!", fpx_model_name, fpx_type_name, blender_group_name)
2060             self.debug_missing_resources.add(fpx_model_name)
2061
2062         # add name to fpt property
2063         blender_empty_object.fpt.add_model(fpx_type_name, fpx_model_name)
2064
2065     ###########################################################################
2066     def FpxLayerToBlenderLayers(self, layer, id=None, render=None, alpha=None, sphere_mapping=None, crystal=None, base=None):
2067         if layer is None:
2068             layer = 0x000
2069
2070         if id is None:
2071             id = 0
2072
2073         if render is None:
2074             render = True
2075
2076         if alpha is None:
2077             alpha = 10
2078
2079         if crystal is None:
2080             crystal = False
2081
2082         if base is None:
2083             base = False
2084
2085         layers = []
2086         for index in range(20):
2087             # layers, left block, top row
2088             if index == 0:
2089                 layers.append(True)
2090             elif index == 1 and (render and alpha > 0): # visible objects
2091                 layers.append(True)
2092             elif index == 2 and (not render or alpha <= 0): # invisible objects
2093                 layers.append(True)
2094             elif index == 3 and id in FptElementType.SET_CURVE_OBJECTS: # curve object
2095                 layers.append(True)
2096             elif index == 4 and id in FptElementType.SET_MESH_OBJECTS: # mesh object
2097                 layers.append(True)
2098
2099             # layers, left block, bottom row
2100             elif index == 10 and id in FptElementType.SET_LIGHT_OBJECTS: # light object
2101                 layers.append(True)
2102             elif index == 11 and id in FptElementType.SET_RUBBER_OBJECTS: # rubber object
2103                 layers.append(True)
2104             elif index == 12 and ((id in FptElementType.SET_SPHERE_MAPPING_OBJECTS and sphere_mapping) or id in FptElementType.SET_WIRE_OBJECTS): # chrome object
2105                 layers.append(True)
2106             elif index == 13 and (crystal or (alpha > 0 and alpha < 10)): # crystal and transparent but visible objects
2107                 layers.append(True)
2108             elif index == 14: # TODO: base mesh object
2109                 layers.append(False)
2110
2111             # layers, right block, top row
2112             elif index == 5 and (layer & 0x002) == 0x002: # layer 2
2113                 layers.append(True)
2114             elif index == 6 and (layer & 0x008) == 0x008: # layer 4
2115                 layers.append(True)
2116             elif index == 7 and (layer & 0x020) == 0x020: # layer 6
2117                 layers.append(True)
2118             elif index == 8 and (layer & 0x080) == 0x080: # layer 8
2119                 layers.append(True)
2120             elif index == 9 and (layer & 0x200) == 0x200: # layer 0
2121                 layers.append(True)
2122
2123             # layers, right block, bottom row
2124             elif index == 15 and (layer & 0x001) == 0x001: # layer 1
2125                 layers.append(True)
2126             elif index == 16 and (layer & 0x004) == 0x004: # layer 3
2127                 layers.append(True)
2128             elif index == 17 and (layer & 0x010) == 0x010: # layer 5
2129                 layers.append(True)
2130             elif index == 18 and (layer & 0x040) == 0x040: # layer 7
2131                 layers.append(True)
2132             elif index == 19 and (layer & 0x100) == 0x100: # layer 9
2133                 layers.append(True)
2134
2135             else:
2136                 layers.append(False)
2137         return tuple(layers)
2138
2139     def LoadObjectLite(self, name, type):
2140         """
2141         name: 'fpmodel.fpl\\<object>.g', 'fpmodel.fpl\\<object>.i'
2142         type: 'IMAGE', 'MODEL'
2143         """
2144         obj = self.LoadFromEmbedded(name, type)
2145
2146         if not obj:
2147             obj = self.LoadFromBlendLibrary(name, type, self.blend_resource_file)
2148
2149         if not obj:
2150             print("#DEBUG resource finally not found", name, type, lib)
2151
2152         return obj
2153
2154     def LoadObject(self, name, type, lib):
2155         """
2156         name: 'fpmodel.fpl\\<object>.g', 'fpmodel.fpl\\<object>.i'
2157         type: 'IMAGE', 'MODEL'
2158         lib_name: fpmodel.fpl
2159         path_name: '.\\', 'C:\\FuturePinball\\Libraries\\', '<addons>\\io_scene_fpx\\'
2160         """
2161         #print("#DEBUG LoadObject", name, type, lib)
2162
2163         obj = self.LoadFromEmbedded(name, type)
2164
2165         if not obj and lib:
2166             obj = self.LoadFromPathLibrary(name, type, lib, self.folder_name)
2167
2168         if not obj:
2169             obj = self.LoadFromBlendLibrary(name, type, self.blend_resource_file)
2170
2171         if not obj and lib:
2172             obj = self.LoadFromPathLibrary(name, type, lib, self.path_libraries)
2173
2174         if not obj:
2175             print("#DEBUG resource finally not found", name, type, lib)
2176
2177         return obj
2178
2179     def LoadFromEmbedded(self, name, type):
2180         #print("#DEBUG LoadFromEmbedded", name, type)
2181
2182         if type == Fpt_PackedLibrary_Type.TYPE_IMAGE:
2183             data_from_dict = self.__blend_data.images
2184         elif type == Fpt_PackedLibrary_Type.TYPE_MODEL:
2185             #print("#DEBUG LoadFromEmbedded groups", [g for g in self.__blend_data.groups])
2186             data_from_dict = self.__blend_data.groups
2187         else:
2188             return None
2189         return data_from_dict.get(name)
2190
2191     def LoadFromBlendLibrary(self, name, type, file):
2192         """
2193         name: 'fpmodel.fpl\\<object>.g'
2194         type: 'IMAGE', 'MODEL'
2195         file: '<addons>\\io_scene_fpx\\fpx_resource.blend'
2196         """
2197         #print("#DEBUG LoadFromBlendLibrary", name, type, file)
2198
2199         with self.__blend_data.libraries.load(file) as (data_from, data_to):
2200             # imports data from library
2201             if type == Fpt_PackedLibrary_Type.TYPE_IMAGE:
2202                 data_from_list = data_from.images
2203                 name_temp = name
2204
2205                 if name_temp not in data_from.images:
2206                     return None
2207                 # embed image data from library
2208                 data_to.images = [name_temp, ]
2209
2210             elif type == Fpt_PackedLibrary_Type.TYPE_MODEL:
2211                 if name.endswith(".g"):
2212                     name_scene = "{}.s".format(name[:-2])
2213                     if name_scene not in data_from.scenes:
2214                         return None
2215                     # embed scene data from library
2216                     data_to.scenes = [name_scene, ]
2217
2218                 if name not in data_from.groups:
2219                     return None
2220                 # embed group data from library
2221                 data_to.groups = [name, ]
2222
2223             else:
2224                 return None
2225
2226         # try to get internal data
2227         return self.LoadFromEmbedded(name, type)
2228
2229     def LoadFromPathLibrary(self, name, type, lib, folder):
2230         """
2231         name: 'fpmodel.fpl\\<object>.g'
2232         type: 'IMAGE', 'MODEL'
2233         lib:  'fpmodel.fpl'
2234         folder: '.\\'
2235         """
2236         #print("#DEBUG LoadFromPathLibrary", name, type, lib, folder)
2237
2238         filepath = path.join(folder, lib)
2239         if path.exists(filepath):
2240             FplImporter(
2241                     report=self.report,
2242                     verbose=self.verbose,
2243                     keep_temp=self.keep_temp,
2244                     use_library_filter=self.use_library_filter,
2245                     use_model_filter=self.use_model_filter,
2246                     use_model_adjustment=self.use_model_adjustment,
2247                     keep_name=False,
2248                     ).read(
2249                             self.__context,
2250                             filepath,
2251                             )
2252
2253             return self.LoadFromEmbedded(name, type)
2254
2255         return None
2256
2257     def GetLinked(self, dictIn, dictOut, type, dst_sub_path_names):
2258         """
2259         loads external resources
2260         """
2261         if type not in self.use_library_filter:
2262             return
2263
2264         for key, fpx_item in dictIn.items():
2265             if not fpx_item:
2266                 continue
2267
2268             fpx_item_name = fpx_item.get_value("name")
2269             fpx_item_name = FpxUtilities.toGoodName(fpx_item_name) ####
2270
2271             if type == Fpt_PackedLibrary_Type.TYPE_IMAGE:
2272                 blender_resource_name_format = FORMAT_IMAGE.format(FORMAT_RESOURCE)
2273             elif type == Fpt_PackedLibrary_Type.TYPE_MODEL:
2274                 blender_resource_name_format = FORMAT_GROUP.format(FORMAT_RESOURCE)
2275             else:
2276                 ## TODO
2277                 continue
2278
2279             lib_name = None
2280
2281             if fpx_item.get_value("linked"):
2282                 #print("#DEBUG GetLinked linked > ", type, fpx_item_name)
2283
2284                 linked_path = fpx_item.get_value("linked_path").lower()
2285                 library_file, file_name = linked_path.split(Fpt_File_Reader.SEPARATOR)
2286                 library_file = FpxUtilities.toGoodName(library_file)
2287                 file_name = FpxUtilities.toGoodName(file_name)
2288
2289                 lib_name = library_file
2290
2291                 if file_name.endswith(".fpm") or file_name.endswith(".jpg") or file_name.endswith(".bmp") or file_name.endswith(".tga"):
2292                     file_name = file_name[:-4]
2293
2294                 if library_file == "":
2295                     prefix_library_file = PREFIX_LOCAL
2296                 else:
2297                     prefix_library_file = library_file
2298
2299                 blender_resource_name = FpxUtilities.toGoodName(blender_resource_name_format.format(prefix_library_file, file_name))
2300
2301                 dictOut[fpx_item_name] = [ blender_resource_name, ]
2302
2303             else:
2304                 #print("#DEBUG GetLinked intern > ", type, fpx_item_name)
2305
2306                 blender_resource_name = FpxUtilities.toGoodName(blender_resource_name_format.format(PREFIX_EMBEDDED, fpx_item_name))
2307                 dictOut[fpx_item_name] = [ blender_resource_name, ]
2308                 item_data = dst_sub_path_names.get("data_{}".format(fpx_item_name))
2309                 if item_data:
2310                     #print("#DEBUG", fpx_item_name, item_data[1]["sub_dir"])
2311                     active_scene = self.__context.screen.scene
2312                     FpmImporter(
2313                             report=self.report,
2314                             verbose=self.verbose,
2315                             keep_temp=self.keep_temp,
2316                             use_scene_per_model=True,
2317                             name_extra="",
2318                             use_model_filter=self.use_model_filter,
2319                             use_model_adjustment=self.use_model_adjustment,
2320                             keep_name=False,
2321                         ).read_ex(
2322                                 blender_context=self.__context,
2323                                 dst_sub_path_names=item_data[1],
2324                                 model_name=FpxUtilities.toGoodName(FORMAT_RESOURCE.format(PREFIX_EMBEDDED, fpx_item_name)),
2325                                 debug_data=[],
2326                                 )
2327                     #rename_active_fpm(self.__context, blender_resource_name)
2328                     self.__context.screen.scene = active_scene
2329
2330             self.LoadObject(blender_resource_name, type, lib_name)
2331
2332     ###########################################################################
2333
2334 ###############################################################################
2335 def get_min_max(blender_object):
2336     min_x = max_x = min_y = max_y = min_z = max_z = None
2337
2338     for vert in blender_object.data.vertices:
2339         x, y, z = vert.co
2340
2341         if min_x is None:
2342             min_x = max_x = x
2343             min_y = max_y = y
2344             min_z = max_z = z
2345             continue
2346
2347         if x < min_x:
2348             min_x = x
2349         elif x > max_x:
2350             max_x = x
2351
2352         if y < min_y:
2353             min_y = y
2354         elif y > max_y:
2355             max_y = y
2356
2357         if z < min_z:
2358             min_z = z
2359         elif z > max_z:
2360             max_z = z
2361
2362     return min_x, min_y, min_z, max_x, max_y, max_z
2363
2364 def adjust_position(blender_context, blender_scene, fpx_model, fpx_model_type=None):
2365     if not blender_context.active_object:
2366         return
2367     fpx_type = fpx_model.get("type")
2368     has_mask = fpx_model.get("mask_model_data")
2369
2370     blender_object = blender_context.active_object
2371     blender_parent = blender_object.parent
2372
2373     # Fpm_Model_Type.OBJECT_OBJECT = 0
2374     ## Fpm_Model_Type.OBJECT_PEG = 1
2375     ## Fpm_Model_Type.OBJECT_ORNAMENT = 8
2376
2377     # Fpm_Model_Type.CONTROL_BUMPER_BASE = 3
2378     # Fpm_Model_Type.CONTROL_BUMPER_CAP = 4
2379
2380     ## Fpm_Model_Type.CONTROL_FLIPPER = 2
2381     # Fpm_Model_Type.CONTROL_PLUNGER = 7
2382     # Fpm_Model_Type.CONTROL_KICKER = 9
2383     # Fpm_Model_Type.CONTROL_DIVERTER = 18
2384     # Fpm_Model_Type.CONTROL_AUTOPLUNGER = 19
2385     # Fpm_Model_Type.CONTROL_POPUP = 20
2386     # Fpm_Model_Type.CONTROL_EMKICKER = 24
2387
2388     # Fpm_Model_Type.TARGET_TARGET = 5
2389     # Fpm_Model_Type.TARGET_DROP = 6
2390     # Fpm_Model_Type.TARGET_VARI = 26
2391
2392     # Fpm_Model_Type.TRIGGER_TRIGGER = 12
2393     # Fpm_Model_Type.TRIGGER_GATE = 15
2394     # Fpm_Model_Type.TRIGGER_SPINNER = 16
2395     # Fpm_Model_Type.TRIGGER_OPTO = 25
2396
2397     ## Fpm_Model_Type.LIGHT_FLASHER = 13
2398     ## Fpm_Model_Type.LIGHT_BULB = 14
2399
2400     ## Fpm_Model_Type.TOY_SPINNINGDISK = 23
2401     ## Fpm_Model_Type.TOY_CUSTOM = 17
2402
2403     # Fpm_Model_Type.GUIDE_LANE = 10
2404     # Fpm_Model_Type.RUBBER_MODEL = 11
2405     # Fpm_Model_Type.RAMP_MODEL = 21
2406     # Fpm_Model_Type.RAMP_WIRE_CAP = 22
2407
2408     # fpx_model_type=None
2409     # fpx_model_type="lo"
2410     # fpx_model_type="ma"
2411     # fpx_model_type="mi"
2412
2413     # bumper ring = top + 31
2414     #
2415     #
2416
2417     blender_location = None
2418     if fpx_type in {
2419             Fpm_Model_Type.OBJECT_PEG, #
2420             Fpm_Model_Type.OBJECT_ORNAMENT, # mask
2421             Fpm_Model_Type.CONTROL_BUMPER_BASE, #
2422             Fpm_Model_Type.CONTROL_FLIPPER, #
2423             Fpm_Model_Type.CONTROL_DIVERTER, #
2424             Fpm_Model_Type.CONTROL_AUTOPLUNGER,
2425             Fpm_Model_Type.CONTROL_KICKER, #
2426             Fpm_Model_Type.LIGHT_FLASHER, # mask
2427             Fpm_Model_Type.LIGHT_BULB, #
2428             Fpm_Model_Type.TRIGGER_OPTO, #
2429             Fpm_Model_Type.TOY_CUSTOM,
2430             }:
2431         static_offset = 0
2432         if has_mask:
2433             #align to top
2434             #blender_location = Vector((blender_parent.location.x, blender_parent.location.y, -blender_object.dimensions.z / 2.0))
2435             blender_location = Vector((blender_parent.location.x, blender_parent.location.y, get_min_max(blender_object)[2] + static_offset))
2436         else:
2437             # align to bottom
2438             #blender_location = Vector((blender_parent.location.x, blender_parent.location.y, blender_object.dimensions.z / 2.0))
2439             blender_location = Vector((blender_parent.location.x, blender_parent.location.y, get_min_max(blender_object)[5] + static_offset))
2440
2441     elif fpx_type in {
2442             Fpm_Model_Type.CONTROL_BUMPER_CAP, #
2443             }:
2444         # align to bottom + static offset
2445         static_offset = 31.5
2446         #blender_location = Vector((blender_parent.location.x, blender_parent.location.y, (blender_object.dimensions.z / 2.0) + static_offset))
2447         blender_location = Vector((blender_parent.location.x, blender_parent.location.y, get_min_max(blender_object)[5] + static_offset))
2448     elif fpx_type in {
2449             Fpm_Model_Type.TOY_SPINNINGDISK, #
2450             }:
2451         # align to top + static offset
2452         static_offset = 0.25
2453         #blender_location = Vector((blender_parent.location.x, blender_parent.location.y, (-blender_object.dimensions.z / 2.0) + static_offset))
2454         blender_location = Vector((blender_parent.location.x, blender_parent.location.y, get_min_max(blender_object)[2] + static_offset))
2455     elif fpx_type in {
2456             Fpm_Model_Type.TRIGGER_TRIGGER, #
2457             }:
2458         # dont touch
2459         pass
2460     elif fpx_type in {
2461             Fpm_Model_Type.TRIGGER_SPINNER, #
2462             }:
2463         # static offset
2464         static_offset = 3
2465         blender_location = Vector((blender_parent.location.x, blender_parent.location.y, blender_parent.location.z + static_offset))
2466     elif fpx_type in {
2467             Fpm_Model_Type.TRIGGER_GATE, #
2468             }:
2469         # align to top + static offset
2470         static_offset = 6
2471         #blender_location = Vector((blender_parent.location.x, blender_parent.location.y, (-blender_object.dimensions.z / 2.0) + static_offset))
2472         blender_location = Vector((blender_parent.location.x, blender_parent.location.y, get_min_max(blender_object)[2] + static_offset))
2473     elif fpx_type in {
2474             Fpm_Model_Type.CONTROL_POPUP, #
2475             }:
2476         # align to top
2477         static_offset = 0
2478         #blender_location = Vector((blender_parent.location.x, blender_parent.location.y, (-blender_object.dimensions.z / 2.0) + static_offset))
2479         blender_location = Vector((blender_parent.location.x, blender_parent.location.y, get_min_max(blender_object)[2] + static_offset))
2480     elif fpx_type in {
2481             Fpm_Model_Type.TARGET_DROP, #
2482             }:
2483         # static offset
2484         static_offset = 12.5
2485         blender_location = Vector((blender_parent.location.x, blender_parent.location.y, blender_parent.location.z + static_offset))
2486     elif fpx_type in {
2487             Fpm_Model_Type.TARGET_TARGET, #
2488             }:
2489         # static offset
2490         static_offset = 15
2491         blender_location = Vector((blender_parent.location.x, blender_parent.location.y, blender_parent.location.z + static_offset))
2492     elif fpx_type in {
2493             Fpm_Model_Type.RAMP_WIRE_CAP, #
2494             }:
2495         # static offset
2496         static_offset = 13
2497         blender_location = Vector((blender_parent.location.x, blender_parent.location.y, blender_parent.location.z + static_offset))
2498     elif fpx_type in {
2499             Fpm_Model_Type.OBJECT_OBJECT, # wire ring
2500             }:
2501         # static offset
2502         static_offset = 11
2503         blender_location = Vector((blender_parent.location.x, blender_parent.location.y, blender_parent.location.z + static_offset))
2504     elif fpx_type in {
2505             Fpm_Model_Type.RUBBER_MODEL, #
2506             Fpm_Model_Type.GUIDE_LANE, #
2507             Fpm_Model_Type.RAMP_MODEL, #
2508             }:
2509         # align to bottom + static offset
2510         static_offset = 0
2511         blender_location = Vector((blender_parent.location.x, blender_parent.location.y, get_min_max(blender_object)[5] + static_offset))
2512     else:
2513         pass
2514
2515     if blender_location:
2516         blender_scene.objects.active = blender_parent
2517         blender_scene.objects.active.location = blender_location
2518         blender_scene.update()
2519
2520 def remove_material(blender_context):
2521     if not blender_context.active_object:
2522         return
2523     active_object = blender_context.active_object
2524     blender_data = blender_context.blend_data
2525
2526     used_materials = []
2527     used_textures = []
2528     used_images = []
2529
2530     for material_slot in active_object.material_slots:
2531         if not material_slot or not material_slot.material:
2532             continue
2533         material = material_slot.material
2534         used_materials.append(material)
2535         for texture_slot in material.texture_slots:
2536             if not texture_slot or not texture_slot.texture:
2537                 continue
2538             texture = texture_slot.texture
2539             used_textures.append(texture)
2540             if texture.type == 'IMAGE' and texture.image:
2541                 used_images.append(texture.image)
2542                 texture.image = None
2543             texture_slot.texture = None
2544         material_slot.material = None
2545
2546     if ops.object.material_slot_remove.poll:
2547         ops.object.material_slot_remove()
2548
2549     for material in used_materials:
2550         material.user_clear()
2551         blender_data.materials.remove(material)
2552
2553     for texture in used_textures:
2554         texture.user_clear()
2555         blender_data.textures.remove(texture)
2556
2557     for image in used_images:
2558         image.user_clear()
2559         blender_data.images.remove(image)
2560
2561 def rename_active_ms3d(blender_context, src_name, dst_name, dst_type=None):
2562     #print("#DEBUG rename_active_ms3d >", blender_context.active_object, src_name, dst_name, dst_type)
2563     if not blender_context.active_object:
2564         return
2565
2566     data = blender_context.blend_data
2567
2568     src_empty_object_name = FORMAT_EMPTY_OBJECT.format(src_name)
2569     src_mesh_object_name = FORMAT_MESH_OBJECT.format(src_name)
2570     src_mesh_name = FORMAT_MESH.format(src_name)
2571     src_armature_name = FORMAT_ARMATURE.format(src_name)
2572     src_armature_object_name = FORMAT_ARMATURE_OBJECT.format(src_name)
2573     src_action_name = FORMAT_ACTION.format(src_name)
2574     src_group_name = FORMAT_GROUP.format(src_name)
2575
2576     if dst_type:
2577         dst_name = "{}.{}".format(dst_name, dst_type)
2578
2579     dst_empty_object_name = FpxUtilities.toGoodName(FORMAT_EMPTY_OBJECT.format(dst_name))
2580     dst_mesh_object_name = FpxUtilities.toGoodName(FORMAT_MESH_OBJECT.format(dst_name))
2581     dst_mesh_name = FpxUtilities.toGoodName(FORMAT_MESH.format(dst_name))
2582     dst_armature_name = FpxUtilities.toGoodName(FORMAT_ARMATURE.format(dst_name))
2583     dst_armature_object_name = FpxUtilities.toGoodName(FORMAT_ARMATURE_OBJECT.format(dst_name))
2584     dst_action_name = FpxUtilities.toGoodName(FORMAT_ACTION.format(dst_name))
2585     dst_group_name = FpxUtilities.toGoodName(FORMAT_GROUP.format(dst_name))
2586
2587     obj = blender_context.blend_data.objects.get(src_empty_object_name)
2588     if obj:
2589         obj.name = dst_empty_object_name
2590
2591     obj = data.objects.get(src_mesh_object_name)
2592     if obj:
2593         obj.name = dst_mesh_object_name
2594         mod = obj.modifiers.get(src_armature_name)
2595         if mod:
2596             mod.name = dst_armature_name
2597
2598     obj = data.objects.get(src_armature_object_name)
2599     if obj:
2600         obj.name = dst_armature_object_name
2601
2602     obj = data.meshes.get(src_mesh_name)
2603     if obj:
2604         obj.name = dst_mesh_name
2605
2606     obj = data.armatures.get(src_armature_name)
2607     if obj:
2608         obj.name = dst_armature_name
2609
2610     obj = data.actions.get(src_action_name)
2611     if obj:
2612         obj.name = dst_action_name
2613
2614     obj = data.groups.get(src_group_name)
2615     if obj:
2616         obj.name = dst_group_name
2617
2618 def rename_active_fpm(blender_context, dst_name):
2619     #print("#DEBUG rename_active_fpm >", blender_context.active_object, dst_name)
2620     if not blender_context.active_object:
2621         return
2622     blender_name = blender_context.active_object.name
2623     fpm_ext = "fpm"
2624     index = blender_name.find(".{}.".format(fpm_ext))
2625     if index < 0:
2626         index = blender_name.rfind(".")
2627         if index < 0:
2628             return
2629
2630     src_name = blender_name[:index]
2631
2632     #print("#DEBUG rename_active_fpm 2>", src_name, dst_name)
2633
2634     #if src_name.endswith(".fpm"):
2635     #    src_name = src_name[:-4]
2636
2637     src_scene_name = blender_context.scene.name
2638
2639     blender_scene = blender_context.blend_data.scenes.get(src_scene_name)
2640     if blender_scene:
2641         blender_scene.name = FORMAT_SCENE.format(dst_name).lower()
2642
2643     for object_type in {'', '.secondary', '.mask', '.reflection', '.collision', }:
2644         fpm_ext_ex = "{}{}".format(src_name, object_type)
2645         rename_active_ms3d(blender_context, fpm_ext_ex, dst_name, object_type);
2646
2647 def get_blend_resource_file_name():
2648     from importlib import ( find_loader, )
2649     from os import ( path, )
2650
2651     module_path = find_loader('io_scene_fpx').path
2652     module_path, module_file = path.split(module_path)
2653
2654     resource_blend = path.join(module_path, 'fpx_resource.blend')
2655
2656     if not path.exists(resource_blend):
2657         print("#DEBUG", "resource not found!")
2658
2659     return resource_blend
2660
2661
2662
2663 ###############################################################################
2664
2665
2666 ###############################################################################
2667 #234567890123456789012345678901234567890123456789012345678901234567890123456789
2668 #--------1---------2---------3---------4---------5---------6---------7---------
2669 # ##### END OF FILE #####