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