armory/blender/arm/write_data.py

780 lines
34 KiB
Python
Raw Normal View History

2018-02-25 19:01:22 +01:00
import glob
import json
2020-12-17 23:34:38 +01:00
import os
import shutil
2018-05-01 13:58:36 +02:00
import stat
import html
2020-12-17 22:57:09 +01:00
from typing import List
2020-12-17 23:34:38 +01:00
import bpy
2017-03-15 12:30:14 +01:00
import arm.assets as assets
import arm.make_state as state
2020-12-17 23:34:38 +01:00
import arm.utils
2015-12-07 21:05:27 +01:00
if arm.is_reload(__name__):
2021-08-04 22:49:38 +02:00
import arm
assets = arm.reload_module(assets)
state = arm.reload_module(state)
arm.utils = arm.reload_module(arm.utils)
else:
arm.enable_reload(__name__)
2021-08-04 22:49:38 +02:00
def on_same_drive(path1: str, path2: str) -> bool:
drive_path1, _ = os.path.splitdrive(path1)
drive_path2, _ = os.path.splitdrive(path2)
return drive_path1 == drive_path2
def add_armory_library(sdk_path: str, name: str, rel_path=False) -> str:
2020-12-17 23:19:56 +01:00
if rel_path:
sdk_path = '../' + os.path.relpath(sdk_path, arm.utils.get_fp()).replace('\\', '/')
2018-07-10 16:05:38 +02:00
return ('project.addLibrary("' + sdk_path + '/' + name + '");\n').replace('\\', '/').replace('//', '/')
2016-08-12 02:29:09 +02:00
def add_assets(path: str, quality=1.0, use_data_dir=False, rel_path=False) -> str:
2018-04-14 15:07:05 +02:00
if not bpy.data.worlds['Arm'].arm_minimize and path.endswith('.arm'):
path = path[:-4] + '.json'
2020-12-17 23:19:56 +01:00
if rel_path:
path = os.path.relpath(path, arm.utils.get_fp()).replace('\\', '/')
2018-08-16 20:48:00 +02:00
2018-03-30 10:49:45 +02:00
notinlist = not path.endswith('.ttf') # TODO
2018-08-16 12:10:50 +02:00
s = 'project.addAssets("' + path + '", { notinlist: ' + str(notinlist).lower() + ' '
2017-09-06 16:37:23 +02:00
if quality < 1.0:
2018-03-30 10:49:45 +02:00
s += ', quality: ' + str(quality)
2018-07-04 10:04:00 +02:00
if use_data_dir:
2018-06-18 12:50:37 +02:00
s += ', destination: "data/{name}"'
2018-03-30 10:49:45 +02:00
s += '});\n'
2017-09-06 16:37:23 +02:00
return s
2017-04-16 17:43:07 +02:00
def add_shaders(path: str, rel_path=False) -> str:
2020-12-17 23:19:56 +01:00
if rel_path:
path = os.path.relpath(path, arm.utils.get_fp())
2018-10-16 12:55:27 +02:00
return 'project.addShaders("' + path.replace('\\', '/').replace('//', '/') + '");\n'
2018-08-16 20:48:00 +02:00
2018-05-01 13:58:36 +02:00
def remove_readonly(func, path, excinfo):
os.chmod(path, stat.S_IWRITE)
func(path)
2020-12-17 22:57:09 +01:00
def write_khafilejs(is_play, export_physics: bool, export_navigation: bool, export_ui: bool, is_publish: bool,
2021-07-24 18:21:06 +02:00
enable_dce: bool, import_traits: List[str]) -> None:
2016-10-12 17:52:27 +02:00
wrd = bpy.data.worlds['Arm']
2016-01-17 22:38:46 +01:00
2020-12-17 22:57:09 +01:00
sdk_path = arm.utils.get_sdk_path()
rel_path = arm.utils.get_relative_paths() # Convert absolute paths to relative
project_path = arm.utils.get_fp()
build_dir = arm.utils.build_dir()
2020-12-17 23:19:56 +01:00
# Whether to use relative paths for paths inside the SDK
do_relpath_sdk = rel_path and on_same_drive(sdk_path, project_path)
2020-10-18 17:50:35 +02:00
with open('khafile.js', 'w', encoding="utf-8") as khafile:
khafile.write(
2015-12-07 21:05:27 +01:00
"""// Auto-generated
2020-12-17 22:57:09 +01:00
let project = new Project('""" + arm.utils.safesrc(wrd.arm_project_name + '-' + wrd.arm_project_version) + """');
2015-12-07 21:05:27 +01:00
project.addSources('Sources');
""")
2017-02-13 13:23:43 +01:00
# Auto-add assets located in Bundled directory
if os.path.exists('Bundled'):
2017-09-06 16:50:30 +02:00
for file in glob.glob("Bundled/**", recursive=True):
if os.path.isfile(file):
assets.add(file)
# Auto-add shape key textures if exists
if os.path.exists('MorphTargets'):
for file in glob.glob("MorphTargets/**", recursive=True):
if os.path.isfile(file):
assets.add(file)
2017-02-13 13:23:43 +01:00
2020-12-17 22:57:09 +01:00
# Add project shaders
2017-09-17 16:59:00 +02:00
if os.path.exists('Shaders'):
2018-04-25 22:38:52 +02:00
# Copy to enable includes
2020-12-17 22:57:09 +01:00
shader_path = os.path.join(build_dir, 'compiled', 'Shaders', 'Project')
if os.path.exists(shader_path):
shutil.rmtree(shader_path, onerror=remove_readonly)
shutil.copytree('Shaders', shader_path)
khafile.write("project.addShaders('" + build_dir + "/compiled/Shaders/Project/**');\n")
2018-04-25 22:38:52 +02:00
# for file in glob.glob("Shaders/**", recursive=True):
2020-12-17 22:57:09 +01:00
# if os.path.isfile(file):
# assets.add_shader(file)
2017-09-17 16:59:00 +02:00
2020-12-17 22:57:09 +01:00
# Add engine sources if the project does not use its own armory/iron versions
if not os.path.exists(os.path.join('Libraries', 'armory')):
2020-12-17 23:19:56 +01:00
khafile.write(add_armory_library(sdk_path, 'armory', rel_path=do_relpath_sdk))
2020-12-17 22:57:09 +01:00
if not os.path.exists(os.path.join('Libraries', 'iron')):
2020-12-17 23:19:56 +01:00
khafile.write(add_armory_library(sdk_path, 'iron', rel_path=do_relpath_sdk))
2017-03-06 02:29:03 +01:00
# Project libraries
2017-09-05 00:36:16 +02:00
if os.path.exists('Libraries'):
libs = os.listdir('Libraries')
for lib in libs:
if os.path.isdir('Libraries/' + lib):
khafile.write('project.addLibrary("{0}");\n'.format(lib.replace('//', '/')))
2019-11-16 14:11:32 +01:00
2018-05-13 21:17:40 +02:00
# Subprojects, merge this with libraries
2018-05-13 22:21:22 +02:00
if os.path.exists('Subprojects'):
libs = os.listdir('Subprojects')
2018-05-13 21:17:40 +02:00
for lib in libs:
2018-05-13 22:21:22 +02:00
if os.path.isdir('Subprojects/' + lib):
khafile.write('await project.addProject("Subprojects/{0}");\n'.format(lib))
2018-05-13 21:17:40 +02:00
if state.target.startswith('krom'):
assets.add_khafile_def('js-es=6')
2016-09-08 14:08:31 +02:00
if export_physics:
2017-05-25 16:48:41 +02:00
assets.add_khafile_def('arm_physics')
if wrd.arm_physics_engine == 'Bullet':
2017-09-30 00:32:06 +02:00
assets.add_khafile_def('arm_bullet')
if not os.path.exists('Libraries/haxebullet'):
2020-12-17 23:19:56 +01:00
khafile.write(add_armory_library(sdk_path + '/lib/', 'haxebullet', rel_path=do_relpath_sdk))
2019-11-16 14:11:32 +01:00
if state.target.startswith('krom'):
ammojs_path = sdk_path + '/lib/haxebullet/ammo/ammo.wasm.js'
ammojs_path = ammojs_path.replace('\\', '/').replace('//', '/')
2020-12-17 23:19:56 +01:00
khafile.write(add_assets(ammojs_path, rel_path=do_relpath_sdk))
2019-11-16 14:11:32 +01:00
ammojs_wasm_path = sdk_path + '/lib/haxebullet/ammo/ammo.wasm.wasm'
ammojs_wasm_path = ammojs_wasm_path.replace('\\', '/').replace('//', '/')
2020-12-17 23:19:56 +01:00
khafile.write(add_assets(ammojs_wasm_path, rel_path=do_relpath_sdk))
2019-11-16 14:11:32 +01:00
elif state.target == 'html5' or state.target == 'node':
2018-12-20 18:04:49 +01:00
ammojs_path = sdk_path + '/lib/haxebullet/ammo/ammo.js'
2018-07-10 16:05:38 +02:00
ammojs_path = ammojs_path.replace('\\', '/').replace('//', '/')
2020-12-17 23:19:56 +01:00
khafile.write(add_assets(ammojs_path, rel_path=do_relpath_sdk))
elif wrd.arm_physics_engine == 'Oimo':
2017-09-30 02:15:21 +02:00
assets.add_khafile_def('arm_oimo')
2017-09-30 00:32:06 +02:00
if not os.path.exists('Libraries/oimo'):
2020-12-17 23:19:56 +01:00
khafile.write(add_armory_library(sdk_path + '/lib/', 'oimo', rel_path=do_relpath_sdk))
2016-08-21 20:00:37 +02:00
2016-12-07 02:01:42 +01:00
if export_navigation:
2017-05-25 16:48:41 +02:00
assets.add_khafile_def('arm_navigation')
2017-09-05 00:36:16 +02:00
if not os.path.exists('Libraries/haxerecast'):
2020-12-17 23:19:56 +01:00
khafile.write(add_armory_library(sdk_path + '/lib/', 'haxerecast', rel_path=do_relpath_sdk))
2018-03-22 22:41:25 +01:00
if state.target.startswith('krom') or state.target == 'html5':
2016-12-07 02:01:42 +01:00
recastjs_path = sdk_path + '/lib/haxerecast/js/recast/recast.js'
2018-07-10 16:05:38 +02:00
recastjs_path = recastjs_path.replace('\\', '/').replace('//', '/')
2020-12-17 23:19:56 +01:00
khafile.write(add_assets(recastjs_path, rel_path=do_relpath_sdk))
2016-12-07 02:01:42 +01:00
2018-06-18 12:50:37 +02:00
if is_publish:
assets.add_khafile_def('arm_published')
2018-07-24 21:28:03 +02:00
if wrd.arm_asset_compression:
assets.add_khafile_def('arm_compress')
2018-06-18 12:50:37 +02:00
else:
2021-08-27 01:26:00 +02:00
assets.add_khafile_def(f'arm_assert_level={wrd.arm_assert_level}')
if wrd.arm_assert_quit:
assets.add_khafile_def('arm_assert_quit')
# khafile.write("""project.addParameter("--macro include('armory.trait')");\n""")
# khafile.write("""project.addParameter("--macro include('armory.trait.internal')");\n""")
2018-06-18 12:50:37 +02:00
# if export_physics:
# khafile.write("""project.addParameter("--macro include('armory.trait.physics')");\n""")
# if wrd.arm_physics_engine == 'Bullet':
# khafile.write("""project.addParameter("--macro include('armory.trait.physics.bullet')");\n""")
2018-06-18 12:50:37 +02:00
# else:
# khafile.write("""project.addParameter("--macro include('armory.trait.physics.oimo')");\n""")
2018-06-18 12:50:37 +02:00
# if export_navigation:
# khafile.write("""project.addParameter("--macro include('armory.trait.navigation')");\n""")
2018-06-18 12:50:37 +02:00
2018-08-21 18:29:05 +02:00
if not wrd.arm_compiler_inline:
khafile.write("project.addParameter('--no-inline');\n")
2018-08-21 18:29:05 +02:00
2017-06-20 15:50:06 +02:00
if enable_dce:
khafile.write("project.addParameter('-dce full');\n")
2017-08-10 14:10:37 +02:00
2021-07-25 20:13:47 +02:00
use_live_patch = arm.utils.is_livepatch_enabled()
if wrd.arm_debug_console or use_live_patch:
2018-06-18 16:00:27 +02:00
import_traits.append('armory.trait.internal.Bridge')
2021-07-25 20:13:47 +02:00
if use_live_patch:
2019-02-10 11:47:42 +01:00
assets.add_khafile_def('arm_patch')
2021-07-20 20:53:22 +02:00
# Include all logic node classes so that they can later
# get instantiated
khafile.write("""project.addParameter("--macro include('armory.logicnode')");\n""")
2017-11-20 15:16:52 +01:00
2017-08-10 14:10:37 +02:00
import_traits = list(set(import_traits))
for i in range(0, len(import_traits)):
khafile.write("project.addParameter('" + import_traits[i] + "');\n")
khafile.write("""project.addParameter("--macro keep('""" + import_traits[i] + """')");\n""")
2017-08-10 14:10:37 +02:00
2019-07-14 15:41:40 +02:00
noembed = wrd.arm_cache_build and not is_publish and state.target == 'krom'
2019-02-12 14:41:44 +01:00
if noembed:
2017-05-24 23:04:24 +02:00
# Load shaders manually
2019-02-12 14:41:44 +01:00
assets.add_khafile_def('arm_noembed')
2017-05-24 23:04:24 +02:00
2019-02-28 23:15:09 +01:00
noembed = False # TODO: always embed shaders for now, check compatibility with haxe compile server
2020-12-17 22:57:09 +01:00
shaders_path = build_dir + '/compiled/Shaders/*.glsl'
2018-12-04 19:10:55 +01:00
if rel_path:
2020-12-17 22:57:09 +01:00
shaders_path = os.path.relpath(shaders_path, project_path).replace('\\', '/')
khafile.write('project.addShaders("' + shaders_path + '", { noembed: ' + str(noembed).lower() + '});\n')
2018-12-04 19:10:55 +01:00
if arm.utils.get_gapi() == 'direct3d11':
# noprocessing flag - gets renamed to .d3d11
2020-12-17 22:57:09 +01:00
shaders_path = build_dir + '/compiled/Hlsl/*.glsl'
if rel_path:
2020-12-17 22:57:09 +01:00
shaders_path = os.path.relpath(shaders_path, project_path).replace('\\', '/')
khafile.write('project.addShaders("' + shaders_path + '", { noprocessing: true, noembed: ' + str(noembed).lower() + ' });\n')
2018-07-04 10:04:00 +02:00
# Move assets for published game to /data folder
2019-01-13 00:22:54 +01:00
use_data_dir = is_publish and (state.target == 'krom-windows' or state.target == 'krom-linux' or state.target == 'windows-hl' or state.target == 'linux-hl')
2018-07-04 10:04:00 +02:00
if use_data_dir:
assets.add_khafile_def('arm_data_dir')
2017-03-20 13:23:31 +01:00
2018-12-04 19:10:55 +01:00
ext = 'arm' if wrd.arm_minimize else 'json'
2020-12-17 22:57:09 +01:00
assets_path = build_dir + '/compiled/Assets/**'
assets_path_sh = build_dir + '/compiled/Shaders/*.' + ext
2018-12-04 19:10:55 +01:00
if rel_path:
2020-12-17 22:57:09 +01:00
assets_path = os.path.relpath(assets_path, project_path).replace('\\', '/')
assets_path_sh = os.path.relpath(assets_path_sh, project_path).replace('\\', '/')
2018-12-04 19:10:55 +01:00
dest = ''
if use_data_dir:
dest += ', destination: "data/{name}"'
khafile.write('project.addAssets("' + assets_path + '", { notinlist: true ' + dest + '});\n')
khafile.write('project.addAssets("' + assets_path_sh + '", { notinlist: true ' + dest + '});\n')
2019-11-16 14:11:32 +01:00
2017-09-06 16:37:23 +02:00
shader_data_references = sorted(list(set(assets.shader_datas)))
for ref in shader_data_references:
2018-07-10 16:05:38 +02:00
ref = ref.replace('\\', '/').replace('//', '/')
2018-12-04 19:10:55 +01:00
if '/compiled/' in ref: # Asset already included
continue
2020-12-17 23:19:56 +01:00
do_relpath_shaders = rel_path and on_same_drive(ref, project_path)
khafile.write(add_assets(ref, use_data_dir=use_data_dir, rel_path=do_relpath_shaders))
2017-09-06 16:37:23 +02:00
asset_references = sorted(list(set(assets.assets)))
for ref in asset_references:
2018-07-10 16:05:38 +02:00
ref = ref.replace('\\', '/').replace('//', '/')
2018-12-04 19:10:55 +01:00
if '/compiled/' in ref: # Asset already included
continue
2017-09-06 16:37:23 +02:00
quality = 1.0
s = ref.lower()
if s.endswith('.wav'):
quality = wrd.arm_sound_quality
elif s.endswith('.png') or s.endswith('.jpg'):
quality = wrd.arm_texture_quality
2020-12-17 23:19:56 +01:00
do_relpath_assets = rel_path and on_same_drive(ref, project_path)
khafile.write(add_assets(ref, quality=quality, use_data_dir=use_data_dir, rel_path=do_relpath_assets))
2017-09-06 16:37:23 +02:00
if wrd.arm_sound_quality < 1.0 or state.target == 'html5':
assets.add_khafile_def('arm_soundcompress')
2020-12-17 22:57:09 +01:00
if wrd.arm_audio == 'Disabled':
assets.add_khafile_def('kha_no_ogg')
else:
assets.add_khafile_def('arm_audio')
2017-09-06 16:37:23 +02:00
if wrd.arm_texture_quality < 1.0:
assets.add_khafile_def('arm_texcompress')
2015-12-07 21:05:27 +01:00
2018-11-13 14:17:47 +01:00
if wrd.arm_debug_console:
2017-11-12 21:56:01 +01:00
assets.add_khafile_def('arm_debug')
2020-12-17 23:19:56 +01:00
khafile.write(add_shaders(sdk_path + "/armory/Shaders/debug_draw/**", rel_path=do_relpath_sdk))
2021-02-25 14:06:31 +01:00
if not is_publish and state.target == 'html5':
khafile.write("project.addParameter('--debug');\n")
2021-09-21 14:56:12 +02:00
if arm.utils.get_pref_or_default('haxe_times', False):
khafile.write("project.addParameter('--times');\n")
2017-06-20 11:35:24 +02:00
if export_ui:
2017-09-05 00:36:16 +02:00
if not os.path.exists('Libraries/zui'):
2020-12-17 23:19:56 +01:00
khafile.write(add_armory_library(sdk_path, 'lib/zui', rel_path=do_relpath_sdk))
2018-11-04 11:02:29 +01:00
p = sdk_path + '/armory/Assets/font_default.ttf'
2018-07-10 16:05:38 +02:00
p = p.replace('//', '/')
2020-12-17 23:19:56 +01:00
khafile.write(add_assets(p.replace('\\', '/'), use_data_dir=use_data_dir, rel_path=do_relpath_sdk))
2017-05-26 16:42:48 +02:00
assets.add_khafile_def('arm_ui')
2019-11-16 14:11:32 +01:00
2020-12-17 22:57:09 +01:00
if not wrd.arm_minimize:
2017-05-25 16:48:41 +02:00
assets.add_khafile_def('arm_json')
2018-04-14 15:07:05 +02:00
2020-12-17 22:57:09 +01:00
if wrd.arm_deinterleaved_buffers:
2017-05-25 16:48:41 +02:00
assets.add_khafile_def('arm_deinterleaved')
2016-08-07 23:12:14 +02:00
2020-12-17 22:57:09 +01:00
if wrd.arm_batch_meshes:
2017-05-25 16:48:41 +02:00
assets.add_khafile_def('arm_batch')
2017-03-12 17:29:22 +01:00
2017-05-14 09:27:47 +02:00
if wrd.arm_stream_scene:
2017-05-25 16:48:41 +02:00
assets.add_khafile_def('arm_stream')
2017-05-14 09:27:47 +02:00
2018-03-15 16:02:56 +01:00
rpdat = arm.utils.get_rp()
2018-07-24 21:28:03 +02:00
if rpdat.arm_skin != 'Off':
assets.add_khafile_def('arm_skin')
2016-08-23 22:55:46 +02:00
if rpdat.arm_particles != 'Off':
assets.add_khafile_def('arm_particles')
2019-03-11 10:32:31 +01:00
if rpdat.rp_draw_order == 'Shader':
assets.add_khafile_def('arm_draworder_shader')
2018-09-08 12:34:12 +02:00
2017-11-10 14:53:40 +01:00
if arm.utils.get_viewport_controls() == 'azerty':
assets.add_khafile_def('arm_azerty')
2020-12-17 22:57:09 +01:00
if os.path.exists(project_path + '/Bundled/config.arm'):
2018-08-26 18:42:10 +02:00
assets.add_khafile_def('arm_config')
if is_publish and wrd.arm_loadscreen:
assets.add_khafile_def('arm_loadscreen')
2019-02-11 17:10:49 +01:00
if wrd.arm_winresize or state.target == 'html5':
assets.add_khafile_def('arm_resizable')
2018-06-15 12:25:53 +02:00
# if bpy.data.scenes[0].unit_settings.system_rotation == 'DEGREES':
# assets.add_khafile_def('arm_degrees')
2021-08-19 22:27:09 +02:00
# Allow libraries to recognize Armory
assets.add_khafile_def('armory')
2016-07-28 13:21:27 +02:00
for d in assets.khafile_defs:
khafile.write("project.addDefine('" + d + "');\n")
2016-07-28 13:21:27 +02:00
2019-05-10 18:29:59 +02:00
for p in assets.khafile_params:
khafile.write("project.addParameter('" + p + "');\n")
2019-05-10 18:29:59 +02:00
2019-09-08 19:08:47 +02:00
if state.target.startswith('android'):
2018-06-13 14:00:01 +02:00
bundle = 'org.armory3d.' + wrd.arm_project_package if wrd.arm_project_bundle == '' else wrd.arm_project_bundle
khafile.write("project.targetOptions.android_native.package = '{0}';\n".format(arm.utils.safestr(bundle)))
if wrd.arm_winorient != 'Multi':
khafile.write("project.targetOptions.android_native.screenOrientation = '{0}';\n".format(wrd.arm_winorient.lower()))
Add Android Settings + LN Set Vibrate 1. For the new settings to fully function, you need to update the submodules so that this Pull Request (https://github.com/Kode/kincmake/pull/100) gets into armsdk. Extended settings via khafile.js. 2. Added Android Settings panel: - invisible until the target platform android-hl is added to the list; - inactive until the target platform android-hl is selected in the list. Options: - Orientation; - Compile Version SDK - from 26 to 30, default 29; - Minimal Version SDK - from 14 to 30, default 14; - Target Version SDK - from 26 to 30, default 29; - Permissions - a list of permissions. If I will duplicate entries in the list, then only unique entries will be included during export. By default, the list is empty; - Android ABI Filters - a list of platforms to build for (arm64-v8a, armeabi-v7a, x86, x86_64). If I will duplicate entries in the list, then only unique entries will be included during export. By default, the list is empty. If the list is empty, then all platforms will be used (as before). 3. The enum (names of permissions) and the function have been added to the utils.py modules, which adds the specified value to the list of permissions. Feature added for ease of use from different locations (different logical nodes). 4. List of permissions: - ACCESS_COARSE_LOCATION - Allows an app to access approximate location; - ACCESS_NETWORK_STATE - Allows applications to access information about networks; - ACCESS_FINE_LOCATION - Allows an app to access precise location; - ACCESS_WIFI_STATE - Allows applications to access information about Wi-Fi network; - BLUETOOTH - Allows applications to connect to paired bluetooth devices; - BLUETOOTH_ADMIN - Allows applications to discover and pair bluetooth devices; - CAMERA - Required to be able to access the camera device; - EXPAND_STATUS_BAR - Allows an application to expand or collapse the status bar; - FOREGROUND_SERVICE - Allows a regular application to use Service.startForeground; - GET_ACCOUNTS - Allows access to the list of accounts in the Accounts Service; - INTERNET - Allows applications to open network sockets'; - READ_EXTERNAL_STORAGE - Allows an application to read from external storage; - VIBRATE - Allows access to the vibrator; - WRITE_EXTERNAL_STORAGE - Allows an application to write to external storage. 5. Added logical node Set Vibrate: Category: Native Pulses the vibration hardware on the device for time in milliseconds, if such hardware exists. Input parameters: - Milliseconds - time in milliseconds (data type Int, default value 100). When adding the logical node Set Vibrate, the permission is automatically added to the list, even if the target android-hl has not been added to the export list (using a function from utils.py).
2020-10-17 15:47:54 +02:00
# Android SDK Versions
khafile.write("project.targetOptions.android_native.compileSdkVersion = '{0}';\n".format(wrd.arm_project_android_sdk_compile))
khafile.write("project.targetOptions.android_native.minSdkVersion = '{0}';\n".format(wrd.arm_project_android_sdk_min))
khafile.write("project.targetOptions.android_native.targetSdkVersion = '{0}';\n".format(wrd.arm_project_android_sdk_target))
# Permissions
if len(wrd.arm_exporter_android_permission_list) > 0:
perms = ''
Add Android Settings + LN Set Vibrate 1. For the new settings to fully function, you need to update the submodules so that this Pull Request (https://github.com/Kode/kincmake/pull/100) gets into armsdk. Extended settings via khafile.js. 2. Added Android Settings panel: - invisible until the target platform android-hl is added to the list; - inactive until the target platform android-hl is selected in the list. Options: - Orientation; - Compile Version SDK - from 26 to 30, default 29; - Minimal Version SDK - from 14 to 30, default 14; - Target Version SDK - from 26 to 30, default 29; - Permissions - a list of permissions. If I will duplicate entries in the list, then only unique entries will be included during export. By default, the list is empty; - Android ABI Filters - a list of platforms to build for (arm64-v8a, armeabi-v7a, x86, x86_64). If I will duplicate entries in the list, then only unique entries will be included during export. By default, the list is empty. If the list is empty, then all platforms will be used (as before). 3. The enum (names of permissions) and the function have been added to the utils.py modules, which adds the specified value to the list of permissions. Feature added for ease of use from different locations (different logical nodes). 4. List of permissions: - ACCESS_COARSE_LOCATION - Allows an app to access approximate location; - ACCESS_NETWORK_STATE - Allows applications to access information about networks; - ACCESS_FINE_LOCATION - Allows an app to access precise location; - ACCESS_WIFI_STATE - Allows applications to access information about Wi-Fi network; - BLUETOOTH - Allows applications to connect to paired bluetooth devices; - BLUETOOTH_ADMIN - Allows applications to discover and pair bluetooth devices; - CAMERA - Required to be able to access the camera device; - EXPAND_STATUS_BAR - Allows an application to expand or collapse the status bar; - FOREGROUND_SERVICE - Allows a regular application to use Service.startForeground; - GET_ACCOUNTS - Allows access to the list of accounts in the Accounts Service; - INTERNET - Allows applications to open network sockets'; - READ_EXTERNAL_STORAGE - Allows an application to read from external storage; - VIBRATE - Allows access to the vibrator; - WRITE_EXTERNAL_STORAGE - Allows an application to write to external storage. 5. Added logical node Set Vibrate: Category: Native Pulses the vibration hardware on the device for time in milliseconds, if such hardware exists. Input parameters: - Milliseconds - time in milliseconds (data type Int, default value 100). When adding the logical node Set Vibrate, the permission is automatically added to the list, even if the target android-hl has not been added to the export list (using a function from utils.py).
2020-10-17 15:47:54 +02:00
for item in wrd.arm_exporter_android_permission_list:
2020-12-17 22:57:09 +01:00
perm = "'android.permission."+ item.arm_android_permissions + "'"
Add Android Settings + LN Set Vibrate 1. For the new settings to fully function, you need to update the submodules so that this Pull Request (https://github.com/Kode/kincmake/pull/100) gets into armsdk. Extended settings via khafile.js. 2. Added Android Settings panel: - invisible until the target platform android-hl is added to the list; - inactive until the target platform android-hl is selected in the list. Options: - Orientation; - Compile Version SDK - from 26 to 30, default 29; - Minimal Version SDK - from 14 to 30, default 14; - Target Version SDK - from 26 to 30, default 29; - Permissions - a list of permissions. If I will duplicate entries in the list, then only unique entries will be included during export. By default, the list is empty; - Android ABI Filters - a list of platforms to build for (arm64-v8a, armeabi-v7a, x86, x86_64). If I will duplicate entries in the list, then only unique entries will be included during export. By default, the list is empty. If the list is empty, then all platforms will be used (as before). 3. The enum (names of permissions) and the function have been added to the utils.py modules, which adds the specified value to the list of permissions. Feature added for ease of use from different locations (different logical nodes). 4. List of permissions: - ACCESS_COARSE_LOCATION - Allows an app to access approximate location; - ACCESS_NETWORK_STATE - Allows applications to access information about networks; - ACCESS_FINE_LOCATION - Allows an app to access precise location; - ACCESS_WIFI_STATE - Allows applications to access information about Wi-Fi network; - BLUETOOTH - Allows applications to connect to paired bluetooth devices; - BLUETOOTH_ADMIN - Allows applications to discover and pair bluetooth devices; - CAMERA - Required to be able to access the camera device; - EXPAND_STATUS_BAR - Allows an application to expand or collapse the status bar; - FOREGROUND_SERVICE - Allows a regular application to use Service.startForeground; - GET_ACCOUNTS - Allows access to the list of accounts in the Accounts Service; - INTERNET - Allows applications to open network sockets'; - READ_EXTERNAL_STORAGE - Allows an application to read from external storage; - VIBRATE - Allows access to the vibrator; - WRITE_EXTERNAL_STORAGE - Allows an application to write to external storage. 5. Added logical node Set Vibrate: Category: Native Pulses the vibration hardware on the device for time in milliseconds, if such hardware exists. Input parameters: - Milliseconds - time in milliseconds (data type Int, default value 100). When adding the logical node Set Vibrate, the permission is automatically added to the list, even if the target android-hl has not been added to the export list (using a function from utils.py).
2020-10-17 15:47:54 +02:00
# Checking In
if perms.find(perm) == -1:
if len(perms) > 0:
2020-12-17 22:57:09 +01:00
perms = perms + ', ' + perm
Add Android Settings + LN Set Vibrate 1. For the new settings to fully function, you need to update the submodules so that this Pull Request (https://github.com/Kode/kincmake/pull/100) gets into armsdk. Extended settings via khafile.js. 2. Added Android Settings panel: - invisible until the target platform android-hl is added to the list; - inactive until the target platform android-hl is selected in the list. Options: - Orientation; - Compile Version SDK - from 26 to 30, default 29; - Minimal Version SDK - from 14 to 30, default 14; - Target Version SDK - from 26 to 30, default 29; - Permissions - a list of permissions. If I will duplicate entries in the list, then only unique entries will be included during export. By default, the list is empty; - Android ABI Filters - a list of platforms to build for (arm64-v8a, armeabi-v7a, x86, x86_64). If I will duplicate entries in the list, then only unique entries will be included during export. By default, the list is empty. If the list is empty, then all platforms will be used (as before). 3. The enum (names of permissions) and the function have been added to the utils.py modules, which adds the specified value to the list of permissions. Feature added for ease of use from different locations (different logical nodes). 4. List of permissions: - ACCESS_COARSE_LOCATION - Allows an app to access approximate location; - ACCESS_NETWORK_STATE - Allows applications to access information about networks; - ACCESS_FINE_LOCATION - Allows an app to access precise location; - ACCESS_WIFI_STATE - Allows applications to access information about Wi-Fi network; - BLUETOOTH - Allows applications to connect to paired bluetooth devices; - BLUETOOTH_ADMIN - Allows applications to discover and pair bluetooth devices; - CAMERA - Required to be able to access the camera device; - EXPAND_STATUS_BAR - Allows an application to expand or collapse the status bar; - FOREGROUND_SERVICE - Allows a regular application to use Service.startForeground; - GET_ACCOUNTS - Allows access to the list of accounts in the Accounts Service; - INTERNET - Allows applications to open network sockets'; - READ_EXTERNAL_STORAGE - Allows an application to read from external storage; - VIBRATE - Allows access to the vibrator; - WRITE_EXTERNAL_STORAGE - Allows an application to write to external storage. 5. Added logical node Set Vibrate: Category: Native Pulses the vibration hardware on the device for time in milliseconds, if such hardware exists. Input parameters: - Milliseconds - time in milliseconds (data type Int, default value 100). When adding the logical node Set Vibrate, the permission is automatically added to the list, even if the target android-hl has not been added to the export list (using a function from utils.py).
2020-10-17 15:47:54 +02:00
else:
perms = perm
if len(perms) > 0:
khafile.write("project.targetOptions.android_native.permissions = [{0}];\n".format(perms))
# Android ABI Filters
if len(wrd.arm_exporter_android_abi_list) > 0:
abis = ''
Add Android Settings + LN Set Vibrate 1. For the new settings to fully function, you need to update the submodules so that this Pull Request (https://github.com/Kode/kincmake/pull/100) gets into armsdk. Extended settings via khafile.js. 2. Added Android Settings panel: - invisible until the target platform android-hl is added to the list; - inactive until the target platform android-hl is selected in the list. Options: - Orientation; - Compile Version SDK - from 26 to 30, default 29; - Minimal Version SDK - from 14 to 30, default 14; - Target Version SDK - from 26 to 30, default 29; - Permissions - a list of permissions. If I will duplicate entries in the list, then only unique entries will be included during export. By default, the list is empty; - Android ABI Filters - a list of platforms to build for (arm64-v8a, armeabi-v7a, x86, x86_64). If I will duplicate entries in the list, then only unique entries will be included during export. By default, the list is empty. If the list is empty, then all platforms will be used (as before). 3. The enum (names of permissions) and the function have been added to the utils.py modules, which adds the specified value to the list of permissions. Feature added for ease of use from different locations (different logical nodes). 4. List of permissions: - ACCESS_COARSE_LOCATION - Allows an app to access approximate location; - ACCESS_NETWORK_STATE - Allows applications to access information about networks; - ACCESS_FINE_LOCATION - Allows an app to access precise location; - ACCESS_WIFI_STATE - Allows applications to access information about Wi-Fi network; - BLUETOOTH - Allows applications to connect to paired bluetooth devices; - BLUETOOTH_ADMIN - Allows applications to discover and pair bluetooth devices; - CAMERA - Required to be able to access the camera device; - EXPAND_STATUS_BAR - Allows an application to expand or collapse the status bar; - FOREGROUND_SERVICE - Allows a regular application to use Service.startForeground; - GET_ACCOUNTS - Allows access to the list of accounts in the Accounts Service; - INTERNET - Allows applications to open network sockets'; - READ_EXTERNAL_STORAGE - Allows an application to read from external storage; - VIBRATE - Allows access to the vibrator; - WRITE_EXTERNAL_STORAGE - Allows an application to write to external storage. 5. Added logical node Set Vibrate: Category: Native Pulses the vibration hardware on the device for time in milliseconds, if such hardware exists. Input parameters: - Milliseconds - time in milliseconds (data type Int, default value 100). When adding the logical node Set Vibrate, the permission is automatically added to the list, even if the target android-hl has not been added to the export list (using a function from utils.py).
2020-10-17 15:47:54 +02:00
for item in wrd.arm_exporter_android_abi_list:
2020-12-17 22:57:09 +01:00
abi = "'"+ item.arm_android_abi + "'"
Add Android Settings + LN Set Vibrate 1. For the new settings to fully function, you need to update the submodules so that this Pull Request (https://github.com/Kode/kincmake/pull/100) gets into armsdk. Extended settings via khafile.js. 2. Added Android Settings panel: - invisible until the target platform android-hl is added to the list; - inactive until the target platform android-hl is selected in the list. Options: - Orientation; - Compile Version SDK - from 26 to 30, default 29; - Minimal Version SDK - from 14 to 30, default 14; - Target Version SDK - from 26 to 30, default 29; - Permissions - a list of permissions. If I will duplicate entries in the list, then only unique entries will be included during export. By default, the list is empty; - Android ABI Filters - a list of platforms to build for (arm64-v8a, armeabi-v7a, x86, x86_64). If I will duplicate entries in the list, then only unique entries will be included during export. By default, the list is empty. If the list is empty, then all platforms will be used (as before). 3. The enum (names of permissions) and the function have been added to the utils.py modules, which adds the specified value to the list of permissions. Feature added for ease of use from different locations (different logical nodes). 4. List of permissions: - ACCESS_COARSE_LOCATION - Allows an app to access approximate location; - ACCESS_NETWORK_STATE - Allows applications to access information about networks; - ACCESS_FINE_LOCATION - Allows an app to access precise location; - ACCESS_WIFI_STATE - Allows applications to access information about Wi-Fi network; - BLUETOOTH - Allows applications to connect to paired bluetooth devices; - BLUETOOTH_ADMIN - Allows applications to discover and pair bluetooth devices; - CAMERA - Required to be able to access the camera device; - EXPAND_STATUS_BAR - Allows an application to expand or collapse the status bar; - FOREGROUND_SERVICE - Allows a regular application to use Service.startForeground; - GET_ACCOUNTS - Allows access to the list of accounts in the Accounts Service; - INTERNET - Allows applications to open network sockets'; - READ_EXTERNAL_STORAGE - Allows an application to read from external storage; - VIBRATE - Allows access to the vibrator; - WRITE_EXTERNAL_STORAGE - Allows an application to write to external storage. 5. Added logical node Set Vibrate: Category: Native Pulses the vibration hardware on the device for time in milliseconds, if such hardware exists. Input parameters: - Milliseconds - time in milliseconds (data type Int, default value 100). When adding the logical node Set Vibrate, the permission is automatically added to the list, even if the target android-hl has not been added to the export list (using a function from utils.py).
2020-10-17 15:47:54 +02:00
# Checking In
if abis.find(abi) == -1:
if len(abis) > 0:
2020-12-17 22:57:09 +01:00
abis = abis + ', ' + abi
Add Android Settings + LN Set Vibrate 1. For the new settings to fully function, you need to update the submodules so that this Pull Request (https://github.com/Kode/kincmake/pull/100) gets into armsdk. Extended settings via khafile.js. 2. Added Android Settings panel: - invisible until the target platform android-hl is added to the list; - inactive until the target platform android-hl is selected in the list. Options: - Orientation; - Compile Version SDK - from 26 to 30, default 29; - Minimal Version SDK - from 14 to 30, default 14; - Target Version SDK - from 26 to 30, default 29; - Permissions - a list of permissions. If I will duplicate entries in the list, then only unique entries will be included during export. By default, the list is empty; - Android ABI Filters - a list of platforms to build for (arm64-v8a, armeabi-v7a, x86, x86_64). If I will duplicate entries in the list, then only unique entries will be included during export. By default, the list is empty. If the list is empty, then all platforms will be used (as before). 3. The enum (names of permissions) and the function have been added to the utils.py modules, which adds the specified value to the list of permissions. Feature added for ease of use from different locations (different logical nodes). 4. List of permissions: - ACCESS_COARSE_LOCATION - Allows an app to access approximate location; - ACCESS_NETWORK_STATE - Allows applications to access information about networks; - ACCESS_FINE_LOCATION - Allows an app to access precise location; - ACCESS_WIFI_STATE - Allows applications to access information about Wi-Fi network; - BLUETOOTH - Allows applications to connect to paired bluetooth devices; - BLUETOOTH_ADMIN - Allows applications to discover and pair bluetooth devices; - CAMERA - Required to be able to access the camera device; - EXPAND_STATUS_BAR - Allows an application to expand or collapse the status bar; - FOREGROUND_SERVICE - Allows a regular application to use Service.startForeground; - GET_ACCOUNTS - Allows access to the list of accounts in the Accounts Service; - INTERNET - Allows applications to open network sockets'; - READ_EXTERNAL_STORAGE - Allows an application to read from external storage; - VIBRATE - Allows access to the vibrator; - WRITE_EXTERNAL_STORAGE - Allows an application to write to external storage. 5. Added logical node Set Vibrate: Category: Native Pulses the vibration hardware on the device for time in milliseconds, if such hardware exists. Input parameters: - Milliseconds - time in milliseconds (data type Int, default value 100). When adding the logical node Set Vibrate, the permission is automatically added to the list, even if the target android-hl has not been added to the export list (using a function from utils.py).
2020-10-17 15:47:54 +02:00
else:
abis = abi
if len(abis) > 0:
khafile.write("project.targetOptions.android_native.abiFilters = [{0}];\n".format(abis))
2018-12-05 10:36:36 +01:00
elif state.target.startswith('ios'):
2018-06-13 14:00:01 +02:00
bundle = 'org.armory3d.' + wrd.arm_project_package if wrd.arm_project_bundle == '' else wrd.arm_project_bundle
khafile.write("project.targetOptions.ios.bundle = '{0}';\n".format(arm.utils.safestr(bundle)))
2018-06-13 14:00:01 +02:00
if wrd.arm_project_icon != '':
2020-12-17 22:57:09 +01:00
shutil.copy(bpy.path.abspath(wrd.arm_project_icon), project_path + '/icon.png')
if wrd.arm_khafile is not None:
khafile.write(wrd.arm_khafile.as_string())
2017-09-08 14:21:57 +02:00
khafile.write("\n\nresolve(project);\n")
2016-01-11 13:07:44 +01:00
2020-12-17 22:57:09 +01:00
2018-02-25 19:01:22 +01:00
def get_winmode(arm_winmode):
if arm_winmode == 'Window':
return 0
2018-07-15 19:55:36 +02:00
else: # Fullscreen
2018-02-25 19:01:22 +01:00
return 1
2020-12-17 22:57:09 +01:00
2018-02-25 19:01:22 +01:00
def write_config(resx, resy):
wrd = bpy.data.worlds['Arm']
2020-12-17 22:57:09 +01:00
p = os.path.join(arm.utils.get_fp(), 'Bundled')
2018-02-25 19:01:22 +01:00
if not os.path.exists(p):
os.makedirs(p)
2020-12-17 22:57:09 +01:00
2018-02-25 19:01:22 +01:00
rpdat = arm.utils.get_rp()
2018-12-10 00:02:40 +01:00
rp_shadowmap_cube = int(rpdat.rp_shadowmap_cube) if rpdat.rp_shadows else 0
2019-01-21 17:59:25 +01:00
rp_shadowmap_cascade = arm.utils.get_cascade_size(rpdat) if rpdat.rp_shadows else 0
2020-12-17 22:57:09 +01:00
output = {
'window_mode': get_winmode(wrd.arm_winmode),
'window_w': int(resx),
'window_h': int(resy),
'window_resizable': wrd.arm_winresize,
'window_maximizable': wrd.arm_winresize and wrd.arm_winmaximize,
'window_minimizable': wrd.arm_winminimize,
'window_vsync': wrd.arm_vsync,
'window_msaa': int(rpdat.arm_samples_per_pixel),
'window_scale': 1.0,
'rp_supersample': float(rpdat.rp_supersampling),
'rp_shadowmap_cube': rp_shadowmap_cube,
'rp_shadowmap_cascade': rp_shadowmap_cascade,
'rp_ssgi': rpdat.rp_ssgi != 'Off',
'rp_ssr': rpdat.rp_ssr != 'Off',
'rp_bloom': rpdat.rp_bloom != 'Off',
'rp_motionblur': rpdat.rp_motionblur != 'Off',
'rp_gi': rpdat.rp_voxelao,
'rp_dynres': rpdat.rp_dynres
}
with open(os.path.join(p, 'config.arm'), 'w') as configfile:
configfile.write(json.dumps(output, sort_keys=True, indent=4))
2018-02-25 19:01:22 +01:00
2019-02-10 11:47:42 +01:00
def write_mainhx(scene_name, resx, resy, is_play, is_publish):
wrd = bpy.data.worlds['Arm']
2017-08-21 20:16:06 +02:00
rpdat = arm.utils.get_rp()
2019-06-22 11:29:05 +02:00
scene_ext = '.lz4' if (wrd.arm_asset_compression and is_publish) else ''
2018-04-14 15:07:05 +02:00
if scene_ext == '' and not wrd.arm_minimize:
scene_ext = '.json'
2018-02-25 19:01:22 +01:00
winmode = get_winmode(wrd.arm_winmode)
2018-08-26 18:42:10 +02:00
# Detect custom render path
pathpack = 'armory'
if os.path.isfile(arm.utils.get_fp() + '/Sources/' + wrd.arm_project_package + '/renderpath/RenderPathCreator.hx'):
pathpack = wrd.arm_project_package
elif rpdat.rp_driver != 'Armory':
pathpack = rpdat.rp_driver.lower()
2020-10-18 17:50:35 +02:00
with open('Sources/Main.hx', 'w', encoding="utf-8") as f:
2016-06-30 13:22:05 +02:00
f.write(
2015-12-07 21:05:27 +01:00
"""// Auto-generated
package ;
class Main {
2017-05-13 17:17:43 +02:00
public static inline var projectName = '""" + arm.utils.safestr(wrd.arm_project_name) + """';
2020-05-10 21:25:01 +02:00
public static inline var projectVersion = '""" + arm.utils.safestr(wrd.arm_project_version) + """';
2018-08-26 18:42:10 +02:00
public static inline var projectPackage = '""" + arm.utils.safestr(wrd.arm_project_package) + """';""")
2017-08-10 14:10:37 +02:00
if rpdat.rp_voxelao:
2017-10-12 12:12:48 +02:00
f.write("""
public static inline var voxelgiVoxelSize = """ + str(rpdat.arm_voxelgi_dimensions) + " / " + str(rpdat.rp_voxelgi_resolution) + """;
2018-08-26 18:42:10 +02:00
public static inline var voxelgiHalfExtents = """ + str(round(rpdat.arm_voxelgi_dimensions / 2.0)) + """;""")
2017-10-12 12:12:48 +02:00
2018-04-20 00:56:54 +02:00
if rpdat.arm_rp_resolution == 'Custom':
f.write("""
2018-08-26 18:42:10 +02:00
public static inline var resolutionSize = """ + str(rpdat.arm_rp_resolution_size) + """;""")
2018-04-20 00:56:54 +02:00
2017-07-03 15:16:15 +02:00
f.write("""
2018-08-26 18:42:10 +02:00
public static function main() {""")
2018-07-24 21:28:03 +02:00
if rpdat.arm_skin != 'Off':
f.write("""
2018-08-26 18:42:10 +02:00
iron.object.BoneAnimation.skinMaxBones = """ + str(rpdat.arm_skin_max_bones) + """;""")
if rpdat.rp_shadows:
if rpdat.rp_shadowmap_cascades != '1':
f.write("""
iron.object.LightObject.cascadeCount = """ + str(rpdat.rp_shadowmap_cascades) + """;
iron.object.LightObject.cascadeSplitFactor = """ + str(rpdat.arm_shadowmap_split) + """;""")
if rpdat.arm_shadowmap_bounds != 1.0:
f.write("""
iron.object.LightObject.cascadeBounds = """ + str(rpdat.arm_shadowmap_bounds) + """;""")
2018-01-22 18:46:38 +01:00
if is_publish and wrd.arm_loadscreen:
2018-02-25 19:01:22 +01:00
asset_references = list(set(assets.assets))
2018-01-22 18:46:38 +01:00
loadscreen_class = 'armory.trait.internal.LoadingScreen'
if os.path.isfile(arm.utils.get_fp() + '/Sources/' + wrd.arm_project_package + '/LoadingScreen.hx'):
loadscreen_class = wrd.arm_project_package + '.LoadingScreen'
2017-11-27 15:10:09 +01:00
f.write("""
2018-08-26 18:42:10 +02:00
armory.system.Starter.numAssets = """ + str(len(asset_references)) + """;
armory.system.Starter.drawLoading = """ + loadscreen_class + """.render;""")
2016-09-23 00:34:42 +02:00
f.write("""
2018-08-26 18:42:10 +02:00
armory.system.Starter.main(
'""" + arm.utils.safestr(scene_name) + scene_ext + """',
""" + str(winmode) + """,
""" + ('true' if wrd.arm_winresize else 'false') + """,
""" + ('true' if wrd.arm_winminimize else 'false') + """,
2019-02-11 17:10:49 +01:00
""" + ('true' if (wrd.arm_winresize and wrd.arm_winmaximize) else 'false') + """,
2018-08-26 18:42:10 +02:00
""" + str(resx) + """,
""" + str(resy) + """,
""" + str(int(rpdat.arm_samples_per_pixel)) + """,
""" + ('true' if wrd.arm_vsync else 'false') + """,
""" + pathpack + """.renderpath.RenderPathCreator.get
);
2016-06-30 13:22:05 +02:00
}
2015-12-07 21:05:27 +01:00
}
""")
2016-07-10 00:51:39 +02:00
def write_indexhtml(w, h, is_publish):
wrd = bpy.data.worlds['Arm']
2017-08-21 20:16:06 +02:00
rpdat = arm.utils.get_rp()
dest = '/html5' if is_publish else '/debug/html5'
if not os.path.exists(arm.utils.build_dir() + dest):
os.makedirs(arm.utils.build_dir() + dest)
popupmenu_in_browser = ''
if wrd.arm_project_html5_popupmenu_in_browser:
popupmenu_in_browser = ' oncontextmenu="return false"'
with open(arm.utils.build_dir() + dest + '/index.html', 'w') as f:
2016-07-10 00:51:39 +02:00
f.write(
"""<!DOCTYPE html>
<html>
<head>
2017-04-26 14:21:22 +02:00
<meta charset="utf-8"/>""")
if rpdat.rp_stereo or wrd.arm_winmode == 'Fullscreen':
2017-04-26 14:21:22 +02:00
f.write("""
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<style>
body {
margin: 0;
}
</style>
""")
f.write("""
<title>"""+html.escape( wrd.arm_project_name)+"""</title>
2016-07-10 00:51:39 +02:00
</head>
2017-05-12 21:49:42 +02:00
<body style="margin: 0; padding: 0;">
2017-04-16 14:46:35 +02:00
""")
if rpdat.rp_stereo or wrd.arm_winmode == 'Fullscreen':
2017-04-16 14:46:35 +02:00
f.write("""
<canvas style="width: 100vw; height: 100vh; display: block;" id='khanvas' tabindex='-1'""" + str(popupmenu_in_browser) + """></canvas>
2017-04-16 14:46:35 +02:00
""")
else:
f.write("""
<p align="center"><canvas align="center" style="outline: none;" id='khanvas' width='""" + str(w) + """' height='""" + str(h) + """' tabindex='-1'""" + str(popupmenu_in_browser) + """></canvas></p>
2017-04-16 14:46:35 +02:00
""")
f.write("""
2016-07-10 00:51:39 +02:00
<script src='kha.js'></script>
</body>
</html>
""")
2018-02-27 20:27:27 +01:00
add_compiledglsl = ''
def write_compiledglsl(defs, make_variants):
2017-08-21 20:16:06 +02:00
rpdat = arm.utils.get_rp()
Add support for shadow map atlasing With this it is now possible to enable atlasing of shadow maps, which solves the existing limitation of 4 lights in a scene. This is done by grouping the rendering of shadow maps, that currently are drawn into their own images for each light, into one or several big textures. This was done because the openGL and webGL version Armory targets do not support dynamic indexing of shadowMapSamplers, meaning that the index that access an array of shadow maps has to be know by the compiler before hand so it can be unrolled into if/else branching. By instead simply using a big shadow map texture and moving the dynamic part to other types of array that are allowed dynamic indexing like vec4 and mat4, this limitation was solved. The premise was simple enough for the shader part, but for the Haxe part, managing and solving where lights shadow maps should go in a shadow map can be tricky. So to keep track and solve this, ShadowMapAtlas and ShadowMapTile were created. These classes have the minimally required logic to solve the basic features needed for this problem: defining some kind of abstraction to prevent overlapping of shadowmaps, finding available space, assigning such space efficiently, locking and freeing this space, etc. This functionality it is used by drawShadowMapAtlas(), which is a modified version of drawShadowMap(). Shadow map atlases are represented with perfectly balanced 4-ary trees, where each tree of the previous definition represents a "tile" or slice that results from dividing a square that represents the image into 4 slices or sub-images. The root of this "tile" it's a reference to the tile-slice, and this tile is divided in 4 slices, and the process is repeated depth-times. If depth is 1, slices are kept at just the initial 4 tiles of max size, which is the default size of the shadow map. #arm_shadowmap_atlas_lod allows controlling if code to support more depth levels is added or not when compiling. the tiles that populate atlases tile trees are simply a data structure that contains a reference to the light they are linked to, inner subtiles in case LOD is enabled, coordinates to where this tile starts in the atlas that go from 0 to Shadow Map Size, and a reference to a linked tile for LOD. This simple definition allows tiles having a theoretically small memory footprint, but in turn this simplicity might make some functionality that might be responsibility of tiles (for example knowing if they are overlapping) a responsibility of the ones that utilizes tiles instead. This decision may complicate maintenance so it is to be revised in future iterations of this feature.
2021-01-27 02:01:06 +01:00
wrd = bpy.data.worlds['Arm']
2019-01-21 17:59:25 +01:00
shadowmap_size = arm.utils.get_cascade_size(rpdat) if rpdat.rp_shadows else 0
with open(arm.utils.build_dir() + '/compiled/Shaders/compiled.inc', 'w') as f:
2016-07-10 00:51:39 +02:00
f.write(
2016-10-17 17:39:40 +02:00
"""#ifndef _COMPILED_GLSL_
#define _COMPILED_GLSL_
2018-02-18 19:10:14 +01:00
""")
for d in defs:
if make_variants and d.endswith('var'):
2018-09-13 12:13:32 +02:00
continue # Write a shader variant instead
2018-02-18 19:10:14 +01:00
f.write("#define " + d + "\n")
2020-05-04 00:19:11 +02:00
f.write("""#if defined(HLSL) || defined(METAL)
#define _InvY
#endif
""")
if state.target == 'html5' or arm.utils.get_gapi() == 'direct3d11':
f.write("#define _FlipY\n")
2018-02-18 19:10:14 +01:00
f.write("""const float PI = 3.1415926535;
2016-07-10 00:51:39 +02:00
const float PI2 = PI * 2.0;
const vec2 shadowmapSize = vec2(""" + str(shadowmap_size) + """, """ + str(shadowmap_size) + """);
2018-03-15 16:02:56 +01:00
const float shadowmapCubePcfSize = """ + str((round(rpdat.arm_pcfsize * 100) / 100) / 1000) + """;
2017-10-12 12:12:48 +02:00
const int shadowmapCascades = """ + str(rpdat.rp_shadowmap_cascades) + """;
2016-07-12 00:09:02 +02:00
""")
2019-04-29 16:21:44 +02:00
if rpdat.rp_water:
2016-07-17 20:29:53 +02:00
f.write(
2019-04-29 16:21:44 +02:00
"""const float waterLevel = """ + str(round(rpdat.arm_water_level * 100) / 100) + """;
const float waterDisplace = """ + str(round(rpdat.arm_water_displace * 100) / 100) + """;
const float waterSpeed = """ + str(round(rpdat.arm_water_speed * 100) / 100) + """;
const float waterFreq = """ + str(round(rpdat.arm_water_freq * 100) / 100) + """;
const vec3 waterColor = vec3(""" + str(round(rpdat.arm_water_color[0] * 100) / 100) + """, """ + str(round(rpdat.arm_water_color[1] * 100) / 100) + """, """ + str(round(rpdat.arm_water_color[2] * 100) / 100) + """);
const float waterDensity = """ + str(round(rpdat.arm_water_density * 100) / 100) + """;
const float waterRefract = """ + str(round(rpdat.arm_water_refract * 100) / 100) + """;
const float waterReflect = """ + str(round(rpdat.arm_water_reflect * 100) / 100) + """;
2016-07-17 20:29:53 +02:00
""")
2018-12-07 13:48:40 +01:00
if rpdat.rp_ssgi == 'SSAO' or rpdat.rp_ssgi == 'RTAO' or rpdat.rp_volumetriclight:
2017-11-04 16:16:07 +01:00
f.write(
2018-12-07 13:48:40 +01:00
"""const float ssaoRadius = """ + str(round(rpdat.arm_ssgi_radius * 100) / 100) + """;
const float ssaoStrength = """ + str(round(rpdat.arm_ssgi_strength * 100) / 100) + """;
const float ssaoScale = """ + ("2.0" if rpdat.arm_ssgi_half_res else "20.0") + """;
2016-07-17 20:29:53 +02:00
""")
2018-12-07 13:48:40 +01:00
2017-11-04 16:16:07 +01:00
if rpdat.rp_ssgi == 'RTGI' or rpdat.rp_ssgi == 'RTAO':
f.write(
2018-03-15 16:02:56 +01:00
"""const int ssgiMaxSteps = """ + str(rpdat.arm_ssgi_max_steps) + """;
const float ssgiRayStep = 0.005 * """ + str(round(rpdat.arm_ssgi_step * 100) / 100) + """;
const float ssgiStrength = """ + str(round(rpdat.arm_ssgi_strength * 100) / 100) + """;
2017-11-04 16:16:07 +01:00
""")
if rpdat.rp_bloom:
f.write(
2018-03-15 16:02:56 +01:00
"""const float bloomThreshold = """ + str(round(rpdat.arm_bloom_threshold * 100) / 100) + """;
const float bloomStrength = """ + str(round(rpdat.arm_bloom_strength * 100) / 100) + """;
const float bloomRadius = """ + str(round(rpdat.arm_bloom_radius * 100) / 100) + """;
""")
2017-11-22 21:17:36 +01:00
if rpdat.rp_motionblur != 'Off':
2017-11-04 16:16:07 +01:00
f.write(
2018-03-15 16:02:56 +01:00
"""const float motionBlurIntensity = """ + str(round(rpdat.arm_motion_blur_intensity * 100) / 100) + """;
""")
2017-11-04 16:16:07 +01:00
if rpdat.rp_ssr:
f.write(
2018-03-15 16:02:56 +01:00
"""const float ssrRayStep = """ + str(round(rpdat.arm_ssr_ray_step * 100) / 100) + """;
const float ssrMinRayStep = """ + str(round(rpdat.arm_ssr_min_ray_step * 100) / 100) + """;
const float ssrSearchDist = """ + str(round(rpdat.arm_ssr_search_dist * 100) / 100) + """;
const float ssrFalloffExp = """ + str(round(rpdat.arm_ssr_falloff_exp * 100) / 100) + """;
const float ssrJitter = """ + str(round(rpdat.arm_ssr_jitter * 100) / 100) + """;
2017-01-17 14:48:47 +01:00
""")
2017-08-21 20:16:06 +02:00
if rpdat.arm_ssrs:
2017-01-17 14:48:47 +01:00
f.write(
2018-03-15 16:02:56 +01:00
"""const float ssrsRayStep = """ + str(round(rpdat.arm_ssrs_ray_step * 100) / 100) + """;
2016-09-08 14:08:31 +02:00
""")
2017-11-04 16:16:07 +01:00
if rpdat.rp_volumetriclight:
f.write(
2018-03-15 16:02:56 +01:00
"""const float volumAirTurbidity = """ + str(round(rpdat.arm_volumetric_light_air_turbidity * 100) / 100) + """;
const vec3 volumAirColor = vec3(""" + str(round(rpdat.arm_volumetric_light_air_color[0] * 100) / 100) + """, """ + str(round(rpdat.arm_volumetric_light_air_color[1] * 100) / 100) + """, """ + str(round(rpdat.arm_volumetric_light_air_color[2] * 100) / 100) + """);
const int volumSteps = """ + str(rpdat.arm_volumetric_light_steps) + """;
2017-11-18 22:52:41 +01:00
""")
if rpdat.rp_autoexposure:
f.write(
2018-03-15 16:02:56 +01:00
"""const float autoExposureStrength = """ + str(rpdat.arm_autoexposure_strength) + """;
2019-05-01 10:52:42 +02:00
const float autoExposureSpeed = """ + str(rpdat.arm_autoexposure_speed) + """;
2016-08-15 23:45:03 +02:00
""")
2016-08-23 22:55:46 +02:00
# Compositor
2018-03-15 16:02:56 +01:00
if rpdat.arm_letterbox:
2016-08-15 23:45:03 +02:00
f.write(
2018-03-15 16:02:56 +01:00
"""const float compoLetterboxSize = """ + str(round(rpdat.arm_letterbox_size * 100) / 100) + """;
2016-08-15 23:45:03 +02:00
""")
2018-03-15 16:02:56 +01:00
if rpdat.arm_grain:
2016-08-15 23:45:03 +02:00
f.write(
2018-03-15 16:02:56 +01:00
"""const float compoGrainStrength = """ + str(round(rpdat.arm_grain_strength * 100) / 100) + """;
2019-02-21 14:20:18 +01:00
""")
if rpdat.arm_vignette:
f.write(
"""const float compoVignetteStrength = """ + str(round(rpdat.arm_vignette_strength * 100) / 100) + """;
2018-03-02 17:02:27 +01:00
""")
2018-03-15 16:02:56 +01:00
if rpdat.arm_sharpen:
2018-03-02 17:02:27 +01:00
f.write(
2018-03-15 16:02:56 +01:00
"""const float compoSharpenStrength = """ + str(round(rpdat.arm_sharpen_strength * 100) / 100) + """;
2016-08-15 23:45:03 +02:00
""")
2019-05-01 10:52:42 +02:00
if bpy.data.scenes[0].view_settings.exposure != 0.0:
2016-08-15 23:45:03 +02:00
f.write(
2019-05-01 10:52:42 +02:00
"""const float compoExposureStrength = """ + str(round(bpy.data.scenes[0].view_settings.exposure * 100) / 100) + """;
2016-08-15 23:45:03 +02:00
""")
2018-03-15 16:02:56 +01:00
if rpdat.arm_fog:
2016-08-15 23:45:03 +02:00
f.write(
2018-03-15 16:02:56 +01:00
"""const float compoFogAmountA = """ + str(round(rpdat.arm_fog_amounta * 100) / 100) + """;
const float compoFogAmountB = """ + str(round(rpdat.arm_fog_amountb * 100) / 100) + """;
const vec3 compoFogColor = vec3(""" + str(round(rpdat.arm_fog_color[0] * 100) / 100) + """, """ + str(round(rpdat.arm_fog_color[1] * 100) / 100) + """, """ + str(round(rpdat.arm_fog_color[2] * 100) / 100) + """);
2019-08-08 18:56:56 +02:00
""")
if rpdat.arm_lens_texture_masking:
f.write(
"""const float compoCenterMinClip = """ + str(round(rpdat.arm_lens_texture_masking_centerMinClip * 100) / 100) + """;
const float compoCenterMaxClip = """ + str(round(rpdat.arm_lens_texture_masking_centerMaxClip * 100) / 100) + """;
const float compoLuminanceMin = """ + str(round(rpdat.arm_lens_texture_masking_luminanceMin * 100) / 100) + """;
const float compoLuminanceMax = """ + str(round(rpdat.arm_lens_texture_masking_luminanceMax * 100) / 100) + """;
const float compoBrightnessExponent = """ + str(round(rpdat.arm_lens_texture_masking_brightnessExp * 100) / 100) + """;
2019-08-08 20:02:42 +02:00
""")
if rpdat.rp_chromatic_aberration:
f.write(
f"""const float compoChromaticStrength = {round(rpdat.arm_chromatic_aberration_strength * 100) / 100};
const int compoChromaticSamples = {rpdat.arm_chromatic_aberration_samples};
2019-08-08 20:02:42 +02:00
""")
if rpdat.arm_chromatic_aberration_type == "Spectral":
f.write("const int compoChromaticType = 1;")
else:
f.write("const int compoChromaticType = 0;")
2016-08-15 23:45:03 +02:00
2019-08-24 11:50:27 +02:00
focus_distance = 0.0
2019-05-24 09:34:59 +02:00
fstop = 0.0
2019-08-24 11:50:27 +02:00
if len(bpy.data.cameras) > 0 and bpy.data.cameras[0].dof.use_dof:
focus_distance = bpy.data.cameras[0].dof.focus_distance
fstop = bpy.data.cameras[0].dof.aperture_fstop
2019-11-16 14:11:32 +01:00
2019-08-24 11:50:27 +02:00
if focus_distance > 0.0:
2016-08-15 23:45:03 +02:00
f.write(
2019-05-20 14:32:48 +02:00
"""const float compoDOFDistance = """ + str(round(focus_distance * 100) / 100) + """;
2019-05-24 09:34:59 +02:00
const float compoDOFFstop = """ + str(round(fstop * 100) / 100) + """;
2017-02-28 13:48:19 +01:00
const float compoDOFLength = 160.0;
""") # str(round(bpy.data.cameras[0].lens * 100) / 100)
2017-02-22 15:50:19 +01:00
if rpdat.rp_voxelao:
2017-10-23 01:55:47 +02:00
halfext = round(rpdat.arm_voxelgi_dimensions / 2.0)
2017-02-22 15:50:19 +01:00
f.write(
2017-10-23 01:55:47 +02:00
"""const ivec3 voxelgiResolution = ivec3(""" + str(rpdat.rp_voxelgi_resolution) + """, """ + str(rpdat.rp_voxelgi_resolution) + """, """ + str(int(int(rpdat.rp_voxelgi_resolution) * float(rpdat.rp_voxelgi_resolution_z))) + """);
const vec3 voxelgiHalfExtents = vec3(""" + str(halfext) + """, """ + str(halfext) + """, """ + str(round(halfext * float(rpdat.rp_voxelgi_resolution_z))) + """);
2018-03-15 16:02:56 +01:00
const float voxelgiOcc = """ + str(round(rpdat.arm_voxelgi_occ * 100) / 100) + """;
const float voxelgiStep = """ + str(round(rpdat.arm_voxelgi_step * 100) / 100) + """;
const float voxelgiRange = """ + str(round(rpdat.arm_voxelgi_range * 100) / 100) + """;
2018-03-26 00:30:24 +02:00
const float voxelgiOffset = """ + str(round(rpdat.arm_voxelgi_offset * 100) / 100) + """;
2019-02-09 15:34:16 +01:00
const float voxelgiAperture = """ + str(round(rpdat.arm_voxelgi_aperture * 100) / 100) + """;
2017-05-23 15:01:56 +02:00
""")
2017-08-21 20:16:06 +02:00
if rpdat.rp_sss_state == 'On':
2017-05-23 15:01:56 +02:00
f.write(
2018-03-15 16:02:56 +01:00
"""const float sssWidth = """ + str(rpdat.arm_sss_width / 10.0) + """;
2016-08-23 22:55:46 +02:00
""")
# Skinning
2019-04-06 14:13:38 +02:00
if rpdat.arm_skin == 'On':
2016-08-23 22:55:46 +02:00
f.write(
2018-03-15 16:02:56 +01:00
"""const int skinMaxBones = """ + str(rpdat.arm_skin_max_bones) + """;
Add support for shadow map atlasing With this it is now possible to enable atlasing of shadow maps, which solves the existing limitation of 4 lights in a scene. This is done by grouping the rendering of shadow maps, that currently are drawn into their own images for each light, into one or several big textures. This was done because the openGL and webGL version Armory targets do not support dynamic indexing of shadowMapSamplers, meaning that the index that access an array of shadow maps has to be know by the compiler before hand so it can be unrolled into if/else branching. By instead simply using a big shadow map texture and moving the dynamic part to other types of array that are allowed dynamic indexing like vec4 and mat4, this limitation was solved. The premise was simple enough for the shader part, but for the Haxe part, managing and solving where lights shadow maps should go in a shadow map can be tricky. So to keep track and solve this, ShadowMapAtlas and ShadowMapTile were created. These classes have the minimally required logic to solve the basic features needed for this problem: defining some kind of abstraction to prevent overlapping of shadowmaps, finding available space, assigning such space efficiently, locking and freeing this space, etc. This functionality it is used by drawShadowMapAtlas(), which is a modified version of drawShadowMap(). Shadow map atlases are represented with perfectly balanced 4-ary trees, where each tree of the previous definition represents a "tile" or slice that results from dividing a square that represents the image into 4 slices or sub-images. The root of this "tile" it's a reference to the tile-slice, and this tile is divided in 4 slices, and the process is repeated depth-times. If depth is 1, slices are kept at just the initial 4 tiles of max size, which is the default size of the shadow map. #arm_shadowmap_atlas_lod allows controlling if code to support more depth levels is added or not when compiling. the tiles that populate atlases tile trees are simply a data structure that contains a reference to the light they are linked to, inner subtiles in case LOD is enabled, coordinates to where this tile starts in the atlas that go from 0 to Shadow Map Size, and a reference to a linked tile for LOD. This simple definition allows tiles having a theoretically small memory footprint, but in turn this simplicity might make some functionality that might be responsibility of tiles (for example knowing if they are overlapping) a responsibility of the ones that utilizes tiles instead. This decision may complicate maintenance so it is to be revised in future iterations of this feature.
2021-01-27 02:01:06 +01:00
""")
if '_Clusters' in wrd.world_defs:
max_lights = "4"
max_lights_clusters = "4"
if rpdat.rp_shadowmap_atlas:
max_lights = str(rpdat.rp_max_lights)
max_lights_clusters = str(rpdat.rp_max_lights_cluster)
# prevent max lights cluster being higher than max lights
if (int(max_lights_clusters) > int(max_lights)):
max_lights_clusters = max_lights
f.write(
"""const int maxLights = """ + max_lights + """;
const int maxLightsCluster = """ + max_lights_clusters + """;
const float clusterNear = 3.0;
2016-07-10 00:51:39 +02:00
""")
2018-02-27 20:27:27 +01:00
f.write(add_compiledglsl + '\n') # External defined constants
2017-02-22 15:50:19 +01:00
f.write("""#endif // _COMPILED_GLSL_
""")
2016-10-17 17:39:40 +02:00
def write_traithx(class_path):
wrd = bpy.data.worlds['Arm']
# Split the haxe package syntax in components that will compose the path
path_components = class_path.split('.')
# extract the full file name (file + ext) from the components
class_name = path_components[-1]
# Create the absolute trait path (os-safe)
package_path = os.sep.join([arm.utils.get_fp(), 'Sources', arm.utils.safestr(wrd.arm_project_package)] + path_components[:-1])
2016-12-08 14:38:04 +01:00
if not os.path.exists(package_path):
os.makedirs(package_path)
package = '.'.join([arm.utils.safestr(wrd.arm_project_package)] + path_components[:-1]);
2016-12-08 14:38:04 +01:00
with open(package_path + '/' + class_name + '.hx', 'w') as f:
2016-07-10 00:51:39 +02:00
f.write(
"""package """ + package + """;
2016-07-10 00:51:39 +02:00
2017-12-11 00:55:26 +01:00
class """ + class_name + """ extends iron.Trait {
2017-08-06 19:21:13 +02:00
\tpublic function new() {
\t\tsuper();
2016-07-10 00:51:39 +02:00
2017-08-06 19:21:13 +02:00
\t\t// notifyOnInit(function() {
\t\t// });
2016-07-10 00:51:39 +02:00
2017-08-06 19:21:13 +02:00
\t\t// notifyOnUpdate(function() {
\t\t// });
\t\t// notifyOnRemove(function() {
\t\t// });
\t}
2016-07-10 00:51:39 +02:00
}
""")
2017-05-26 16:05:14 +02:00
def write_canvasjson(canvas_name):
canvas_path = arm.utils.get_fp() + '/Bundled/canvas'
if not os.path.exists(canvas_path):
os.makedirs(canvas_path)
with open(canvas_path + '/' + canvas_name + '.json', 'w') as f:
f.write(
"""{ "name": "untitled", "x": 0.0, "y": 0.0, "width": 1280, "height": 720, "theme": "Default Light", "elements": [], "assets": [] }""")