2015-12-07 21:05:27 +01:00
|
|
|
import bpy
|
|
|
|
import os
|
2017-03-20 13:23:31 +01:00
|
|
|
import shutil
|
2018-02-25 19:01:22 +01:00
|
|
|
import glob
|
|
|
|
import json
|
2018-05-01 13:58:36 +02:00
|
|
|
import stat
|
2017-03-15 12:30:14 +01:00
|
|
|
import arm.utils
|
|
|
|
import arm.assets as assets
|
|
|
|
import arm.make_state as state
|
2015-12-07 21:05:27 +01:00
|
|
|
|
2020-12-17 22:25:00 +01: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:
|
|
|
|
project_path = arm.utils.get_fp()
|
|
|
|
if rel_path and on_same_drive(sdk_path, project_path):
|
|
|
|
sdk_path = '../' + os.path.relpath(sdk_path, project_path).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
|
|
|
|
2020-12-17 22:25:00 +01: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 22:25:00 +01:00
|
|
|
project_path = arm.utils.get_fp()
|
|
|
|
if rel_path and on_same_drive(path, project_path):
|
|
|
|
path = os.path.relpath(path, project_path).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
|
|
|
|
2020-12-17 22:25:00 +01:00
|
|
|
|
|
|
|
def add_shaders(path: str, rel_path=False) -> str:
|
|
|
|
project_path = arm.utils.get_fp()
|
|
|
|
if rel_path and on_same_drive(path, project_path):
|
|
|
|
path = os.path.relpath(path, project_path)
|
2018-10-16 12:55:27 +02:00
|
|
|
return 'project.addShaders("' + path.replace('\\', '/').replace('//', '/') + '");\n'
|
2018-08-16 20:48:00 +02:00
|
|
|
|
2020-12-17 22:25:00 +01:00
|
|
|
|
2018-05-01 13:58:36 +02:00
|
|
|
def remove_readonly(func, path, excinfo):
|
|
|
|
os.chmod(path, stat.S_IWRITE)
|
|
|
|
func(path)
|
|
|
|
|
2019-02-10 11:47:42 +01:00
|
|
|
def write_khafilejs(is_play, export_physics, export_navigation, export_ui, is_publish, enable_dce, import_traits, import_logicnodes):
|
2017-03-15 12:30:14 +01:00
|
|
|
sdk_path = arm.utils.get_sdk_path()
|
2018-08-16 20:48:00 +02:00
|
|
|
rel_path = arm.utils.get_relative_paths() # Convert absolute paths to relative
|
2016-10-12 17:52:27 +02:00
|
|
|
wrd = bpy.data.worlds['Arm']
|
2016-01-17 22:38:46 +01:00
|
|
|
|
2020-10-18 17:50:35 +02:00
|
|
|
with open('khafile.js', 'w', encoding="utf-8") as khafile:
|
2020-08-28 13:25:54 +02:00
|
|
|
khafile.write(
|
2015-12-07 21:05:27 +01:00
|
|
|
"""// Auto-generated
|
Windows Settings – Publish and action after
Windows Settings:
Visual Studio Version - select the studio version for which the project will be exported. Options: 2010, 2012, 2013, 2015, 2017, 2019. Default: 2019.
Update - checks the installed versions of Visual Studio on the PC and adds (installed) to the version in the list if available (for information). Example:
sample_vs_2
Action After Publishing - select an action after a successful publication. Options:
Nothing - do nothing. Default value;
Open In Visual Studio - open the project in the corresponding studio version;
Compile - compilation of the project;
Compile and Run - compile and run the project. Then the executable file will be copied to the windows-hl folder (where the resources are located).
Mode - compilation mode. Options: Debug, Release. Default: Debug.
Architecture - the architecture for which the application will be built. Options: x86, x64. Default: version of the user’s PC architecture.
Compile Log Parameter - setting the output of messages during compilation:
Summary - show the error and warning summary at the end. Default value;
No Summary - don \ 't show the error and warning summary at the end;
Warnings and Errors Only - show only warnings and errors;
Warnings Only - show only warnings;
Errors Only - show only errors.
More details can be found here - MSBuild command-line reference (I took only part of the settings).
Count CPU - specifies the maximum number of concurrent processes to use when building. More details can be found here - MSBuild command-line reference. The default is 1. Maximum value: the number of CPUs in the system (function multiprocessing.cpu_count()).
Open Build Directory - open the folder with the application after a successful build. If the Compile and Run option is selected, then the executable file will be copied to the windows-hl folder (where the resources are located) and this folder will open. Otherwise, the folder where the given Visual Studio file is going will open.
The user will also receive a message if the studio version selected for export and for opening in the studio or compilation is not on the PC. And a list of installed ones will be issued. Example:
Visual Studio 2017 (version 15) not found.
The following are installed on the PC:
- Visual Studio Community 2019 (version 16.8.30711.63)
To obtain information about the installed versions of Visual Studio, use the vswhere.exe utility (open source) included in Kha (located in the …\ArmorySDK\Kha\Kinc\Tools\kincmake\Data\windows).
2020-11-24 18:41:50 +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
|
|
|
|
2020-09-11 14:43:59 +02:00
|
|
|
# Let libraries differentiate between Armory and pure Kha
|
|
|
|
assets.add_khafile_def('armory')
|
|
|
|
|
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)
|
2017-02-13 13:23:43 +01:00
|
|
|
|
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
|
2018-05-01 13:58:36 +02:00
|
|
|
if os.path.exists(arm.utils.build_dir() + '/compiled/Shaders/Project'):
|
|
|
|
shutil.rmtree(arm.utils.build_dir() + '/compiled/Shaders/Project', onerror=remove_readonly)
|
2018-04-25 22:38:52 +02:00
|
|
|
shutil.copytree('Shaders', arm.utils.build_dir() + '/compiled/Shaders/Project')
|
2020-08-28 13:25:54 +02:00
|
|
|
khafile.write("project.addShaders('" + arm.utils.build_dir() + "/compiled/Shaders/Project/**');\n")
|
2018-04-25 22:38:52 +02:00
|
|
|
# for file in glob.glob("Shaders/**", recursive=True):
|
|
|
|
# if os.path.isfile(file):
|
|
|
|
# assets.add_shader(file)
|
2017-09-17 16:59:00 +02:00
|
|
|
|
2017-09-05 00:36:16 +02:00
|
|
|
if not os.path.exists('Libraries/armory'):
|
2020-08-28 13:25:54 +02:00
|
|
|
khafile.write(add_armory_library(sdk_path, 'armory', rel_path=rel_path))
|
2017-02-09 00:33:19 +01:00
|
|
|
|
2017-09-05 00:36:16 +02:00
|
|
|
if not os.path.exists('Libraries/iron'):
|
2020-08-28 13:25:54 +02:00
|
|
|
khafile.write(add_armory_library(sdk_path, 'iron', rel_path=rel_path))
|
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):
|
2020-08-28 13:25:54 +02:00
|
|
|
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):
|
2020-08-28 13:25:54 +02:00
|
|
|
khafile.write('await project.addProject("Subprojects/{0}");\n'.format(lib))
|
2018-05-13 21:17:40 +02:00
|
|
|
|
2017-12-06 19:55:08 +01:00
|
|
|
if wrd.arm_audio == 'Disabled':
|
2018-09-25 07:29:39 +02:00
|
|
|
assets.add_khafile_def('kha_no_ogg')
|
2019-06-22 17:06:02 +02:00
|
|
|
else:
|
|
|
|
assets.add_khafile_def('arm_audio')
|
2017-12-06 19:55:08 +01:00
|
|
|
|
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')
|
2018-06-24 13:05:24 +02:00
|
|
|
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-08-28 13:25:54 +02:00
|
|
|
khafile.write(add_armory_library(sdk_path + '/lib/', 'haxebullet', rel_path=rel_path))
|
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-08-28 13:25:54 +02:00
|
|
|
khafile.write(add_assets(ammojs_path, rel_path=rel_path))
|
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-08-28 13:25:54 +02:00
|
|
|
khafile.write(add_assets(ammojs_wasm_path, rel_path=rel_path))
|
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-08-28 13:25:54 +02:00
|
|
|
khafile.write(add_assets(ammojs_path, rel_path=rel_path))
|
2018-06-24 13:05:24 +02:00
|
|
|
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-08-28 13:25:54 +02:00
|
|
|
khafile.write(add_armory_library(sdk_path + '/lib/', 'oimo', rel_path=rel_path))
|
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-08-28 13:25:54 +02:00
|
|
|
khafile.write(add_armory_library(sdk_path + '/lib/', 'haxerecast', rel_path=rel_path))
|
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-08-28 13:25:54 +02:00
|
|
|
khafile.write(add_assets(recastjs_path, rel_path=rel_path))
|
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:
|
|
|
|
pass
|
2020-08-28 13:25:54 +02:00
|
|
|
# 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:
|
2020-08-28 13:25:54 +02:00
|
|
|
# khafile.write("""project.addParameter("--macro include('armory.trait.physics')");\n""")
|
2018-06-24 13:05:24 +02:00
|
|
|
# if wrd.arm_physics_engine == 'Bullet':
|
2020-08-28 13:25:54 +02:00
|
|
|
# khafile.write("""project.addParameter("--macro include('armory.trait.physics.bullet')");\n""")
|
2018-06-18 12:50:37 +02:00
|
|
|
# else:
|
2020-08-28 13:25:54 +02:00
|
|
|
# khafile.write("""project.addParameter("--macro include('armory.trait.physics.oimo')");\n""")
|
2018-06-18 12:50:37 +02:00
|
|
|
# if export_navigation:
|
2020-08-28 13:25:54 +02:00
|
|
|
# khafile.write("""project.addParameter("--macro include('armory.trait.navigation')");\n""")
|
2018-06-18 12:50:37 +02:00
|
|
|
|
|
|
|
# if import_logicnodes: # Live patching for logic nodes
|
2020-08-28 13:25:54 +02:00
|
|
|
# khafile.write("""project.addParameter("--macro include('armory.logicnode')");\n""")
|
2017-08-10 14:39:08 +02:00
|
|
|
|
2018-08-21 18:29:05 +02:00
|
|
|
if not wrd.arm_compiler_inline:
|
2020-08-28 13:25:54 +02:00
|
|
|
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:
|
2020-08-28 13:25:54 +02:00
|
|
|
khafile.write("project.addParameter('-dce full');\n")
|
2017-08-10 14:10:37 +02:00
|
|
|
|
2019-02-14 11:08:36 +01:00
|
|
|
live_patch = wrd.arm_live_patch and state.target == 'krom'
|
|
|
|
if wrd.arm_debug_console or live_patch:
|
2018-06-18 16:00:27 +02:00
|
|
|
import_traits.append('armory.trait.internal.Bridge')
|
2019-02-14 11:08:36 +01:00
|
|
|
if live_patch:
|
2019-02-10 11:47:42 +01:00
|
|
|
assets.add_khafile_def('arm_patch')
|
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)):
|
2020-08-28 13:25:54 +02:00
|
|
|
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
|
|
|
|
|
2018-12-04 19:10:55 +01:00
|
|
|
shaders_path = arm.utils.build_dir() + '/compiled/Shaders/*.glsl'
|
|
|
|
if rel_path:
|
|
|
|
shaders_path = os.path.relpath(shaders_path, arm.utils.get_fp()).replace('\\', '/')
|
2020-08-28 13:25:54 +02:00
|
|
|
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
|
|
|
|
shaders_path = arm.utils.build_dir() + '/compiled/Hlsl/*.glsl'
|
2018-08-30 15:42:25 +02:00
|
|
|
if rel_path:
|
|
|
|
shaders_path = os.path.relpath(shaders_path, arm.utils.get_fp()).replace('\\', '/')
|
2020-08-28 13:25:54 +02:00
|
|
|
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'
|
|
|
|
assets_path = arm.utils.build_dir() + '/compiled/Assets/**'
|
|
|
|
assets_path_sh = arm.utils.build_dir() + '/compiled/Shaders/*.' + ext
|
|
|
|
if rel_path:
|
|
|
|
assets_path = os.path.relpath(assets_path, arm.utils.get_fp()).replace('\\', '/')
|
|
|
|
assets_path_sh = os.path.relpath(assets_path_sh, arm.utils.get_fp()).replace('\\', '/')
|
|
|
|
dest = ''
|
|
|
|
if use_data_dir:
|
|
|
|
dest += ', destination: "data/{name}"'
|
2020-08-28 13:25:54 +02:00
|
|
|
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)))
|
2016-09-28 00:00:59 +02:00
|
|
|
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
|
2018-08-30 15:42:25 +02:00
|
|
|
continue
|
2020-08-28 13:25:54 +02:00
|
|
|
khafile.write(add_assets(ref, use_data_dir=use_data_dir, rel_path=rel_path))
|
2016-09-28 00:00:59 +02:00
|
|
|
|
2017-09-06 16:37:23 +02:00
|
|
|
asset_references = sorted(list(set(assets.assets)))
|
2016-09-28 00:00:59 +02:00
|
|
|
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
|
2018-08-30 15:42:25 +02:00
|
|
|
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-08-28 13:25:54 +02:00
|
|
|
khafile.write(add_assets(ref, quality=quality, use_data_dir=use_data_dir, rel_path=rel_path))
|
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')
|
|
|
|
|
|
|
|
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-08-28 13:25:54 +02:00
|
|
|
khafile.write(add_shaders(sdk_path + "/armory/Shaders/debug_draw/**", rel_path=rel_path))
|
2020-06-25 13:02:56 +02:00
|
|
|
|
|
|
|
if wrd.arm_verbose_output:
|
2020-08-28 13:25:54 +02:00
|
|
|
khafile.write("project.addParameter('--times');\n")
|
2017-01-02 22:46:18 +01:00
|
|
|
|
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-08-28 13:25:54 +02:00
|
|
|
khafile.write(add_armory_library(sdk_path, 'lib/zui', rel_path=rel_path))
|
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-08-28 13:25:54 +02:00
|
|
|
khafile.write(add_assets(p.replace('\\', '/'), use_data_dir=use_data_dir, rel_path=rel_path))
|
2017-05-26 16:42:48 +02:00
|
|
|
assets.add_khafile_def('arm_ui')
|
2019-11-16 14:11:32 +01:00
|
|
|
|
2016-10-17 00:02:51 +02:00
|
|
|
if wrd.arm_minimize == False:
|
2017-05-25 16:48:41 +02:00
|
|
|
assets.add_khafile_def('arm_json')
|
2018-04-14 15:07:05 +02:00
|
|
|
|
2016-10-17 00:02:51 +02:00
|
|
|
if wrd.arm_deinterleaved_buffers == True:
|
2017-05-25 16:48:41 +02:00
|
|
|
assets.add_khafile_def('arm_deinterleaved')
|
2016-08-07 23:12:14 +02:00
|
|
|
|
2017-03-12 17:29:22 +01:00
|
|
|
if wrd.arm_batch_meshes == True:
|
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
|
|
|
|
2018-08-16 17:16:35 +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')
|
|
|
|
|
2018-08-26 18:42:10 +02:00
|
|
|
if os.path.exists(arm.utils.get_fp() + '/Bundled/config.arm'):
|
|
|
|
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')
|
|
|
|
|
2016-07-28 13:21:27 +02:00
|
|
|
for d in assets.khafile_defs:
|
2020-08-28 13:25:54 +02:00
|
|
|
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:
|
2020-08-28 13:25:54 +02:00
|
|
|
khafile.write("project.addParameter('" + p + "');\n")
|
2019-05-10 18:29:59 +02:00
|
|
|
|
2019-03-01 11:11:51 +01:00
|
|
|
if wrd.arm_khafile != None:
|
2020-08-28 13:25:54 +02:00
|
|
|
khafile.write(wrd.arm_khafile.as_string())
|
2016-01-11 13:50:54 +01: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
|
2020-08-28 13:25:54 +02:00
|
|
|
khafile.write("project.targetOptions.android_native.package = '{0}';\n".format(arm.utils.safestr(bundle)))
|
2017-12-11 14:43:40 +01:00
|
|
|
if wrd.arm_winorient != 'Multi':
|
2020-08-28 13:25:54 +02:00
|
|
|
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
|
2020-11-05 11:04:11 +01:00
|
|
|
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:
|
|
|
|
perm = "'android.permission."+ item.arm_android_permissions +"'"
|
|
|
|
# Checking In
|
|
|
|
if perms.find(perm) == -1:
|
|
|
|
if len(perms) > 0:
|
|
|
|
perms = perms +', '+ perm
|
|
|
|
else:
|
|
|
|
perms = perm
|
|
|
|
if len(perms) > 0:
|
|
|
|
khafile.write("project.targetOptions.android_native.permissions = [{0}];\n".format(perms))
|
|
|
|
# Android ABI Filters
|
2020-11-05 11:04:11 +01:00
|
|
|
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:
|
|
|
|
abi = "'"+ item.arm_android_abi +"'"
|
|
|
|
# Checking In
|
|
|
|
if abis.find(abi) == -1:
|
|
|
|
if len(abis) > 0:
|
|
|
|
abis = abis +', '+ abi
|
|
|
|
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
|
2020-08-28 13:25:54 +02:00
|
|
|
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 != '':
|
|
|
|
shutil.copy(bpy.path.abspath(wrd.arm_project_icon), arm.utils.get_fp() + '/icon.png')
|
2017-09-08 14:21:57 +02:00
|
|
|
|
2020-08-28 13:25:54 +02:00
|
|
|
khafile.write("\n\nresolve(project);\n")
|
2016-01-11 13:07:44 +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
|
|
|
|
|
|
|
|
def write_config(resx, resy):
|
|
|
|
wrd = bpy.data.worlds['Arm']
|
|
|
|
p = arm.utils.get_fp() + '/Bundled'
|
|
|
|
if not os.path.exists(p):
|
|
|
|
os.makedirs(p)
|
|
|
|
output = {}
|
|
|
|
output['window_mode'] = get_winmode(wrd.arm_winmode)
|
2018-08-20 16:27:09 +02:00
|
|
|
output['window_w'] = int(resx)
|
|
|
|
output['window_h'] = int(resy)
|
2018-08-29 23:01:23 +02:00
|
|
|
output['window_resizable'] = wrd.arm_winresize
|
2019-02-11 17:10:49 +01:00
|
|
|
output['window_maximizable'] = wrd.arm_winresize and wrd.arm_winmaximize
|
2018-08-29 23:01:23 +02:00
|
|
|
output['window_minimizable'] = wrd.arm_winminimize
|
|
|
|
output['window_vsync'] = wrd.arm_vsync
|
2018-02-25 19:01:22 +01:00
|
|
|
rpdat = arm.utils.get_rp()
|
2018-08-20 16:27:09 +02:00
|
|
|
output['window_msaa'] = int(rpdat.arm_samples_per_pixel)
|
2018-08-29 23:01:23 +02:00
|
|
|
output['window_scale'] = 1.0
|
|
|
|
output['rp_supersample'] = float(rpdat.rp_supersampling)
|
2018-12-10 00:02:40 +01:00
|
|
|
rp_shadowmap_cube = int(rpdat.rp_shadowmap_cube) if rpdat.rp_shadows else 0
|
|
|
|
output['rp_shadowmap_cube'] = rp_shadowmap_cube
|
2019-01-21 17:59:25 +01:00
|
|
|
rp_shadowmap_cascade = arm.utils.get_cascade_size(rpdat) if rpdat.rp_shadows else 0
|
2018-12-10 00:02:40 +01:00
|
|
|
output['rp_shadowmap_cascade'] = rp_shadowmap_cascade
|
2018-08-29 23:01:23 +02:00
|
|
|
output['rp_ssgi'] = rpdat.rp_ssgi != 'Off'
|
|
|
|
output['rp_ssr'] = rpdat.rp_ssr != 'Off'
|
|
|
|
output['rp_bloom'] = rpdat.rp_bloom != 'Off'
|
|
|
|
output['rp_motionblur'] = rpdat.rp_motionblur != 'Off'
|
2019-04-06 18:52:21 +02:00
|
|
|
output['rp_gi'] = rpdat.rp_voxelao
|
2019-02-10 17:30:31 +01:00
|
|
|
output['rp_dynres'] = rpdat.rp_dynres
|
2018-02-25 19:01:22 +01:00
|
|
|
with open(p + '/config.arm', 'w') as f:
|
|
|
|
f.write(json.dumps(output, sort_keys=True, indent=4))
|
|
|
|
|
2019-02-10 11:47:42 +01:00
|
|
|
def write_mainhx(scene_name, resx, resy, is_play, is_publish):
|
2016-09-02 23:11:04 +02:00
|
|
|
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
|
|
|
|
2019-04-06 18:52:21 +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) + """;""")
|
2020-11-05 11:04:11 +01:00
|
|
|
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
|
|
|
|
2017-11-27 13:57:13 +01:00
|
|
|
def write_indexhtml(w, h, is_publish):
|
2017-12-11 14:47:55 +01:00
|
|
|
wrd = bpy.data.worlds['Arm']
|
2017-08-21 20:16:06 +02:00
|
|
|
rpdat = arm.utils.get_rp()
|
2017-11-27 13:57:13 +01:00
|
|
|
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)
|
2020-12-02 18:54:14 +01:00
|
|
|
popupmenu_in_browser = ''
|
|
|
|
if wrd.arm_project_html5_popupmenu_in_browser:
|
|
|
|
popupmenu_in_browser = ' oncontextmenu="return false"'
|
2017-11-27 13:57:13 +01:00
|
|
|
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"/>""")
|
2017-12-11 14:47:55 +01:00
|
|
|
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("""
|
2016-08-04 22:38:56 +02:00
|
|
|
<title>Armory</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
|
|
|
""")
|
2017-12-11 14:47:55 +01:00
|
|
|
if rpdat.rp_stereo or wrd.arm_winmode == 'Fullscreen':
|
2017-04-16 14:46:35 +02:00
|
|
|
f.write("""
|
2020-12-02 18:54:14 +01:00
|
|
|
<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("""
|
2020-12-02 18:54:14 +01:00
|
|
|
<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 = ''
|
2019-01-27 18:56:04 +01:00
|
|
|
def write_compiledglsl(defs, make_variants):
|
2017-08-21 20:16:06 +02:00
|
|
|
rpdat = arm.utils.get_rp()
|
2019-01-21 17:59:25 +01:00
|
|
|
shadowmap_size = arm.utils.get_cascade_size(rpdat) if rpdat.rp_shadows else 0
|
2018-08-30 15:42:25 +02:00
|
|
|
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:
|
2019-01-27 18:56:04 +01:00
|
|
|
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
|
|
|
|
""")
|
|
|
|
|
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
|
|
|
""")
|
2020-07-06 18:14:15 +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) + """;
|
2016-07-18 20:28:52 +02:00
|
|
|
""")
|
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) + """;
|
2016-07-18 20:28:52 +02:00
|
|
|
""")
|
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(
|
2020-11-28 23:12:21 +01:00
|
|
|
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
|
|
|
""")
|
|
|
|
|
2020-11-28 23:12:21 +01: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
|
|
|
|
2019-04-06 18:52:21 +02: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) + """;
|
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
|
|
|
|
2019-01-31 13:37:54 +01:00
|
|
|
def write_traithx(class_path):
|
2016-09-02 23:11:04 +02:00
|
|
|
wrd = bpy.data.worlds['Arm']
|
2019-01-31 13:37:54 +01:00
|
|
|
# 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)
|
2019-01-31 13:37:54 +01:00
|
|
|
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(
|
2019-01-31 13:37:54 +01:00
|
|
|
"""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(
|
2020-01-13 11:04:39 +01:00
|
|
|
"""{ "name": "untitled", "x": 0.0, "y": 0.0, "width": 1280, "height": 720, "theme": "Default Light", "elements": [], "assets": [] }""")
|