5f8b92f9c3
The same concept for spot lights was applied for point lights; the difference is that because point lights require to do the projection in the shader, the first inversion was applied in sampleCube() when returning the uv coordinates. "_FlipY" was added to replace "_InvY" in "PCFFakeCube()" because the inversion is only necessary for shaders and not anything else, otherwise it would break rendering of other parts.
903 lines
37 KiB
Python
Executable file
903 lines
37 KiB
Python
Executable file
import glob
|
|
import json
|
|
import os
|
|
import shutil
|
|
import time
|
|
import stat
|
|
import subprocess
|
|
import threading
|
|
import webbrowser
|
|
import shlex
|
|
import errno
|
|
import math
|
|
|
|
import bpy
|
|
|
|
import arm.assets as assets
|
|
from arm.exporter import ArmoryExporter
|
|
import arm.lib.make_datas
|
|
import arm.lib.server
|
|
import arm.log as log
|
|
import arm.make_logic as make_logic
|
|
import arm.make_renderpath as make_renderpath
|
|
import arm.make_state as state
|
|
import arm.make_world as make_world
|
|
import arm.utils
|
|
import arm.write_data as write_data
|
|
|
|
scripts_mtime = 0 # Monitor source changes
|
|
profile_time = 0
|
|
|
|
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
|
|
|
|
def compile_shader_pass(res, raw_shaders_path, shader_name, defs, make_variants):
|
|
os.chdir(raw_shaders_path + '/' + shader_name)
|
|
|
|
# Open json file
|
|
json_name = shader_name + '.json'
|
|
with open(json_name) as f:
|
|
json_file = f.read()
|
|
json_data = json.loads(json_file)
|
|
|
|
fp = arm.utils.get_fp_build()
|
|
arm.lib.make_datas.make(res, shader_name, json_data, fp, defs, make_variants)
|
|
|
|
path = fp + '/compiled/Shaders'
|
|
c = json_data['contexts'][0]
|
|
for s in ['vertex_shader', 'fragment_shader', 'geometry_shader', 'tesscontrol_shader', 'tesseval_shader']:
|
|
if s in c:
|
|
shutil.copy(c[s], path + '/' + c[s].split('/')[-1])
|
|
|
|
def remove_readonly(func, path, excinfo):
|
|
os.chmod(path, stat.S_IWRITE)
|
|
func(path)
|
|
|
|
def export_data(fp, sdk_path):
|
|
wrd = bpy.data.worlds['Arm']
|
|
|
|
print('Armory v{0} ({1})'.format(wrd.arm_version, wrd.arm_commit))
|
|
if wrd.arm_verbose_output:
|
|
print(f'Blender: {bpy.app.version_string}, Target: {state.target}, GAPI: {arm.utils.get_gapi()}')
|
|
|
|
# Clean compiled variants if cache is disabled
|
|
build_dir = arm.utils.get_fp_build()
|
|
if not wrd.arm_cache_build:
|
|
if os.path.isdir(build_dir + '/debug/html5-resources'):
|
|
shutil.rmtree(build_dir + '/debug/html5-resources', onerror=remove_readonly)
|
|
if os.path.isdir(build_dir + '/krom-resources'):
|
|
shutil.rmtree(build_dir + '/krom-resources', onerror=remove_readonly)
|
|
if os.path.isdir(build_dir + '/debug/krom-resources'):
|
|
shutil.rmtree(build_dir + '/debug/krom-resources', onerror=remove_readonly)
|
|
if os.path.isdir(build_dir + '/windows-resources'):
|
|
shutil.rmtree(build_dir + '/windows-resources', onerror=remove_readonly)
|
|
if os.path.isdir(build_dir + '/linux-resources'):
|
|
shutil.rmtree(build_dir + '/linux-resources', onerror=remove_readonly)
|
|
if os.path.isdir(build_dir + '/osx-resources'):
|
|
shutil.rmtree(build_dir + '/osx-resources', onerror=remove_readonly)
|
|
if os.path.isdir(build_dir + '/compiled/Shaders'):
|
|
shutil.rmtree(build_dir + '/compiled/Shaders', onerror=remove_readonly)
|
|
|
|
raw_shaders_path = sdk_path + '/armory/Shaders/'
|
|
assets_path = sdk_path + '/armory/Assets/'
|
|
export_physics = bpy.data.worlds['Arm'].arm_physics != 'Disabled'
|
|
export_navigation = bpy.data.worlds['Arm'].arm_navigation != 'Disabled'
|
|
export_ui = bpy.data.worlds['Arm'].arm_ui != 'Disabled'
|
|
assets.reset()
|
|
|
|
# Build node trees
|
|
ArmoryExporter.import_traits = []
|
|
make_logic.build()
|
|
make_world.build()
|
|
make_renderpath.build()
|
|
|
|
# Export scene data
|
|
assets.embedded_data = sorted(list(set(assets.embedded_data)))
|
|
physics_found = False
|
|
navigation_found = False
|
|
ui_found = False
|
|
ArmoryExporter.compress_enabled = state.is_publish and wrd.arm_asset_compression
|
|
ArmoryExporter.optimize_enabled = state.is_publish and wrd.arm_optimize_data
|
|
if not os.path.exists(build_dir + '/compiled/Assets'):
|
|
os.makedirs(build_dir + '/compiled/Assets')
|
|
# have a "zoo" collection in the current scene
|
|
export_coll = bpy.data.collections.new("export_coll")
|
|
bpy.context.scene.collection.children.link(export_coll)
|
|
for scene in bpy.data.scenes:
|
|
if scene == bpy.context.scene: continue
|
|
for o in scene.collection.all_objects:
|
|
if o.type == "MESH" or o.type == "EMPTY":
|
|
if o.name not in export_coll.all_objects.keys():
|
|
export_coll.objects.link(o)
|
|
depsgraph = bpy.context.evaluated_depsgraph_get()
|
|
bpy.data.collections.remove(export_coll) # destroy "zoo" collection
|
|
|
|
for scene in bpy.data.scenes:
|
|
if scene.arm_export:
|
|
ext = '.lz4' if ArmoryExporter.compress_enabled else '.arm'
|
|
asset_path = build_dir + '/compiled/Assets/' + arm.utils.safestr(scene.name) + ext
|
|
ArmoryExporter.export_scene(bpy.context, asset_path, scene=scene, depsgraph=depsgraph)
|
|
if ArmoryExporter.export_physics:
|
|
physics_found = True
|
|
if ArmoryExporter.export_navigation:
|
|
navigation_found = True
|
|
if ArmoryExporter.export_ui:
|
|
ui_found = True
|
|
assets.add(asset_path)
|
|
|
|
if physics_found == False: # Disable physics if no rigid body is exported
|
|
export_physics = False
|
|
|
|
if navigation_found == False:
|
|
export_navigation = False
|
|
|
|
if ui_found == False:
|
|
export_ui = False
|
|
|
|
if wrd.arm_ui == 'Enabled':
|
|
export_ui = True
|
|
|
|
modules = []
|
|
if wrd.arm_audio == 'Enabled':
|
|
modules.append('audio')
|
|
if export_physics:
|
|
modules.append('physics')
|
|
if export_navigation:
|
|
modules.append('navigation')
|
|
if export_ui:
|
|
modules.append('ui')
|
|
|
|
defs = arm.utils.def_strings_to_array(wrd.world_defs)
|
|
cdefs = arm.utils.def_strings_to_array(wrd.compo_defs)
|
|
|
|
if wrd.arm_verbose_output:
|
|
print('Exported modules:', ', '.join(modules))
|
|
print('Shader flags:', ' '.join(defs))
|
|
print('Compositor flags:', ' '.join(cdefs))
|
|
print('Khafile flags:', ' '.join(assets.khafile_defs))
|
|
|
|
# Render path is configurable at runtime
|
|
has_config = wrd.arm_write_config or os.path.exists(arm.utils.get_fp() + '/Bundled/config.arm')
|
|
|
|
# Write compiled.inc
|
|
shaders_path = build_dir + '/compiled/Shaders'
|
|
if not os.path.exists(shaders_path):
|
|
os.makedirs(shaders_path)
|
|
write_data.write_compiledglsl(defs + cdefs, make_variants=has_config)
|
|
|
|
# Write referenced shader passes
|
|
if not os.path.isfile(build_dir + '/compiled/Shaders/shader_datas.arm') or state.last_world_defs != wrd.world_defs:
|
|
res = {'shader_datas': []}
|
|
|
|
for ref in assets.shader_passes:
|
|
# Ensure shader pass source exists
|
|
if not os.path.exists(raw_shaders_path + '/' + ref):
|
|
continue
|
|
assets.shader_passes_assets[ref] = []
|
|
if ref.startswith('compositor_pass'):
|
|
compile_shader_pass(res, raw_shaders_path, ref, defs + cdefs, make_variants=has_config)
|
|
else:
|
|
compile_shader_pass(res, raw_shaders_path, ref, defs, make_variants=has_config)
|
|
|
|
# Workaround to also export non-material world shaders
|
|
res['shader_datas'] += make_world.shader_datas
|
|
|
|
arm.utils.write_arm(shaders_path + '/shader_datas.arm', res)
|
|
|
|
for ref in assets.shader_passes:
|
|
for s in assets.shader_passes_assets[ref]:
|
|
assets.add_shader(shaders_path + '/' + s + '.glsl')
|
|
for file in assets.shaders_external:
|
|
name = file.split('/')[-1].split('\\')[-1]
|
|
target = build_dir + '/compiled/Shaders/' + name
|
|
if not os.path.exists(target):
|
|
shutil.copy(file, target)
|
|
state.last_world_defs = wrd.world_defs
|
|
|
|
# Reset path
|
|
os.chdir(fp)
|
|
|
|
# Copy std shaders
|
|
if not os.path.isdir(build_dir + '/compiled/Shaders/std'):
|
|
shutil.copytree(raw_shaders_path + 'std', build_dir + '/compiled/Shaders/std')
|
|
|
|
# Write config.arm
|
|
resx, resy = arm.utils.get_render_resolution(arm.utils.get_active_scene())
|
|
if wrd.arm_write_config:
|
|
write_data.write_config(resx, resy)
|
|
|
|
# Change project version (Build, Publish)
|
|
if (not state.is_play) and (wrd.arm_project_version_autoinc):
|
|
wrd.arm_project_version = arm.utils.arm.utils.change_version_project(wrd.arm_project_version)
|
|
|
|
# Write khafile.js
|
|
enable_dce = state.is_publish and wrd.arm_dce
|
|
import_logic = not state.is_publish and arm.utils.logic_editor_space() != None
|
|
write_data.write_khafilejs(state.is_play, export_physics, export_navigation, export_ui, state.is_publish, enable_dce, ArmoryExporter.import_traits, import_logic)
|
|
|
|
# Write Main.hx - depends on write_khafilejs for writing number of assets
|
|
scene_name = arm.utils.get_project_scene_name()
|
|
write_data.write_mainhx(scene_name, resx, resy, state.is_play, state.is_publish)
|
|
if scene_name != state.last_scene or resx != state.last_resx or resy != state.last_resy:
|
|
wrd.arm_recompile = True
|
|
state.last_resx = resx
|
|
state.last_resy = resy
|
|
state.last_scene = scene_name
|
|
|
|
def compile(assets_only=False):
|
|
wrd = bpy.data.worlds['Arm']
|
|
fp = arm.utils.get_fp()
|
|
os.chdir(fp)
|
|
|
|
# Set build command
|
|
target_name = state.target
|
|
|
|
node_path = arm.utils.get_node_path()
|
|
khamake_path = arm.utils.get_khamake_path()
|
|
cmd = [node_path, khamake_path]
|
|
|
|
kha_target_name = arm.utils.get_kha_target(target_name)
|
|
if kha_target_name != '':
|
|
cmd.append(kha_target_name)
|
|
|
|
# Custom exporter
|
|
if state.is_export:
|
|
item = wrd.arm_exporterlist[wrd.arm_exporterlist_index]
|
|
if item.arm_project_target == 'custom' and item.arm_project_khamake != '':
|
|
for s in item.arm_project_khamake.split(' '):
|
|
cmd.append(s)
|
|
|
|
ffmpeg_path = arm.utils.get_ffmpeg_path() # Path to binary
|
|
if ffmpeg_path != '':
|
|
cmd.append('--ffmpeg')
|
|
cmd.append(ffmpeg_path) # '"' + ffmpeg_path + '"'
|
|
|
|
state.export_gapi = arm.utils.get_gapi()
|
|
cmd.append('-g')
|
|
cmd.append(state.export_gapi)
|
|
# Windows - Set Visual Studio Version
|
|
if state.target.startswith('windows'):
|
|
cmd.append('-visualstudio')
|
|
vs_ver, vs_year, vs_name, vs_id = arm.utils.get_visual_studio_from_version(wrd.arm_project_win_list_vs)
|
|
cmd.append(vs_id)
|
|
|
|
if arm.utils.get_legacy_shaders() or 'ios' in state.target:
|
|
if 'html5' in state.target or 'ios' in state.target:
|
|
pass
|
|
else:
|
|
cmd.append('--shaderversion')
|
|
cmd.append('110')
|
|
elif 'android' in state.target or 'html5' in state.target:
|
|
cmd.append('--shaderversion')
|
|
cmd.append('300')
|
|
else:
|
|
cmd.append('--shaderversion')
|
|
cmd.append('330')
|
|
|
|
if '_VR' in wrd.world_defs:
|
|
cmd.append('--vr')
|
|
cmd.append('webvr')
|
|
|
|
if arm.utils.get_pref_or_default('khamake_debug', False):
|
|
cmd.append('--debug')
|
|
|
|
if arm.utils.get_rp().rp_renderer == 'Raytracer':
|
|
cmd.append('--raytrace')
|
|
cmd.append('dxr')
|
|
dxc_path = fp + '/HlslShaders/dxc.exe'
|
|
subprocess.Popen([dxc_path, '-Zpr', '-Fo', fp + '/Bundled/raytrace.cso', '-T', 'lib_6_3', fp + '/HlslShaders/raytrace.hlsl']).wait()
|
|
|
|
if arm.utils.get_khamake_threads() > 1:
|
|
cmd.append('--parallelAssetConversion')
|
|
cmd.append(str(arm.utils.get_khamake_threads()))
|
|
|
|
compilation_server = False
|
|
|
|
cmd.append('--to')
|
|
if (kha_target_name == 'krom' and not state.is_publish) or (kha_target_name == 'html5' and not state.is_publish):
|
|
cmd.append(arm.utils.build_dir() + '/debug')
|
|
# Start compilation server
|
|
if kha_target_name == 'krom' and arm.utils.get_compilation_server() and not assets_only and wrd.arm_cache_build:
|
|
compilation_server = True
|
|
arm.lib.server.run_haxe(arm.utils.get_haxe_path())
|
|
else:
|
|
cmd.append(arm.utils.build_dir())
|
|
|
|
if not wrd.arm_verbose_output:
|
|
cmd.append("--quiet")
|
|
else:
|
|
print("Using project from " + arm.utils.get_fp())
|
|
print("Running: ", *cmd)
|
|
|
|
#Project needs to be compiled at least once
|
|
#before compilation server can work
|
|
if not os.path.exists(arm.utils.build_dir() + '/debug/krom/krom.js') and not state.is_publish:
|
|
state.proc_build = run_proc(cmd, build_done)
|
|
else:
|
|
if assets_only or compilation_server:
|
|
cmd.append('--nohaxe')
|
|
cmd.append('--noproject')
|
|
state.proc_build = run_proc(cmd, assets_done if compilation_server else build_done)
|
|
|
|
def build(target, is_play=False, is_publish=False, is_export=False):
|
|
global profile_time
|
|
profile_time = time.time()
|
|
|
|
state.target = target
|
|
state.is_play = is_play
|
|
state.is_publish = is_publish
|
|
state.is_export = is_export
|
|
|
|
# Save blend
|
|
if arm.utils.get_save_on_build():
|
|
bpy.ops.wm.save_mainfile()
|
|
|
|
log.clear(clear_warnings=True)
|
|
|
|
# Set camera in active scene
|
|
active_scene = arm.utils.get_active_scene()
|
|
if active_scene.camera == None:
|
|
for o in active_scene.objects:
|
|
if o.type == 'CAMERA':
|
|
active_scene.camera = o
|
|
break
|
|
|
|
# Get paths
|
|
sdk_path = arm.utils.get_sdk_path()
|
|
raw_shaders_path = sdk_path + '/armory/Shaders/'
|
|
|
|
# Set dir
|
|
fp = arm.utils.get_fp()
|
|
os.chdir(fp)
|
|
|
|
# Create directories
|
|
wrd = bpy.data.worlds['Arm']
|
|
sources_path = 'Sources/' + arm.utils.safestr(wrd.arm_project_package)
|
|
if not os.path.exists(sources_path):
|
|
os.makedirs(sources_path)
|
|
|
|
# Save external scripts edited inside Blender
|
|
write_texts = False
|
|
for text in bpy.data.texts:
|
|
if text.filepath != '' and text.is_dirty:
|
|
write_texts = True
|
|
break
|
|
if write_texts:
|
|
area = bpy.context.area
|
|
old_type = area.type
|
|
area.type = 'TEXT_EDITOR'
|
|
for text in bpy.data.texts:
|
|
if text.filepath != '' and text.is_dirty and os.path.isfile(text.filepath):
|
|
area.spaces[0].text = text
|
|
bpy.ops.text.save()
|
|
area.type = old_type
|
|
|
|
# Save internal Haxe scripts
|
|
for text in bpy.data.texts:
|
|
if text.filepath == '' and text.name[-3:] == '.hx':
|
|
with open('Sources/' + arm.utils.safestr(wrd.arm_project_package) + '/' + text.name, 'w') as f:
|
|
f.write(text.as_string())
|
|
|
|
# Export data
|
|
export_data(fp, sdk_path)
|
|
|
|
if state.target == 'html5':
|
|
w, h = arm.utils.get_render_resolution(arm.utils.get_active_scene())
|
|
write_data.write_indexhtml(w, h, is_publish)
|
|
# Bundle files from include dir
|
|
if os.path.isdir('include'):
|
|
dest = '/html5/' if is_publish else '/debug/html5/'
|
|
for fn in glob.iglob(os.path.join('include', '**'), recursive=False):
|
|
shutil.copy(fn, arm.utils.build_dir() + dest + os.path.basename(fn))
|
|
|
|
def play_done():
|
|
state.proc_play = None
|
|
state.redraw_ui = True
|
|
log.clear()
|
|
|
|
def assets_done():
|
|
if state.proc_build == None:
|
|
return
|
|
result = state.proc_build.poll()
|
|
if result == 0:
|
|
# Connect to the compilation server
|
|
os.chdir(arm.utils.build_dir() + '/debug/')
|
|
cmd = [arm.utils.get_haxe_path(), '--connect', '6000', 'project-krom.hxml']
|
|
state.proc_build = run_proc(cmd, compilation_server_done)
|
|
else:
|
|
state.proc_build = None
|
|
state.redraw_ui = True
|
|
log.error('Build failed, check console')
|
|
|
|
def compilation_server_done():
|
|
if state.proc_build == None:
|
|
return
|
|
result = state.proc_build.poll()
|
|
if result == 0:
|
|
if os.path.exists('krom/krom.js.temp'):
|
|
os.chmod('krom/krom.js', stat.S_IWRITE)
|
|
os.remove('krom/krom.js')
|
|
os.rename('krom/krom.js.temp', 'krom/krom.js')
|
|
build_done()
|
|
else:
|
|
state.proc_build = None
|
|
state.redraw_ui = True
|
|
log.error('Build failed, check console')
|
|
|
|
def build_done():
|
|
print('Finished in {:0.3f}s'.format(time.time() - profile_time))
|
|
if log.num_warnings > 0:
|
|
log.print_warn(f'{log.num_warnings} warnings occurred during compilation')
|
|
if state.proc_build is None:
|
|
return
|
|
result = state.proc_build.poll()
|
|
state.proc_build = None
|
|
state.redraw_ui = True
|
|
if result == 0:
|
|
bpy.data.worlds['Arm'].arm_recompile = False
|
|
build_success()
|
|
else:
|
|
log.error('Build failed, check console')
|
|
|
|
def patch():
|
|
if state.proc_build != None:
|
|
return
|
|
assets.invalidate_enabled = False
|
|
fp = arm.utils.get_fp()
|
|
os.chdir(fp)
|
|
asset_path = arm.utils.get_fp_build() + '/compiled/Assets/' + arm.utils.safestr(bpy.context.scene.name) + '.arm'
|
|
ArmoryExporter.export_scene(bpy.context, asset_path, scene=bpy.context.scene)
|
|
if not os.path.isdir(arm.utils.build_dir() + '/compiled/Shaders/std'):
|
|
raw_shaders_path = arm.utils.get_sdk_path() + '/armory/Shaders/'
|
|
shutil.copytree(raw_shaders_path + 'std', arm.utils.build_dir() + '/compiled/Shaders/std')
|
|
node_path = arm.utils.get_node_path()
|
|
khamake_path = arm.utils.get_khamake_path()
|
|
|
|
cmd = [node_path, khamake_path, 'krom']
|
|
cmd.extend(('--shaderversion', '330', '--parallelAssetConversion', '4',
|
|
'--to', arm.utils.build_dir() + '/debug', '--nohaxe', '--noproject'))
|
|
|
|
assets.invalidate_enabled = True
|
|
state.proc_build = run_proc(cmd, patch_done)
|
|
|
|
def patch_done():
|
|
js = 'iron.Scene.patch();'
|
|
write_patch(js)
|
|
state.proc_build = None
|
|
|
|
patch_id = 0
|
|
|
|
def write_patch(js):
|
|
global patch_id
|
|
with open(arm.utils.get_fp_build() + '/debug/krom/krom.patch', 'w') as f:
|
|
patch_id += 1
|
|
f.write(str(patch_id) + '\n')
|
|
f.write(js)
|
|
|
|
def runtime_to_target():
|
|
wrd = bpy.data.worlds['Arm']
|
|
if wrd.arm_runtime == 'Krom':
|
|
return 'krom'
|
|
else:
|
|
return 'html5'
|
|
|
|
def get_khajs_path(target):
|
|
if target == 'krom':
|
|
return arm.utils.build_dir() + '/debug/krom/krom.js'
|
|
else: # Browser
|
|
return arm.utils.build_dir() + '/debug/html5/kha.js'
|
|
|
|
def play():
|
|
global scripts_mtime
|
|
wrd = bpy.data.worlds['Arm']
|
|
|
|
build(target=runtime_to_target(), is_play=True)
|
|
|
|
khajs_path = get_khajs_path(state.target)
|
|
if not wrd.arm_cache_build or \
|
|
not os.path.isfile(khajs_path) or \
|
|
assets.khafile_defs_last != assets.khafile_defs or \
|
|
state.last_target != state.target:
|
|
wrd.arm_recompile = True
|
|
|
|
state.last_target = state.target
|
|
|
|
# Trait sources modified
|
|
state.mod_scripts = []
|
|
script_path = arm.utils.get_fp() + '/Sources/' + arm.utils.safestr(wrd.arm_project_package)
|
|
if os.path.isdir(script_path):
|
|
new_mtime = scripts_mtime
|
|
for fn in glob.iglob(os.path.join(script_path, '**', '*.hx'), recursive=True):
|
|
mtime = os.path.getmtime(fn)
|
|
if scripts_mtime < mtime:
|
|
arm.utils.fetch_script_props(fn) # Trait props
|
|
fn = fn.split('Sources/')[1]
|
|
fn = fn[:-3] #.hx
|
|
fn = fn.replace('/', '.')
|
|
state.mod_scripts.append(fn)
|
|
wrd.arm_recompile = True
|
|
if new_mtime < mtime:
|
|
new_mtime = mtime
|
|
scripts_mtime = new_mtime
|
|
if len(state.mod_scripts) > 0: # Trait props
|
|
arm.utils.fetch_trait_props()
|
|
|
|
compile(assets_only=(not wrd.arm_recompile))
|
|
|
|
def build_success():
|
|
log.clear()
|
|
wrd = bpy.data.worlds['Arm']
|
|
|
|
if state.is_play:
|
|
if wrd.arm_runtime == 'Browser':
|
|
# Start server
|
|
os.chdir(arm.utils.get_fp())
|
|
t = threading.Thread(name='localserver', target=arm.lib.server.run_tcp)
|
|
t.daemon = True
|
|
t.start()
|
|
html5_app_path = 'http://localhost:8040/' + arm.utils.build_dir() + '/debug/html5'
|
|
webbrowser.open(html5_app_path)
|
|
elif wrd.arm_runtime == 'Krom':
|
|
if wrd.arm_live_patch:
|
|
open(arm.utils.get_fp_build() + '/debug/krom/krom.patch', 'w').close()
|
|
krom_location, krom_path = arm.utils.krom_paths()
|
|
os.chdir(krom_location)
|
|
cmd = [krom_path, arm.utils.get_fp_build() + '/debug/krom', arm.utils.get_fp_build() + '/debug/krom-resources']
|
|
if arm.utils.get_os() == 'win':
|
|
cmd.append('--consolepid')
|
|
cmd.append(str(os.getpid()))
|
|
if wrd.arm_audio == 'Disabled':
|
|
cmd.append('--nosound')
|
|
if wrd.arm_verbose_output:
|
|
print("Running: ", *cmd)
|
|
state.proc_play = run_proc(cmd, play_done)
|
|
|
|
elif state.is_publish:
|
|
sdk_path = arm.utils.get_sdk_path()
|
|
target_name = arm.utils.get_kha_target(state.target)
|
|
files_path = os.path.join(arm.utils.get_fp_build(), target_name)
|
|
|
|
if (target_name == 'html5' or target_name == 'krom') and wrd.arm_minify_js:
|
|
# Minify JS
|
|
minifier_path = sdk_path + '/lib/armory_tools/uglifyjs/bin/uglifyjs'
|
|
if target_name == 'html5':
|
|
jsfile = files_path + '/kha.js'
|
|
else:
|
|
jsfile = files_path + '/krom.js'
|
|
args = [arm.utils.get_node_path(), minifier_path, jsfile, '-o', jsfile]
|
|
proc = subprocess.Popen(args)
|
|
proc.wait()
|
|
|
|
if target_name == 'krom':
|
|
# Copy Krom binaries
|
|
if state.target == 'krom-windows':
|
|
gapi = state.export_gapi
|
|
ext = '' if gapi == 'direct3d11' else '_' + gapi
|
|
krom_location = sdk_path + '/Krom/Krom' + ext + '.exe'
|
|
shutil.copy(krom_location, files_path + '/Krom.exe')
|
|
krom_exe = arm.utils.safestr(wrd.arm_project_name) + '.exe'
|
|
os.rename(files_path + '/Krom.exe', files_path + '/' + krom_exe)
|
|
elif state.target == 'krom-linux':
|
|
krom_location = sdk_path + '/Krom/Krom'
|
|
shutil.copy(krom_location, files_path)
|
|
krom_exe = arm.utils.safestr(wrd.arm_project_name)
|
|
os.rename(files_path + '/Krom', files_path + '/' + krom_exe)
|
|
krom_exe = './' + krom_exe
|
|
else:
|
|
krom_location = sdk_path + '/Krom/Krom.app'
|
|
shutil.copytree(krom_location, files_path + '/Krom.app')
|
|
game_files = os.listdir(files_path)
|
|
for f in game_files:
|
|
f = files_path + '/' + f
|
|
if os.path.isfile(f):
|
|
shutil.move(f, files_path + '/Krom.app/Contents/MacOS')
|
|
krom_exe = arm.utils.safestr(wrd.arm_project_name) + '.app'
|
|
os.rename(files_path + '/Krom.app', files_path + '/' + krom_exe)
|
|
|
|
# Rename
|
|
ext = state.target.split('-')[-1] # krom-windows
|
|
new_files_path = files_path + '-' + ext
|
|
os.rename(files_path, new_files_path)
|
|
files_path = new_files_path
|
|
|
|
if target_name == 'html5':
|
|
project_path = files_path
|
|
print('Exported HTML5 package to ' + project_path)
|
|
elif target_name.startswith('ios') or target_name.startswith('osx'): # TODO: to macos
|
|
project_path = files_path + '-build'
|
|
print('Exported XCode project to ' + project_path)
|
|
elif target_name.startswith('windows'):
|
|
project_path = files_path + '-build'
|
|
vs_ver, vs_year, vs_name, vs_id = arm.utils.get_visual_studio_from_version(wrd.arm_project_win_list_vs)
|
|
print('Exported '+ vs_name +' project to ' + project_path)
|
|
elif target_name.startswith('android'):
|
|
project_name = arm.utils.safesrc(wrd.arm_project_name +'-'+ wrd.arm_project_version)
|
|
project_path = os.path.join(files_path + '-build', project_name)
|
|
print('Exported Android Studio project to ' + project_path)
|
|
elif target_name.startswith('krom'):
|
|
project_path = files_path
|
|
print('Exported Krom package to ' + project_path)
|
|
else:
|
|
project_path = files_path + '-build'
|
|
print('Exported makefiles to ' + project_path)
|
|
|
|
if arm.utils.get_arm_preferences().open_build_directory:
|
|
arm.utils.open_folder(project_path)
|
|
|
|
# Android build APK
|
|
if target_name.startswith('android'):
|
|
if (arm.utils.get_project_android_build_apk()) and (len(arm.utils.get_android_sdk_root_path()) > 0):
|
|
print("\nBuilding APK")
|
|
# Check settings
|
|
path_sdk = arm.utils.get_android_sdk_root_path()
|
|
if len(path_sdk) > 0:
|
|
# Check Environment Variables - ANDROID_SDK_ROOT
|
|
if os.getenv('ANDROID_SDK_ROOT') == None:
|
|
# Set value from settings
|
|
os.environ['ANDROID_SDK_ROOT'] = path_sdk
|
|
else:
|
|
project_path = ''
|
|
|
|
# Build start
|
|
if len(project_path) > 0:
|
|
os.chdir(project_path) # set work folder
|
|
if arm.utils.get_os_is_windows():
|
|
state.proc_publish_build = run_proc(os.path.join(project_path, "gradlew.bat assembleDebug"), done_gradlew_build)
|
|
else:
|
|
cmd = shlex.split(os.path.join(project_path, "gradlew assembleDebug"))
|
|
state.proc_publish_build = run_proc(cmd, done_gradlew_build)
|
|
else:
|
|
print('\nBuilding APK Warning: ANDROID_SDK_ROOT is not specified in environment variables and "Android SDK Path" setting is not specified in preferences: \n- If you specify an environment variable ANDROID_SDK_ROOT, then you need to restart Blender;\n- If you specify the setting "Android SDK Path" in the preferences, then repeat operation "Publish"')
|
|
|
|
# HTML5 After Publish
|
|
if target_name.startswith('html5'):
|
|
if len(arm.utils.get_html5_copy_path()) > 0 and (wrd.arm_project_html5_copy):
|
|
project_name = arm.utils.safesrc(wrd.arm_project_name +'-'+ wrd.arm_project_version)
|
|
dst = os.path.join(arm.utils.get_html5_copy_path(), project_name)
|
|
if os.path.exists(dst):
|
|
shutil.rmtree(dst)
|
|
try:
|
|
shutil.copytree(project_path, dst)
|
|
print("Copied files to " + dst)
|
|
except OSError as exc:
|
|
if exc.errno == errno.ENOTDIR:
|
|
shutil.copy(project_path, dst)
|
|
else: raise
|
|
if len(arm.utils.get_link_web_server()) and (wrd.arm_project_html5_start_browser):
|
|
link_html5_app = arm.utils.get_link_web_server() +'/'+ project_name
|
|
print("Running a browser with a link " + link_html5_app)
|
|
webbrowser.open(link_html5_app)
|
|
|
|
# Windows After Publish
|
|
if target_name.startswith('windows'):
|
|
list_vs = []
|
|
err = ''
|
|
# Print message
|
|
project_name = arm.utils.safesrc(wrd.arm_project_name +'-'+ wrd.arm_project_version)
|
|
if int(wrd.arm_project_win_build) == 1:
|
|
print('\nOpen in Visual Studio ' + os.path.join(project_path, project_name + '.sln"'))
|
|
if int(wrd.arm_project_win_build) == 2:
|
|
print('\nCompile project ' + os.path.join(project_path, project_name + '.vcxproj'))
|
|
if int(wrd.arm_project_win_build) == 3:
|
|
print('\nCompile and run project ' + os.path.join(project_path, project_name + '.vcxproj'))
|
|
if int(wrd.arm_project_win_build) > 0:
|
|
# Check Visual Studio
|
|
list_vs, err = arm.utils.get_list_installed_vs(True, True, True)
|
|
if len(err) > 0:
|
|
print(err)
|
|
return
|
|
if len(list_vs) == 0:
|
|
print('No Visual Studio found')
|
|
return
|
|
is_check = False
|
|
for vs in list_vs:
|
|
if vs[0] == wrd.arm_project_win_list_vs:
|
|
is_check = True
|
|
break
|
|
if not is_check:
|
|
vs_ver, vs_year, vs_name, vs_id = arm.utils.get_visual_studio_from_version(wrd.arm_project_win_list_vs)
|
|
print(vs_name + ' not found.')
|
|
print('The following are installed on the PC:')
|
|
for vs in list_vs:
|
|
print('- ' + vs[1] + ' (version ' + vs[3] +')')
|
|
return
|
|
# Current VS
|
|
vs_path = ''
|
|
for vs in list_vs:
|
|
if vs[0] == wrd.arm_project_win_list_vs:
|
|
vs_path = vs[2]
|
|
break
|
|
# Open in Visual Studio
|
|
if int(wrd.arm_project_win_build) == 1:
|
|
cmd = os.path.join('start "' + vs_path, 'Common7', 'IDE', 'devenv.exe" "' + os.path.join(project_path, project_name + '.sln"'))
|
|
subprocess.Popen(cmd, shell=True)
|
|
# Compile
|
|
if int(wrd.arm_project_win_build) > 1:
|
|
bits = '64' if wrd.arm_project_win_build_arch == 'x64' else '32'
|
|
# vcvars
|
|
cmd = os.path.join(vs_path, 'VC', 'Auxiliary', 'Build', 'vcvars' + bits + '.bat')
|
|
if not os.path.isfile(cmd):
|
|
print('File "'+ cmd +'" not found. Verify ' + vs_name + ' was installed correctly')
|
|
log.error('Compile failed, check console')
|
|
return
|
|
state.proc_publish_build = run_proc(cmd, done_vs_vars)
|
|
|
|
def done_gradlew_build():
|
|
if state.proc_publish_build == None:
|
|
return
|
|
result = state.proc_publish_build.poll()
|
|
if result == 0:
|
|
state.proc_publish_build = None
|
|
|
|
wrd = bpy.data.worlds['Arm']
|
|
path_apk = os.path.join(arm.utils.get_fp_build(), arm.utils.get_kha_target(state.target))
|
|
project_name = arm.utils.safesrc(wrd.arm_project_name +'-'+ wrd.arm_project_version)
|
|
path_apk = os.path.join(path_apk + '-build', project_name, 'app', 'build', 'outputs', 'apk', 'debug')
|
|
|
|
print("\nBuild APK to " + path_apk)
|
|
# Rename APK
|
|
apk_name = 'app-debug.apk'
|
|
file_name = os.path.join(path_apk, apk_name)
|
|
if wrd.arm_project_android_rename_apk:
|
|
apk_name = project_name + '.apk'
|
|
os.rename(file_name, os.path.join(path_apk, apk_name))
|
|
file_name = os.path.join(path_apk, apk_name)
|
|
print("\nRename APK to " + apk_name)
|
|
# Copy APK
|
|
if wrd.arm_project_android_copy_apk:
|
|
shutil.copyfile(file_name, os.path.join(arm.utils.get_android_apk_copy_path(), apk_name))
|
|
print("Copy APK to " + arm.utils.get_android_apk_copy_path())
|
|
# Open directory with APK
|
|
if arm.utils.get_android_open_build_apk_directory():
|
|
arm.utils.open_folder(path_apk)
|
|
# Open directory after copy APK
|
|
if arm.utils.get_android_apk_copy_open_directory():
|
|
arm.utils.open_folder(arm.utils.get_android_apk_copy_path())
|
|
# Running emulator
|
|
if wrd.arm_project_android_run_avd:
|
|
run_android_emulators(arm.utils.get_android_emulator_name())
|
|
state.redraw_ui = True
|
|
else:
|
|
state.proc_publish_build = None
|
|
state.redraw_ui = True
|
|
os.environ['ANDROID_SDK_ROOT'] = ''
|
|
log.error('Building the APK failed, check console')
|
|
|
|
def run_android_emulators(avd_name):
|
|
if len(avd_name.strip()) == 0:
|
|
return
|
|
print('\nRunning Emulator "'+ avd_name +'"')
|
|
path_file = arm.utils.get_android_emulator_file()
|
|
if len(path_file) > 0:
|
|
if arm.utils.get_os_is_windows():
|
|
run_proc(path_file + " -avd "+ avd_name, None)
|
|
else:
|
|
cmd = shlex.split(path_file + " -avd "+ avd_name)
|
|
run_proc(cmd, None)
|
|
else:
|
|
print('Update List Emulators Warning: File "'+ path_file +'" not found. Check that the variable ANDROID_SDK_ROOT is correct in environment variables or in "Android SDK Path" setting: \n- If you specify an environment variable ANDROID_SDK_ROOT, then you need to restart Blender;\n- If you specify the setting "Android SDK Path", then repeat operation "Publish"')
|
|
|
|
def done_vs_vars():
|
|
if state.proc_publish_build == None:
|
|
return
|
|
result = state.proc_publish_build.poll()
|
|
if result == 0:
|
|
state.proc_publish_build = None
|
|
# MSBuild
|
|
wrd = bpy.data.worlds['Arm']
|
|
list_vs, err = arm.utils.get_list_installed_vs(True, True, True)
|
|
# Current VS
|
|
vs_path = ''
|
|
vs_name = ''
|
|
for vs in list_vs:
|
|
if vs[0] == wrd.arm_project_win_list_vs:
|
|
vs_name = vs[1]
|
|
vs_path = vs[2]
|
|
break
|
|
msbuild = os.path.join(vs_path, 'MSBuild', 'Current', 'Bin', 'MSBuild.exe')
|
|
if not os.path.isfile(msbuild):
|
|
print('File "'+ msbuild +'" not found. Verify ' + vs_name + ' was installed correctly')
|
|
log.error('Compile failed, check console')
|
|
state.redraw_ui = True
|
|
return
|
|
project_name = arm.utils.safesrc(wrd.arm_project_name +'-'+ wrd.arm_project_version)
|
|
project_path = os.path.join(arm.utils.get_fp_build(), arm.utils.get_kha_target(state.target)) + '-build'
|
|
cmd = '"' + msbuild + '" "' + os.path.join(project_path, project_name + '.vcxproj"')
|
|
# Arguments
|
|
platform = 'x64' if wrd.arm_project_win_build_arch == 'x64' else 'win32'
|
|
log_param = wrd.arm_project_win_build_log
|
|
if log_param == 'WarningsAndErrorsOnly':
|
|
log_param = 'WarningsOnly;ErrorsOnly'
|
|
cmd = cmd + ' -m:' + str(wrd.arm_project_win_build_cpu) + ' -clp:'+ log_param +' /p:Configuration='+ wrd.arm_project_win_build_mode +' /p:Platform=' + platform
|
|
print('\nCompiling the project ' + os.path.join(project_path, project_name + '.vcxproj"'))
|
|
state.proc_publish_build = run_proc(cmd, done_vs_build)
|
|
state.redraw_ui = True
|
|
else:
|
|
state.proc_publish_build = None
|
|
state.redraw_ui = True
|
|
log.error('\nCompile failed, check console')
|
|
|
|
def done_vs_build():
|
|
if state.proc_publish_build == None:
|
|
return
|
|
result = state.proc_publish_build.poll()
|
|
if result == 0:
|
|
state.proc_publish_build = None
|
|
|
|
wrd = bpy.data.worlds['Arm']
|
|
project_path = os.path.join(arm.utils.get_fp_build(), arm.utils.get_kha_target(state.target)) + '-build'
|
|
if wrd.arm_project_win_build_arch == 'x64':
|
|
path = os.path.join(project_path, 'x64', wrd.arm_project_win_build_mode)
|
|
else:
|
|
path = os.path.join(project_path, wrd.arm_project_win_build_mode)
|
|
print('\nCompilation completed in ' + path)
|
|
# Run
|
|
if int(wrd.arm_project_win_build) == 3:
|
|
# Copying the executable file
|
|
res_path = os.path.join(arm.utils.get_fp_build(), arm.utils.get_kha_target(state.target))
|
|
file_name = arm.utils.safesrc(wrd.arm_project_name +'-'+ wrd.arm_project_version) + '.exe'
|
|
print('\nCopy the executable file from ' + path + ' to ' + res_path)
|
|
shutil.copyfile(os.path.join(path, file_name), os.path.join(res_path, file_name))
|
|
path = res_path
|
|
# Run project
|
|
cmd = os.path.join('"' + res_path, file_name + '"')
|
|
print('Run the executable file to ' + cmd)
|
|
os.chdir(res_path) # set work folder
|
|
subprocess.Popen(cmd, shell=True)
|
|
# Open Build Directory
|
|
if wrd.arm_project_win_build_open:
|
|
arm.utils.open_folder(path)
|
|
state.redraw_ui = True
|
|
else:
|
|
state.proc_publish_build = None
|
|
state.redraw_ui = True
|
|
log.error('Compile failed, check console')
|
|
|
|
def clean():
|
|
os.chdir(arm.utils.get_fp())
|
|
wrd = bpy.data.worlds['Arm']
|
|
|
|
# Remove build and compiled data
|
|
try:
|
|
if os.path.isdir(arm.utils.build_dir()):
|
|
shutil.rmtree(arm.utils.build_dir(), onerror=remove_readonly)
|
|
if os.path.isdir(arm.utils.get_fp() + '/build'): # Kode Studio build dir
|
|
shutil.rmtree(arm.utils.get_fp() + '/build', onerror=remove_readonly)
|
|
except:
|
|
print('Armory Warning: Some files in the build folder are locked')
|
|
|
|
# Remove compiled nodes
|
|
pkg_dir = arm.utils.safestr(wrd.arm_project_package).replace('.', '/')
|
|
nodes_path = 'Sources/' + pkg_dir + '/node/'
|
|
if os.path.isdir(nodes_path):
|
|
shutil.rmtree(nodes_path, onerror=remove_readonly)
|
|
|
|
# Remove khafile/Main.hx
|
|
if os.path.isfile('khafile.js'):
|
|
os.remove('khafile.js')
|
|
if os.path.isfile('Sources/Main.hx'):
|
|
os.remove('Sources/Main.hx')
|
|
|
|
# Remove Sources/ dir if empty
|
|
if os.path.exists('Sources/' + pkg_dir) and os.listdir('Sources/' + pkg_dir) == []:
|
|
shutil.rmtree('Sources/' + pkg_dir, onerror=remove_readonly)
|
|
if os.path.exists('Sources') and os.listdir('Sources') == []:
|
|
shutil.rmtree('Sources/', onerror=remove_readonly)
|
|
|
|
# To recache signatures for batched materials
|
|
for mat in bpy.data.materials:
|
|
mat.signature = ''
|
|
mat.arm_cached = False
|
|
|
|
# Restart compilation server
|
|
if arm.utils.get_compilation_server():
|
|
arm.lib.server.kill_haxe()
|
|
|
|
print('Project cleaned')
|