diff --git a/Kha/Backends/Kinc-HL/kfile.js b/Kha/Backends/Kinc-HL/kfile.js index a92b2a0..12c23a8 100644 --- a/Kha/Backends/Kinc-HL/kfile.js +++ b/Kha/Backends/Kinc-HL/kfile.js @@ -9,6 +9,7 @@ if (platform == Platform.OSX) project.addDefine('KORE_DEBUGDIR="osx-hl"'); if (platform == Platform.iOS) project.addDefine('KORE_DEBUGDIR="ios-hl"'); if (platform !== Platform.Windows || audio !== AudioApi.DirectSound) { project.addDefine('KORE_MULTITHREADED_AUDIO'); + project.addDefine('KINC_MULTITHREADED_AUDIO'); } project.addDefine('KORE'); diff --git a/Kha/Backends/Kinc-HL/kha/graphics4/FragmentShader.hx b/Kha/Backends/Kinc-HL/kha/graphics4/FragmentShader.hx index c68c702..8cffb85 100644 --- a/Kha/Backends/Kinc-HL/kha/graphics4/FragmentShader.hx +++ b/Kha/Backends/Kinc-HL/kha/graphics4/FragmentShader.hx @@ -1,31 +1,70 @@ -package kha.graphics4; - -import kha.Blob; - -class FragmentShader { - public var _shader: Pointer; - - public function new(sources: Array, files: Array) { - initShader(sources[0]); - } - - function initShader(source: Blob): Void { - _shader = kinc_create_fragmentshader(source.bytes.getData(), source.bytes.getData().length); - } - - public static function fromSource(source: String): FragmentShader { - var sh = new FragmentShader(null, null); - sh._shader = kinc_fragmentshader_from_source(StringHelper.convert(source)); - return sh; - } - - public function delete(): Void {} - - @:hlNative("std", "kinc_create_fragmentshader") static function kinc_create_fragmentshader(data: hl.Bytes, length: Int): Pointer { - return null; - } - - @:hlNative("std", "kinc_fragmentshader_from_source") static function kinc_fragmentshader_from_source(source: hl.Bytes): Pointer { - return null; - } -} +package kha.graphics4; +using StringTools; +import kha.Blob; + +class FragmentShader { + public var _shader: Pointer; + + public function new(sources: Array, files: Array) { + //initShader(sources[0]); + var shaderBlob: Blob = null; + var shaderFile: String = null; + + #if kha_opengl + final expectedExtension = ".glsl"; + #elseif kha_direct3d11 + final expectedExtension = ".d3d11"; + #elseif kha_direct3d12 + final expectedExtension = ".d3d12"; + #elseif kha_metal + final expectedExtension = ".metal"; + #elseif kha_vulkan + final expectedExtension = ".spirv"; + #else + final expectedExtension = ".glsl"; + #end + + if (sources != null && files != null) { + for (i in 0...files.length) { + if (files[i].endsWith(expectedExtension)) { + shaderBlob = sources[i]; + shaderFile = files[i]; + break; + } + } + } + + if (shaderBlob == null && sources != null && sources.length > 0) { + trace('Warning: Could not find shader with extension ${expectedExtension}. Falling back to sources[0]: ${files != null && files.length > 0 ? files[0] : "Unknown"}'); + shaderBlob = sources[0]; + } + + if (shaderBlob != null) { + initShader(shaderBlob); + } else { + trace('Error: No suitable fragment shader source found!'); + } + + } + + function initShader(source: Blob): Void { + //_shader = kinc_create_fragmentshader(source.bytes.getData(), source.bytes.getData().length); + _shader = kinc_create_fragmentshader(source.bytes.getData(), source.length); // Use source.length here + } + + public static function fromSource(source: String): FragmentShader { + var sh = new FragmentShader(null, null); + sh._shader = kinc_fragmentshader_from_source(StringHelper.convert(source)); + return sh; + } + + public function delete(): Void {} + + @:hlNative("std", "kinc_create_fragmentshader") static function kinc_create_fragmentshader(data: hl.Bytes, length: Int): Pointer { + return null; + } + + @:hlNative("std", "kinc_fragmentshader_from_source") static function kinc_fragmentshader_from_source(source: hl.Bytes): Pointer { + return null; + } +} diff --git a/Kha/Backends/Kinc-HL/kinc-bridge/compute.c.h b/Kha/Backends/Kinc-HL/kinc-bridge/compute.c.h index fd14120..1d2e3ba 100644 --- a/Kha/Backends/Kinc-HL/kinc-bridge/compute.c.h +++ b/Kha/Backends/Kinc-HL/kinc-bridge/compute.c.h @@ -3,36 +3,36 @@ #include -vbyte *hl_kinc_g4_compute_create_shader(vbyte *data, int length) { +vbyte *hl_kinc_compute_create_shader(vbyte *data, int length) { kinc_g4_compute_shader *shader = (kinc_g4_compute_shader *)malloc(sizeof(kinc_g4_compute_shader)); kinc_g4_compute_shader_init(shader, data, length); return (vbyte *)shader; } -void hl_kinc_g4_compute_delete_shader(vbyte *shader) { +void hl_kinc_compute_delete_shader(vbyte *shader) { kinc_g4_compute_shader *sh = (kinc_g4_compute_shader *)shader; kinc_g4_compute_shader_destroy(sh); free(sh); } -vbyte *hl_kinc_g4_compute_get_constantlocation(vbyte *shader, vbyte *name) { +vbyte *hl_kinc_compute_get_constantlocation(vbyte *shader, vbyte *name) { kinc_g4_compute_shader *sh = (kinc_g4_compute_shader *)shader; kinc_g4_constant_location_t *location = (kinc_g4_constant_location_t *)malloc(sizeof(kinc_g4_constant_location_t)); - *location = kinc_g4_compute_shader_get_constant_location(sh, (char *)name), sizeof(kinc_g4_constant_location_t); + *location = kinc_g4_compute_shader_get_constant_location(sh, (char *)name); return (vbyte *)location; } -vbyte *hl_kinc_g4_compute_get_textureunit(vbyte *shader, vbyte *name) { +vbyte *hl_kinc_compute_get_textureunit(vbyte *shader, vbyte *name) { kinc_g4_compute_shader *sh = (kinc_g4_compute_shader *)shader; kinc_g4_texture_unit_t *unit = (kinc_g4_texture_unit_t *)malloc(sizeof(kinc_g4_texture_unit_t)); - *unit = kinc_g4_compute_shader_get_texture_unit(sh, (char *)name), sizeof(kinc_g4_texture_unit_t); + *unit = kinc_g4_compute_shader_get_texture_unit(sh, (char *)name); return (vbyte *)unit; } -void hl_kinc_g4_set_compute_shader(vbyte *shader) { +void hl_kinc_set_compute_shader(vbyte *shader) { kinc_g4_set_compute_shader((kinc_g4_compute_shader *)shader); } -void hl_kinc_g4_compute(int x, int y, int z) { +void hl_kinc_compute(int x, int y, int z) { kinc_g4_compute(x, y, z); } diff --git a/Kha/Backends/Kinc-HL/kinc-bridge/kinc.c.h b/Kha/Backends/Kinc-HL/kinc-bridge/kinc.c.h index d7d5dd2..14686f8 100644 --- a/Kha/Backends/Kinc-HL/kinc-bridge/kinc.c.h +++ b/Kha/Backends/Kinc-HL/kinc-bridge/kinc.c.h @@ -94,8 +94,8 @@ void hl_init_kore(vbyte *title, int width, int height, int samplesPerPixel, bool void hl_kinc_init_audio(vclosure *callCallback, vclosure *readSample, int *outSamplesPerSecond) { audioCallCallback = *((FN_AUDIO_CALL_CALLBACK *)(&callCallback->fun)); audioReadSample = *((FN_AUDIO_READ_SAMPLE *)(&readSample->fun)); - kinc_a2_set_callback(mix, NULL); kinc_a2_init(); + kinc_a2_set_callback(mix, NULL); *outSamplesPerSecond = kinc_a2_samples_per_second(); } diff --git a/Kha/Kinc/Tools/freebsd_x64/icon.png b/Kha/Kinc/Tools/freebsd_x64/icon.png index 33cc846..5dd0e8d 100644 Binary files a/Kha/Kinc/Tools/freebsd_x64/icon.png and b/Kha/Kinc/Tools/freebsd_x64/icon.png differ diff --git a/Kha/Kinc/Tools/linux_arm/icon.png b/Kha/Kinc/Tools/linux_arm/icon.png index 33cc846..5dd0e8d 100644 Binary files a/Kha/Kinc/Tools/linux_arm/icon.png and b/Kha/Kinc/Tools/linux_arm/icon.png differ diff --git a/Kha/Kinc/Tools/linux_arm64/icon.png b/Kha/Kinc/Tools/linux_arm64/icon.png index 33cc846..5dd0e8d 100644 Binary files a/Kha/Kinc/Tools/linux_arm64/icon.png and b/Kha/Kinc/Tools/linux_arm64/icon.png differ diff --git a/Kha/Kinc/Tools/linux_x64/icon.png b/Kha/Kinc/Tools/linux_x64/icon.png index 33cc846..5dd0e8d 100644 Binary files a/Kha/Kinc/Tools/linux_x64/icon.png and b/Kha/Kinc/Tools/linux_x64/icon.png differ diff --git a/Kha/Kinc/Tools/macos/icon.png b/Kha/Kinc/Tools/macos/icon.png index 33cc846..5dd0e8d 100644 Binary files a/Kha/Kinc/Tools/macos/icon.png and b/Kha/Kinc/Tools/macos/icon.png differ diff --git a/Kha/Kinc/Tools/windows_x64/icon.png b/Kha/Kinc/Tools/windows_x64/icon.png index 33cc846..5dd0e8d 100644 Binary files a/Kha/Kinc/Tools/windows_x64/icon.png and b/Kha/Kinc/Tools/windows_x64/icon.png differ diff --git a/leenkx/Sources/iron/system/LnxPack.hx b/leenkx/Sources/iron/system/LnxPack.hx index cb2dd39..32d4668 100644 --- a/leenkx/Sources/iron/system/LnxPack.hx +++ b/leenkx/Sources/iron/system/LnxPack.hx @@ -160,6 +160,7 @@ class LnxPack { case "anim": TAnimation; case "tracks": TTrack; case "morph_target": TMorphTarget; + case "vertex_groups": TVertex_groups; case _: TSceneFormat; } } diff --git a/leenkx/blender/lnx/make.py b/leenkx/blender/lnx/make.py index dfdae56..fdcb0e9 100644 --- a/leenkx/blender/lnx/make.py +++ b/leenkx/blender/lnx/make.py @@ -427,6 +427,19 @@ def build(target, is_play=False, is_publish=False, is_export=False): global profile_time profile_time = time.time() + wrd = bpy.data.worlds['Lnx'] + + if is_play and wrd.lnx_runtime == 'Hashlink': + current_os = lnx.utils.get_os() + if current_os == 'win': + target = 'windows-hl' + elif current_os == 'linux': + target = 'linux-hl' + elif current_os == 'macos': + target = 'macos-hl' + else: + log.error(f"Unsupported OS '{current_os}' for Hashlink runtime.") + state.target = target state.is_play = is_play state.is_publish = is_publish @@ -680,6 +693,148 @@ def build_success(): cmd.append(str(pid)) if wrd.lnx_audio == 'Disabled': cmd.append('--nosound') + + elif state.target.startswith(('windows-hl', 'linux-hl', 'macos-hl')): + log.info(f"Runtime Hashlink/C target: {state.target}") + + hl_build_dir, _, _ = lnx.utils.hashlink_paths(state.target) + + if not hl_build_dir: + log.error(f"Could not find build directory for target {state.target}. Playback aborted.") + return + + if state.target == 'windows-hl': + vs_version_major = wrd.lnx_project_win_list_vs + build_mode = wrd.lnx_project_win_build_mode # Debug or Release + build_arch = wrd.lnx_project_win_build_arch # x64 or x86 (maps to Win32 for MSBuild) + platform = 'x64' if build_arch == 'x64' else 'Win32' # MSBuild uses Win32 for x86 + + installation = lnx.utils_vs.get_installed_version(vs_version_major, re_fetch=True) + if installation is None: + vs_info = lnx.utils_vs.get_supported_version(vs_version_major) + log.error(f'Visual Studio {vs_info["name"]} not found. Cannot compile {state.target}.') + return + msbuild_path = os.path.join(installation['path'], 'MSBuild', 'Current', 'Bin', 'MSBuild.exe') + if not os.path.isfile(msbuild_path): + msbuild_path = os.path.join(installation['path'], 'MSBuild', '15.0', 'Bin', 'MSBuild.exe') # VS 2017 fallback + if not os.path.isfile(msbuild_path): + log.error(f'MSBuild.exe not found for {installation["name"]}. Cannot compile {state.target}.') + return + + proj_name = lnx.utils.blend_name() + vcxproj_path = os.path.join(hl_build_dir, proj_name + '.vcxproj') + if not os.path.isfile(vcxproj_path): + found_vcxproj = None + for file in os.listdir(hl_build_dir): + if file.endswith(".vcxproj"): + found_vcxproj = os.path.join(hl_build_dir, file) + log.warn(f"Could not find '{proj_name}.vcxproj', using found '{file}' instead.") + break + if not found_vcxproj: + 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] + + msbuild_cmd = [ + msbuild_path, + vcxproj_path, + f'/p:Configuration={build_mode}', + f'/p:Platform={platform}', + '/m' + ] + + log.info(f"Compiling {state.target} project with MSBuild...") + log.info(f"Command: {' '.join(msbuild_cmd)}") + compile_success = False + try: + compile_result = subprocess.run(msbuild_cmd, cwd=hl_build_dir, check=False, capture_output=True, text=True) + if compile_result.returncode == 0: + log.info(f"MSBuild compilation successful.") + compile_success = True + else: + log.error(f"MSBuild compilation failed (Exit Code: {compile_result.returncode}).") + log.error(f"MSBuild Output:\n{compile_result.stdout}") + log.error(f"MSBuild Errors:\n{compile_result.stderr}") + except Exception as e: + log.error(f"Error running MSBuild: {e}") + traceback.print_exc() + + if not compile_success: + return + + exe_path = os.path.join(hl_build_dir, platform, build_mode, proj_name + '.exe') + if not os.path.isfile(exe_path): + exe_path = os.path.join(hl_build_dir, build_mode, proj_name + '.exe') + if not os.path.isfile(exe_path): + log.error(f'Compiled executable not found at expected location: {exe_path} (or variants). Cannot run.') + 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) + dest_path = os.path.join(dest_dir, dest_exe_name) + try: + shutil.move(exe_path, dest_path) + cmd = [dest_path] + except Exception as e: + cmd = [exe_path] + os.chdir(dest_dir) + + elif state.target in ('linux-hl', 'macos-hl'): + wrd = bpy.data.worlds['Lnx'] + paths = lnx.utils.hashlink_paths(state.target) + hl_build_dir = paths[0] + # TO DO switch from default Release + build_mode = 'Release' + proj_name = lnx.utils.blend_name() + 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 + makefile_path = os.path.join(exe_path, 'makefile') + if not os.path.isfile(makefile_path): + log.error(f"Makefile not found at '{makefile_path}'. Cannot compile C code.") + return + + make_cmd = ['make'] + + log.info(f"Compiling C code using 'make' in directory '{exe_path}'...") + log.info(f"Make command: {' '.join(make_cmd)}") + try: + result = subprocess.run(make_cmd, cwd=exe_path, check=True, capture_output=True, text=True, encoding='utf-8') + log.info("'make' compilation successful.") + 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 + except FileNotFoundError: + log.error("'make' command not found. Ensure 'make' is installed and in your system's PATH.") + return + except Exception as e: + log.error(f"An unexpected error occurred running make: {e}") + return + + log.info(f"Found compiled executable: {exe_path}") + + 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) + 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] + except Exception as e: + log.error(f"Failed to move executable: {e}. Attempting to run from original location.") + cmd = [exe_path] + os.chdir(dest_dir) + log.info(f"Hashlink final command: {' '.join(cmd)}") try: state.proc_play = run_proc(cmd, play_done) except Exception: diff --git a/leenkx/blender/lnx/props.py b/leenkx/blender/lnx/props.py index 429aa48..14104c4 100644 --- a/leenkx/blender/lnx/props.py +++ b/leenkx/blender/lnx/props.py @@ -308,7 +308,8 @@ def init_properties(): bpy.types.World.lnx_verbose_output = BoolProperty(name="Verbose Output", description="Print additional information to the console during compilation", default=False) bpy.types.World.lnx_runtime = EnumProperty( items=[('Krom', 'Krom', 'Krom'), - ('Browser', 'Browser', 'Browser')], + ('Browser', 'Browser', 'Browser'), + ('Hashlink', 'Hashlink', 'Hashlink')], name="Runtime", description="Runtime to use when launching the game", default='Krom', update=assets.invalidate_shader_cache) bpy.types.World.lnx_loadscreen = BoolProperty(name="Loading Screen", description="Show asset loading progress on published builds", default=True) bpy.types.World.lnx_vsync = BoolProperty(name="VSync", description="Vertical Synchronization", default=True, update=assets.invalidate_compiler_cache) diff --git a/leenkx/blender/lnx/utils.py b/leenkx/blender/lnx/utils.py index 0dc4b97..4730cfd 100644 --- a/leenkx/blender/lnx/utils.py +++ b/leenkx/blender/lnx/utils.py @@ -363,6 +363,25 @@ def krom_paths(): krom_path = krom_location + '/Krom' return krom_location, krom_path +def hashlink_paths(target): + """Returns path for Hashlink runtime target.""" + sdk_path = get_sdk_path() + proj_name = blend_name() + build_base_dir = get_fp_build() + + + if target in ('windows-hl', 'linux-hl', 'macos-hl'): + hl_build_dir = os.path.join(build_base_dir, target + '-build') + log.info(f"Identified Hashlink/C build directory: {hl_build_dir}") + + return hl_build_dir, None, None # Return build_dir, no file, no interpreter + + else: + return '', None, None + + + + def fetch_bundled_script_names(): wrd = bpy.data.worlds['Lnx'] wrd.lnx_bundled_scripts_list.clear() diff --git a/lib/aura/Sources/aura/Aura.hx b/lib/aura/Sources/aura/Aura.hx index db587a7..2ebc1bc 100644 --- a/lib/aura/Sources/aura/Aura.hx +++ b/lib/aura/Sources/aura/Aura.hx @@ -63,7 +63,12 @@ class Aura { listener = new Listener(); BufferCache.init(); - + + // Sample buffer to prevent allocation + final initialBufferSize = 4096; + if (!BufferCache.getBuffer(TFloat32Array, p_samplesBuffer, 1, initialBufferSize)) { + trace('CRITICAL: Failed to allocate initial sample buffer during Aura.init()!'); + } // Create a few preconfigured mix channels masterChannel = createMixChannel("master"); createMixChannel("music").setMixChannel(masterChannel);