diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 00d5fcb2a..a29e6821d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -116,6 +116,13 @@ jobs: include: ${{ fromJSON(github.event_name == 'pull_request' && '[{"os":"ubuntu-latest","rust":"stable","lane":"full","run_windows_native_clippy":false},{"os":"ubuntu-latest","rust":"nightly","lane":"smoke","run_windows_native_clippy":false},{"os":"macos-latest","rust":"stable","lane":"smoke","run_windows_native_clippy":false},{"os":"windows-latest","rust":"stable","lane":"clippy-only","run_windows_native_clippy":true}]' || '[{"os":"ubuntu-latest","rust":"stable","lane":"full","run_windows_native_clippy":false},{"os":"macos-latest","rust":"stable","lane":"full","run_windows_native_clippy":false},{"os":"windows-latest","rust":"stable","lane":"full","run_windows_native_clippy":true},{"os":"ubuntu-latest","rust":"nightly","lane":"smoke","run_windows_native_clippy":false},{"os":"macos-latest","rust":"nightly","lane":"smoke","run_windows_native_clippy":false}]') }} steps: + - name: Free disk space (Ubuntu) + if: matrix.os == 'ubuntu-latest' && matrix.lane == 'full' + run: | + sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache/CodeQL + sudo docker image prune --all --force || true + df -h / + - uses: actions/checkout@v6 - name: Download shared codegen artifact diff --git a/.gitignore b/.gitignore index 05cb2915e..fee4b0b68 100644 --- a/.gitignore +++ b/.gitignore @@ -111,6 +111,7 @@ sdks/c/tests/build/ # Claude worktrees (ephemeral, per-session) .claude/worktrees/ +.worktrees/ .mcp.json # Gradle build artifacts diff --git a/codegen/ffi_manifest.json b/codegen/ffi_manifest.json index b02f79f76..9b3db4ee8 100644 --- a/codegen/ffi_manifest.json +++ b/codegen/ffi_manifest.json @@ -1779,6 +1779,113 @@ "return_type": "bool", "is_unsafe": true }, + "goud_input_gamepad_axis": { + "source_file": "ffi/input/gamepad.rs", + "params": [ + "context_id: GoudContextId", + "gamepad_id: u32", + "axis: u32" + ], + "return_type": "f32", + "is_unsafe": false + }, + "goud_input_gamepad_button_just_pressed": { + "source_file": "ffi/input/gamepad.rs", + "params": [ + "context_id: GoudContextId", + "gamepad_id: u32", + "button: u32" + ], + "return_type": "bool", + "is_unsafe": false + }, + "goud_input_gamepad_button_just_released": { + "source_file": "ffi/input/gamepad.rs", + "params": [ + "context_id: GoudContextId", + "gamepad_id: u32", + "button: u32" + ], + "return_type": "bool", + "is_unsafe": false + }, + "goud_input_gamepad_button_pressed": { + "source_file": "ffi/input/gamepad.rs", + "params": [ + "context_id: GoudContextId", + "gamepad_id: u32", + "button: u32" + ], + "return_type": "bool", + "is_unsafe": false + }, + "goud_input_gamepad_connected": { + "source_file": "ffi/input/gamepad.rs", + "params": [ + "context_id: GoudContextId", + "gamepad_id: u32" + ], + "return_type": "bool", + "is_unsafe": false + }, + "goud_input_gamepad_connected_count": { + "source_file": "ffi/input/gamepad.rs", + "params": [ + "context_id: GoudContextId" + ], + "return_type": "u32", + "is_unsafe": false + }, + "goud_input_gamepad_left_stick": { + "source_file": "ffi/input/gamepad.rs", + "params": [ + "context_id: GoudContextId", + "gamepad_id: u32", + "out_x: *mut f32", + "out_y: *mut f32" + ], + "return_type": "bool", + "is_unsafe": true + }, + "goud_input_gamepad_left_trigger": { + "source_file": "ffi/input/gamepad.rs", + "params": [ + "context_id: GoudContextId", + "gamepad_id: u32" + ], + "return_type": "f32", + "is_unsafe": false + }, + "goud_input_gamepad_right_stick": { + "source_file": "ffi/input/gamepad.rs", + "params": [ + "context_id: GoudContextId", + "gamepad_id: u32", + "out_x: *mut f32", + "out_y: *mut f32" + ], + "return_type": "bool", + "is_unsafe": true + }, + "goud_input_gamepad_right_trigger": { + "source_file": "ffi/input/gamepad.rs", + "params": [ + "context_id: GoudContextId", + "gamepad_id: u32" + ], + "return_type": "f32", + "is_unsafe": false + }, + "goud_input_gamepad_set_vibration": { + "source_file": "ffi/input/gamepad.rs", + "params": [ + "context_id: GoudContextId", + "gamepad_id: u32", + "intensity: f32" + ], + "return_type": "bool", + "is_unsafe": false + }, "goud_input_get_mouse_delta": { "source_file": "ffi/input/mouse.rs", "params": [ @@ -3098,6 +3205,20 @@ "return_type": "bool", "is_unsafe": false }, + "goud_renderer3d_configure_fog_linear": { + "source_file": "ffi/renderer3d/environment/mod.rs", + "params": [ + "context_id: GoudContextId", + "enabled: bool", + "start_distance: f32", + "end_distance: f32", + "r: f32", + "g: f32", + "b: f32" + ], + "return_type": "bool", + "is_unsafe": false + }, "goud_renderer3d_configure_grid": { "source_file": "ffi/renderer3d/environment/mod.rs", "params": [ @@ -6404,5 +6525,5 @@ "is_unsafe": false } }, - "total_count": 649 + "total_count": 661 } diff --git a/codegen/ffi_mapping.json b/codegen/ffi_mapping.json index 4f347b3c8..32e5df592 100644 --- a/codegen/ffi_mapping.json +++ b/codegen/ffi_mapping.json @@ -650,6 +650,7 @@ "goud_renderer3d_set_grid_enabled": {}, "goud_renderer3d_configure_skybox": {}, "goud_renderer3d_configure_fog": {}, + "goud_renderer3d_configure_fog_linear": {}, "goud_renderer3d_set_fog_enabled": {}, "goud_renderer3d_set_frustum_culling_enabled": {}, "goud_renderer3d_set_skinning_mode": {}, @@ -763,7 +764,20 @@ "goud_input_mouse_button_just_released": {}, "goud_input_get_mouse_position": {}, "goud_input_get_mouse_delta": {}, - "goud_input_get_scroll_delta": {} + "goud_input_get_scroll_delta": {}, + "goud_input_gamepad_button_pressed": {}, + "goud_input_gamepad_button_just_pressed": {}, + "goud_input_gamepad_button_just_released": {}, + "goud_input_gamepad_axis": {} + }, + "input_gamepad": { + "goud_input_gamepad_connected": {}, + "goud_input_gamepad_connected_count": {}, + "goud_input_gamepad_left_stick": {}, + "goud_input_gamepad_right_stick": {}, + "goud_input_gamepad_left_trigger": {}, + "goud_input_gamepad_right_trigger": {}, + "goud_input_gamepad_set_vibration": {} }, "input_touch": { "goud_input_touch_count": {}, diff --git a/codegen/gen_ts_web.py b/codegen/gen_ts_web.py index 698f19bbc..8c11b13bb 100644 --- a/codegen/gen_ts_web.py +++ b/codegen/gen_ts_web.py @@ -842,6 +842,13 @@ def gen_web_wrapper(): lines.append(" getTouchDelta(_touchId: number): IVec2 { return { x: 0, y: 0 }; }") lines.append("") + # Gamepad input (stubs — WASM gamepad bindings not yet implemented) + lines.append(" isGamepadButtonPressed(_gamepadId: number, _button: number): boolean { return false; }") + lines.append(" isGamepadButtonJustPressed(_gamepadId: number, _button: number): boolean { return false; }") + lines.append(" isGamepadButtonJustReleased(_gamepadId: number, _button: number): boolean { return false; }") + lines.append(" getGamepadAxis(_gamepadId: number, _axis: number): number { return 0; }") + lines.append("") + emit_jsdoc(lines, _method_docs.get("map_action_key")) lines.append(" mapActionKey(action: string, key: number): boolean { return this.handle.map_action_key(action, key); }") emit_jsdoc(lines, _method_docs.get("is_action_pressed")) @@ -1124,6 +1131,7 @@ def gen_web_wrapper(): lines.append(" setGridEnabled(_enabled: boolean): boolean { return false; }") lines.append(" configureSkybox(_enabled: boolean, _r: number, _g: number, _b: number, _a: number): boolean { return false; }") lines.append(" configureFog(_enabled: boolean, _r: number, _g: number, _b: number, _density: number): boolean { return false; }") + lines.append(" configureFogLinear(_enabled: boolean, _startDistance: number, _endDistance: number, _r: number, _g: number, _b: number): boolean { return false; }") lines.append(" setFogEnabled(_enabled: boolean): boolean { return false; }") lines.append(" render3D(): boolean { return false; }") lines.append(" setFrustumCullingEnabled(_enabled: boolean): number { return 0; }") diff --git a/codegen/generated/goud_engine.h b/codegen/generated/goud_engine.h index 5a9c15936..347f45ac7 100644 --- a/codegen/generated/goud_engine.h +++ b/codegen/generated/goud_engine.h @@ -2278,6 +2278,11 @@ bool goud_renderer3d_configure_skybox(struct GoudContextId context_id, bool enab */ bool goud_renderer3d_configure_fog(struct GoudContextId context_id, bool enabled, float r, float g, float b, float density); +/** + * Configures linear fog with explicit start/end distances. + */ +bool goud_renderer3d_configure_fog_linear(struct GoudContextId context_id, bool enabled, float start_distance, float end_distance, float r, float g, float b); + /** * Sets fog enabled state. */ diff --git a/codegen/goud_sdk.schema.json b/codegen/goud_sdk.schema.json index b3b463623..aa48d4c92 100644 --- a/codegen/goud_sdk.schema.json +++ b/codegen/goud_sdk.schema.json @@ -4756,6 +4756,66 @@ ], "returns": "Vec2" }, + { + "name": "isGamepadButtonPressed", + "doc": "Returns true if the specified gamepad button is currently pressed", + "params": [ + { + "name": "gamepadId", + "type": "u32" + }, + { + "name": "button", + "type": "u32" + } + ], + "returns": "bool" + }, + { + "name": "isGamepadButtonJustPressed", + "doc": "Returns true if the specified gamepad button was just pressed this frame", + "params": [ + { + "name": "gamepadId", + "type": "u32" + }, + { + "name": "button", + "type": "u32" + } + ], + "returns": "bool" + }, + { + "name": "isGamepadButtonJustReleased", + "doc": "Returns true if the specified gamepad button was just released this frame", + "params": [ + { + "name": "gamepadId", + "type": "u32" + }, + { + "name": "button", + "type": "u32" + } + ], + "returns": "bool" + }, + { + "name": "getGamepadAxis", + "doc": "Returns the current value of a gamepad analog axis (-1.0 to 1.0)", + "params": [ + { + "name": "gamepadId", + "type": "u32" + }, + { + "name": "axis", + "type": "u32" + } + ], + "returns": "f32" + }, { "$ref": "EcsMethods", "name": "spawnEmpty" @@ -5359,6 +5419,37 @@ ], "returns": "bool" }, + { + "name": "configureFogLinear", + "doc": "Configures linear fog with explicit start and end distances", + "params": [ + { + "name": "enabled", + "type": "bool" + }, + { + "name": "startDistance", + "type": "f32" + }, + { + "name": "endDistance", + "type": "f32" + }, + { + "name": "r", + "type": "f32" + }, + { + "name": "g", + "type": "f32" + }, + { + "name": "b", + "type": "f32" + } + ], + "returns": "bool" + }, { "name": "setFogEnabled", "doc": "Sets fog visibility", @@ -8659,6 +8750,18 @@ { "method": "getTouchDelta" }, + { + "method": "isGamepadButtonPressed" + }, + { + "method": "isGamepadButtonJustPressed" + }, + { + "method": "isGamepadButtonJustReleased" + }, + { + "method": "getGamepadAxis" + }, { "method": "mapActionKey" }, @@ -12576,6 +12679,18 @@ ], "returns_struct": "Vec2" }, + "isGamepadButtonPressed": { + "ffi": "goud_input_gamepad_button_pressed" + }, + "isGamepadButtonJustPressed": { + "ffi": "goud_input_gamepad_button_just_pressed" + }, + "isGamepadButtonJustReleased": { + "ffi": "goud_input_gamepad_button_just_released" + }, + "getGamepadAxis": { + "ffi": "goud_input_gamepad_axis" + }, "getTouchCount": { "ffi": "goud_input_touch_count" }, @@ -12861,6 +12976,9 @@ "configureFog": { "ffi": "goud_renderer3d_configure_fog" }, + "configureFogLinear": { + "ffi": "goud_renderer3d_configure_fog_linear" + }, "setFogEnabled": { "ffi": "goud_renderer3d_set_fog_enabled" }, diff --git a/goud_engine/build_support/ffi_manifest.rs b/goud_engine/build_support/ffi_manifest.rs index 2564698a8..2e2ced67d 100644 --- a/goud_engine/build_support/ffi_manifest.rs +++ b/goud_engine/build_support/ffi_manifest.rs @@ -92,6 +92,7 @@ const FFI_SOURCE_FILES: &[&str] = &[ "src/ffi/input/mouse.rs", "src/ffi/input/actions.rs", "src/ffi/input/touch.rs", + "src/ffi/input/gamepad.rs", // collision (not yet split) "src/ffi/collision.rs", // scene module diff --git a/goud_engine/src/ffi/renderer3d/environment/mod.rs b/goud_engine/src/ffi/renderer3d/environment/mod.rs index fa3a0da48..3c66ca861 100644 --- a/goud_engine/src/ffi/renderer3d/environment/mod.rs +++ b/goud_engine/src/ffi/renderer3d/environment/mod.rs @@ -19,7 +19,7 @@ use crate::core::error::{set_last_error, GoudError}; use crate::ffi::context::{GoudContextId, GOUD_INVALID_CONTEXT_ID}; use crate::ffi::window::with_window_state; use crate::libs::graphics::renderer3d::TextureManagerTrait; -use crate::libs::graphics::renderer3d::{FogConfig, GridConfig, SkyboxConfig}; +use crate::libs::graphics::renderer3d::{FogConfig, FogMode, GridConfig, SkyboxConfig}; use cgmath::{Vector3, Vector4}; struct WindowTextureBridge { @@ -193,7 +193,44 @@ pub extern "C" fn goud_renderer3d_configure_fog( renderer.configure_fog(FogConfig { enabled, color: Vector3::new(r, g, b), - density, + mode: FogMode::Exponential { density }, + }); + true + }) + .unwrap_or(false) +} + +/// Configures linear fog with explicit start/end distances. +#[no_mangle] +pub extern "C" fn goud_renderer3d_configure_fog_linear( + context_id: GoudContextId, + enabled: bool, + start_distance: f32, + end_distance: f32, + r: f32, + g: f32, + b: f32, +) -> bool { + if context_id == GOUD_INVALID_CONTEXT_ID { + set_last_error(GoudError::InvalidContext); + return false; + } + + if ensure_renderer3d_state(context_id).is_err() { + set_last_error(GoudError::InternalError( + "Renderer state not found".to_string(), + )); + return false; + } + + with_renderer(context_id, |renderer| { + renderer.configure_fog(FogConfig { + enabled, + color: Vector3::new(r, g, b), + mode: FogMode::Linear { + start: start_distance, + end: end_distance, + }, }); true }) diff --git a/goud_engine/src/ffi/renderer3d/environment/tests.rs b/goud_engine/src/ffi/renderer3d/environment/tests.rs index 9d5168b85..d9b5ec5b0 100644 --- a/goud_engine/src/ffi/renderer3d/environment/tests.rs +++ b/goud_engine/src/ffi/renderer3d/environment/tests.rs @@ -272,6 +272,15 @@ fn test_environment_functions_invalid_context_return_false() { GOUD_INVALID_CONTEXT_ID, true )); + assert!(!goud_renderer3d_configure_fog_linear( + GOUD_INVALID_CONTEXT_ID, + true, + 80.0, + 200.0, + 0.5, + 0.5, + 0.5 + )); assert!(!goud_renderer3d_render(GOUD_INVALID_CONTEXT_ID)); assert!(!goud_renderer3d_render_all(GOUD_INVALID_CONTEXT_ID)); } diff --git a/goud_engine/src/jni/generated.rs b/goud_engine/src/jni/generated.rs index 7fdf42cb9..6c0456c9a 100644 --- a/goud_engine/src/jni/generated.rs +++ b/goud_engine/src/jni/generated.rs @@ -2338,7 +2338,7 @@ pub extern "system" fn Java_com_goudengine_internal_GoudGameNative_isGamepadButt crate::jni::helpers::catch_jni_panic(&mut env, "Java_com_goudengine_internal_GoudGameNative_isGamepadButtonPressed", jni::sys::JNI_FALSE, |env| -> crate::jni::helpers::JniCallResult { crate::jni::helpers::prepare_call(env)?; crate::jni::helpers::clear_last_error(); - let result = crate::ffi::input::gamepad::goud_input_gamepad_button_pressed(goud_context_id_from_jlong(contextId), gamepadId as u32, button as u32); + let result = crate::ffi::input::goud_input_gamepad_button_pressed(goud_context_id_from_jlong(contextId), gamepadId as _, button as _); if !result && crate::jni::helpers::last_error_code() != 0 { let _ = crate::jni::helpers::throw_engine_error(env, "goud_input_gamepad_button_pressed", None); return Err(()); @@ -2359,7 +2359,7 @@ pub extern "system" fn Java_com_goudengine_internal_GoudGameNative_isGamepadButt crate::jni::helpers::catch_jni_panic(&mut env, "Java_com_goudengine_internal_GoudGameNative_isGamepadButtonJustPressed", jni::sys::JNI_FALSE, |env| -> crate::jni::helpers::JniCallResult { crate::jni::helpers::prepare_call(env)?; crate::jni::helpers::clear_last_error(); - let result = crate::ffi::input::gamepad::goud_input_gamepad_button_just_pressed(goud_context_id_from_jlong(contextId), gamepadId as u32, button as u32); + let result = crate::ffi::input::goud_input_gamepad_button_just_pressed(goud_context_id_from_jlong(contextId), gamepadId as _, button as _); if !result && crate::jni::helpers::last_error_code() != 0 { let _ = crate::jni::helpers::throw_engine_error(env, "goud_input_gamepad_button_just_pressed", None); return Err(()); @@ -2380,7 +2380,7 @@ pub extern "system" fn Java_com_goudengine_internal_GoudGameNative_isGamepadButt crate::jni::helpers::catch_jni_panic(&mut env, "Java_com_goudengine_internal_GoudGameNative_isGamepadButtonJustReleased", jni::sys::JNI_FALSE, |env| -> crate::jni::helpers::JniCallResult { crate::jni::helpers::prepare_call(env)?; crate::jni::helpers::clear_last_error(); - let result = crate::ffi::input::gamepad::goud_input_gamepad_button_just_released(goud_context_id_from_jlong(contextId), gamepadId as u32, button as u32); + let result = crate::ffi::input::goud_input_gamepad_button_just_released(goud_context_id_from_jlong(contextId), gamepadId as _, button as _); if !result && crate::jni::helpers::last_error_code() != 0 { let _ = crate::jni::helpers::throw_engine_error(env, "goud_input_gamepad_button_just_released", None); return Err(()); @@ -2401,7 +2401,7 @@ pub extern "system" fn Java_com_goudengine_internal_GoudGameNative_getGamepadAxi crate::jni::helpers::catch_jni_panic(&mut env, "Java_com_goudengine_internal_GoudGameNative_getGamepadAxis", 0.0, |env| -> crate::jni::helpers::JniCallResult { crate::jni::helpers::prepare_call(env)?; crate::jni::helpers::clear_last_error(); - let result = crate::ffi::input::gamepad::goud_input_gamepad_axis(goud_context_id_from_jlong(contextId), gamepadId as u32, axis as u32); + let result = crate::ffi::input::goud_input_gamepad_axis(goud_context_id_from_jlong(contextId), gamepadId as _, axis as _); if crate::jni::helpers::last_error_code() != 0 { let _ = crate::jni::helpers::throw_engine_error(env, "goud_input_gamepad_axis", None); return Err(()); @@ -2410,45 +2410,6 @@ pub extern "system" fn Java_com_goudengine_internal_GoudGameNative_getGamepadAxi }) } -#[allow(non_snake_case)] -#[no_mangle] -pub extern "system" fn Java_com_goudengine_internal_GoudGameNative_isGamepadConnected<'local>( - mut env: jni::JNIEnv<'local>, - _class: jni::objects::JClass<'local>, - contextId: jni::sys::jlong, - gamepadId: jni::sys::jint, -) -> jni::sys::jboolean { - crate::jni::helpers::catch_jni_panic(&mut env, "Java_com_goudengine_internal_GoudGameNative_isGamepadConnected", jni::sys::JNI_FALSE, |env| -> crate::jni::helpers::JniCallResult { - crate::jni::helpers::prepare_call(env)?; - crate::jni::helpers::clear_last_error(); - let result = crate::ffi::input::gamepad::goud_input_gamepad_connected(goud_context_id_from_jlong(contextId), gamepadId as u32); - if !result && crate::jni::helpers::last_error_code() != 0 { - let _ = crate::jni::helpers::throw_engine_error(env, "goud_input_gamepad_connected", None); - return Err(()); - } - Ok(crate::jni::helpers::to_jboolean(result)) - }) -} - -#[allow(non_snake_case)] -#[no_mangle] -pub extern "system" fn Java_com_goudengine_internal_GoudGameNative_getGamepadConnectedCount<'local>( - mut env: jni::JNIEnv<'local>, - _class: jni::objects::JClass<'local>, - contextId: jni::sys::jlong, -) -> jni::sys::jint { - crate::jni::helpers::catch_jni_panic(&mut env, "Java_com_goudengine_internal_GoudGameNative_getGamepadConnectedCount", 0, |env| -> crate::jni::helpers::JniCallResult { - crate::jni::helpers::prepare_call(env)?; - crate::jni::helpers::clear_last_error(); - let result = crate::ffi::input::gamepad::goud_input_gamepad_connected_count(goud_context_id_from_jlong(contextId)); - if crate::jni::helpers::last_error_code() != 0 { - let _ = crate::jni::helpers::throw_engine_error(env, "goud_input_gamepad_connected_count", None); - return Err(()); - } - Ok(result as jni::sys::jint) - }) -} - #[allow(non_snake_case)] #[no_mangle] pub extern "system" fn Java_com_goudengine_internal_GoudGameNative_spawnEmpty<'local>( @@ -3601,6 +3562,31 @@ pub extern "system" fn Java_com_goudengine_internal_GoudGameNative_configureFog< }) } +#[allow(non_snake_case)] +#[no_mangle] +pub extern "system" fn Java_com_goudengine_internal_GoudGameNative_configureFogLinear<'local>( + mut env: jni::JNIEnv<'local>, + _class: jni::objects::JClass<'local>, + contextId: jni::sys::jlong, + enabled: jni::sys::jboolean, + startDistance: jni::sys::jfloat, + endDistance: jni::sys::jfloat, + r: jni::sys::jfloat, + g: jni::sys::jfloat, + b: jni::sys::jfloat, +) -> jni::sys::jboolean { + crate::jni::helpers::catch_jni_panic(&mut env, "Java_com_goudengine_internal_GoudGameNative_configureFogLinear", jni::sys::JNI_FALSE, |env| -> crate::jni::helpers::JniCallResult { + crate::jni::helpers::prepare_call(env)?; + crate::jni::helpers::clear_last_error(); + let result = crate::ffi::renderer3d::goud_renderer3d_configure_fog_linear(goud_context_id_from_jlong(contextId), enabled != 0, startDistance as _, endDistance as _, r as _, g as _, b as _); + if !result && crate::jni::helpers::last_error_code() != 0 { + let _ = crate::jni::helpers::throw_engine_error(env, "goud_renderer3d_configure_fog_linear", None); + return Err(()); + } + Ok(crate::jni::helpers::to_jboolean(result)) + }) +} + #[allow(non_snake_case)] #[no_mangle] pub extern "system" fn Java_com_goudengine_internal_GoudGameNative_setFogEnabled<'local>( diff --git a/goud_engine/src/libs/graphics/renderer3d/mod.rs b/goud_engine/src/libs/graphics/renderer3d/mod.rs index 977ca4754..b99cb3ed8 100644 --- a/goud_engine/src/libs/graphics/renderer3d/mod.rs +++ b/goud_engine/src/libs/graphics/renderer3d/mod.rs @@ -56,9 +56,9 @@ pub use core::Renderer3D; pub use scene::Scene3D; pub use texture::TextureManagerTrait; pub use types::{ - AntiAliasingMode, BloomPass, Bone3D, Camera3D, ColorGradePass, FogConfig, GaussianBlurPass, - GridConfig, GridRenderMode, InstanceTransform, Light, LightType, Material3D, MaterialType, - ParticleEmitterConfig, PbrProperties, PostProcessPipeline, PrimitiveCreateInfo, PrimitiveType, - RenderPass, Renderer3DStats, Skeleton3D, SkinnedMesh3D, SkyboxConfig, MAX_BONES, + AntiAliasingMode, BloomPass, Bone3D, Camera3D, ColorGradePass, FogConfig, FogMode, + GaussianBlurPass, GridConfig, GridRenderMode, InstanceTransform, Light, LightType, Material3D, + MaterialType, ParticleEmitterConfig, PbrProperties, PostProcessPipeline, PrimitiveCreateInfo, + PrimitiveType, RenderPass, Renderer3DStats, Skeleton3D, SkinnedMesh3D, SkyboxConfig, MAX_BONES, MAX_BONE_INFLUENCES, MAX_LIGHTS, }; diff --git a/goud_engine/src/libs/graphics/renderer3d/render/mod.rs b/goud_engine/src/libs/graphics/renderer3d/render/mod.rs index dd7dff388..35bd5fdea 100644 --- a/goud_engine/src/libs/graphics/renderer3d/render/mod.rs +++ b/goud_engine/src/libs/graphics/renderer3d/render/mod.rs @@ -141,7 +141,13 @@ impl Renderer3D { eff_fog.color.z, ); self.backend - .set_uniform_float(self.grid_uniforms.fog_density, eff_fog.density); + .set_uniform_float(self.grid_uniforms.fog_density, eff_fog.density()); + self.backend + .set_uniform_int(self.grid_uniforms.fog_mode, eff_fog.mode_int()); + self.backend + .set_uniform_float(self.grid_uniforms.fog_start, eff_fog.start()); + self.backend + .set_uniform_float(self.grid_uniforms.fog_end, eff_fog.end()); self.backend.enable_blending(); self.backend .set_blend_func(BlendFactor::SrcAlpha, BlendFactor::OneMinusSrcAlpha); diff --git a/goud_engine/src/libs/graphics/renderer3d/render_helpers.rs b/goud_engine/src/libs/graphics/renderer3d/render_helpers.rs index ec7fc6a1b..190ec2f74 100644 --- a/goud_engine/src/libs/graphics/renderer3d/render_helpers.rs +++ b/goud_engine/src/libs/graphics/renderer3d/render_helpers.rs @@ -144,10 +144,6 @@ impl Renderer3D { } /// Render skinned Model3D and ModelInstance3D entries using the skinned shader. - /// - /// Iterates models and instances that have `is_skinned == true`, binds the - /// skinned shader, uploads bone matrices from the matching `AnimationPlayer`, - /// and draws each sub-mesh with the skinned vertex layout. pub(super) fn render_skinned_models( &mut self, view_arr: &[f32; 16], @@ -401,8 +397,6 @@ impl Renderer3D { } } - // render_instanced_skinned_models is in render_instanced_skinned.rs - /// Upload view/projection/fog/light uniforms to the currently-bound shader. pub(super) fn apply_main_uniforms( &mut self, @@ -429,7 +423,12 @@ impl Renderer3D { self.backend .set_uniform_vec3(uniforms.fog_color, fog.color.x, fog.color.y, fog.color.z); self.backend - .set_uniform_float(uniforms.fog_density, fog.density); + .set_uniform_float(uniforms.fog_density, fog.density()); + self.backend + .set_uniform_int(uniforms.fog_mode, fog.mode_int()); + self.backend + .set_uniform_float(uniforms.fog_start, fog.start()); + self.backend.set_uniform_float(uniforms.fog_end, fog.end()); let light_count = lights.len().min(MAX_LIGHTS) as i32; self.backend .set_uniform_int(uniforms.num_lights, light_count); diff --git a/goud_engine/src/libs/graphics/renderer3d/render_instanced.rs b/goud_engine/src/libs/graphics/renderer3d/render_instanced.rs index d85bdb53e..5d9851b07 100644 --- a/goud_engine/src/libs/graphics/renderer3d/render_instanced.rs +++ b/goud_engine/src/libs/graphics/renderer3d/render_instanced.rs @@ -153,7 +153,13 @@ impl Renderer3D { eff_fog.color.z, ); self.backend - .set_uniform_float(self.grid_uniforms.fog_density, eff_fog.density); + .set_uniform_float(self.grid_uniforms.fog_density, eff_fog.density()); + self.backend + .set_uniform_int(self.grid_uniforms.fog_mode, eff_fog.mode_int()); + self.backend + .set_uniform_float(self.grid_uniforms.fog_start, eff_fog.start()); + self.backend + .set_uniform_float(self.grid_uniforms.fog_end, eff_fog.end()); self.backend.enable_blending(); self.backend .set_blend_func(BlendFactor::SrcAlpha, BlendFactor::OneMinusSrcAlpha); diff --git a/goud_engine/src/libs/graphics/renderer3d/shader_sources.in b/goud_engine/src/libs/graphics/renderer3d/shader_sources.in index 0bdc62aaa..27a565c4d 100644 --- a/goud_engine/src/libs/graphics/renderer3d/shader_sources.in +++ b/goud_engine/src/libs/graphics/renderer3d/shader_sources.in @@ -104,8 +104,14 @@ vec3 calculateSpotLight(Light light, vec3 normal, vec3 fragPos, vec3 viewDir) { uniform bool fogEnabled; uniform vec3 fogColor; uniform float fogDensity; +uniform int fogMode; +uniform float fogStart; +uniform float fogEnd; float calculateFog(float distance) { + if (fogMode == 1) { + return clamp((fogEnd - distance) / max(fogEnd - fogStart, 0.00001), 0.0, 1.0); + } float fog = exp(-pow(fogDensity * distance, 2.0)); return clamp(fog, 0.0, 1.0); } @@ -245,6 +251,9 @@ uniform vec4 objectColor; uniform bool fogEnabled; uniform vec3 fogColor; uniform float fogDensity; +uniform int fogMode; +uniform float fogStart; +uniform float fogEnd; uniform bool shadowsEnabled; uniform float shadowBias; uniform float shadowStrength; @@ -285,6 +294,9 @@ vec3 calculateSpotLight(Light light, vec3 normal, vec3 fragPos, vec3 viewDir) { } float calculateFog(float distance) { + if (fogMode == 1) { + return clamp((fogEnd - distance) / max(fogEnd - fogStart, 0.00001), 0.0, 1.0); + } float fog = exp(-pow(fogDensity * distance, 2.0)); return clamp(fog, 0.0, 1.0); } @@ -378,6 +390,9 @@ uniform vec3 viewPos; uniform bool fogEnabled; uniform vec3 fogColor; uniform float fogDensity; +uniform int fogMode; +uniform float fogStart; +uniform float fogEnd; uniform float alpha; void main() @@ -386,8 +401,13 @@ void main() if (fogEnabled) { float distance = length(viewPos - fragPos); - float fogFactor = exp(-fogDensity * distance); - fogFactor = clamp(fogFactor, 0.0, 1.0); + float fogFactor; + if (fogMode == 1) { + fogFactor = clamp((fogEnd - distance) / max(fogEnd - fogStart, 0.00001), 0.0, 1.0); + } else { + fogFactor = exp(-fogDensity * distance); + fogFactor = clamp(fogFactor, 0.0, 1.0); + } color = mix(fogColor, color, fogFactor); } @@ -427,6 +447,10 @@ struct Uniforms { primaryLightIntensity: f32, primaryLightColor: vec3, _pad4: f32, + fogMode: i32, + fogStart: f32, + fogEnd: f32, + _padFog: f32, lights: array, } @@ -498,6 +522,10 @@ struct Uniforms { primaryLightIntensity: f32, primaryLightColor: vec3, _pad4: f32, + fogMode: i32, + fogStart: f32, + fogEnd: f32, + _padFog: f32, lights: array, } @@ -634,7 +662,12 @@ fn main( if uniforms.fogEnabled != 0 { let dist = length(frag_pos - uniforms.viewPos); - let fog_factor = clamp(exp(-pow(uniforms.fogDensity * dist, 2.0)), 0.0, 1.0); + var fog_factor: f32; + if uniforms.fogMode == 1 { + fog_factor = clamp((uniforms.fogEnd - dist) / max(uniforms.fogEnd - uniforms.fogStart, 0.00001), 0.0, 1.0); + } else { + fog_factor = clamp(exp(-pow(uniforms.fogDensity * dist, 2.0)), 0.0, 1.0); + } final_color = mix(uniforms.fogColor, final_color, fog_factor); } @@ -674,6 +707,10 @@ struct Uniforms { primaryLightIntensity: f32, primaryLightColor: vec3, _pad4: f32, + fogMode: i32, + fogStart: f32, + fogEnd: f32, + _padFog: f32, lights: array, } @@ -738,6 +775,10 @@ struct Uniforms { primaryLightIntensity: f32, primaryLightColor: vec3, _pad4: f32, + fogMode: i32, + fogStart: f32, + fogEnd: f32, + _padFog: f32, lights: array, } @@ -872,7 +913,12 @@ fn main( if uniforms.fogEnabled != 0 { let dist = length(frag_pos - uniforms.viewPos); - let fog_factor = clamp(exp(-pow(uniforms.fogDensity * dist, 2.0)), 0.0, 1.0); + var fog_factor: f32; + if uniforms.fogMode == 1 { + fog_factor = clamp((uniforms.fogEnd - dist) / max(uniforms.fogEnd - uniforms.fogStart, 0.00001), 0.0, 1.0); + } else { + fog_factor = clamp(exp(-pow(uniforms.fogDensity * dist, 2.0)), 0.0, 1.0); + } final_color = mix(uniforms.fogColor, final_color, fog_factor); } @@ -889,6 +935,9 @@ struct Uniforms { fogColor: vec3, fogEnabled: i32, alpha: f32, + fogMode: i32, + fogStart: f32, + fogEnd: f32, } @group(0) @binding(0) var uniforms: Uniforms; @@ -923,6 +972,9 @@ struct Uniforms { fogColor: vec3, fogEnabled: i32, alpha: f32, + fogMode: i32, + fogStart: f32, + fogEnd: f32, } @group(0) @binding(0) var uniforms: Uniforms; @@ -936,7 +988,12 @@ fn main( if uniforms.fogEnabled != 0 { let dist = length(uniforms.viewPos - frag_pos); - let fog_factor = clamp(exp(-pow(uniforms.fogDensity * dist, 2.0)), 0.0, 1.0); + var fog_factor: f32; + if uniforms.fogMode == 1 { + fog_factor = clamp((uniforms.fogEnd - dist) / max(uniforms.fogEnd - uniforms.fogStart, 0.00001), 0.0, 1.0); + } else { + fog_factor = clamp(exp(-pow(uniforms.fogDensity * dist, 2.0)), 0.0, 1.0); + } final_color = mix(uniforms.fogColor, final_color, fog_factor); } @@ -1160,6 +1217,10 @@ struct Uniforms { primaryLightIntensity: f32, primaryLightColor: vec3, _pad4: f32, + fogMode: i32, + fogStart: f32, + fogEnd: f32, + _padFog: f32, lights: array, } @@ -1288,6 +1349,10 @@ struct Uniforms { primaryLightIntensity: f32, primaryLightColor: vec3, _pad4: f32, + fogMode: i32, + fogStart: f32, + fogEnd: f32, + _padFog: f32, lights: array, } diff --git a/goud_engine/src/libs/graphics/renderer3d/shaders.rs b/goud_engine/src/libs/graphics/renderer3d/shaders.rs index e461c100c..118e29f0f 100644 --- a/goud_engine/src/libs/graphics/renderer3d/shaders.rs +++ b/goud_engine/src/libs/graphics/renderer3d/shaders.rs @@ -41,6 +41,9 @@ pub(super) struct MainUniforms { pub(super) fog_enabled: i32, pub(super) fog_color: i32, pub(super) fog_density: i32, + pub(super) fog_mode: i32, + pub(super) fog_start: i32, + pub(super) fog_end: i32, pub(super) lights: Vec, pub(super) primary_light_dir: i32, pub(super) primary_light_color: i32, @@ -55,6 +58,9 @@ pub(super) struct GridUniforms { pub(super) fog_enabled: i32, pub(super) fog_color: i32, pub(super) fog_density: i32, + pub(super) fog_mode: i32, + pub(super) fog_start: i32, + pub(super) fog_end: i32, pub(super) alpha: i32, } @@ -110,6 +116,9 @@ pub(super) fn resolve_main_uniforms( fog_enabled: loc("fogEnabled"), fog_color: loc("fogColor"), fog_density: loc("fogDensity"), + fog_mode: loc("fogMode"), + fog_start: loc("fogStart"), + fog_end: loc("fogEnd"), lights, primary_light_dir: loc("primaryLightDir"), primary_light_color: loc("primaryLightColor"), @@ -196,6 +205,9 @@ pub(super) fn resolve_grid_uniforms( fog_enabled: loc("fogEnabled"), fog_color: loc("fogColor"), fog_density: loc("fogDensity"), + fog_mode: loc("fogMode"), + fog_start: loc("fogStart"), + fog_end: loc("fogEnd"), alpha: loc("alpha"), } } diff --git a/goud_engine/src/libs/graphics/renderer3d/types.rs b/goud_engine/src/libs/graphics/renderer3d/types.rs index 59ec0a78a..66834935b 100644 --- a/goud_engine/src/libs/graphics/renderer3d/types.rs +++ b/goud_engine/src/libs/graphics/renderer3d/types.rs @@ -11,10 +11,6 @@ pub use super::render_pass::{ }; pub use super::skinned_mesh::{Bone3D, Skeleton3D, SkinnedMesh3D, MAX_BONES, MAX_BONE_INFLUENCES}; -// ============================================================================ -// Constants -// ============================================================================ - /// Maximum number of lights supported pub const MAX_LIGHTS: usize = 8; @@ -381,6 +377,23 @@ impl Default for SkyboxConfig { } } +/// Fog calculation mode. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum FogMode { + /// Classic exponential fog. Visibility = exp(-(density * distance)^2). + Exponential { + /// Fog density factor. + density: f32, + }, + /// Linear fog with explicit start/end distances. Clear before start, fully fogged after end. + Linear { + /// Distance at which fog begins. + start: f32, + /// Distance at which fog is fully opaque. + end: f32, + }, +} + /// Fog configuration #[derive(Debug, Clone)] #[allow(missing_docs)] @@ -389,8 +402,39 @@ pub struct FogConfig { pub enabled: bool, /// Fog color (RGB) pub color: Vector3, - /// Fog density - pub density: f32, + /// Fog mode (exponential or linear) + pub mode: FogMode, +} + +impl FogConfig { + /// Returns 0 for exponential, 1 for linear (used as shader uniform). + pub fn mode_int(&self) -> i32 { + match self.mode { + FogMode::Exponential { .. } => 0, + FogMode::Linear { .. } => 1, + } + } + /// Returns density (0.0 for linear mode). + pub fn density(&self) -> f32 { + match self.mode { + FogMode::Exponential { density } => density, + FogMode::Linear { .. } => 0.0, + } + } + /// Returns fog start distance (0.0 for exponential mode). + pub fn start(&self) -> f32 { + match self.mode { + FogMode::Exponential { .. } => 0.0, + FogMode::Linear { start, .. } => start, + } + } + /// Returns fog end distance (0.0 for exponential mode). + pub fn end(&self) -> f32 { + match self.mode { + FogMode::Exponential { .. } => 0.0, + FogMode::Linear { end, .. } => end, + } + } } impl Default for FogConfig { @@ -398,7 +442,7 @@ impl Default for FogConfig { Self { enabled: true, color: Vector3::new(0.05, 0.05, 0.1), - density: 0.02, + mode: FogMode::Exponential { density: 0.02 }, } } } @@ -426,6 +470,10 @@ impl Default for Camera3D { } } +#[cfg(test)] +#[path = "types_tests.rs"] +mod types_tests; + impl Camera3D { /// Get the view matrix pub fn view_matrix(&self) -> Matrix4 { diff --git a/goud_engine/src/libs/graphics/renderer3d/types_tests.rs b/goud_engine/src/libs/graphics/renderer3d/types_tests.rs new file mode 100644 index 000000000..8ec826852 --- /dev/null +++ b/goud_engine/src/libs/graphics/renderer3d/types_tests.rs @@ -0,0 +1,88 @@ +use super::*; + +#[test] +fn test_fog_config_mode_int() { + let exp = FogConfig { + enabled: true, + color: Vector3::new(0.5, 0.5, 0.5), + mode: FogMode::Exponential { density: 0.05 }, + }; + assert_eq!(exp.mode_int(), 0); + + let lin = FogConfig { + enabled: true, + color: Vector3::new(0.5, 0.5, 0.5), + mode: FogMode::Linear { + start: 10.0, + end: 100.0, + }, + }; + assert_eq!(lin.mode_int(), 1); +} + +#[test] +fn test_fog_config_density() { + let exp = FogConfig { + enabled: true, + color: Vector3::new(0.0, 0.0, 0.0), + mode: FogMode::Exponential { density: 0.03 }, + }; + assert!((exp.density() - 0.03).abs() < f32::EPSILON); + + let lin = FogConfig { + enabled: true, + color: Vector3::new(0.0, 0.0, 0.0), + mode: FogMode::Linear { + start: 10.0, + end: 100.0, + }, + }; + assert!((lin.density() - 0.0).abs() < f32::EPSILON); +} + +#[test] +fn test_fog_config_start() { + let exp = FogConfig { + enabled: true, + color: Vector3::new(0.0, 0.0, 0.0), + mode: FogMode::Exponential { density: 0.02 }, + }; + assert!((exp.start() - 0.0).abs() < f32::EPSILON); + + let lin = FogConfig { + enabled: true, + color: Vector3::new(0.0, 0.0, 0.0), + mode: FogMode::Linear { + start: 80.0, + end: 200.0, + }, + }; + assert!((lin.start() - 80.0).abs() < f32::EPSILON); +} + +#[test] +fn test_fog_config_end() { + let exp = FogConfig { + enabled: true, + color: Vector3::new(0.0, 0.0, 0.0), + mode: FogMode::Exponential { density: 0.02 }, + }; + assert!((exp.end() - 0.0).abs() < f32::EPSILON); + + let lin = FogConfig { + enabled: true, + color: Vector3::new(0.0, 0.0, 0.0), + mode: FogMode::Linear { + start: 80.0, + end: 200.0, + }, + }; + assert!((lin.end() - 200.0).abs() < f32::EPSILON); +} + +#[test] +fn test_fog_config_default_is_exponential() { + let cfg = FogConfig::default(); + assert_eq!(cfg.mode_int(), 0); + assert!(matches!(cfg.mode, FogMode::Exponential { .. })); +} diff --git a/goud_engine/src/sdk/game/instance/lua_bindings/tools.g.rs b/goud_engine/src/sdk/game/instance/lua_bindings/tools.g.rs index 2e852530f..39e7ea6ec 100644 --- a/goud_engine/src/sdk/game/instance/lua_bindings/tools.g.rs +++ b/goud_engine/src/sdk/game/instance/lua_bindings/tools.g.rs @@ -69,6 +69,10 @@ use crate::ffi::window::goud_fixed_timestep_begin; use crate::ffi::window::goud_fixed_timestep_set; use crate::ffi::window::goud_fixed_timestep_set_max_steps; use crate::ffi::arena::goud_frame_arena_reset; +use crate::ffi::input::goud_input_gamepad_axis; +use crate::ffi::input::goud_input_gamepad_button_just_pressed; +use crate::ffi::input::goud_input_gamepad_button_just_released; +use crate::ffi::input::goud_input_gamepad_button_pressed; use crate::ffi::input::goud_input_touch_active; use crate::ffi::input::goud_input_touch_count; use crate::ffi::input::goud_input_touch_just_pressed; @@ -171,6 +175,7 @@ use crate::ffi::renderer3d::goud_renderer3d_add_object_to_scene; use crate::ffi::renderer3d::goud_renderer3d_blend_animations; use crate::ffi::renderer3d::goud_renderer3d_clear_current_scene; use crate::ffi::renderer3d::goud_renderer3d_configure_fog; +use crate::ffi::renderer3d::goud_renderer3d_configure_fog_linear; use crate::ffi::renderer3d::goud_renderer3d_configure_grid; use crate::ffi::renderer3d::goud_renderer3d_configure_skybox; use crate::ffi::renderer3d::goud_renderer3d_create_cube; @@ -324,6 +329,26 @@ pub(crate) fn register_goud_game_tools(lua: &Lua, ctx_id: u64) -> LuaResult<()> Ok(goud_window_destroy(ctx)) })?; tbl.set("destroy", f_destroy)?; + // GoudGame.isGamepadButtonPressed + let f_is_gamepad_button_pressed = lua.create_function(move |_, (arg0, arg1): (i64, i64)| { + Ok(goud_input_gamepad_button_pressed(ctx, arg0 as u32, arg1 as u32)) + })?; + tbl.set("is_gamepad_button_pressed", f_is_gamepad_button_pressed)?; + // GoudGame.isGamepadButtonJustPressed + let f_is_gamepad_button_just_pressed = lua.create_function(move |_, (arg0, arg1): (i64, i64)| { + Ok(goud_input_gamepad_button_just_pressed(ctx, arg0 as u32, arg1 as u32)) + })?; + tbl.set("is_gamepad_button_just_pressed", f_is_gamepad_button_just_pressed)?; + // GoudGame.isGamepadButtonJustReleased + let f_is_gamepad_button_just_released = lua.create_function(move |_, (arg0, arg1): (i64, i64)| { + Ok(goud_input_gamepad_button_just_released(ctx, arg0 as u32, arg1 as u32)) + })?; + tbl.set("is_gamepad_button_just_released", f_is_gamepad_button_just_released)?; + // GoudGame.getGamepadAxis + let f_get_gamepad_axis = lua.create_function(move |_, (arg0, arg1): (i64, i64)| { + Ok(goud_input_gamepad_axis(ctx, arg0 as u32, arg1 as u32) as f64) + })?; + tbl.set("get_gamepad_axis", f_get_gamepad_axis)?; // GoudGame.getTouchCount let f_get_touch_count = lua.create_function(move |_, _: ()| { Ok(goud_input_touch_count(ctx) as i64) @@ -465,6 +490,11 @@ pub(crate) fn register_goud_game_tools(lua: &Lua, ctx_id: u64) -> LuaResult<()> Ok(goud_renderer3d_configure_fog(ctx, arg0, arg1 as f32, arg2 as f32, arg3 as f32, arg4 as f32)) })?; tbl.set("configure_fog", f_configure_fog)?; + // GoudGame.configureFogLinear + let f_configure_fog_linear = lua.create_function(move |_, (arg0, arg1, arg2, arg3, arg4, arg5): (bool, f64, f64, f64, f64, f64)| { + Ok(goud_renderer3d_configure_fog_linear(ctx, arg0, arg1 as f32, arg2 as f32, arg3 as f32, arg4 as f32, arg5 as f32)) + })?; + tbl.set("configure_fog_linear", f_configure_fog_linear)?; // GoudGame.setFogEnabled let f_set_fog_enabled = lua.create_function(move |_, arg0: bool| { Ok(goud_renderer3d_set_fog_enabled(ctx, arg0)) diff --git a/goud_engine/src/sdk/rendering_3d.rs b/goud_engine/src/sdk/rendering_3d.rs index f60520502..e6a9f27fe 100644 --- a/goud_engine/src/sdk/rendering_3d.rs +++ b/goud_engine/src/sdk/rendering_3d.rs @@ -1,18 +1,11 @@ -//! # SDK 3D Rendering API -//! -//! Provides methods on [`GoudGame`] for 3D rendering operations -//! including primitive creation, object manipulation, lighting, camera control, -//! and scene configuration. -//! -//! # Availability -//! -//! This module requires the `native` feature (desktop platform with OpenGL). +//! SDK 3D Rendering API: primitives, objects, lighting, camera, and scene configuration. +//! Requires the `native` feature (desktop platform). use super::GoudGame; #[cfg(feature = "native")] use crate::libs::graphics::backend::native_backend::SharedNativeRenderBackend; use crate::libs::graphics::renderer3d::{ - AntiAliasingMode, FogConfig, GridConfig, InstanceTransform, Light, LightType, + AntiAliasingMode, FogConfig, FogMode, GridConfig, InstanceTransform, Light, LightType, ParticleEmitterConfig, PrimitiveCreateInfo, PrimitiveType, Renderer3DStats, SkyboxConfig, TextureManagerTrait, }; @@ -363,14 +356,40 @@ impl GoudGame { } } - /// Configures fog settings. + /// Configures exponential fog settings. pub fn configure_fog(&mut self, enabled: bool, r: f32, g: f32, b: f32, density: f32) -> bool { match &mut self.renderer_3d { Some(renderer) => { renderer.configure_fog(FogConfig { enabled, color: Vector3::new(r, g, b), - density, + mode: FogMode::Exponential { density }, + }); + true + } + None => false, + } + } + + /// Configures linear fog with explicit start/end distances. + pub fn configure_fog_linear( + &mut self, + enabled: bool, + start_distance: f32, + end_distance: f32, + r: f32, + g: f32, + b: f32, + ) -> bool { + match &mut self.renderer_3d { + Some(renderer) => { + renderer.configure_fog(FogConfig { + enabled, + color: Vector3::new(r, g, b), + mode: FogMode::Linear { + start: start_distance, + end: end_distance, + }, }); true } diff --git a/goud_engine/src/sdk/rendering_3d/tests.rs b/goud_engine/src/sdk/rendering_3d/tests.rs index 08ac37a4f..b782fe5fd 100644 --- a/goud_engine/src/sdk/rendering_3d/tests.rs +++ b/goud_engine/src/sdk/rendering_3d/tests.rs @@ -65,6 +65,14 @@ fn test_configure_fog_headless() { assert!(!game.configure_fog(true, 0.5, 0.5, 0.5, 0.01)); } +#[test] +fn test_configure_fog_linear_headless() { + // Without a renderer, configure_fog_linear should return false gracefully + // (no panic, no UB) -- the SDK wrapper handles missing renderer state. + let mut game = GoudGame::new(GameConfig::default()).unwrap(); + assert!(!game.configure_fog_linear(true, 80.0, 200.0, 0.5, 0.5, 0.5)); +} + #[test] fn test_set_fog_enabled_headless() { let mut game = GoudGame::new(GameConfig::default()).unwrap(); diff --git a/goud_engine/tests/jni/java/com/goudengine/internal/GoudGameNative.java b/goud_engine/tests/jni/java/com/goudengine/internal/GoudGameNative.java index 97b73fc78..7f7a2ce29 100644 --- a/goud_engine/tests/jni/java/com/goudengine/internal/GoudGameNative.java +++ b/goud_engine/tests/jni/java/com/goudengine/internal/GoudGameNative.java @@ -35,6 +35,10 @@ private GoudGameNative() {} public static native boolean isTouchJustPressed(long contextId, long touchId); public static native boolean isTouchJustReleased(long contextId, long touchId); public static native Vec2 getTouchDelta(long contextId, long touchId); + public static native boolean isGamepadButtonPressed(long contextId, int gamepadId, int button); + public static native boolean isGamepadButtonJustPressed(long contextId, int gamepadId, int button); + public static native boolean isGamepadButtonJustReleased(long contextId, int gamepadId, int button); + public static native float getGamepadAxis(long contextId, int gamepadId, int axis); public static native long spawnEmpty(long contextId); public static native boolean despawn(long contextId, long entity); public static native long cloneEntity(long contextId, long entity); @@ -79,6 +83,7 @@ private GoudGameNative() {} public static native boolean setGridEnabled(long contextId, boolean enabled); public static native boolean configureSkybox(long contextId, boolean enabled, float r, float g, float b, float a); public static native boolean configureFog(long contextId, boolean enabled, float r, float g, float b, float density); + public static native boolean configureFogLinear(long contextId, boolean enabled, float startDistance, float endDistance, float r, float g, float b); public static native boolean setFogEnabled(long contextId, boolean enabled); public static native int setFrustumCullingEnabled(long contextId, boolean enabled); public static native int setSkinningMode(long contextId, int mode); diff --git a/sdks/csharp/generated/GoudGame.g.cs b/sdks/csharp/generated/GoudGame.g.cs index abb77fe0f..baed05085 100644 --- a/sdks/csharp/generated/GoudGame.g.cs +++ b/sdks/csharp/generated/GoudGame.g.cs @@ -360,6 +360,30 @@ public Vec2 GetTouchDelta(ulong touchId) return new Vec2(_dx, _dy); } + /// Returns true if the specified gamepad button is currently pressed + public bool IsGamepadButtonPressed(uint gamepadId, uint button) + { + return NativeMethods.goud_input_gamepad_button_pressed(_ctx, gamepadId, button); + } + + /// Returns true if the specified gamepad button was just pressed this frame + public bool IsGamepadButtonJustPressed(uint gamepadId, uint button) + { + return NativeMethods.goud_input_gamepad_button_just_pressed(_ctx, gamepadId, button); + } + + /// Returns true if the specified gamepad button was just released this frame + public bool IsGamepadButtonJustReleased(uint gamepadId, uint button) + { + return NativeMethods.goud_input_gamepad_button_just_released(_ctx, gamepadId, button); + } + + /// Returns the current value of a gamepad analog axis (-1.0 to 1.0) + public float GetGamepadAxis(uint gamepadId, uint axis) + { + return NativeMethods.goud_input_gamepad_axis(_ctx, gamepadId, axis); + } + /// Creates a new empty entity public Entity SpawnEmpty() { @@ -756,6 +780,12 @@ public bool ConfigureFog(bool enabled, float r, float g, float b, float density) return NativeMethods.goud_renderer3d_configure_fog(_ctx, enabled, r, g, b, density); } + /// Configures linear fog with explicit start and end distances + public bool ConfigureFogLinear(bool enabled, float startDistance, float endDistance, float r, float g, float b) + { + return NativeMethods.goud_renderer3d_configure_fog_linear(_ctx, enabled, startDistance, endDistance, r, g, b); + } + /// Sets fog visibility public bool SetFogEnabled(bool enabled) { diff --git a/sdks/csharp/generated/NativeMethods.g.cs b/sdks/csharp/generated/NativeMethods.g.cs index aaaa9c1a6..f693fb37e 100644 --- a/sdks/csharp/generated/NativeMethods.g.cs +++ b/sdks/csharp/generated/NativeMethods.g.cs @@ -807,6 +807,10 @@ public static unsafe class NativeMethods [return: MarshalAs(UnmanagedType.U1)] public static extern bool goud_renderer3d_configure_fog(GoudContextId context_id, [MarshalAs(UnmanagedType.U1)] bool enabled, float r, float g, float b, float density); + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool goud_renderer3d_configure_fog_linear(GoudContextId context_id, [MarshalAs(UnmanagedType.U1)] bool enabled, float start_distance, float end_distance, float r, float g, float b); + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] [return: MarshalAs(UnmanagedType.U1)] public static extern bool goud_renderer3d_set_fog_enabled(GoudContextId context_id, [MarshalAs(UnmanagedType.U1)] bool enabled); @@ -1191,6 +1195,47 @@ public static unsafe class NativeMethods [return: MarshalAs(UnmanagedType.U1)] public static extern bool goud_input_get_scroll_delta(GoudContextId context_id, ref float out_dx, ref float out_dy); + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool goud_input_gamepad_button_pressed(GoudContextId context_id, uint gamepad_id, uint button); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool goud_input_gamepad_button_just_pressed(GoudContextId context_id, uint gamepad_id, uint button); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool goud_input_gamepad_button_just_released(GoudContextId context_id, uint gamepad_id, uint button); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern float goud_input_gamepad_axis(GoudContextId context_id, uint gamepad_id, uint axis); + + // input_gamepad + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool goud_input_gamepad_connected(GoudContextId context_id, uint gamepad_id); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern uint goud_input_gamepad_connected_count(GoudContextId context_id); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool goud_input_gamepad_left_stick(GoudContextId context_id, uint gamepad_id, ref float out_x, ref float out_y); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool goud_input_gamepad_right_stick(GoudContextId context_id, uint gamepad_id, ref float out_x, ref float out_y); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern float goud_input_gamepad_left_trigger(GoudContextId context_id, uint gamepad_id); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern float goud_input_gamepad_right_trigger(GoudContextId context_id, uint gamepad_id); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool goud_input_gamepad_set_vibration(GoudContextId context_id, uint gamepad_id, float intensity); + // input_touch [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] public static extern uint goud_input_touch_count(GoudContextId context_id); diff --git a/sdks/csharp/include/goud_engine.h b/sdks/csharp/include/goud_engine.h index 85b7120a6..347f45ac7 100644 --- a/sdks/csharp/include/goud_engine.h +++ b/sdks/csharp/include/goud_engine.h @@ -40,6 +40,11 @@ extern "C" { */ #define PROTOCOL_VERSION 1 +/** + * Maximum number of gamepad slots supported by the engine. + */ +#define MAX_GAMEPAD_SLOTS 4 + /** * Sentinel value for an invalid entity ID. */ @@ -2273,6 +2278,11 @@ bool goud_renderer3d_configure_skybox(struct GoudContextId context_id, bool enab */ bool goud_renderer3d_configure_fog(struct GoudContextId context_id, bool enabled, float r, float g, float b, float density); +/** + * Configures linear fog with explicit start/end distances. + */ +bool goud_renderer3d_configure_fog_linear(struct GoudContextId context_id, bool enabled, float start_distance, float end_distance, float r, float g, float b); + /** * Sets fog enabled state. */ diff --git a/sdks/go/goud/game.go b/sdks/go/goud/game.go index 518377550..cfa86396e 100644 --- a/sdks/go/goud/game.go +++ b/sdks/go/goud/game.go @@ -223,6 +223,26 @@ func (g *Game) GetTouchDelta(touchId uint64) Vec2 { return Vec2{} } +// IsGamepadButtonPressed Returns true if the specified gamepad button is currently pressed +func (g *Game) IsGamepadButtonPressed(gamepadId uint32, button uint32) bool { + return false +} + +// IsGamepadButtonJustPressed Returns true if the specified gamepad button was just pressed this frame +func (g *Game) IsGamepadButtonJustPressed(gamepadId uint32, button uint32) bool { + return false +} + +// IsGamepadButtonJustReleased Returns true if the specified gamepad button was just released this frame +func (g *Game) IsGamepadButtonJustReleased(gamepadId uint32, button uint32) bool { + return false +} + +// GetGamepadAxis Returns the current value of a gamepad analog axis (-1.0 to 1.0) +func (g *Game) GetGamepadAxis(gamepadId uint32, axis uint32) float32 { + return 0 +} + // SpawnEmpty creates a new empty entity. func (g *Game) SpawnEmpty() EntityID { return NewEntityID(ffi.EntitySpawnEmpty(g.ctx)) @@ -504,6 +524,11 @@ func (g *Game) ConfigureFog(enabled bool, r float32, gVal float32, b float32, de return false } +// ConfigureFogLinear Configures linear fog with explicit start and end distances +func (g *Game) ConfigureFogLinear(enabled bool, startDistance float32, endDistance float32, r float32, gVal float32, b float32) bool { + return false +} + // SetFogEnabled Sets fog visibility func (g *Game) SetFogEnabled(enabled bool) bool { return false diff --git a/sdks/go/include/goud_engine.h b/sdks/go/include/goud_engine.h index 5a9c15936..347f45ac7 100644 --- a/sdks/go/include/goud_engine.h +++ b/sdks/go/include/goud_engine.h @@ -2278,6 +2278,11 @@ bool goud_renderer3d_configure_skybox(struct GoudContextId context_id, bool enab */ bool goud_renderer3d_configure_fog(struct GoudContextId context_id, bool enabled, float r, float g, float b, float density); +/** + * Configures linear fog with explicit start/end distances. + */ +bool goud_renderer3d_configure_fog_linear(struct GoudContextId context_id, bool enabled, float start_distance, float end_distance, float r, float g, float b); + /** * Sets fog enabled state. */ diff --git a/sdks/go/internal/ffi/ffi.go b/sdks/go/internal/ffi/ffi.go index 62ccee60d..29c7c736e 100644 --- a/sdks/go/internal/ffi/ffi.go +++ b/sdks/go/internal/ffi/ffi.go @@ -1137,6 +1137,73 @@ func GoudInputActionPressed(context_id C.GoudContextId, action_name *C.char) boo return bool(C.goud_input_action_pressed(context_id, action_name)) } +// GoudInputGamepadAxis wraps goud_input_gamepad_axis. +func GoudInputGamepadAxis(context_id C.GoudContextId, gamepad_id uint32, axis uint32) float32 { + return float32(C.goud_input_gamepad_axis(context_id, C.uint32_t(gamepad_id), C.uint32_t(axis))) +} + +// GoudInputGamepadButtonJustPressed wraps goud_input_gamepad_button_just_pressed. +func GoudInputGamepadButtonJustPressed(context_id C.GoudContextId, gamepad_id uint32, button uint32) bool { + return bool(C.goud_input_gamepad_button_just_pressed(context_id, C.uint32_t(gamepad_id), C.uint32_t(button))) +} + +// GoudInputGamepadButtonJustReleased wraps goud_input_gamepad_button_just_released. +func GoudInputGamepadButtonJustReleased(context_id C.GoudContextId, gamepad_id uint32, button uint32) bool { + return bool(C.goud_input_gamepad_button_just_released(context_id, C.uint32_t(gamepad_id), C.uint32_t(button))) +} + +// GoudInputGamepadButtonPressed wraps goud_input_gamepad_button_pressed. +func GoudInputGamepadButtonPressed(context_id C.GoudContextId, gamepad_id uint32, button uint32) bool { + return bool(C.goud_input_gamepad_button_pressed(context_id, C.uint32_t(gamepad_id), C.uint32_t(button))) +} + +// GoudInputGamepadConnected wraps goud_input_gamepad_connected. +func GoudInputGamepadConnected(context_id C.GoudContextId, gamepad_id uint32) bool { + return bool(C.goud_input_gamepad_connected(context_id, C.uint32_t(gamepad_id))) +} + +// GoudInputGamepadConnectedCount wraps goud_input_gamepad_connected_count. +func GoudInputGamepadConnectedCount(context_id C.GoudContextId) uint32 { + return uint32(C.goud_input_gamepad_connected_count(context_id)) +} + +// GoudInputGamepadLeftStick wraps goud_input_gamepad_left_stick. +func GoudInputGamepadLeftStick(context_id C.GoudContextId, gamepad_id uint32, out_x *C.float, out_y *C.float) bool { + if out_x == nil { + return false + } + if out_y == nil { + return false + } + return bool(C.goud_input_gamepad_left_stick(context_id, C.uint32_t(gamepad_id), out_x, out_y)) +} + +// GoudInputGamepadLeftTrigger wraps goud_input_gamepad_left_trigger. +func GoudInputGamepadLeftTrigger(context_id C.GoudContextId, gamepad_id uint32) float32 { + return float32(C.goud_input_gamepad_left_trigger(context_id, C.uint32_t(gamepad_id))) +} + +// GoudInputGamepadRightStick wraps goud_input_gamepad_right_stick. +func GoudInputGamepadRightStick(context_id C.GoudContextId, gamepad_id uint32, out_x *C.float, out_y *C.float) bool { + if out_x == nil { + return false + } + if out_y == nil { + return false + } + return bool(C.goud_input_gamepad_right_stick(context_id, C.uint32_t(gamepad_id), out_x, out_y)) +} + +// GoudInputGamepadRightTrigger wraps goud_input_gamepad_right_trigger. +func GoudInputGamepadRightTrigger(context_id C.GoudContextId, gamepad_id uint32) float32 { + return float32(C.goud_input_gamepad_right_trigger(context_id, C.uint32_t(gamepad_id))) +} + +// GoudInputGamepadSetVibration wraps goud_input_gamepad_set_vibration. +func GoudInputGamepadSetVibration(context_id C.GoudContextId, gamepad_id uint32, intensity float32) bool { + return bool(C.goud_input_gamepad_set_vibration(context_id, C.uint32_t(gamepad_id), C.float(intensity))) +} + // GoudInputGetMouseDelta wraps goud_input_get_mouse_delta. func GoudInputGetMouseDelta(context_id C.GoudContextId, out_dx *C.float, out_dy *C.float) bool { if out_dx == nil { @@ -1987,6 +2054,11 @@ func GoudRenderer3dConfigureFog(context_id C.GoudContextId, enabled bool, r floa return bool(C.goud_renderer3d_configure_fog(context_id, C._Bool(enabled), C.float(r), C.float(g), C.float(b), C.float(density))) } +// GoudRenderer3dConfigureFogLinear wraps goud_renderer3d_configure_fog_linear. +func GoudRenderer3dConfigureFogLinear(context_id C.GoudContextId, enabled bool, start_distance float32, end_distance float32, r float32, g float32, b float32) bool { + return bool(C.goud_renderer3d_configure_fog_linear(context_id, C._Bool(enabled), C.float(start_distance), C.float(end_distance), C.float(r), C.float(g), C.float(b))) +} + // GoudRenderer3dConfigureGrid wraps goud_renderer3d_configure_grid. func GoudRenderer3dConfigureGrid(context_id C.GoudContextId, enabled bool, size float32, divisions uint32) bool { return bool(C.goud_renderer3d_configure_grid(context_id, C._Bool(enabled), C.float(size), C.uint32_t(divisions))) diff --git a/sdks/kotlin/src/main/java/com/goudengine/internal/GoudGameNative.java b/sdks/kotlin/src/main/java/com/goudengine/internal/GoudGameNative.java index 4250809ac..7f7a2ce29 100644 --- a/sdks/kotlin/src/main/java/com/goudengine/internal/GoudGameNative.java +++ b/sdks/kotlin/src/main/java/com/goudengine/internal/GoudGameNative.java @@ -35,6 +35,10 @@ private GoudGameNative() {} public static native boolean isTouchJustPressed(long contextId, long touchId); public static native boolean isTouchJustReleased(long contextId, long touchId); public static native Vec2 getTouchDelta(long contextId, long touchId); + public static native boolean isGamepadButtonPressed(long contextId, int gamepadId, int button); + public static native boolean isGamepadButtonJustPressed(long contextId, int gamepadId, int button); + public static native boolean isGamepadButtonJustReleased(long contextId, int gamepadId, int button); + public static native float getGamepadAxis(long contextId, int gamepadId, int axis); public static native long spawnEmpty(long contextId); public static native boolean despawn(long contextId, long entity); public static native long cloneEntity(long contextId, long entity); @@ -79,6 +83,7 @@ private GoudGameNative() {} public static native boolean setGridEnabled(long contextId, boolean enabled); public static native boolean configureSkybox(long contextId, boolean enabled, float r, float g, float b, float a); public static native boolean configureFog(long contextId, boolean enabled, float r, float g, float b, float density); + public static native boolean configureFogLinear(long contextId, boolean enabled, float startDistance, float endDistance, float r, float g, float b); public static native boolean setFogEnabled(long contextId, boolean enabled); public static native int setFrustumCullingEnabled(long contextId, boolean enabled); public static native int setSkinningMode(long contextId, int mode); @@ -274,12 +279,6 @@ private GoudGameNative() {} public static native int audioUpdateCrossfades(long contextId, float deltaSec); public static native int audioActiveCrossfadeCount(long contextId); public static native int audioActivate(long contextId); - public static native boolean isGamepadButtonPressed(long contextId, int gamepadId, int button); - public static native boolean isGamepadButtonJustPressed(long contextId, int gamepadId, int button); - public static native boolean isGamepadButtonJustReleased(long contextId, int gamepadId, int button); - public static native float getGamepadAxis(long contextId, int gamepadId, int axis); - public static native boolean isGamepadConnected(long contextId, int gamepadId); - public static native int getGamepadConnectedCount(long contextId); public static native boolean checkHotSwapShortcut(long contextId); public static native long p2pCreateMesh(long contextId, int protocol, int port, P2pMeshConfig config); public static native long p2pJoinMesh(long contextId, int protocol, String address, int port, P2pMeshConfig config); diff --git a/sdks/kotlin/src/main/kotlin/com/goudengine/core/GoudGame.kt b/sdks/kotlin/src/main/kotlin/com/goudengine/core/GoudGame.kt index 05e9fcf36..4345ac102 100644 --- a/sdks/kotlin/src/main/kotlin/com/goudengine/core/GoudGame.kt +++ b/sdks/kotlin/src/main/kotlin/com/goudengine/core/GoudGame.kt @@ -137,8 +137,6 @@ class GoudGame internal constructor(internal val contextId: Long) : AutoCloseabl return com.goudengine.types.Vec2.fromNative(r) } - // -- Gamepad input -------------------------------------------------------- - fun isGamepadButtonPressed(gamepadId: Int, button: com.goudengine.input.GamepadButton): Boolean = GoudGameNative.isGamepadButtonPressed(contextId, gamepadId, button.value) @@ -151,12 +149,6 @@ class GoudGame internal constructor(internal val contextId: Long) : AutoCloseabl fun getGamepadAxis(gamepadId: Int, axis: com.goudengine.input.GamepadAxis): Float = GoudGameNative.getGamepadAxis(contextId, gamepadId, axis.value) - fun isGamepadConnected(gamepadId: Int): Boolean = - GoudGameNative.isGamepadConnected(contextId, gamepadId) - - fun getGamepadConnectedCount(): Int = - GoudGameNative.getGamepadConnectedCount(contextId) - fun spawnEmpty(): com.goudengine.core.EntityHandle { val r = GoudGameNative.spawnEmpty(contextId) return com.goudengine.core.EntityHandle(r) @@ -303,6 +295,9 @@ class GoudGame internal constructor(internal val contextId: Long) : AutoCloseabl fun configureFog(enabled: Boolean, r: Float, g: Float, b: Float, density: Float): Boolean = GoudGameNative.configureFog(contextId, enabled, r, g, b, density) + fun configureFogLinear(enabled: Boolean, startDistance: Float, endDistance: Float, r: Float, g: Float, b: Float): Boolean = + GoudGameNative.configureFogLinear(contextId, enabled, startDistance, endDistance, r, g, b) + fun setFogEnabled(enabled: Boolean): Boolean = GoudGameNative.setFogEnabled(contextId, enabled) diff --git a/sdks/python/goudengine/generated/_ffi.py b/sdks/python/goudengine/generated/_ffi.py index 58efe3bfb..3fb6881d1 100644 --- a/sdks/python/goudengine/generated/_ffi.py +++ b/sdks/python/goudengine/generated/_ffi.py @@ -699,6 +699,8 @@ def _setup(): _lib.goud_renderer3d_configure_skybox.restype = ctypes.c_bool _lib.goud_renderer3d_configure_fog.argtypes = [GoudContextId, ctypes.c_bool, ctypes.c_float, ctypes.c_float, ctypes.c_float, ctypes.c_float] _lib.goud_renderer3d_configure_fog.restype = ctypes.c_bool + _lib.goud_renderer3d_configure_fog_linear.argtypes = [GoudContextId, ctypes.c_bool, ctypes.c_float, ctypes.c_float, ctypes.c_float, ctypes.c_float, ctypes.c_float] + _lib.goud_renderer3d_configure_fog_linear.restype = ctypes.c_bool _lib.goud_renderer3d_set_fog_enabled.argtypes = [GoudContextId, ctypes.c_bool] _lib.goud_renderer3d_set_fog_enabled.restype = ctypes.c_bool _lib.goud_renderer3d_set_frustum_culling_enabled.argtypes = [GoudContextId, ctypes.c_bool] @@ -921,6 +923,30 @@ def _setup(): _lib.goud_input_get_mouse_delta.restype = ctypes.c_bool _lib.goud_input_get_scroll_delta.argtypes = [GoudContextId, ctypes.POINTER(ctypes.c_float), ctypes.POINTER(ctypes.c_float)] _lib.goud_input_get_scroll_delta.restype = ctypes.c_bool + _lib.goud_input_gamepad_button_pressed.argtypes = [GoudContextId, ctypes.c_uint32, ctypes.c_uint32] + _lib.goud_input_gamepad_button_pressed.restype = ctypes.c_bool + _lib.goud_input_gamepad_button_just_pressed.argtypes = [GoudContextId, ctypes.c_uint32, ctypes.c_uint32] + _lib.goud_input_gamepad_button_just_pressed.restype = ctypes.c_bool + _lib.goud_input_gamepad_button_just_released.argtypes = [GoudContextId, ctypes.c_uint32, ctypes.c_uint32] + _lib.goud_input_gamepad_button_just_released.restype = ctypes.c_bool + _lib.goud_input_gamepad_axis.argtypes = [GoudContextId, ctypes.c_uint32, ctypes.c_uint32] + _lib.goud_input_gamepad_axis.restype = ctypes.c_float + + # input_gamepad + _lib.goud_input_gamepad_connected.argtypes = [GoudContextId, ctypes.c_uint32] + _lib.goud_input_gamepad_connected.restype = ctypes.c_bool + _lib.goud_input_gamepad_connected_count.argtypes = [GoudContextId] + _lib.goud_input_gamepad_connected_count.restype = ctypes.c_uint32 + _lib.goud_input_gamepad_left_stick.argtypes = [GoudContextId, ctypes.c_uint32, ctypes.POINTER(ctypes.c_float), ctypes.POINTER(ctypes.c_float)] + _lib.goud_input_gamepad_left_stick.restype = ctypes.c_bool + _lib.goud_input_gamepad_right_stick.argtypes = [GoudContextId, ctypes.c_uint32, ctypes.POINTER(ctypes.c_float), ctypes.POINTER(ctypes.c_float)] + _lib.goud_input_gamepad_right_stick.restype = ctypes.c_bool + _lib.goud_input_gamepad_left_trigger.argtypes = [GoudContextId, ctypes.c_uint32] + _lib.goud_input_gamepad_left_trigger.restype = ctypes.c_float + _lib.goud_input_gamepad_right_trigger.argtypes = [GoudContextId, ctypes.c_uint32] + _lib.goud_input_gamepad_right_trigger.restype = ctypes.c_float + _lib.goud_input_gamepad_set_vibration.argtypes = [GoudContextId, ctypes.c_uint32, ctypes.c_float] + _lib.goud_input_gamepad_set_vibration.restype = ctypes.c_bool # input_touch _lib.goud_input_touch_count.argtypes = [GoudContextId] diff --git a/sdks/python/goudengine/generated/_game.py b/sdks/python/goudengine/generated/_game.py index dcfa243a0..a7236d9b3 100644 --- a/sdks/python/goudengine/generated/_game.py +++ b/sdks/python/goudengine/generated/_game.py @@ -266,6 +266,22 @@ def get_touch_delta(self, touch_id): self._lib.goud_input_touch_delta(self._ctx, touch_id, ctypes.byref(_dx), ctypes.byref(_dy)) return Vec2(_dx.value, _dy.value) + def is_gamepad_button_pressed(self, gamepad_id, button): + """Returns true if the specified gamepad button is currently pressed""" + return self._lib.goud_input_gamepad_button_pressed(self._ctx, gamepad_id, button) + + def is_gamepad_button_just_pressed(self, gamepad_id, button): + """Returns true if the specified gamepad button was just pressed this frame""" + return self._lib.goud_input_gamepad_button_just_pressed(self._ctx, gamepad_id, button) + + def is_gamepad_button_just_released(self, gamepad_id, button): + """Returns true if the specified gamepad button was just released this frame""" + return self._lib.goud_input_gamepad_button_just_released(self._ctx, gamepad_id, button) + + def get_gamepad_axis(self, gamepad_id, axis): + """Returns the current value of a gamepad analog axis (-1.0 to 1.0)""" + return self._lib.goud_input_gamepad_axis(self._ctx, gamepad_id, axis) + def spawn_empty(self): """Creates a new empty entity""" bits = self._lib.goud_entity_spawn_empty(self._ctx) @@ -480,6 +496,10 @@ def configure_fog(self, enabled, r, g, b, density): """Configures fog settings""" return self._lib.goud_renderer3d_configure_fog(self._ctx, enabled, r, g, b, density) + def configure_fog_linear(self, enabled, start_distance, end_distance, r, g, b): + """Configures linear fog with explicit start and end distances""" + return self._lib.goud_renderer3d_configure_fog_linear(self._ctx, enabled, start_distance, end_distance, r, g, b) + def set_fog_enabled(self, enabled): """Sets fog visibility""" return self._lib.goud_renderer3d_set_fog_enabled(self._ctx, enabled) diff --git a/sdks/python/goudengine/include/goud_engine.h b/sdks/python/goudengine/include/goud_engine.h index 85b7120a6..347f45ac7 100644 --- a/sdks/python/goudengine/include/goud_engine.h +++ b/sdks/python/goudengine/include/goud_engine.h @@ -40,6 +40,11 @@ extern "C" { */ #define PROTOCOL_VERSION 1 +/** + * Maximum number of gamepad slots supported by the engine. + */ +#define MAX_GAMEPAD_SLOTS 4 + /** * Sentinel value for an invalid entity ID. */ @@ -2273,6 +2278,11 @@ bool goud_renderer3d_configure_skybox(struct GoudContextId context_id, bool enab */ bool goud_renderer3d_configure_fog(struct GoudContextId context_id, bool enabled, float r, float g, float b, float density); +/** + * Configures linear fog with explicit start/end distances. + */ +bool goud_renderer3d_configure_fog_linear(struct GoudContextId context_id, bool enabled, float start_distance, float end_distance, float r, float g, float b); + /** * Sets fog enabled state. */ diff --git a/sdks/swift/Sources/CGoudEngine/include/goud_engine.h b/sdks/swift/Sources/CGoudEngine/include/goud_engine.h index 5a9c15936..347f45ac7 100644 --- a/sdks/swift/Sources/CGoudEngine/include/goud_engine.h +++ b/sdks/swift/Sources/CGoudEngine/include/goud_engine.h @@ -2278,6 +2278,11 @@ bool goud_renderer3d_configure_skybox(struct GoudContextId context_id, bool enab */ bool goud_renderer3d_configure_fog(struct GoudContextId context_id, bool enabled, float r, float g, float b, float density); +/** + * Configures linear fog with explicit start/end distances. + */ +bool goud_renderer3d_configure_fog_linear(struct GoudContextId context_id, bool enabled, float start_distance, float end_distance, float r, float g, float b); + /** * Sets fog enabled state. */ diff --git a/sdks/swift/Sources/GoudEngine/generated/GoudGame.g.swift b/sdks/swift/Sources/GoudEngine/generated/GoudGame.g.swift index 7700cf66f..5e1cf9fb5 100644 --- a/sdks/swift/Sources/GoudEngine/generated/GoudGame.g.swift +++ b/sdks/swift/Sources/GoudEngine/generated/GoudGame.g.swift @@ -163,6 +163,26 @@ public final class GoudGame { return goud_input_touch_just_released(_ctx, touchId) } + /// Returns true if the specified gamepad button is currently pressed + public func isGamepadButtonPressed(gamepadId: UInt32, button: UInt32) -> Bool { + return goud_input_gamepad_button_pressed(_ctx, gamepadId, button) + } + + /// Returns true if the specified gamepad button was just pressed this frame + public func isGamepadButtonJustPressed(gamepadId: UInt32, button: UInt32) -> Bool { + return goud_input_gamepad_button_just_pressed(_ctx, gamepadId, button) + } + + /// Returns true if the specified gamepad button was just released this frame + public func isGamepadButtonJustReleased(gamepadId: UInt32, button: UInt32) -> Bool { + return goud_input_gamepad_button_just_released(_ctx, gamepadId, button) + } + + /// Returns the current value of a gamepad analog axis (-1.0 to 1.0) + public func getGamepadAxis(gamepadId: UInt32, axis: UInt32) -> Float { + return goud_input_gamepad_axis(_ctx, gamepadId, axis) + } + /// Creates a new empty entity public func spawnEmpty() -> Entity { return Entity(bits: goud_entity_spawn_empty(_ctx)) @@ -278,6 +298,11 @@ public final class GoudGame { return goud_renderer3d_configure_fog(_ctx, enabled, r, g, b, density) } + /// Configures linear fog with explicit start and end distances + public func configureFogLinear(enabled: Bool, startDistance: Float, endDistance: Float, r: Float, g: Float, b: Float) -> Bool { + return goud_renderer3d_configure_fog_linear(_ctx, enabled, startDistance, endDistance, r, g, b) + } + /// Sets fog visibility public func setFogEnabled(enabled: Bool) -> Bool { return goud_renderer3d_set_fog_enabled(_ctx, enabled) diff --git a/sdks/typescript/src/generated/node/index.g.ts b/sdks/typescript/src/generated/node/index.g.ts index 965f87adf..8bbbb835c 100644 --- a/sdks/typescript/src/generated/node/index.g.ts +++ b/sdks/typescript/src/generated/node/index.g.ts @@ -372,6 +372,26 @@ export class GoudGame implements IGoudGame { return (this.native as any).getTouchDelta(touchId); } + /** Returns true if the specified gamepad button is currently pressed */ + isGamepadButtonPressed(gamepadId: number, button: number): boolean { + return (this.native as any).isGamepadButtonPressed(gamepadId, button); + } + + /** Returns true if the specified gamepad button was just pressed this frame */ + isGamepadButtonJustPressed(gamepadId: number, button: number): boolean { + return (this.native as any).isGamepadButtonJustPressed(gamepadId, button); + } + + /** Returns true if the specified gamepad button was just released this frame */ + isGamepadButtonJustReleased(gamepadId: number, button: number): boolean { + return (this.native as any).isGamepadButtonJustReleased(gamepadId, button); + } + + /** Returns the current value of a gamepad analog axis (-1.0 to 1.0) */ + getGamepadAxis(gamepadId: number, axis: number): number { + return (this.native as any).getGamepadAxis(gamepadId, axis); + } + /** Creates a new empty entity */ spawnEmpty(): IEntity { return this.native.spawnEmpty() as unknown as IEntity; @@ -600,6 +620,11 @@ export class GoudGame implements IGoudGame { return this.native.configureFog(enabled, r, g, b, density); } + /** Configures linear fog with explicit start and end distances */ + configureFogLinear(enabled: boolean, startDistance: number, endDistance: number, r: number, g: number, b: number): boolean { + return (this.native as any).configureFogLinear(enabled, startDistance, endDistance, r, g, b); + } + /** Sets fog visibility */ setFogEnabled(enabled: boolean): boolean { return this.native.setFogEnabled(enabled); diff --git a/sdks/typescript/src/generated/types/engine.g.ts b/sdks/typescript/src/generated/types/engine.g.ts index 69d4b410b..791090fd1 100644 --- a/sdks/typescript/src/generated/types/engine.g.ts +++ b/sdks/typescript/src/generated/types/engine.g.ts @@ -234,6 +234,14 @@ export interface IGoudGame { isTouchJustReleased(touchId: number): boolean; /** Returns the movement delta for the given touch point since last frame */ getTouchDelta(touchId: number): IVec2; + /** Returns true if the specified gamepad button is currently pressed */ + isGamepadButtonPressed(gamepadId: number, button: number): boolean; + /** Returns true if the specified gamepad button was just pressed this frame */ + isGamepadButtonJustPressed(gamepadId: number, button: number): boolean; + /** Returns true if the specified gamepad button was just released this frame */ + isGamepadButtonJustReleased(gamepadId: number, button: number): boolean; + /** Returns the current value of a gamepad analog axis (-1.0 to 1.0) */ + getGamepadAxis(gamepadId: number, axis: number): number; /** Creates a new empty entity */ spawnEmpty(): IEntity; /** Destroys an entity and all its components */ @@ -324,6 +332,8 @@ export interface IGoudGame { configureSkybox(enabled: boolean, r: number, g: number, b: number, a: number): boolean; /** Configures fog settings */ configureFog(enabled: boolean, r: number, g: number, b: number, density: number): boolean; + /** Configures linear fog with explicit start and end distances */ + configureFogLinear(enabled: boolean, startDistance: number, endDistance: number, r: number, g: number, b: number): boolean; /** Sets fog visibility */ setFogEnabled(enabled: boolean): boolean; /** Enables or disables frustum culling */ diff --git a/sdks/typescript/src/generated/web/index.g.ts b/sdks/typescript/src/generated/web/index.g.ts index 5fc4d5122..99c5b08d8 100644 --- a/sdks/typescript/src/generated/web/index.g.ts +++ b/sdks/typescript/src/generated/web/index.g.ts @@ -750,6 +750,11 @@ export class GoudGame implements IGoudGame { /** Returns the movement delta for the given touch point since last frame */ getTouchDelta(_touchId: number): IVec2 { return { x: 0, y: 0 }; } + isGamepadButtonPressed(_gamepadId: number, _button: number): boolean { return false; } + isGamepadButtonJustPressed(_gamepadId: number, _button: number): boolean { return false; } + isGamepadButtonJustReleased(_gamepadId: number, _button: number): boolean { return false; } + getGamepadAxis(_gamepadId: number, _axis: number): number { return 0; } + /** Maps an action name to a key */ mapActionKey(action: string, key: number): boolean { return this.handle.map_action_key(action, key); } /** Returns true if the action is currently pressed */ @@ -1014,6 +1019,7 @@ export class GoudGame implements IGoudGame { setGridEnabled(_enabled: boolean): boolean { return false; } configureSkybox(_enabled: boolean, _r: number, _g: number, _b: number, _a: number): boolean { return false; } configureFog(_enabled: boolean, _r: number, _g: number, _b: number, _density: number): boolean { return false; } + configureFogLinear(_enabled: boolean, _startDistance: number, _endDistance: number, _r: number, _g: number, _b: number): boolean { return false; } setFogEnabled(_enabled: boolean): boolean { return false; } render3D(): boolean { return false; } setFrustumCullingEnabled(_enabled: boolean): number { return 0; }