diff --git a/leenkx/Sources/iron/RenderPath.hx b/leenkx/Sources/iron/RenderPath.hx index 704826d8..28b148b2 100644 --- a/leenkx/Sources/iron/RenderPath.hx +++ b/leenkx/Sources/iron/RenderPath.hx @@ -34,9 +34,9 @@ class RenderPath { public static var vrCalibrationPosition:Vec4 = null; public static var vrCalibrationRotation:iron.math.Quat = null; public static var vrCalibrationSaved:Bool = false; - + public static var vrCenterCameraWorld:Mat4 = null; - + static var vrOriginalSuperSample:Float = -1.0; public static inline function isVRPresenting(): Bool { #if (kha_webgl && lnx_vr) @@ -45,7 +45,7 @@ class RenderPath { return false; #end } - + public static inline function isVRSimulateMode(): Bool { return vrSimulateMode; } @@ -162,24 +162,24 @@ class RenderPath { var appW = iron.App.w(); var appH = iron.App.h(); - + // use native XR framebuffer dimensions #if (kha_webgl && lnx_vr) if (kha.vr.VrInterface.instance != null) { var vr = kha.vr.VrInterface.instance; var isPresenting = vr != null && vr.IsPresenting(); - + // save/restore camera position between modes if (!wasVRPresenting && isPresenting) { if (Scene.active != null && Scene.active.camera != null) { if (vrCalibrationPosition == null) vrCalibrationPosition = new Vec4(); if (vrCalibrationRotation == null) vrCalibrationRotation = new Quat(); - + vrCalibrationPosition.setFrom(Scene.active.camera.transform.loc); vrCalibrationRotation.setFrom(Scene.active.camera.transform.rot); vrCalibrationSaved = true; } - + // save original super sampling for later vrOriginalSuperSample = leenkx.renderpath.Inc.superSample; @@ -196,18 +196,18 @@ class RenderPath { #if (kha_webgl && lnx_vr) iron.system.Time.vrFrameTime = -1.0; #end - + if (vrCalibrationSaved && Scene.active != null && Scene.active.camera != null) { Scene.active.camera.transform.loc.setFrom(vrCalibrationPosition); Scene.active.camera.transform.rot.setFrom(vrCalibrationRotation); Scene.active.camera.buildMatrix(); Scene.active.camera.buildProjection(); } - + // restore original super sampling from simulate mode if (vrOriginalSuperSample >= 0.0) { leenkx.renderpath.Inc.superSample = vrOriginalSuperSample; - + for (rt in renderTargets) { if (rt.raw.width == 0 && rt.raw.scale != null) { rt.raw.scale = vrOriginalSuperSample; @@ -216,13 +216,13 @@ class RenderPath { resize(); vrOriginalSuperSample = -1.0; } - + // reset offset for next session vrCameraOffsetSet = false; vrCameraOffset = null; } wasVRPresenting = isPresenting; - + if (isPresenting) { // TODO: re-investigate using super sampling to avoid pixelation in simulate mode while giving max quality in headset if (vrOriginalSuperSample >= 0.0 && leenkx.renderpath.Inc.superSample != 4.0) { @@ -234,7 +234,7 @@ class RenderPath { } resize(); } - + var xrVr: kha.js.vr.VrInterface = cast vr; if (xrVr.xrGLLayer != null) { appW = xrVr.xrGLLayer.framebufferWidth; @@ -384,7 +384,7 @@ class RenderPath { if (currentG != null) end(); currentG = g; additionalTargets = additionalRenderTargets; - + // we still bind but skip begin() when explicitly rendering to XR framebuffer (renderToXRFramebuffer flag) #if lnx_vr if (!renderToXRFramebuffer) { @@ -405,7 +405,7 @@ class RenderPath { currentG.disableScissor(); scissorSet = false; } - + #if lnx_vr if (beginCalled) { currentG.end(); @@ -692,7 +692,7 @@ class RenderPath { if (vr == null || vr._glContext == null || vr.xrGLLayer == null) { return; } - + var gl: js.html.webgl.WebGL2RenderingContext = cast vr._glContext; var source = renderTargets.get(sourceTarget); if (source == null) { @@ -703,21 +703,21 @@ class RenderPath { return; } // trace('Framebuffer OK'); - + renderToXRFramebuffer = true; gl.bindFramebuffer(js.html.webgl.WebGL2RenderingContext.DRAW_FRAMEBUFFER, vr.xrGLLayer.framebuffer); gl.bindFramebuffer(js.html.webgl.WebGL2RenderingContext.READ_FRAMEBUFFER, sourceFB); - + var readStatus = gl.checkFramebufferStatus(js.html.webgl.WebGL2RenderingContext.READ_FRAMEBUFFER); var drawStatus = gl.checkFramebufferStatus(js.html.webgl.WebGL2RenderingContext.DRAW_FRAMEBUFFER); - if (readStatus != js.html.webgl.WebGL2RenderingContext.FRAMEBUFFER_COMPLETE || + if (readStatus != js.html.webgl.WebGL2RenderingContext.FRAMEBUFFER_COMPLETE || drawStatus != js.html.webgl.WebGL2RenderingContext.FRAMEBUFFER_COMPLETE) { return; } - + var halfWidth = Std.int(source.image.width / 2); var fullHeight = source.image.height; - + if (vr._leftViewport != null) { var vp = vr._leftViewport; gl.blitFramebuffer( @@ -736,7 +736,7 @@ class RenderPath { js.html.webgl.WebGL2RenderingContext.LINEAR ); } - + gl.bindFramebuffer(js.html.webgl.WebGL2RenderingContext.FRAMEBUFFER, null); renderToXRFramebuffer = false; #end @@ -749,24 +749,24 @@ class RenderPath { if (currentG == null && frameG != null) { currentG = frameG; } - + var appw = iron.App.w(); var apph = iron.App.h(); var g = currentG; - + // get render target dimensions not App.w/h gbuffer is scaled in simulate mode with supersampling var gbuffer0 = renderTargets.get("gbuffer0"); var actualWidth = (gbuffer0 != null && gbuffer0.image != null) ? gbuffer0.image.width : appw; var actualHeight = (gbuffer0 != null && gbuffer0.image != null) ? gbuffer0.image.height : apph; var actualHalfWidth = Std.int(actualWidth / 2); - + var vrFBWidth = actualWidth; var vrFBHeight = actualHeight; var vrHalfWidth = actualHalfWidth; var isVRPresenting = false; vrSimulateMode = false; - + var vr:Dynamic = null; var vrExists = false; #if (kha_webgl && lnx_vr) @@ -779,7 +779,7 @@ class RenderPath { if (vrExists && vr != null && vr.IsPresenting()) { vrSimulateMode = false; isVRPresenting = true; - + // get framebuffer dimensions from XR layer #if (kha_webgl && lnx_vr) var xrVr: kha.js.vr.VrInterface = cast vr; @@ -789,16 +789,16 @@ class RenderPath { vrHalfWidth = Std.int(vrFBWidth / 2); } #end - + if (Scene.active == null || Scene.active.camera == null) { return; } - + #if (kha_webgl && lnx_vr) if (vrCenterCameraWorld == null) vrCenterCameraWorld = Mat4.identity(); vrCenterCameraWorld.setFrom(Scene.active.camera.transform.world); #end - + // LEFT EYE // HMD center for room scale position tracking #if (kha_webgl && lnx_vr) @@ -808,10 +808,10 @@ class RenderPath { if (viewerTransform != null && viewerTransform.position != null) { // VR present calibration is used to position objects in world space not the camera var pos = viewerTransform.position; - + // camera follows headset directly in local floor space Scene.active.camera.transform.loc.set(pos.x, pos.y, pos.z); - + if (viewerTransform.orientation != null) { Scene.active.camera.transform.rot.set( viewerTransform.orientation.x, @@ -825,7 +825,7 @@ class RenderPath { } iron.system.VRController.updatePoses(); #end - + Scene.active.camera.V.self = vr.GetViewMatrix(0); Scene.active.camera.P.self = vr.GetProjectionMatrix(0); Scene.active.camera.VP.setFrom(Scene.active.camera.P); @@ -835,7 +835,7 @@ class RenderPath { var renderWidth = actualWidth; var renderHeight = actualHeight; var renderHalfWidth = actualHalfWidth; - + // left half of render target g.viewport(0, 0, renderHalfWidth, renderHeight); g.scissor(0, 0, renderHalfWidth, renderHeight); @@ -847,12 +847,12 @@ class RenderPath { Scene.active.camera.VP.setFrom(Scene.active.camera.P); Scene.active.camera.VP.multmat(Scene.active.camera.V); Scene.active.camera.buildMatrix(); - + // right half of render target g.viewport(renderHalfWidth, 0, renderHalfWidth, renderHeight); g.scissor(renderHalfWidth, 0, renderHalfWidth, renderHeight); drawMeshes(); - + // restore for post-processing g.disableScissor(); g.viewport(0, 0, renderWidth, renderHeight); @@ -865,7 +865,7 @@ class RenderPath { if (vrCenterCameraWorld == null) vrCenterCameraWorld = Mat4.identity(); vrCenterCameraWorld.setFrom(Scene.active.camera.transform.world); #end - + Scene.active.camera.buildProjection(actualHalfWidth / actualHeight); Scene.active.camera.transform.move(Scene.active.camera.right(), -ipd_offset); diff --git a/leenkx/blender/lnx/make.py b/leenkx/blender/lnx/make.py index 9d8bd846..861df316 100644 --- a/leenkx/blender/lnx/make.py +++ b/leenkx/blender/lnx/make.py @@ -301,18 +301,18 @@ def export_data_impl(fp, sdk_path): for scene_name, asset_path in scene_targets: # Reset shader comparison arrays to prevent cross-scene shader merging assets.reset_shader_cons() - + scene = bpy.data.scenes[scene_name] - + try: if bpy.context.window.scene != scene: bpy.context.window.scene = scene bpy.context.view_layer.update() - + scene_depsgraph = bpy.context.evaluated_depsgraph_get() - + LeenkxExporter.export_scene(bpy.context, asset_path, scene=scene, depsgraph=scene_depsgraph, build_cache=build_cache) - + finally: if bpy.context.window.scene != initial_window_scene: bpy.context.window.scene = initial_window_scene @@ -684,7 +684,7 @@ def _get_viewport_shmem_name(viewport_id): def _kill_viewport_process(viewport_id): """Kill a specific viewport's Krom process.""" global _viewport_processes - + if viewport_id in _viewport_processes: proc = _viewport_processes[viewport_id] try: @@ -700,7 +700,7 @@ def _kill_viewport_process(viewport_id): def _kill_all_viewport_processes(): """Kill all viewport Krom processes.""" global _viewport_processes - + for viewport_id in list(_viewport_processes.keys()): _kill_viewport_process(viewport_id) @@ -711,7 +711,7 @@ def _kill_all_viewport_processes(): capture_output=True, text=True, timeout=5 ) if 'Krom.exe' in result.stdout: - subprocess.run(['taskkill', '/F', '/IM', 'Krom.exe'], + subprocess.run(['taskkill', '/F', '/IM', 'Krom.exe'], capture_output=True, timeout=5) import time time.sleep(0.3) @@ -729,7 +729,7 @@ def run_viewport_runtime(viewport_id, width=1920, height=1080): _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' @@ -744,7 +744,7 @@ def run_viewport_runtime(viewport_id, width=1920, height=1080): open(path + '/krom.patch', 'w', encoding='utf-8').close() os.chdir(krom_location) - + cmd = [krom_path, path, path_resources] if lnx.utils.get_os() == 'win': cmd.append('--consolepid') @@ -759,7 +759,7 @@ def run_viewport_runtime(viewport_id, width=1920, height=1080): cmd.append(str(width)) cmd.append('--viewport-height') cmd.append(str(height)) - + try: proc = subprocess.Popen(cmd) _viewport_processes[viewport_id] = proc @@ -780,7 +780,7 @@ _viewport_proc_build = None # Separate process tracker for viewport builds def compile_viewport(assets_only=False): """Compile for viewport mode using separate process tracking.""" global _viewport_proc_build - + wrd = bpy.data.worlds['Lnx'] fp = lnx.utils.get_fp() os.chdir(fp) @@ -788,50 +788,50 @@ def compile_viewport(assets_only=False): node_path = lnx.utils.get_node_path() khamake_path = lnx.utils.get_khamake_path() cmd = [node_path, khamake_path, 'krom'] - + ffmpeg_path = lnx.utils.get_ffmpeg_path() if ffmpeg_path not in (None, ''): cmd.append('--ffmpeg') cmd.append(ffmpeg_path) - + cmd.append('-g') cmd.append(lnx.utils.get_gapi()) cmd.append('--shaderversion') cmd.append('330') - + if lnx.utils.get_khamake_threads() != 1: cmd.append('--parallelAssetConversion') cmd.append(str(lnx.utils.get_khamake_threads())) - + cmd.append('--to') cmd.append(lnx.utils.build_dir() + '/debug') - + if not wrd.lnx_verbose_output: cmd.append("--quiet") - + krom_js_path = lnx.utils.build_dir() + '/debug/krom/krom.js' if assets_only and os.path.exists(krom_js_path): cmd.append('--nohaxe') cmd.append('--noproject') - + log.info(f'Running: {" ".join(cmd)}') _viewport_proc_build = run_proc(cmd, viewport_build_done) def viewport_build_done(): """Called when viewport build completes - launches all pending Krom processes.""" global _viewport_build_in_progress, _viewport_pending_launches, _viewport_proc_build - + log.info('Viewport compilation finished') - + if _viewport_proc_build is None: _viewport_build_in_progress = False return - + result = _viewport_proc_build.poll() _viewport_proc_build = None _viewport_build_in_progress = False state.redraw_ui = True - + if result == 0: bpy.data.worlds['Lnx'].lnx_recompile = False @@ -1103,7 +1103,7 @@ def build_success(): cmd.append(str(state.viewport_width)) cmd.append('--viewport-height') cmd.append(str(state.viewport_height)) - + elif state.target.startswith(('windows-hl', 'linux-hl', 'macos-hl')): log.info(f"Runtime Hashlink/C target: {state.target}") @@ -1111,7 +1111,7 @@ def build_success(): if not hl_build_dir: log.error(f"Could not find build directory for target {state.target}. Playback aborted.") - return + return if state.target == 'windows-hl': vs_version_major = wrd.lnx_project_win_list_vs @@ -1146,14 +1146,14 @@ def build_success(): log.error(f'.vcxproj file not found in {hl_build_dir}. Cannot compile.') return vcxproj_path = found_vcxproj - proj_name = os.path.splitext(os.path.basename(vcxproj_path))[0] + proj_name = os.path.splitext(os.path.basename(vcxproj_path))[0] msbuild_cmd = [ msbuild_path, vcxproj_path, f'/p:Configuration={build_mode}', f'/p:Platform={platform}', - '/m' + '/m' ] log.info(f"Compiling {state.target} project with MSBuild...") @@ -1183,7 +1183,7 @@ def build_success(): return log.info(f"Found compiled executable: {exe_path}") - + dest_exe_name = proj_name + '.exe' base_build_dir = lnx.utils.get_fp_build() dest_dir = os.path.join(base_build_dir, state.target) @@ -1202,7 +1202,7 @@ def build_success(): # TO DO switch from default Release build_mode = 'Release' proj_name = lnx.utils.blend_name() - exe_path = str(hl_build_dir + "/" + build_mode) + exe_path = str(hl_build_dir + "/" + build_mode) if not exe_path: log.error(f"Build finished, but could not find the executable for {state.target}.") return @@ -1221,7 +1221,7 @@ def build_success(): except subprocess.CalledProcessError as e: log.error(f"'make' compilation failed with return code {e.returncode}.") log.error(f"Make Error Output:\n{e.stderr}") - return + return except FileNotFoundError: log.error("'make' command not found. Ensure 'make' is installed and in your system's PATH.") return @@ -1233,15 +1233,15 @@ def build_success(): dest_exe_name = lnx.utils.safesrc(wrd.lnx_project_name + '-' + wrd.lnx_project_version) base_build_dir = lnx.utils.get_fp_build() - dest_dir = os.path.join(base_build_dir, state.target) + dest_dir = os.path.join(base_build_dir, state.target) og_path = os.path.join(exe_path, dest_exe_name) dest_path = os.path.join(dest_dir, dest_exe_name) os.makedirs(dest_dir, exist_ok=True) try: log.info(f"Moving '{og_path}' to '{dest_dir}'...") - shutil.move(og_path, dest_dir) - cmd = [dest_path] + shutil.move(og_path, dest_dir) + cmd = [dest_path] except Exception as e: log.error(f"Failed to move executable: {e}. Attempting to run from original location.") cmd = [exe_path] diff --git a/leenkx/blender/lnx/make_renderpath.py b/leenkx/blender/lnx/make_renderpath.py index 74d6b4ff..0b085158 100644 --- a/leenkx/blender/lnx/make_renderpath.py +++ b/leenkx/blender/lnx/make_renderpath.py @@ -40,7 +40,7 @@ def add_world_defs(): # Store contexts if rpdat.rp_hdr == False: wrd.world_defs += '_LDR' - + if lnx.utils.get_active_scene().world is not None: if lnx.utils.get_active_scene().world.lnx_light_ies_texture: wrd.world_defs += '_LightIES'