This commit is contained in:
2026-04-27 19:21:50 -07:00
parent 98856b3f54
commit a3d5fa846b
16 changed files with 513 additions and 337 deletions

View File

@ -9,6 +9,7 @@ import shutil
import stat
from string import Template
import subprocess
import tempfile
import threading
import time
import traceback
@ -181,6 +182,14 @@ def clear_external_scenes():
appended_scenes = []
def export_data(fp, sdk_path):
state.is_exporting = True
try:
export_data_impl(fp, sdk_path)
finally:
state.is_exporting = False
def export_data_impl(fp, sdk_path):
load_external_blends()
wrd = bpy.data.worlds['Lnx']
@ -316,7 +325,11 @@ def export_data(fp, sdk_path):
shaders_path = build_dir + '/compiled/Shaders'
if not os.path.exists(shaders_path):
os.makedirs(shaders_path)
write_data.write_compiledglsl(defs + cdefs, make_variants=has_config)
inc_changed = write_data.write_compiledglsl(defs + cdefs, make_variants=has_config)
if inc_changed:
for g in glob.glob(shaders_path + '/*.glsl'):
os.utime(g, None)
# Write referenced shader passes
if not os.path.isfile(build_dir + '/compiled/Shaders/shader_datas.lnx') or state.last_world_defs != wrd.world_defs:
@ -625,7 +638,7 @@ def _kill_all_viewport_processes():
for viewport_id in list(_viewport_processes.keys()):
_kill_viewport_process(viewport_id)
if lnx.utils.get_os() == 'win':
try:
result = subprocess.run(
@ -643,20 +656,20 @@ def _kill_all_viewport_processes():
def run_viewport_runtime(viewport_id, width=1920, height=1080):
"""Launch a viewport which gets its own Krom process with unique shared memory."""
global _viewport_processes
if 'Lnx' not in bpy.data.worlds:
log.warn('No Lnx world found - cannot start viewport server')
return None, None
_kill_viewport_process(viewport_id)
shmem_name = _get_viewport_shmem_name(viewport_id)
wrd = bpy.data.worlds['Lnx']
krom_location, krom_path = lnx.utils.krom_paths()
path = lnx.utils.get_fp_build() + '/debug/krom'
path_resources = path + '-resources'
if not os.path.exists(path + '/krom.js'):
log.warn(f'Krom build not found at {path}/krom.js - build project first')
return None, None
@ -669,7 +682,7 @@ def run_viewport_runtime(viewport_id, width=1920, height=1080):
cmd.append(str(os.getpid()))
if wrd.lnx_audio == 'Disabled':
cmd.append('--nosound')
cmd.append('--viewport-server')
cmd.append('--shmem')
cmd.append(shmem_name)
@ -703,13 +716,13 @@ def build_viewport(viewport_id, width=1920, height=1080):
if not wrd:
log.warn('No Lnx world found - cannot build for viewport')
return
krom_js_path = lnx.utils.get_fp_build() + '/debug/krom/krom.js'
if os.path.exists(krom_js_path) and not wrd.lnx_recompile:
log.info(f'Using cached viewport build for {viewport_id}')
run_viewport_runtime(viewport_id, width, height)
return
pending_entry = (viewport_id, width, height)
if pending_entry not in _viewport_pending_launches:
_viewport_pending_launches.append(pending_entry)
@ -719,7 +732,7 @@ def build_viewport(viewport_id, width=1920, height=1080):
# _viewport_proc_build checks viewport ,not state.proc_build (which is for play button)
if _viewport_build_in_progress:
if _viewport_proc_build is not None and _viewport_proc_build.poll() is None:
# log.info(f'Build in progress: {viewport_id} - launching when ready')
log.info(f'Build in progress, viewport {viewport_id} will launch when ready')
return
else:
log.info(f'Resetting stale viewport build state')
@ -738,20 +751,20 @@ def build_viewport(viewport_id, width=1920, height=1080):
state.is_play = False # NOT play mode
state.is_publish = False
state.is_export = False
log.clear(clear_warnings=True, clear_errors=True)
sdk_path = lnx.utils.get_sdk_path()
fp = lnx.utils.get_fp()
os.chdir(fp)
sources_path = 'Sources/' + lnx.utils.safestr(wrd.lnx_project_package)
if not os.path.exists(sources_path):
os.makedirs(sources_path)
log.info('Exporting scene data...')
export_data(fp, sdk_path)
log.info('Starting Krom compilation for viewport...')
compile_viewport(assets_only=(not wrd.lnx_recompile))
@ -811,7 +824,7 @@ def viewport_build_done():
if result == 0:
bpy.data.worlds['Lnx'].lnx_recompile = False
if _viewport_pending_launches:
pending = _viewport_pending_launches.copy()
_viewport_pending_launches.clear()
@ -829,18 +842,20 @@ def play_viewport(viewport_id, width=1920, height=1080):
global _viewport_build_in_progress, _viewport_pending_launches, _viewport_proc_build
if not viewport_id:
log.error('No viewport_id: play_viewport requires an id')
return
if 'Lnx' not in bpy.data.worlds:
log.error('No Lnx world found - cannot start viewport server')
return
wrd = bpy.data.worlds['Lnx']
krom_js_path = lnx.utils.get_fp_build() + '/debug/krom/krom.js'
if os.path.exists(krom_js_path) and not wrd.lnx_recompile:
run_viewport_runtime(viewport_id, width, height)
return
pending_entry = (viewport_id, width, height)
if pending_entry not in _viewport_pending_launches:
_viewport_pending_launches.append(pending_entry)
@ -851,13 +866,13 @@ def play_viewport(viewport_id, width=1920, height=1080):
return
else:
_viewport_build_in_progress = False # Reset stale state
_viewport_build_in_progress = True
sdk_path = lnx.utils.get_sdk_path()
fp = lnx.utils.get_fp()
os.chdir(fp)
sources_path = 'Sources/' + lnx.utils.safestr(wrd.lnx_project_package)
if not os.path.exists(sources_path):
os.makedirs(sources_path)
@ -867,7 +882,7 @@ def play_viewport(viewport_id, width=1920, height=1080):
state.is_publish = False
state.is_export = False
export_data(fp, sdk_path)
compile_viewport(assets_only=(not wrd.lnx_recompile))
@ -982,16 +997,8 @@ def build_success():
if wrd.lnx_runtime == 'Browser':
os.chdir(lnx.utils.get_fp())
prefs = lnx.utils.get_lnx_preferences()
host = 'localhost'
t = threading.Thread(name='localserver',
target=lnx.lib.server.run_tcp,
args=(prefs.html5_server_port,
prefs.html5_server_log),
daemon=True)
t.start()
build_dir = lnx.utils.build_dir()
path = '{}/debug/html5/'.format(build_dir)
url = 'http://{}:{}/{}'.format(host, prefs.html5_server_port, path)
browser = webbrowser.get()
browsername = None
if hasattr(browser, "name"):
@ -1004,6 +1011,14 @@ def build_success():
if len(envcmd) == 0:
log.warn(f"Your {envvar} environment variable is set to an empty string")
else:
host = 'localhost'
t = threading.Thread(name='localserver',
target=lnx.lib.server.run_tcp,
args=(prefs.html5_server_port,
prefs.html5_server_log),
daemon=True)
t.start()
url = 'http://{}:{}/{}'.format(host, prefs.html5_server_port, path)
tplstr = Template(envcmd).safe_substitute({
'host': host,
'port': prefs.html5_server_port,
@ -1016,10 +1031,27 @@ def build_success():
})
cmd = re.split(' +', tplstr)
if len(cmd) == 0:
# try file:// protocol with a Chromium-based browser
fast = browsername if browsername and any(b in browsername.lower() for b in ('chrome', 'chromium', 'edge', 'msedge')) else lnx.utils.find_browser()
if fast is not None:
file_url = 'file:///' + os.path.abspath(path + 'index.html').replace('\\', '/')
subprocess.Popen([fast, '--allow-file-access-from-files', '--no-first-run',
'--user-data-dir=' + os.path.join(tempfile.gettempdir(), 'leenkx_browser'),
file_url])
return
host = 'localhost'
t = threading.Thread(name='localserver',
target=lnx.lib.server.run_tcp,
args=(prefs.html5_server_port,
prefs.html5_server_log),
daemon=True)
t.start()
url = 'http://{}:{}/{}'.format(host, prefs.html5_server_port, path)
if browsername in (None, '', 'default'):
webbrowser.open(url)
return
cmd = [browsername, url]
else:
cmd = [browsername, url]
elif wrd.lnx_runtime == 'Krom':
if wrd.lnx_live_patch:
live_patch.start()