2016-06-30 13:22:05 +02:00
|
|
|
import bpy
|
|
|
|
import json
|
|
|
|
import os
|
2016-07-10 00:51:39 +02:00
|
|
|
import glob
|
2016-08-12 02:29:09 +02:00
|
|
|
import platform
|
2016-10-15 12:17:33 +02:00
|
|
|
import zipfile
|
2016-11-21 16:49:32 +01:00
|
|
|
import re
|
2017-11-20 14:32:36 +01:00
|
|
|
import subprocess
|
2018-03-25 12:00:43 +02:00
|
|
|
import webbrowser
|
2018-12-24 15:46:41 +01:00
|
|
|
import numpy as np
|
2017-05-17 17:06:52 +02:00
|
|
|
import arm.lib.armpack
|
2017-08-21 15:36:21 +02:00
|
|
|
import arm.make_state as state
|
2018-05-24 22:16:28 +02:00
|
|
|
import arm.log as log
|
2016-06-30 13:22:05 +02:00
|
|
|
|
2018-12-24 15:46:41 +01:00
|
|
|
class NumpyEncoder(json.JSONEncoder):
|
|
|
|
def default(self, obj):
|
|
|
|
if isinstance(obj, np.ndarray):
|
|
|
|
return obj.tolist()
|
|
|
|
return json.JSONEncoder.default(self, obj)
|
|
|
|
|
2016-07-20 17:33:17 +02:00
|
|
|
def write_arm(filepath, output):
|
2016-10-15 12:17:33 +02:00
|
|
|
if filepath.endswith('.zip'):
|
|
|
|
with zipfile.ZipFile(filepath, 'w', zipfile.ZIP_DEFLATED) as zip_file:
|
2016-10-17 00:02:51 +02:00
|
|
|
if bpy.data.worlds['Arm'].arm_minimize:
|
2017-05-17 17:06:52 +02:00
|
|
|
zip_file.writestr('data.arm', arm.lib.armpack.packb(output))
|
2016-10-15 12:17:33 +02:00
|
|
|
else:
|
2018-12-24 15:46:41 +01:00
|
|
|
zip_file.writestr('data.json', json.dumps(output, sort_keys=True, indent=4, cls=NumpyEncoder))
|
2016-07-20 17:33:17 +02:00
|
|
|
else:
|
2016-10-17 00:02:51 +02:00
|
|
|
if bpy.data.worlds['Arm'].arm_minimize:
|
2016-10-15 12:17:33 +02:00
|
|
|
with open(filepath, 'wb') as f:
|
2017-05-17 17:06:52 +02:00
|
|
|
f.write(arm.lib.armpack.packb(output))
|
2016-10-15 12:17:33 +02:00
|
|
|
else:
|
2018-04-14 15:07:05 +02:00
|
|
|
filepath_json = filepath.split('.arm')[0] + '.json'
|
|
|
|
with open(filepath_json, 'w') as f:
|
2018-12-24 15:46:41 +01:00
|
|
|
f.write(json.dumps(output, sort_keys=True, indent=4, cls=NumpyEncoder))
|
2016-06-30 13:22:05 +02:00
|
|
|
|
2018-03-26 16:28:27 +02:00
|
|
|
def unpack_image(image, path, file_format='JPEG'):
|
|
|
|
print('Armory Info: Unpacking to ' + path)
|
2018-03-19 12:52:08 +01:00
|
|
|
image.filepath_raw = path
|
|
|
|
image.file_format = file_format
|
|
|
|
image.save()
|
2016-11-07 22:10:11 +01:00
|
|
|
|
2018-03-26 16:28:27 +02:00
|
|
|
def convert_image(image, path, file_format='JPEG'):
|
|
|
|
# Convert image to compatible format
|
|
|
|
print('Armory Info: Converting to ' + path)
|
|
|
|
ren = bpy.context.scene.render
|
|
|
|
orig_quality = ren.image_settings.quality
|
|
|
|
orig_file_format = ren.image_settings.file_format
|
2018-11-12 22:25:08 +01:00
|
|
|
orig_color_mode = ren.image_settings.color_mode
|
2018-03-26 16:28:27 +02:00
|
|
|
ren.image_settings.quality = 90
|
|
|
|
ren.image_settings.file_format = file_format
|
2018-11-12 22:25:08 +01:00
|
|
|
if file_format == 'PNG':
|
|
|
|
ren.image_settings.color_mode = 'RGBA'
|
2019-01-12 12:51:28 +01:00
|
|
|
image.save_render(path, scene=bpy.context.scene)
|
2018-03-26 16:28:27 +02:00
|
|
|
ren.image_settings.quality = orig_quality
|
|
|
|
ren.image_settings.file_format = orig_file_format
|
2018-11-12 22:25:08 +01:00
|
|
|
ren.image_settings.color_mode = orig_color_mode
|
2018-03-26 16:28:27 +02:00
|
|
|
|
2017-05-23 01:03:44 +02:00
|
|
|
def blend_name():
|
|
|
|
return bpy.path.basename(bpy.context.blend_data.filepath).rsplit('.')[0]
|
|
|
|
|
|
|
|
def build_dir():
|
|
|
|
return 'build_' + safestr(blend_name())
|
|
|
|
|
2016-06-30 13:22:05 +02:00
|
|
|
def get_fp():
|
2017-10-06 18:56:05 +02:00
|
|
|
wrd = bpy.data.worlds['Arm']
|
|
|
|
if wrd.arm_project_root != '':
|
|
|
|
return bpy.path.abspath(wrd.arm_project_root)
|
|
|
|
else:
|
|
|
|
s = bpy.data.filepath.split(os.path.sep)
|
|
|
|
s.pop()
|
|
|
|
return os.path.sep.join(s)
|
2016-06-30 13:22:05 +02:00
|
|
|
|
2017-05-23 01:03:44 +02:00
|
|
|
def get_fp_build():
|
|
|
|
return get_fp() + '/' + build_dir()
|
|
|
|
|
2016-08-12 02:29:09 +02:00
|
|
|
def get_os():
|
|
|
|
s = platform.system()
|
|
|
|
if s == 'Windows':
|
|
|
|
return 'win'
|
|
|
|
elif s == 'Darwin':
|
|
|
|
return 'mac'
|
|
|
|
else:
|
|
|
|
return 'linux'
|
|
|
|
|
2017-04-19 11:48:30 +02:00
|
|
|
def get_gapi():
|
2017-08-21 15:36:21 +02:00
|
|
|
wrd = bpy.data.worlds['Arm']
|
2018-06-19 23:21:53 +02:00
|
|
|
if state.is_viewport:
|
2018-03-24 14:46:54 +01:00
|
|
|
return 'opengl'
|
2017-08-21 15:36:21 +02:00
|
|
|
if state.is_export:
|
|
|
|
item = wrd.arm_exporterlist[wrd.arm_exporterlist_index]
|
2017-11-20 14:32:36 +01:00
|
|
|
return getattr(item, target_to_gapi(item.arm_project_target))
|
2018-11-13 14:17:47 +01:00
|
|
|
if wrd.arm_runtime == 'Browser':
|
2018-03-24 14:46:54 +01:00
|
|
|
return 'webgl'
|
|
|
|
return arm.utils.get_player_gapi()
|
2017-04-19 11:48:30 +02:00
|
|
|
|
2017-08-21 20:16:06 +02:00
|
|
|
def get_rp():
|
|
|
|
wrd = bpy.data.worlds['Arm']
|
|
|
|
return wrd.arm_rplist[wrd.arm_rplist_index]
|
|
|
|
|
2018-05-24 22:16:28 +02:00
|
|
|
def bundled_sdk_path():
|
|
|
|
if get_os() == 'mac':
|
|
|
|
# SDK on MacOS is located in .app folder due to security
|
|
|
|
p = bpy.app.binary_path
|
|
|
|
if p.endswith('Contents/MacOS/blender'):
|
|
|
|
return p[:-len('Contents/MacOS/blender')] + '/armsdk/'
|
|
|
|
else:
|
|
|
|
return p[:-len('Contents/MacOS/./blender')] + '/armsdk/'
|
|
|
|
elif get_os() == 'linux':
|
|
|
|
# /blender
|
|
|
|
return bpy.app.binary_path.rsplit('/', 1)[0] + '/armsdk/'
|
|
|
|
else:
|
|
|
|
# /blender.exe
|
|
|
|
return bpy.app.binary_path.replace('\\', '/').rsplit('/', 1)[0] + '/armsdk/'
|
|
|
|
|
2018-08-16 23:07:50 +02:00
|
|
|
# Passed by load_post handler when armsdk is found in project folder
|
|
|
|
use_local_sdk = False
|
2016-09-28 00:00:59 +02:00
|
|
|
def get_sdk_path():
|
2018-12-23 08:39:25 +01:00
|
|
|
preferences = bpy.context.preferences
|
|
|
|
addon_prefs = preferences.addons["armory"].preferences
|
2018-05-24 22:16:28 +02:00
|
|
|
p = bundled_sdk_path()
|
2018-08-16 23:07:50 +02:00
|
|
|
if use_local_sdk:
|
|
|
|
return get_fp() + '/armsdk/'
|
|
|
|
elif os.path.exists(p) and addon_prefs.sdk_bundled:
|
2018-05-24 22:16:28 +02:00
|
|
|
return p
|
2016-09-28 00:00:59 +02:00
|
|
|
else:
|
|
|
|
return addon_prefs.sdk_path
|
|
|
|
|
|
|
|
def get_ffmpeg_path():
|
2018-12-23 08:39:25 +01:00
|
|
|
preferences = bpy.context.preferences
|
|
|
|
addon_prefs = preferences.addons['armory'].preferences
|
2016-09-28 00:00:59 +02:00
|
|
|
return addon_prefs.ffmpeg_path
|
|
|
|
|
2018-03-22 21:43:22 +01:00
|
|
|
def get_renderdoc_path():
|
2018-12-23 08:39:25 +01:00
|
|
|
preferences = bpy.context.preferences
|
|
|
|
addon_prefs = preferences.addons['armory'].preferences
|
2018-03-22 21:43:22 +01:00
|
|
|
p = addon_prefs.renderdoc_path
|
|
|
|
if p == '' and get_os() == 'win':
|
|
|
|
pdefault = 'C:\\Program Files\\RenderDoc\\qrenderdoc.exe'
|
|
|
|
if os.path.exists(pdefault):
|
|
|
|
p = pdefault
|
|
|
|
return p
|
|
|
|
|
2018-03-24 14:46:54 +01:00
|
|
|
def get_player_gapi():
|
2018-12-23 08:39:25 +01:00
|
|
|
preferences = bpy.context.preferences
|
|
|
|
addon_prefs = preferences.addons['armory'].preferences
|
2018-03-24 14:46:54 +01:00
|
|
|
return 'opengl' if not hasattr(addon_prefs, 'player_gapi_' + get_os()) else getattr(addon_prefs, 'player_gapi_' + get_os())
|
|
|
|
|
2018-03-25 12:00:43 +02:00
|
|
|
def get_code_editor():
|
2018-12-23 08:39:25 +01:00
|
|
|
preferences = bpy.context.preferences
|
|
|
|
addon_prefs = preferences.addons['armory'].preferences
|
2018-03-25 12:00:43 +02:00
|
|
|
return 'kodestudio' if not hasattr(addon_prefs, 'code_editor') else addon_prefs.code_editor
|
|
|
|
|
2018-03-25 21:48:30 +02:00
|
|
|
def get_ui_scale():
|
2018-12-23 08:39:25 +01:00
|
|
|
preferences = bpy.context.preferences
|
|
|
|
addon_prefs = preferences.addons['armory'].preferences
|
2018-03-25 21:48:30 +02:00
|
|
|
return 1.0 if not hasattr(addon_prefs, 'ui_scale') else addon_prefs.ui_scale
|
|
|
|
|
2018-08-30 15:42:25 +02:00
|
|
|
def get_khamake_threads():
|
2018-12-23 08:39:25 +01:00
|
|
|
preferences = bpy.context.preferences
|
|
|
|
addon_prefs = preferences.addons['armory'].preferences
|
2018-08-30 15:42:25 +02:00
|
|
|
return 1 if not hasattr(addon_prefs, 'khamake_threads') else addon_prefs.khamake_threads
|
|
|
|
|
2017-02-22 16:14:55 +01:00
|
|
|
def get_save_on_build():
|
2018-12-23 08:39:25 +01:00
|
|
|
preferences = bpy.context.preferences
|
|
|
|
addon_prefs = preferences.addons['armory'].preferences
|
2017-02-22 16:14:55 +01:00
|
|
|
return True if not hasattr(addon_prefs, 'save_on_build') else addon_prefs.save_on_build
|
|
|
|
|
2017-11-10 14:53:40 +01:00
|
|
|
def get_viewport_controls():
|
2018-12-23 08:39:25 +01:00
|
|
|
preferences = bpy.context.preferences
|
|
|
|
addon_prefs = preferences.addons['armory'].preferences
|
2017-11-10 14:53:40 +01:00
|
|
|
return 'qwerty' if not hasattr(addon_prefs, 'viewport_controls') else addon_prefs.viewport_controls
|
|
|
|
|
2018-02-16 18:09:03 +01:00
|
|
|
def get_legacy_shaders():
|
2018-12-23 08:39:25 +01:00
|
|
|
preferences = bpy.context.preferences
|
|
|
|
addon_prefs = preferences.addons['armory'].preferences
|
2018-02-16 18:09:03 +01:00
|
|
|
return False if not hasattr(addon_prefs, 'legacy_shaders') else addon_prefs.legacy_shaders
|
|
|
|
|
2018-08-16 20:48:00 +02:00
|
|
|
def get_relative_paths():
|
|
|
|
# Convert absolute paths to relative
|
2018-12-23 08:39:25 +01:00
|
|
|
preferences = bpy.context.preferences
|
|
|
|
addon_prefs = preferences.addons['armory'].preferences
|
2018-08-16 20:48:00 +02:00
|
|
|
return False if not hasattr(addon_prefs, 'relative_paths') else addon_prefs.relative_paths
|
|
|
|
|
2017-02-09 00:33:19 +01:00
|
|
|
def get_node_path():
|
|
|
|
if get_os() == 'win':
|
|
|
|
return get_sdk_path() + '/nodejs/node.exe'
|
|
|
|
elif get_os() == 'mac':
|
|
|
|
return get_sdk_path() + '/nodejs/node-osx'
|
|
|
|
else:
|
|
|
|
return get_sdk_path() + '/nodejs/node-linux64'
|
|
|
|
|
2017-03-20 13:23:31 +01:00
|
|
|
def get_kha_path():
|
2017-02-09 00:33:19 +01:00
|
|
|
if os.path.exists('Kha'):
|
2017-03-20 13:23:31 +01:00
|
|
|
return 'Kha'
|
2018-01-31 02:51:09 +01:00
|
|
|
return get_sdk_path() + '/Kha'
|
2017-03-20 13:23:31 +01:00
|
|
|
|
2017-07-02 20:48:19 +02:00
|
|
|
def get_haxe_path():
|
|
|
|
if get_os() == 'win':
|
2017-10-17 14:45:19 +02:00
|
|
|
return get_kha_path() + '/Tools/haxe/haxe.exe'
|
2017-07-02 20:48:19 +02:00
|
|
|
elif get_os() == 'mac':
|
2017-10-17 14:45:19 +02:00
|
|
|
return get_kha_path() + '/Tools/haxe/haxe-osx'
|
2017-07-02 20:48:19 +02:00
|
|
|
else:
|
2017-10-17 14:45:19 +02:00
|
|
|
return get_kha_path() + '/Tools/haxe/haxe-linux64'
|
2017-07-02 20:48:19 +02:00
|
|
|
|
2017-03-20 13:23:31 +01:00
|
|
|
def get_khamake_path():
|
|
|
|
return get_kha_path() + '/make'
|
2017-02-09 00:33:19 +01:00
|
|
|
|
2018-03-24 14:46:54 +01:00
|
|
|
def krom_paths(bin_ext=''):
|
2017-06-05 02:32:51 +02:00
|
|
|
sdk_path = get_sdk_path()
|
|
|
|
if arm.utils.get_os() == 'win':
|
2018-09-05 08:49:44 +02:00
|
|
|
krom_location = sdk_path + '/Krom'
|
2018-03-24 14:46:54 +01:00
|
|
|
krom_path = krom_location + '/Krom' + bin_ext + '.exe'
|
2017-06-05 02:32:51 +02:00
|
|
|
elif arm.utils.get_os() == 'mac':
|
2018-09-05 08:49:44 +02:00
|
|
|
krom_location = sdk_path + '/Krom/Krom.app/Contents/MacOS'
|
2018-03-24 14:46:54 +01:00
|
|
|
krom_path = krom_location + '/Krom' + bin_ext
|
2017-06-05 02:32:51 +02:00
|
|
|
else:
|
2018-09-05 08:49:44 +02:00
|
|
|
krom_location = sdk_path + '/Krom'
|
2018-03-24 14:46:54 +01:00
|
|
|
krom_path = krom_location + '/Krom' + bin_ext
|
2017-06-05 02:32:51 +02:00
|
|
|
return krom_location, krom_path
|
|
|
|
|
2017-02-22 17:32:34 +01:00
|
|
|
def fetch_bundled_script_names():
|
2016-09-02 23:11:04 +02:00
|
|
|
wrd = bpy.data.worlds['Arm']
|
2017-08-21 12:17:55 +02:00
|
|
|
wrd.arm_bundled_scripts_list.clear()
|
2017-02-09 00:33:19 +01:00
|
|
|
os.chdir(get_sdk_path() + '/armory/Sources/armory/trait')
|
2016-07-10 00:51:39 +02:00
|
|
|
for file in glob.glob('*.hx'):
|
2017-08-21 12:17:55 +02:00
|
|
|
wrd.arm_bundled_scripts_list.add().name = file.rsplit('.')[0]
|
2017-02-22 17:32:34 +01:00
|
|
|
|
2017-07-24 02:27:22 +02:00
|
|
|
script_props = {}
|
2017-08-08 19:56:47 +02:00
|
|
|
script_props_defaults = {}
|
2017-07-24 02:27:22 +02:00
|
|
|
def fetch_script_props(file):
|
|
|
|
with open(file) as f:
|
|
|
|
if '/' in file:
|
|
|
|
file = file.split('/')[-1]
|
|
|
|
if '\\' in file:
|
|
|
|
file = file.split('\\')[-1]
|
|
|
|
name = file.rsplit('.')[0]
|
|
|
|
script_props[name] = []
|
2017-08-08 19:56:47 +02:00
|
|
|
script_props_defaults[name] = []
|
2017-07-24 02:27:22 +02:00
|
|
|
lines = f.read().splitlines()
|
2017-10-25 14:39:41 +02:00
|
|
|
read_prop = False
|
2017-07-24 02:27:22 +02:00
|
|
|
for l in lines:
|
2017-10-25 14:39:41 +02:00
|
|
|
if not read_prop:
|
2018-03-08 11:23:40 +01:00
|
|
|
read_prop = l.lstrip().startswith('@prop')
|
2017-10-25 14:39:41 +02:00
|
|
|
if read_prop and 'var ' in l:
|
2017-07-24 02:27:22 +02:00
|
|
|
p = l.split('var ')[1]
|
2017-10-25 14:39:41 +02:00
|
|
|
|
|
|
|
valid_prop = False
|
|
|
|
# Has type
|
2017-07-24 02:27:22 +02:00
|
|
|
if ':' in p:
|
2017-10-25 14:39:41 +02:00
|
|
|
# Fetch default value
|
|
|
|
if '=' in p:
|
2017-08-08 19:56:47 +02:00
|
|
|
s = p.split('=')
|
2017-10-25 14:39:41 +02:00
|
|
|
ps = s[0].split(':')
|
|
|
|
prop = (ps[0].strip(), ps[1].split(';')[0].strip())
|
|
|
|
prop_value = s[1].split(';')[0].replace('\'', '').replace('"', '').strip()
|
|
|
|
valid_prop = True
|
2017-08-08 19:56:47 +02:00
|
|
|
else:
|
2017-10-25 14:39:41 +02:00
|
|
|
ps = p.split(':')
|
|
|
|
prop = (ps[0].strip(), ps[1].split(';')[0].strip())
|
|
|
|
prop_value = ''
|
|
|
|
valid_prop = True
|
|
|
|
# Fetch default value
|
2017-07-24 02:27:22 +02:00
|
|
|
elif '=' in p:
|
2017-08-08 19:56:47 +02:00
|
|
|
s = p.split('=')
|
2017-10-25 14:39:41 +02:00
|
|
|
prop = (s[0].strip(), None)
|
|
|
|
prop_value = s[1].split(';')[0].replace('\'', '').replace('"', '').strip()
|
|
|
|
valid_prop = True
|
|
|
|
# Register prop
|
|
|
|
if valid_prop:
|
|
|
|
script_props[name].append(prop)
|
|
|
|
script_props_defaults[name].append(prop_value)
|
|
|
|
|
|
|
|
read_prop = False
|
2017-07-24 02:27:22 +02:00
|
|
|
|
2017-02-22 17:32:34 +01:00
|
|
|
def fetch_script_names():
|
|
|
|
if bpy.data.filepath == "":
|
|
|
|
return
|
|
|
|
wrd = bpy.data.worlds['Arm']
|
2017-05-26 16:05:14 +02:00
|
|
|
# Sources
|
2017-08-21 12:17:55 +02:00
|
|
|
wrd.arm_scripts_list.clear()
|
2017-05-13 17:17:43 +02:00
|
|
|
sources_path = get_fp() + '/Sources/' + safestr(wrd.arm_project_package)
|
2016-07-10 00:51:39 +02:00
|
|
|
if os.path.isdir(sources_path):
|
|
|
|
os.chdir(sources_path)
|
|
|
|
for file in glob.glob('*.hx'):
|
2017-07-24 02:27:22 +02:00
|
|
|
name = file.rsplit('.')[0]
|
2017-08-21 12:17:55 +02:00
|
|
|
wrd.arm_scripts_list.add().name = name
|
2017-07-24 02:27:22 +02:00
|
|
|
fetch_script_props(file)
|
|
|
|
|
2017-05-26 16:05:14 +02:00
|
|
|
# Canvas
|
2017-08-21 12:17:55 +02:00
|
|
|
wrd.arm_canvas_list.clear()
|
2017-05-26 16:05:14 +02:00
|
|
|
canvas_path = get_fp() + '/Bundled/canvas'
|
|
|
|
if os.path.isdir(canvas_path):
|
|
|
|
os.chdir(canvas_path)
|
|
|
|
for file in glob.glob('*.json'):
|
2017-08-21 12:17:55 +02:00
|
|
|
wrd.arm_canvas_list.add().name = file.rsplit('.')[0]
|
2016-07-10 00:51:39 +02:00
|
|
|
os.chdir(get_fp())
|
2016-07-17 23:29:30 +02:00
|
|
|
|
2018-04-15 11:55:42 +02:00
|
|
|
def fetch_wasm_names():
|
|
|
|
if bpy.data.filepath == "":
|
|
|
|
return
|
|
|
|
wrd = bpy.data.worlds['Arm']
|
|
|
|
# WASM modules
|
|
|
|
wrd.arm_wasm_list.clear()
|
|
|
|
sources_path = get_fp() + '/Bundled'
|
|
|
|
if os.path.isdir(sources_path):
|
|
|
|
os.chdir(sources_path)
|
|
|
|
for file in glob.glob('*.wasm'):
|
|
|
|
name = file.rsplit('.')[0]
|
|
|
|
wrd.arm_wasm_list.add().name = name
|
|
|
|
os.chdir(get_fp())
|
|
|
|
|
2017-07-24 02:27:22 +02:00
|
|
|
def fetch_trait_props():
|
|
|
|
for o in bpy.data.objects:
|
2017-11-10 12:16:39 +01:00
|
|
|
fetch_prop(o)
|
2017-12-11 19:00:32 +01:00
|
|
|
for s in bpy.data.scenes:
|
|
|
|
fetch_prop(s)
|
2017-11-10 12:16:39 +01:00
|
|
|
|
|
|
|
def fetch_prop(o):
|
|
|
|
for item in o.arm_traitlist:
|
|
|
|
if item.name not in script_props:
|
|
|
|
continue
|
|
|
|
props = script_props[item.name]
|
|
|
|
defaults = script_props_defaults[item.name]
|
|
|
|
# Remove old props
|
|
|
|
for i in range(len(item.arm_traitpropslist) - 1, -1, -1):
|
|
|
|
ip = item.arm_traitpropslist[i]
|
|
|
|
# if ip.name not in props:
|
|
|
|
if ip.name.split('(')[0] not in [p[0] for p in props]:
|
|
|
|
item.arm_traitpropslist.remove(i)
|
|
|
|
# Add new props
|
|
|
|
for i in range(0, len(props)):
|
|
|
|
p = props[i]
|
|
|
|
found = False
|
|
|
|
for ip in item.arm_traitpropslist:
|
|
|
|
if ip.name.replace(')', '').split('(')[0] == p[0]:
|
|
|
|
found = ip
|
|
|
|
break
|
|
|
|
# Not in list
|
|
|
|
if not found:
|
|
|
|
prop = item.arm_traitpropslist.add()
|
|
|
|
prop.name = p[0] + ('(' + p[1] + ')' if p[1] else '')
|
|
|
|
prop.value = defaults[i]
|
|
|
|
|
|
|
|
if found:
|
|
|
|
prop = item.arm_traitpropslist[found.name]
|
|
|
|
f = found.name.replace(')', '').split('(')
|
|
|
|
|
|
|
|
# Default value added and current value is blank (no override)
|
2017-12-11 00:55:26 +01:00
|
|
|
if (not found.value and defaults[i]):
|
2017-08-08 19:56:47 +02:00
|
|
|
prop.value = defaults[i]
|
2017-11-10 12:16:39 +01:00
|
|
|
# Type has changed, update displayed name
|
2017-12-11 00:55:26 +01:00
|
|
|
if (len(f) == 1 or (len(f) > 1 and f[1] != p[1])):
|
2017-11-10 12:16:39 +01:00
|
|
|
prop.name = p[0] + ('(' + p[1] + ')' if p[1] else '')
|
2017-10-25 14:39:41 +02:00
|
|
|
|
2017-12-11 19:00:32 +01:00
|
|
|
def fetch_bundled_trait_props():
|
|
|
|
# Bundled script props
|
|
|
|
for o in bpy.data.objects:
|
|
|
|
for t in o.arm_traitlist:
|
|
|
|
if t.type_prop == 'Bundled Script':
|
|
|
|
file_path = get_sdk_path() + '/armory/Sources/armory/trait/' + t.name + '.hx'
|
|
|
|
if os.path.exists(file_path):
|
|
|
|
fetch_script_props(file_path)
|
|
|
|
fetch_prop(o)
|
|
|
|
|
2018-12-29 16:34:04 +01:00
|
|
|
# def update_trait_groups():
|
|
|
|
# if not hasattr(bpy.data, 'groups'):
|
|
|
|
# return
|
|
|
|
# for g in bpy.data.groups:
|
|
|
|
# if g.name.startswith('Trait|'):
|
|
|
|
# bpy.data.groups.remove(g)
|
|
|
|
# for o in bpy.data.objects:
|
|
|
|
# for t in o.arm_traitlist:
|
|
|
|
# if 'Trait|' + t.name not in bpy.data.groups:
|
|
|
|
# g = bpy.data.groups.new('Trait|' + t.name)
|
|
|
|
# else:
|
|
|
|
# g = bpy.data.groups['Trait|' + t.name]
|
|
|
|
# g.objects.link(o)
|
2017-12-11 00:55:26 +01:00
|
|
|
|
2016-07-17 23:29:30 +02:00
|
|
|
def to_hex(val):
|
|
|
|
return '#%02x%02x%02x%02x' % (int(val[3] * 255), int(val[0] * 255), int(val[1] * 255), int(val[2] * 255))
|
2016-07-27 14:25:01 +02:00
|
|
|
|
2016-09-02 23:11:04 +02:00
|
|
|
def color_to_int(val):
|
|
|
|
return (int(val[3] * 255) << 24) + (int(val[0] * 255) << 16) + (int(val[1] * 255) << 8) + int(val[2] * 255)
|
|
|
|
|
2017-05-13 17:17:43 +02:00
|
|
|
def safesrc(s):
|
|
|
|
s = safestr(s).replace('.', '_').replace('-', '_').replace(' ', '')
|
2016-12-21 00:51:04 +01:00
|
|
|
if s[0].isdigit():
|
|
|
|
s = '_' + s
|
|
|
|
return s
|
2016-12-07 10:19:45 +01:00
|
|
|
|
2017-05-13 17:17:43 +02:00
|
|
|
def safestr(s):
|
|
|
|
for c in r'[]/\;,><&*:%=+@!#^()|?^':
|
|
|
|
s = s.replace(c, '_')
|
|
|
|
return ''.join([i if ord(i) < 128 else '_' for i in s])
|
|
|
|
|
2017-10-06 11:16:29 +02:00
|
|
|
def asset_name(bdata):
|
|
|
|
s = bdata.name
|
|
|
|
# Append library name if linked
|
|
|
|
if bdata.library != None:
|
|
|
|
s += '_' + bdata.library.name
|
|
|
|
return s
|
|
|
|
|
2017-05-13 17:17:43 +02:00
|
|
|
def asset_path(s):
|
2016-11-24 23:24:55 +01:00
|
|
|
return s[2:] if s[:2] == '//' else s # Remove leading '//'
|
2016-08-22 21:56:28 +02:00
|
|
|
|
2016-09-14 11:49:32 +02:00
|
|
|
def extract_filename(s):
|
2017-05-13 17:17:43 +02:00
|
|
|
return os.path.basename(asset_path(s))
|
2016-09-14 11:49:32 +02:00
|
|
|
|
2017-01-19 00:26:18 +01:00
|
|
|
def get_render_resolution(scene):
|
|
|
|
render = scene.render
|
2016-08-15 23:45:03 +02:00
|
|
|
scale = render.resolution_percentage / 100
|
|
|
|
return int(render.resolution_x * scale), int(render.resolution_y * scale)
|
2016-09-08 14:08:31 +02:00
|
|
|
|
|
|
|
def get_project_scene_name():
|
2017-11-09 15:05:58 +01:00
|
|
|
return get_active_scene().name
|
2016-09-12 02:24:20 +02:00
|
|
|
|
2017-01-19 00:26:18 +01:00
|
|
|
def get_active_scene():
|
2017-11-09 15:05:58 +01:00
|
|
|
if not state.is_export:
|
|
|
|
return bpy.context.scene
|
2017-09-10 15:37:38 +02:00
|
|
|
else:
|
2017-11-09 15:05:58 +01:00
|
|
|
wrd = bpy.data.worlds['Arm']
|
|
|
|
item = wrd.arm_exporterlist[wrd.arm_exporterlist_index]
|
2019-01-24 15:09:49 +01:00
|
|
|
return item.arm_project_scene
|
2017-01-19 00:26:18 +01:00
|
|
|
|
2019-01-07 11:19:04 +01:00
|
|
|
def logic_editor_space(context_screen=None):
|
|
|
|
if context_screen == None:
|
|
|
|
context_screen = bpy.context.screen
|
|
|
|
if context_screen != None:
|
|
|
|
areas = context_screen.areas
|
2017-07-03 15:16:15 +02:00
|
|
|
for area in areas:
|
|
|
|
if area.type == 'NODE_EDITOR':
|
|
|
|
for space in area.spaces:
|
|
|
|
if space.type == 'NODE_EDITOR':
|
|
|
|
if space.node_tree != None and space.node_tree.bl_idname == 'ArmLogicTreeType': # and space.node_tree.is_updated:
|
|
|
|
return space
|
|
|
|
return None
|
|
|
|
|
2018-01-28 17:28:13 +01:00
|
|
|
def voxel_support():
|
2018-06-02 14:07:43 +02:00
|
|
|
# macos does not support opengl 4.5, needs metal
|
|
|
|
return state.target != 'html5' and get_os() != 'mac'
|
2018-01-28 17:28:13 +01:00
|
|
|
|
2019-01-21 17:59:25 +01:00
|
|
|
def get_cascade_size(rpdat):
|
|
|
|
cascade_size = int(rpdat.rp_shadowmap_cascade)
|
|
|
|
# Clamp to 4096 per cascade
|
|
|
|
if int(rpdat.rp_shadowmap_cascades) > 1 and cascade_size > 4096:
|
|
|
|
cascade_size = 4096
|
|
|
|
return cascade_size
|
|
|
|
|
2016-12-08 14:38:04 +01:00
|
|
|
def check_saved(self):
|
|
|
|
if bpy.data.filepath == "":
|
2018-05-24 22:16:28 +02:00
|
|
|
msg = "Save blend file first"
|
|
|
|
self.report({"ERROR"}, msg) if self != None else log.print_info(msg)
|
2016-12-08 14:38:04 +01:00
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
2017-09-08 14:07:41 +02:00
|
|
|
def check_path(s):
|
2017-01-12 22:20:14 +01:00
|
|
|
for c in r'[];><&*%=+@!#^()|?^':
|
2017-01-08 00:56:49 +01:00
|
|
|
if c in s:
|
|
|
|
return False
|
2017-01-23 20:41:45 +01:00
|
|
|
for c in s:
|
|
|
|
if ord(c) > 127:
|
|
|
|
return False
|
2017-01-08 00:56:49 +01:00
|
|
|
return True
|
|
|
|
|
2017-09-08 14:07:41 +02:00
|
|
|
def check_sdkpath(self):
|
|
|
|
s = get_sdk_path()
|
|
|
|
if check_path(s) == False:
|
2018-05-24 22:16:28 +02:00
|
|
|
msg = "SDK path '{0}' contains special characters. Please move SDK to different path for now.".format(s)
|
|
|
|
self.report({"ERROR"}, msg) if self != None else log.print_info(msg)
|
2017-09-08 14:07:41 +02:00
|
|
|
return False
|
|
|
|
else:
|
|
|
|
return True
|
|
|
|
|
|
|
|
def check_projectpath(self):
|
|
|
|
s = get_fp()
|
|
|
|
if check_path(s) == False:
|
2018-05-24 22:16:28 +02:00
|
|
|
msg = "Project path '{0}' contains special characters, build process may fail.".format(s)
|
|
|
|
self.report({"ERROR"}, msg) if self != None else log.print_info(msg)
|
2017-09-08 14:07:41 +02:00
|
|
|
return False
|
|
|
|
else:
|
|
|
|
return True
|
|
|
|
|
2017-11-26 14:45:36 +01:00
|
|
|
def disp_enabled(target):
|
2017-08-21 20:16:06 +02:00
|
|
|
rpdat = get_rp()
|
2018-05-07 23:09:38 +02:00
|
|
|
if rpdat.arm_rp_displacement == 'Tessellation':
|
|
|
|
return target == 'krom' or target == 'native'
|
|
|
|
return rpdat.arm_rp_displacement != 'Off'
|
2016-12-17 15:34:43 +01:00
|
|
|
|
2016-12-19 01:25:22 +01:00
|
|
|
def is_object_animation_enabled(bobject):
|
|
|
|
# Checks if animation is present and enabled
|
2017-08-21 12:17:55 +02:00
|
|
|
if bobject.arm_animation_enabled == False or bobject.type == 'BONE' or bobject.type == 'ARMATURE':
|
2016-12-19 01:25:22 +01:00
|
|
|
return False
|
|
|
|
if bobject.animation_data and bobject.animation_data.action:
|
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
def is_bone_animation_enabled(bobject):
|
|
|
|
# Checks if animation is present and enabled for parented armature
|
|
|
|
if bobject.parent and bobject.parent.type == 'ARMATURE':
|
2017-08-21 12:17:55 +02:00
|
|
|
if bobject.parent.arm_animation_enabled == False:
|
2016-12-19 01:25:22 +01:00
|
|
|
return False
|
2018-05-18 13:40:01 +02:00
|
|
|
# Check for present actions
|
|
|
|
adata = bobject.parent.animation_data
|
|
|
|
has_actions = adata != None and adata.action != None
|
|
|
|
if not has_actions and adata != None:
|
|
|
|
if hasattr(adata, 'nla_tracks') and adata.nla_tracks != None:
|
|
|
|
for track in adata.nla_tracks:
|
|
|
|
if track.strips == None:
|
|
|
|
continue
|
|
|
|
for strip in track.strips:
|
|
|
|
if strip.action == None:
|
|
|
|
continue
|
|
|
|
has_actions = True
|
|
|
|
break
|
|
|
|
if has_actions:
|
|
|
|
break
|
|
|
|
if adata != None and has_actions:
|
2016-12-19 01:25:22 +01:00
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
2017-04-04 23:11:31 +02:00
|
|
|
def export_bone_data(bobject):
|
2018-03-15 16:02:56 +01:00
|
|
|
return bobject.find_armature() and is_bone_animation_enabled(bobject) and get_rp().arm_skin.startswith('GPU')
|
2017-04-04 23:11:31 +02:00
|
|
|
|
2018-01-31 02:51:09 +01:00
|
|
|
def kode_studio_mklink_win(sdk_path):
|
2017-11-20 14:32:36 +01:00
|
|
|
# Fight long-path issues on Windows
|
2018-09-21 21:32:45 +02:00
|
|
|
if not os.path.exists(sdk_path + '/win32/resources/app/kodeExtensions/kha/Kha'):
|
|
|
|
source = sdk_path + '/win32/resources/app/kodeExtensions/kha/Kha'
|
2018-01-31 00:56:38 +01:00
|
|
|
target = sdk_path + '/Kha'
|
2017-11-20 14:32:36 +01:00
|
|
|
subprocess.check_call('mklink /J "%s" "%s"' % (source, target), shell=True)
|
2018-09-21 21:32:45 +02:00
|
|
|
if not os.path.exists(sdk_path + '/win32/resources/app/kodeExtensions/krom/Krom'):
|
|
|
|
source = sdk_path + '/win32/resources/app/kodeExtensions/krom/Krom'
|
2018-01-31 00:56:38 +01:00
|
|
|
target = sdk_path + '/Krom'
|
2017-11-20 14:32:36 +01:00
|
|
|
subprocess.check_call('mklink /J "%s" "%s"' % (source, target), shell=True)
|
|
|
|
|
2018-01-31 02:51:09 +01:00
|
|
|
def kode_studio_mklink_linux(sdk_path):
|
2018-09-21 21:32:45 +02:00
|
|
|
if not os.path.exists(sdk_path + '/linux64/resources/app/kodeExtensions/kha/Kha'):
|
|
|
|
source = sdk_path + '/linux64/resources/app/kodeExtensions/kha/Kha'
|
2018-01-31 02:51:09 +01:00
|
|
|
target = sdk_path + '/Kha'
|
|
|
|
subprocess.check_call('ln -s "%s" "%s"' % (target, source), shell=True)
|
2018-09-21 21:32:45 +02:00
|
|
|
if not os.path.exists(sdk_path + '/linux64/resources/app/kodeExtensions/krom/Krom'):
|
|
|
|
source = sdk_path + '/linux64/resources/app/kodeExtensions/krom/Krom'
|
2018-01-31 02:51:09 +01:00
|
|
|
target = sdk_path + '/Krom'
|
|
|
|
subprocess.check_call('ln -s "%s" "%s"' % (target, source), shell=True)
|
|
|
|
|
|
|
|
def kode_studio_mklink_mac(sdk_path):
|
2018-09-21 21:32:45 +02:00
|
|
|
if not os.path.exists(sdk_path + '/KodeStudio.app/Contents/Resources/app/kodeExtensions/kha/Kha'):
|
|
|
|
source = sdk_path + '/KodeStudio.app/Contents/Resources/app/kodeExtensions/kha/Kha'
|
2018-01-31 02:51:09 +01:00
|
|
|
target = sdk_path + '/Kha'
|
2018-02-23 07:43:29 +01:00
|
|
|
subprocess.check_call('ln -fs "%s" "%s"' % (target, source), shell=True)
|
2018-09-21 21:32:45 +02:00
|
|
|
if not os.path.exists(sdk_path + '/KodeStudio.app/Contents/Resources/app/kodeExtensions/krom/Krom'):
|
|
|
|
source = sdk_path + '/KodeStudio.app/Contents/Resources/app/kodeExtensions/krom/Krom'
|
2018-01-31 02:51:09 +01:00
|
|
|
target = sdk_path + '/Krom'
|
2018-02-23 07:43:29 +01:00
|
|
|
subprocess.check_call('ln -fs "%s" "%s"' % (target, source), shell=True)
|
2018-01-31 02:51:09 +01:00
|
|
|
|
2018-08-07 08:56:48 +02:00
|
|
|
def get_kode_path():
|
|
|
|
if get_os() == 'win':
|
|
|
|
return get_sdk_path() + '/win32/Kode Studio.exe'
|
|
|
|
elif get_os() == 'mac':
|
2018-09-21 21:32:45 +02:00
|
|
|
return get_sdk_path() + '/KodeStudio.app/Contents/MacOS/Electron'
|
2018-08-07 08:56:48 +02:00
|
|
|
else:
|
|
|
|
return get_sdk_path() + '/linux64/kodestudio'
|
|
|
|
|
|
|
|
def kode_studio(hx_path=None):
|
2017-11-20 14:32:36 +01:00
|
|
|
project_path = arm.utils.get_fp()
|
2018-08-07 08:56:48 +02:00
|
|
|
kode_path = get_kode_path()
|
2019-01-21 16:15:15 +01:00
|
|
|
if os.path.exists(kode_path) and get_code_editor() == 'kodestudio':
|
2018-08-07 08:56:48 +02:00
|
|
|
if arm.utils.get_os() == 'win':
|
|
|
|
kode_studio_mklink_win(get_sdk_path())
|
|
|
|
args = [kode_path, arm.utils.get_fp()]
|
|
|
|
if hx_path != None:
|
|
|
|
args.append(hx_path)
|
|
|
|
subprocess.Popen(args)
|
|
|
|
elif arm.utils.get_os() == 'mac':
|
|
|
|
kode_studio_mklink_mac(get_sdk_path())
|
|
|
|
args = ['"' + kode_path + '"' + ' "' + arm.utils.get_fp() + '"']
|
|
|
|
if hx_path != None:
|
|
|
|
args[0] += ' "' + hx_path + '"'
|
|
|
|
subprocess.Popen(args, shell=True)
|
2018-03-25 12:00:43 +02:00
|
|
|
else:
|
2018-08-07 08:56:48 +02:00
|
|
|
kode_studio_mklink_linux(get_sdk_path())
|
|
|
|
args = [kode_path, arm.utils.get_fp()]
|
|
|
|
if hx_path != None:
|
|
|
|
args.append(hx_path)
|
|
|
|
subprocess.Popen(args)
|
2017-11-20 14:32:36 +01:00
|
|
|
else:
|
2018-08-07 08:56:48 +02:00
|
|
|
fp = hx_path if hx_path != None else arm.utils.get_fp()
|
|
|
|
webbrowser.open('file://' + fp)
|
2017-11-20 14:32:36 +01:00
|
|
|
|
|
|
|
def def_strings_to_array(strdefs):
|
|
|
|
defs = strdefs.split('_')
|
|
|
|
defs = defs[1:]
|
|
|
|
defs = ['_' + d for d in defs] # Restore _
|
|
|
|
return defs
|
|
|
|
|
|
|
|
def get_kha_target(target_name): # TODO: remove
|
2018-12-05 10:36:36 +01:00
|
|
|
if target_name == 'macos-hl':
|
|
|
|
return 'osx-hl'
|
|
|
|
elif target_name.startswith('krom'): # krom-windows
|
2018-03-04 19:38:40 +01:00
|
|
|
return 'krom'
|
2018-12-05 10:36:36 +01:00
|
|
|
elif target_name == 'custom':
|
|
|
|
return ''
|
2017-11-20 14:32:36 +01:00
|
|
|
return target_name
|
|
|
|
|
|
|
|
def target_to_gapi(arm_project_target):
|
|
|
|
# TODO: align target names
|
|
|
|
if arm_project_target == 'krom':
|
|
|
|
return 'arm_gapi_' + arm.utils.get_os()
|
2018-03-04 19:38:40 +01:00
|
|
|
elif arm_project_target == 'krom-windows':
|
|
|
|
return 'arm_gapi_win'
|
2018-04-06 17:39:22 +02:00
|
|
|
elif arm_project_target == 'windows-hl':
|
|
|
|
return 'arm_gapi_win'
|
2018-03-04 19:38:40 +01:00
|
|
|
elif arm_project_target == 'krom-linux':
|
|
|
|
return 'arm_gapi_linux'
|
2018-04-13 19:14:11 +02:00
|
|
|
elif arm_project_target == 'linux-hl':
|
|
|
|
return 'arm_gapi_linux'
|
2018-03-04 19:38:40 +01:00
|
|
|
elif arm_project_target == 'krom-macos':
|
|
|
|
return 'arm_gapi_mac'
|
2018-04-13 19:14:11 +02:00
|
|
|
elif arm_project_target == 'macos-hl':
|
|
|
|
return 'arm_gapi_mac'
|
2018-12-05 10:36:36 +01:00
|
|
|
elif arm_project_target == 'android-native-hl':
|
2017-11-20 14:32:36 +01:00
|
|
|
return 'arm_gapi_android'
|
2018-12-05 10:36:36 +01:00
|
|
|
elif arm_project_target == 'ios-hl':
|
|
|
|
return 'arm_gapi_ios'
|
2017-11-20 14:32:36 +01:00
|
|
|
elif arm_project_target == 'node':
|
|
|
|
return 'arm_gapi_html5'
|
2018-12-05 10:36:36 +01:00
|
|
|
else: # html5, custom
|
2017-11-20 14:32:36 +01:00
|
|
|
return 'arm_gapi_' + arm_project_target
|
|
|
|
|
2018-08-15 15:42:25 +02:00
|
|
|
def check_default_props():
|
2017-11-22 21:17:36 +01:00
|
|
|
wrd = bpy.data.worlds['Arm']
|
|
|
|
if len(wrd.arm_rplist) == 0:
|
|
|
|
wrd.arm_rplist.add()
|
|
|
|
wrd.arm_rplist_index = 0
|
|
|
|
|
2018-08-15 15:42:25 +02:00
|
|
|
if wrd.arm_project_name == '':
|
|
|
|
# Take blend file name
|
|
|
|
wrd.arm_project_name = arm.utils.blend_name()
|
|
|
|
|
2018-08-16 23:07:50 +02:00
|
|
|
def register(local_sdk=False):
|
|
|
|
global use_local_sdk
|
|
|
|
use_local_sdk = local_sdk
|
2016-09-12 02:24:20 +02:00
|
|
|
|
|
|
|
def unregister():
|
|
|
|
pass
|