armory/blender/addon/armory.py

316 lines
12 KiB
Python
Raw Normal View History

2017-11-22 22:13:55 +01:00
# Armory 3D Engine
# https://github.com/armory3d/armory
bl_info = {
"name": "Armory",
"category": "Render",
2018-12-18 23:48:38 +01:00
"location": "Properties -> Render -> Armory Player",
"description": "3D Game Engine for Blender",
2017-11-22 22:13:55 +01:00
"author": "Armory3D.org",
2018-10-13 15:02:47 +02:00
"version": (0, 6, 0),
2018-08-14 16:48:46 +02:00
"blender": (2, 80, 0),
2017-11-22 22:13:55 +01:00
"wiki_url": "http://armory3d.org/manual",
"tracker_url": "https://github.com/armory3d/armory/issues"
}
import os
import sys
import stat
import shutil
import webbrowser
import subprocess
2018-08-08 10:19:18 +02:00
import threading
2017-11-22 22:13:55 +01:00
import bpy
import platform
2017-11-22 22:13:55 +01:00
from bpy.types import Operator, AddonPreferences
from bpy.props import *
from bpy.app.handlers import persistent
def get_os():
s = platform.system()
if s == 'Windows':
return 'win'
elif s == 'Darwin':
return 'mac'
else:
return 'linux'
2017-11-22 22:13:55 +01:00
class ArmoryAddonPreferences(AddonPreferences):
bl_idname = __name__
def sdk_path_update(self, context):
if self.skip_update:
return
self.skip_update = True
self.sdk_path = bpy.path.reduce_dirs([bpy.path.abspath(self.sdk_path)])[0] + '/'
def ffmpeg_path_update(self, context):
if self.skip_update:
return
self.skip_update = True
self.ffmpeg_path = bpy.path.reduce_dirs([bpy.path.abspath(self.ffmpeg_path)])[0]
def renderdoc_path_update(self, context):
if self.skip_update:
return
self.skip_update = True
self.renderdoc_path = bpy.path.reduce_dirs([bpy.path.abspath(self.renderdoc_path)])[0]
2018-12-18 23:48:38 +01:00
sdk_bundled: BoolProperty(name="Bundled SDK", default=True)
sdk_path: StringProperty(name="SDK Path", subtype="FILE_PATH", update=sdk_path_update, default="")
show_advanced: BoolProperty(name="Show Advanced", default=False)
player_gapi_win: EnumProperty(
2018-12-15 13:33:59 +01:00
items = [('direct3d11', 'Auto', 'direct3d11'),
('opengl', 'OpenGL', 'opengl'),
('direct3d11', 'Direct3D11', 'direct3d11')],
2018-12-15 13:33:59 +01:00
name="Player Graphics API", default='direct3d11', description='Use this graphics API when launching the game in Krom player(F5)')
2018-12-18 23:48:38 +01:00
player_gapi_linux: EnumProperty(
items = [('opengl', 'Auto', 'opengl'),
('opengl', 'OpenGL', 'opengl')],
name="Player Graphics API", default='opengl', description='Use this graphics API when launching the game in Krom player(F5)')
2018-12-18 23:48:38 +01:00
player_gapi_mac: EnumProperty(
items = [('opengl', 'Auto', 'opengl'),
('opengl', 'OpenGL', 'opengl')],
name="Player Graphics API", default='opengl', description='Use this graphics API when launching the game in Krom player(F5)')
2018-12-18 23:48:38 +01:00
code_editor: EnumProperty(
2018-03-25 12:00:43 +02:00
items = [('kodestudio', 'Kode Studio', 'kodestudio'),
('default', 'System Default', 'default')],
name="Code Editor", default='kodestudio', description='Use this editor for editing scripts')
2018-12-18 23:48:38 +01:00
ui_scale: FloatProperty(name='UI Scale', description='Adjust UI scale for Armory tools', default=1.0, min=1.0, max=4.0)
khamake_threads: IntProperty(name='Khamake Threads', description='Allow Khamake to spawn multiple processes for faster builds', default=4, min=1)
renderdoc_path: StringProperty(name="RenderDoc Path", description="Binary path", subtype="FILE_PATH", update=renderdoc_path_update, default="")
ffmpeg_path: StringProperty(name="FFMPEG Path", description="Binary path", subtype="FILE_PATH", update=ffmpeg_path_update, default="")
save_on_build: BoolProperty(name="Save on Build", description="Save .blend", default=False)
legacy_shaders: BoolProperty(name="Legacy Shaders", description="Attempt to compile shaders runnable on older hardware", default=False)
relative_paths: BoolProperty(name="Generate Relative Paths", description="Write relative paths in khafile", default=False)
viewport_controls: EnumProperty(
2017-11-22 22:13:55 +01:00
items=[('qwerty', 'qwerty', 'qwerty'),
('azerty', 'azerty', 'azerty')],
name="Viewport Controls", default='qwerty', description='Viewport camera mode controls')
2018-12-18 23:48:38 +01:00
skip_update: BoolProperty(name="", default=False)
2017-11-22 22:13:55 +01:00
def draw(self, context):
self.skip_update = False
layout = self.layout
2018-12-18 23:48:38 +01:00
layout.label(text="Welcome to Armory! Click 'Save Preferences' at the bottom to keep Armory enabled.")
2018-05-24 22:16:28 +02:00
p = bundled_sdk_path()
if os.path.exists(p):
2017-11-22 22:13:55 +01:00
layout.prop(self, "sdk_bundled")
if not self.sdk_bundled:
layout.prop(self, "sdk_path")
else:
layout.prop(self, "sdk_path")
2018-03-29 12:56:52 +02:00
box = layout.box().column()
2018-09-05 10:20:02 +02:00
box.label(text="Armory Updater")
box.label(text="Note: Development version may run unstable!")
2018-03-29 12:56:52 +02:00
row = box.row(align=True)
row.alignment = 'EXPAND'
2018-11-12 15:32:12 +01:00
row.operator("arm_addon.help", icon="URL")
2018-03-29 12:56:52 +02:00
row.operator("arm_addon.update", icon="FILE_REFRESH")
row.operator("arm_addon.restore")
2018-11-16 11:34:56 +01:00
box.label(text="Check console for download progress. Please restart Blender after successful SDK update.")
2017-11-22 22:13:55 +01:00
layout.prop(self, "show_advanced")
if self.show_advanced:
box = layout.box().column()
box.prop(self, "player_gapi_" + get_os())
2018-03-25 12:00:43 +02:00
box.prop(self, "code_editor")
box.prop(self, "renderdoc_path")
box.prop(self, "ffmpeg_path")
box.prop(self, "viewport_controls")
2018-03-25 21:48:30 +02:00
box.prop(self, "ui_scale")
box.prop(self, "khamake_threads")
box.prop(self, "save_on_build")
box.prop(self, "legacy_shaders")
2018-08-16 20:48:00 +02:00
box.prop(self, "relative_paths")
2017-11-22 22:13:55 +01:00
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
def get_fp():
if bpy.data.filepath == '':
return ''
s = bpy.data.filepath.split(os.path.sep)
s.pop()
return os.path.sep.join(s)
2017-11-22 22:13:55 +01:00
def get_sdk_path(context):
preferences = 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 os.path.exists(get_fp() + '/armsdk'):
return get_fp() + '/armsdk'
elif os.path.exists(p) and addon_prefs.sdk_bundled:
2018-05-24 22:16:28 +02:00
return p
2017-11-22 22:13:55 +01:00
else:
return addon_prefs.sdk_path
def remove_readonly(func, path, excinfo):
os.chmod(path, stat.S_IWRITE)
func(path)
2018-08-08 10:19:18 +02:00
def run_proc(cmd, done):
def fn(p, done):
p.wait()
if done != None:
done()
p = subprocess.Popen(cmd)
threading.Thread(target=fn, args=(p, done)).start()
return p
2018-11-16 11:34:56 +01:00
def git_clone(done, p, gitn, n, recursive=False):
2017-11-22 22:13:55 +01:00
if not os.path.exists(p + '/' + n + '_backup'):
os.rename(p + '/' + n, p + '/' + n + '_backup')
if os.path.exists(p + '/' + n):
shutil.rmtree(p + '/' + n, onerror=remove_readonly)
2018-11-16 11:34:56 +01:00
if recursive:
run_proc(['git', 'clone', '--recursive', 'https://github.com/' + gitn, p + '/' + n, '--depth', '1', '--shallow-submodules', '--jobs', '4'], done)
else:
run_proc(['git', 'clone', 'https://github.com/' + gitn, p + '/' + n, '--depth', '1'], done)
2017-11-22 22:13:55 +01:00
def restore_repo(p, n):
if os.path.exists(p + '/' + n + '_backup'):
if os.path.exists(p + '/' + n):
shutil.rmtree(p + '/' + n, onerror=remove_readonly)
os.rename(p + '/' + n + '_backup', p + '/' + n)
class ArmAddonStartButton(bpy.types.Operator):
'''Start Armory integration'''
bl_idname = "arm_addon.start"
bl_label = "Start"
running = False
def execute(self, context):
sdk_path = get_sdk_path(context)
if sdk_path == "":
2018-08-16 23:07:50 +02:00
print("Configure Armory SDK path first")
2017-11-22 22:13:55 +01:00
return {"CANCELLED"}
scripts_path = sdk_path + "/armory/blender/"
sys.path.append(scripts_path)
2018-08-16 23:07:50 +02:00
local_sdk = os.path.exists(get_fp() + '/armsdk')
2017-11-22 22:13:55 +01:00
import start
2018-08-16 23:07:50 +02:00
start.register(local_sdk=local_sdk)
2017-11-22 22:13:55 +01:00
ArmAddonStartButton.running = True
return {"FINISHED"}
class ArmAddonStopButton(bpy.types.Operator):
'''Stop Armory integration'''
bl_idname = "arm_addon.stop"
bl_label = "Stop"
def execute(self, context):
import start
start.unregister()
ArmAddonStartButton.running = False
return {"FINISHED"}
class ArmAddonUpdateButton(bpy.types.Operator):
'''Update Armory SDK'''
bl_idname = "arm_addon.update"
bl_label = "Update SDK"
bl_description = "Update to the latest development version"
2018-08-08 10:19:18 +02:00
2017-11-22 22:13:55 +01:00
def execute(self, context):
2018-11-16 11:34:56 +01:00
sdk_path = get_sdk_path(context)
if sdk_path == "":
2018-08-16 23:07:50 +02:00
self.report({"ERROR"}, "Configure Armory SDK path first")
2017-11-22 22:13:55 +01:00
return {"CANCELLED"}
2018-11-16 11:34:56 +01:00
self.report({'INFO'}, 'Updating Armory SDK, check console for details.')
2017-12-06 12:36:39 +01:00
print('Armory (add-on v' + str(bl_info['version']) + '): Cloning [armory, iron, haxebullet, haxerecast, zui] repositories')
2018-11-16 11:34:56 +01:00
os.chdir(sdk_path)
2018-08-08 10:19:18 +02:00
global repos_updated
global repos_total
repos_updated = 0
2018-11-16 11:34:56 +01:00
repos_total = 9
2018-08-08 10:19:18 +02:00
def done():
global repos_updated
global repos_total
repos_updated += 1
if repos_updated == repos_total:
print('Armory SDK updated, please restart Blender')
2018-11-16 11:34:56 +01:00
git_clone(done, sdk_path, 'armory3d/armory', 'armory')
git_clone(done, sdk_path, 'armory3d/iron', 'iron')
git_clone(done, sdk_path, 'armory3d/haxebullet', 'lib/haxebullet')
git_clone(done, sdk_path, 'armory3d/haxerecast', 'lib/haxerecast')
git_clone(done, sdk_path, 'armory3d/zui', 'lib/zui')
git_clone(done, sdk_path, 'armory3d/armory_tools', 'lib/armory_tools')
git_clone(done, sdk_path, 'armory3d/iron_format', 'lib/iron_format')
2018-12-18 23:48:38 +01:00
git_clone(done, sdk_path, 'armory3d/Krom_bin', 'Krom')
2019-01-12 01:20:55 +01:00
git_clone(done, sdk_path, 'armory3d/Kha', 'Kha', recursive=True)
2017-11-22 22:13:55 +01:00
return {"FINISHED"}
class ArmAddonRestoreButton(bpy.types.Operator):
'''Update Armory SDK'''
bl_idname = "arm_addon.restore"
bl_label = "Restore SDK"
bl_description = "Restore stable version"
def execute(self, context):
2018-11-16 11:34:56 +01:00
sdk_path = get_sdk_path(context)
if sdk_path == "":
2018-08-16 23:07:50 +02:00
self.report({"ERROR"}, "Configure Armory SDK path first")
2017-11-22 22:13:55 +01:00
return {"CANCELLED"}
2018-11-16 11:34:56 +01:00
os.chdir(sdk_path)
restore_repo(sdk_path, 'armory')
restore_repo(sdk_path, 'iron')
restore_repo(sdk_path, 'lib/haxebullet')
restore_repo(sdk_path, 'lib/haxerecast')
restore_repo(sdk_path, 'lib/zui')
restore_repo(sdk_path, 'lib/armory_tools')
restore_repo(sdk_path, 'lib/iron_format')
restore_repo(sdk_path, 'Kha')
restore_repo(sdk_path, 'Krom')
2018-03-25 12:31:22 +02:00
self.report({'INFO'}, 'Restored stable version')
2017-11-22 22:13:55 +01:00
return {"FINISHED"}
2018-11-12 15:32:12 +01:00
class ArmAddonHelpButton(bpy.types.Operator):
'''Updater help'''
bl_idname = "arm_addon.help"
bl_label = "Help"
2017-11-22 22:13:55 +01:00
bl_description = "Git is required for Armory Updater to work"
def execute(self, context):
2018-11-12 15:32:12 +01:00
webbrowser.open('https://armory3d.org/manual/#/dev/gitversion')
2017-11-22 22:13:55 +01:00
return {"FINISHED"}
@persistent
2018-08-16 23:07:50 +02:00
def on_load_post(context):
if ArmAddonStartButton.running:
return
2017-11-22 22:13:55 +01:00
bpy.ops.arm_addon.start()
def register():
2018-08-14 16:48:46 +02:00
bpy.utils.register_class(ArmoryAddonPreferences)
bpy.utils.register_class(ArmAddonStartButton)
bpy.utils.register_class(ArmAddonStopButton)
bpy.utils.register_class(ArmAddonUpdateButton)
bpy.utils.register_class(ArmAddonRestoreButton)
2018-11-12 15:32:12 +01:00
bpy.utils.register_class(ArmAddonHelpButton)
2018-08-16 23:07:50 +02:00
bpy.app.handlers.load_post.append(on_load_post)
2017-11-22 22:13:55 +01:00
def unregister():
bpy.ops.arm_addon.stop()
2018-08-14 16:48:46 +02:00
bpy.utils.unregister_class(ArmoryAddonPreferences)
bpy.utils.unregister_class(ArmAddonStartButton)
bpy.utils.unregister_class(ArmAddonStopButton)
bpy.utils.unregister_class(ArmAddonUpdateButton)
bpy.utils.unregister_class(ArmAddonRestoreButton)
2018-11-12 15:32:12 +01:00
bpy.utils.unregister_class(ArmAddonHelpButton)
2018-08-16 23:07:50 +02:00
bpy.app.handlers.load_post.remove(on_load_post)
2017-11-22 22:13:55 +01:00
if __name__ == "__main__":
register()