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