From 01ae94cf07d49a97f72a6852e5fd37408cb0c0c0 Mon Sep 17 00:00:00 2001 From: Aram Hammoudeh Date: Fri, 3 Apr 2026 13:47:17 -0600 Subject: [PATCH 1/4] fix(renderer3d): static batch overflow renders objects invisible, expose missing config to SDK Static objects exceeding the 50k vertex batch budget were silently dropped from both the static batch AND the individual draw path, making them invisible. Track which objects are actually batched via a HashSet so overflow objects fall back to per-object draws. Also fixes: - Model transform changes (position/rotation/scale) now trigger static batch rebuild when the affected object is static - compute_bounding_sphere accepts floats_per_vertex parameter instead of hardcoding 8, fixing wrong bounding spheres for skinned models - Expose max_static_batch_vertices, max_bones_per_mesh, shadow_strength, and shadow_auto_disable_threshold to all SDKs via FFI - Remove per-frame clone of static_batch_groups in render loop Co-Authored-By: Claude Opus 4.6 (1M context) --- codegen/ffi_manifest.json | 70 +++++++- codegen/ffi_mapping.json | 8 + codegen/generated/goud_engine.h | 40 +++++ codegen/goud_sdk.schema.json | 112 +++++++++++++ .../renderer3d/environment/config_getters.rs | 35 ++++ .../renderer3d/environment/config_setters.rs | 80 +++++++++ goud_engine/src/jni/generated.rs | 156 ++++++++++++++++++ .../src/libs/graphics/renderer3d/core/mod.rs | 22 ++- .../renderer3d/core_models/lifecycle.rs | 7 + .../graphics/renderer3d/core_models/mod.rs | 2 +- .../graphics/renderer3d/core_primitives.rs | 2 +- .../graphics/renderer3d/core_static_batch.rs | 9 +- .../libs/graphics/renderer3d/render/mod.rs | 2 +- .../graphics/renderer3d/render_instanced.rs | 5 +- .../src/libs/graphics/renderer3d/types.rs | 11 +- .../sdk/game/instance/lua_bindings/tools.g.rs | 48 ++++++ .../goudengine/internal/GoudGameNative.java | 8 + sdks/csharp/generated/GoudGame.g.cs | 48 ++++++ sdks/csharp/generated/NativeMethods.g.cs | 24 +++ sdks/csharp/include/goud_engine.h | 40 +++++ sdks/go/goud/game.go | 40 +++++ sdks/go/include/goud_engine.h | 40 +++++ sdks/go/internal/ffi/ffi.go | 40 +++++ .../goudengine/internal/GoudGameNative.java | 8 + .../kotlin/com/goudengine/core/GoudGame.kt | 40 ++++- sdks/python/goudengine/generated/_ffi.py | 16 ++ sdks/python/goudengine/generated/_game.py | 32 ++++ sdks/python/goudengine/include/goud_engine.h | 40 +++++ .../Sources/CGoudEngine/include/goud_engine.h | 40 +++++ .../GoudEngine/generated/GoudGame.g.swift | 40 +++++ sdks/typescript/src/generated/node/index.g.ts | 40 +++++ .../src/generated/types/engine.g.ts | 16 ++ 32 files changed, 1098 insertions(+), 23 deletions(-) diff --git a/codegen/ffi_manifest.json b/codegen/ffi_manifest.json index 9b3db4ee8..377630b3e 100644 --- a/codegen/ffi_manifest.json +++ b/codegen/ffi_manifest.json @@ -3546,6 +3546,22 @@ "return_type": "bool", "is_unsafe": false }, + "goud_renderer3d_get_max_bones_per_mesh": { + "source_file": "ffi/renderer3d/environment/config_getters.rs", + "params": [ + "context_id: GoudContextId" + ], + "return_type": "i32", + "is_unsafe": false + }, + "goud_renderer3d_get_max_static_batch_vertices": { + "source_file": "ffi/renderer3d/environment/config_getters.rs", + "params": [ + "context_id: GoudContextId" + ], + "return_type": "i32", + "is_unsafe": false + }, "goud_renderer3d_get_min_instances_for_batching": { "source_file": "ffi/renderer3d/environment/config_getters.rs", "params": [ @@ -3587,6 +3603,14 @@ "return_type": "u32", "is_unsafe": false }, + "goud_renderer3d_get_shadow_auto_disable_threshold": { + "source_file": "ffi/renderer3d/environment/config_getters.rs", + "params": [ + "context_id: GoudContextId" + ], + "return_type": "i32", + "is_unsafe": false + }, "goud_renderer3d_get_shadow_bias": { "source_file": "ffi/renderer3d/environment/config_getters.rs", "params": [ @@ -3603,6 +3627,14 @@ "return_type": "i32", "is_unsafe": false }, + "goud_renderer3d_get_shadow_strength": { + "source_file": "ffi/renderer3d/environment/config_getters.rs", + "params": [ + "context_id: GoudContextId" + ], + "return_type": "f32", + "is_unsafe": false + }, "goud_renderer3d_get_shadows_enabled": { "source_file": "ffi/renderer3d/environment/config_getters.rs", "params": [ @@ -3972,6 +4004,24 @@ "return_type": "i32", "is_unsafe": false }, + "goud_renderer3d_set_max_bones_per_mesh": { + "source_file": "ffi/renderer3d/environment/config_setters.rs", + "params": [ + "context_id: GoudContextId", + "bones: u32" + ], + "return_type": "i32", + "is_unsafe": false + }, + "goud_renderer3d_set_max_static_batch_vertices": { + "source_file": "ffi/renderer3d/environment/config_setters.rs", + "params": [ + "context_id: GoudContextId", + "vertices: u32" + ], + "return_type": "i32", + "is_unsafe": false + }, "goud_renderer3d_set_min_instances_for_batching": { "source_file": "ffi/renderer3d/environment/config_setters.rs", "params": [ @@ -4105,6 +4155,15 @@ "return_type": "bool", "is_unsafe": false }, + "goud_renderer3d_set_shadow_auto_disable_threshold": { + "source_file": "ffi/renderer3d/environment/config_setters.rs", + "params": [ + "context_id: GoudContextId", + "threshold: u32" + ], + "return_type": "i32", + "is_unsafe": false + }, "goud_renderer3d_set_shadow_bias": { "source_file": "ffi/renderer3d/environment/config_setters.rs", "params": [ @@ -4123,6 +4182,15 @@ "return_type": "i32", "is_unsafe": false }, + "goud_renderer3d_set_shadow_strength": { + "source_file": "ffi/renderer3d/environment/config_setters.rs", + "params": [ + "context_id: GoudContextId", + "strength: f32" + ], + "return_type": "i32", + "is_unsafe": false + }, "goud_renderer3d_set_shadows_enabled": { "source_file": "ffi/renderer3d/environment/config_setters.rs", "params": [ @@ -6525,5 +6593,5 @@ "is_unsafe": false } }, - "total_count": 661 + "total_count": 669 } diff --git a/codegen/ffi_mapping.json b/codegen/ffi_mapping.json index 32e5df592..1301e4d51 100644 --- a/codegen/ffi_mapping.json +++ b/codegen/ffi_mapping.json @@ -690,6 +690,14 @@ "goud_renderer3d_get_shadow_map_size": {}, "goud_renderer3d_set_shadow_bias": {}, "goud_renderer3d_get_shadow_bias": {}, + "goud_renderer3d_set_max_static_batch_vertices": {}, + "goud_renderer3d_get_max_static_batch_vertices": {}, + "goud_renderer3d_set_max_bones_per_mesh": {}, + "goud_renderer3d_get_max_bones_per_mesh": {}, + "goud_renderer3d_set_shadow_strength": {}, + "goud_renderer3d_get_shadow_strength": {}, + "goud_renderer3d_set_shadow_auto_disable_threshold": {}, + "goud_renderer3d_get_shadow_auto_disable_threshold": {}, "goud_renderer3d_instantiate_model_batch": {}, "goud_renderer3d_set_model_positions_batch": {}, "goud_renderer3d_add_models_to_scene_batch": {}, diff --git a/codegen/generated/goud_engine.h b/codegen/generated/goud_engine.h index 347f45ac7..f5392336d 100644 --- a/codegen/generated/goud_engine.h +++ b/codegen/generated/goud_engine.h @@ -2388,6 +2388,26 @@ int32_t goud_renderer3d_get_shadow_map_size(struct GoudContextId context_id); */ float goud_renderer3d_get_shadow_bias(struct GoudContextId context_id); +/** + * Returns the maximum vertex count for static batching, or -1 on error. + */ +int32_t goud_renderer3d_get_max_static_batch_vertices(struct GoudContextId context_id); + +/** + * Returns the maximum bones per mesh, or -1 on error. + */ +int32_t goud_renderer3d_get_max_bones_per_mesh(struct GoudContextId context_id); + +/** + * Returns the shadow strength (0.0-1.0), or -1.0 on error. + */ +float goud_renderer3d_get_shadow_strength(struct GoudContextId context_id); + +/** + * Returns the vertex threshold for shadow auto-disable, or -1 on error. + */ +int32_t goud_renderer3d_get_shadow_auto_disable_threshold(struct GoudContextId context_id); + /** * Enables or disables frustum culling. */ @@ -2488,6 +2508,26 @@ int32_t goud_renderer3d_set_shadow_map_size(struct GoudContextId context_id, uin */ int32_t goud_renderer3d_set_shadow_bias(struct GoudContextId context_id, float bias); +/** + * Sets the maximum vertex count for static batching. + */ +int32_t goud_renderer3d_set_max_static_batch_vertices(struct GoudContextId context_id, uint32_t vertices); + +/** + * Sets the maximum bones per mesh for skeletal animation. + */ +int32_t goud_renderer3d_set_max_bones_per_mesh(struct GoudContextId context_id, uint32_t bones); + +/** + * Sets the shadow strength (darkness, 0.0 = invisible, 1.0 = fully opaque). + */ +int32_t goud_renderer3d_set_shadow_strength(struct GoudContextId context_id, float strength); + +/** + * Sets the vertex threshold at which shadows are automatically disabled. + */ +int32_t goud_renderer3d_set_shadow_auto_disable_threshold(struct GoudContextId context_id, uint32_t threshold); + /** * Returns the number of draw calls issued during the last `render()` call. */ diff --git a/codegen/goud_sdk.schema.json b/codegen/goud_sdk.schema.json index aa48d4c92..0871dca9f 100644 --- a/codegen/goud_sdk.schema.json +++ b/codegen/goud_sdk.schema.json @@ -5790,6 +5790,74 @@ "params": [], "returns": "f32" }, + { + "name": "setMaxStaticBatchVertices", + "doc": "Sets the maximum vertex count for static batching", + "params": [ + { + "name": "vertices", + "type": "u32" + } + ], + "returns": "i32" + }, + { + "name": "getMaxStaticBatchVertices", + "doc": "Returns the maximum vertex count for static batching", + "params": [], + "returns": "i32" + }, + { + "name": "setMaxBonesPerMesh", + "doc": "Sets the maximum bones per mesh for skeletal animation", + "params": [ + { + "name": "bones", + "type": "u32" + } + ], + "returns": "i32" + }, + { + "name": "getMaxBonesPerMesh", + "doc": "Returns the maximum bones per mesh", + "params": [], + "returns": "i32" + }, + { + "name": "setShadowStrength", + "doc": "Sets the shadow strength (darkness, 0.0-1.0)", + "params": [ + { + "name": "strength", + "type": "f32" + } + ], + "returns": "i32" + }, + { + "name": "getShadowStrength", + "doc": "Returns the shadow strength (0.0-1.0)", + "params": [], + "returns": "f32" + }, + { + "name": "setShadowAutoDisableThreshold", + "doc": "Sets the vertex threshold at which shadows are automatically disabled", + "params": [ + { + "name": "threshold", + "type": "u32" + } + ], + "returns": "i32" + }, + { + "name": "getShadowAutoDisableThreshold", + "doc": "Returns the vertex threshold for shadow auto-disable", + "params": [], + "returns": "i32" + }, { "name": "instantiateModelBatch", "doc": "Creates multiple instances of a model in one call", @@ -13158,6 +13226,50 @@ "ffi": "goud_renderer3d_get_shadow_bias", "returns": "f32" }, + "setMaxStaticBatchVertices": { + "ffi": "goud_renderer3d_set_max_static_batch_vertices", + "params": { + "vertices": "u32" + }, + "returns": "i32" + }, + "getMaxStaticBatchVertices": { + "ffi": "goud_renderer3d_get_max_static_batch_vertices", + "returns": "i32" + }, + "setMaxBonesPerMesh": { + "ffi": "goud_renderer3d_set_max_bones_per_mesh", + "params": { + "bones": "u32" + }, + "returns": "i32" + }, + "getMaxBonesPerMesh": { + "ffi": "goud_renderer3d_get_max_bones_per_mesh", + "returns": "i32" + }, + "setShadowStrength": { + "ffi": "goud_renderer3d_set_shadow_strength", + "params": { + "strength": "f32" + }, + "returns": "i32" + }, + "getShadowStrength": { + "ffi": "goud_renderer3d_get_shadow_strength", + "returns": "f32" + }, + "setShadowAutoDisableThreshold": { + "ffi": "goud_renderer3d_set_shadow_auto_disable_threshold", + "params": { + "threshold": "u32" + }, + "returns": "i32" + }, + "getShadowAutoDisableThreshold": { + "ffi": "goud_renderer3d_get_shadow_auto_disable_threshold", + "returns": "i32" + }, "instantiateModelBatch": { "ffi": "goud_renderer3d_instantiate_model_batch", "unsafe": true, diff --git a/goud_engine/src/ffi/renderer3d/environment/config_getters.rs b/goud_engine/src/ffi/renderer3d/environment/config_getters.rs index 151fadd72..5fe47c963 100644 --- a/goud_engine/src/ffi/renderer3d/environment/config_getters.rs +++ b/goud_engine/src/ffi/renderer3d/environment/config_getters.rs @@ -159,3 +159,38 @@ pub extern "C" fn goud_renderer3d_get_shadow_map_size(context_id: GoudContextId) pub extern "C" fn goud_renderer3d_get_shadow_bias(context_id: GoudContextId) -> f32 { with_renderer(context_id, |r| r.render_config().shadows.bias).unwrap_or(-1.0) } + +/// Returns the maximum vertex count for static batching, or -1 on error. +#[no_mangle] +pub extern "C" fn goud_renderer3d_get_max_static_batch_vertices(context_id: GoudContextId) -> i32 { + with_renderer(context_id, |r| { + r.render_config().batching.max_static_batch_vertices as i32 + }) + .unwrap_or(-1) +} + +/// Returns the maximum bones per mesh, or -1 on error. +#[no_mangle] +pub extern "C" fn goud_renderer3d_get_max_bones_per_mesh(context_id: GoudContextId) -> i32 { + with_renderer(context_id, |r| { + r.render_config().skinning.max_bones_per_mesh as i32 + }) + .unwrap_or(-1) +} + +/// Returns the shadow strength (0.0-1.0), or -1.0 on error. +#[no_mangle] +pub extern "C" fn goud_renderer3d_get_shadow_strength(context_id: GoudContextId) -> f32 { + with_renderer(context_id, |r| r.render_config().shadows.shadow_strength).unwrap_or(-1.0) +} + +/// Returns the vertex threshold for shadow auto-disable, or -1 on error. +#[no_mangle] +pub extern "C" fn goud_renderer3d_get_shadow_auto_disable_threshold( + context_id: GoudContextId, +) -> i32 { + with_renderer(context_id, |r| { + r.render_config().shadows.auto_disable_vertex_threshold as i32 + }) + .unwrap_or(-1) +} diff --git a/goud_engine/src/ffi/renderer3d/environment/config_setters.rs b/goud_engine/src/ffi/renderer3d/environment/config_setters.rs index 840ee65fd..66387c1ee 100644 --- a/goud_engine/src/ffi/renderer3d/environment/config_setters.rs +++ b/goud_engine/src/ffi/renderer3d/environment/config_setters.rs @@ -414,3 +414,83 @@ pub extern "C" fn goud_renderer3d_set_shadow_bias(context_id: GoudContextId, bia }) .unwrap_or(-1) } + +/// Sets the maximum vertex count for static batching. +#[no_mangle] +pub extern "C" fn goud_renderer3d_set_max_static_batch_vertices( + context_id: GoudContextId, + vertices: u32, +) -> i32 { + if context_id == GOUD_INVALID_CONTEXT_ID { + set_last_error(GoudError::InvalidContext); + return -1; + } + + with_renderer(context_id, |renderer| { + let mut config = renderer.render_config().clone(); + config.batching.max_static_batch_vertices = vertices.max(1) as usize; + renderer.set_render_config(config); + 0 + }) + .unwrap_or(-1) +} + +/// Sets the maximum bones per mesh for skeletal animation. +#[no_mangle] +pub extern "C" fn goud_renderer3d_set_max_bones_per_mesh( + context_id: GoudContextId, + bones: u32, +) -> i32 { + if context_id == GOUD_INVALID_CONTEXT_ID { + set_last_error(GoudError::InvalidContext); + return -1; + } + + with_renderer(context_id, |renderer| { + let mut config = renderer.render_config().clone(); + config.skinning.max_bones_per_mesh = bones.max(1); + renderer.set_render_config(config); + 0 + }) + .unwrap_or(-1) +} + +/// Sets the shadow strength (darkness, 0.0 = invisible, 1.0 = fully opaque). +#[no_mangle] +pub extern "C" fn goud_renderer3d_set_shadow_strength( + context_id: GoudContextId, + strength: f32, +) -> i32 { + if context_id == GOUD_INVALID_CONTEXT_ID { + set_last_error(GoudError::InvalidContext); + return -1; + } + + with_renderer(context_id, |renderer| { + let mut config = renderer.render_config().clone(); + config.shadows.shadow_strength = strength.clamp(0.0, 1.0); + renderer.set_render_config(config); + 0 + }) + .unwrap_or(-1) +} + +/// Sets the vertex threshold at which shadows are automatically disabled. +#[no_mangle] +pub extern "C" fn goud_renderer3d_set_shadow_auto_disable_threshold( + context_id: GoudContextId, + threshold: u32, +) -> i32 { + if context_id == GOUD_INVALID_CONTEXT_ID { + set_last_error(GoudError::InvalidContext); + return -1; + } + + with_renderer(context_id, |renderer| { + let mut config = renderer.render_config().clone(); + config.shadows.auto_disable_vertex_threshold = threshold as usize; + renderer.set_render_config(config); + 0 + }) + .unwrap_or(-1) +} diff --git a/goud_engine/src/jni/generated.rs b/goud_engine/src/jni/generated.rs index 6c0456c9a..395636b62 100644 --- a/goud_engine/src/jni/generated.rs +++ b/goud_engine/src/jni/generated.rs @@ -4332,6 +4332,162 @@ pub extern "system" fn Java_com_goudengine_internal_GoudGameNative_getShadowBias }) } +#[allow(non_snake_case)] +#[no_mangle] +pub extern "system" fn Java_com_goudengine_internal_GoudGameNative_setMaxStaticBatchVertices<'local>( + mut env: jni::JNIEnv<'local>, + _class: jni::objects::JClass<'local>, + contextId: jni::sys::jlong, + vertices: jni::sys::jint, +) -> jni::sys::jint { + crate::jni::helpers::catch_jni_panic(&mut env, "Java_com_goudengine_internal_GoudGameNative_setMaxStaticBatchVertices", 0, |env| -> crate::jni::helpers::JniCallResult { + crate::jni::helpers::prepare_call(env)?; + crate::jni::helpers::clear_last_error(); + let result = crate::ffi::renderer3d::goud_renderer3d_set_max_static_batch_vertices(goud_context_id_from_jlong(contextId), vertices as _); + if crate::jni::helpers::last_error_code() != 0 { + let _ = crate::jni::helpers::throw_engine_error(env, "goud_renderer3d_set_max_static_batch_vertices", Some(result as i64)); + return Err(()); + } + Ok(result as i32) + }) +} + +#[allow(non_snake_case)] +#[no_mangle] +pub extern "system" fn Java_com_goudengine_internal_GoudGameNative_getMaxStaticBatchVertices<'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_getMaxStaticBatchVertices", 0, |env| -> crate::jni::helpers::JniCallResult { + crate::jni::helpers::prepare_call(env)?; + crate::jni::helpers::clear_last_error(); + let result = crate::ffi::renderer3d::goud_renderer3d_get_max_static_batch_vertices(goud_context_id_from_jlong(contextId)); + if crate::jni::helpers::last_error_code() != 0 { + let _ = crate::jni::helpers::throw_engine_error(env, "goud_renderer3d_get_max_static_batch_vertices", Some(result as i64)); + return Err(()); + } + Ok(result as i32) + }) +} + +#[allow(non_snake_case)] +#[no_mangle] +pub extern "system" fn Java_com_goudengine_internal_GoudGameNative_setMaxBonesPerMesh<'local>( + mut env: jni::JNIEnv<'local>, + _class: jni::objects::JClass<'local>, + contextId: jni::sys::jlong, + bones: jni::sys::jint, +) -> jni::sys::jint { + crate::jni::helpers::catch_jni_panic(&mut env, "Java_com_goudengine_internal_GoudGameNative_setMaxBonesPerMesh", 0, |env| -> crate::jni::helpers::JniCallResult { + crate::jni::helpers::prepare_call(env)?; + crate::jni::helpers::clear_last_error(); + let result = crate::ffi::renderer3d::goud_renderer3d_set_max_bones_per_mesh(goud_context_id_from_jlong(contextId), bones as _); + if crate::jni::helpers::last_error_code() != 0 { + let _ = crate::jni::helpers::throw_engine_error(env, "goud_renderer3d_set_max_bones_per_mesh", Some(result as i64)); + return Err(()); + } + Ok(result as i32) + }) +} + +#[allow(non_snake_case)] +#[no_mangle] +pub extern "system" fn Java_com_goudengine_internal_GoudGameNative_getMaxBonesPerMesh<'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_getMaxBonesPerMesh", 0, |env| -> crate::jni::helpers::JniCallResult { + crate::jni::helpers::prepare_call(env)?; + crate::jni::helpers::clear_last_error(); + let result = crate::ffi::renderer3d::goud_renderer3d_get_max_bones_per_mesh(goud_context_id_from_jlong(contextId)); + if crate::jni::helpers::last_error_code() != 0 { + let _ = crate::jni::helpers::throw_engine_error(env, "goud_renderer3d_get_max_bones_per_mesh", Some(result as i64)); + return Err(()); + } + Ok(result as i32) + }) +} + +#[allow(non_snake_case)] +#[no_mangle] +pub extern "system" fn Java_com_goudengine_internal_GoudGameNative_setShadowStrength<'local>( + mut env: jni::JNIEnv<'local>, + _class: jni::objects::JClass<'local>, + contextId: jni::sys::jlong, + strength: jni::sys::jfloat, +) -> jni::sys::jint { + crate::jni::helpers::catch_jni_panic(&mut env, "Java_com_goudengine_internal_GoudGameNative_setShadowStrength", 0, |env| -> crate::jni::helpers::JniCallResult { + crate::jni::helpers::prepare_call(env)?; + crate::jni::helpers::clear_last_error(); + let result = crate::ffi::renderer3d::goud_renderer3d_set_shadow_strength(goud_context_id_from_jlong(contextId), strength as _); + if crate::jni::helpers::last_error_code() != 0 { + let _ = crate::jni::helpers::throw_engine_error(env, "goud_renderer3d_set_shadow_strength", Some(result as i64)); + return Err(()); + } + Ok(result as i32) + }) +} + +#[allow(non_snake_case)] +#[no_mangle] +pub extern "system" fn Java_com_goudengine_internal_GoudGameNative_getShadowStrength<'local>( + mut env: jni::JNIEnv<'local>, + _class: jni::objects::JClass<'local>, + contextId: jni::sys::jlong, +) -> jni::sys::jfloat { + crate::jni::helpers::catch_jni_panic(&mut env, "Java_com_goudengine_internal_GoudGameNative_getShadowStrength", 0.0, |env| -> crate::jni::helpers::JniCallResult { + crate::jni::helpers::prepare_call(env)?; + crate::jni::helpers::clear_last_error(); + let result = crate::ffi::renderer3d::goud_renderer3d_get_shadow_strength(goud_context_id_from_jlong(contextId)); + if crate::jni::helpers::last_error_code() != 0 { + let _ = crate::jni::helpers::throw_engine_error(env, "goud_renderer3d_get_shadow_strength", None); + return Err(()); + } + Ok(result) + }) +} + +#[allow(non_snake_case)] +#[no_mangle] +pub extern "system" fn Java_com_goudengine_internal_GoudGameNative_setShadowAutoDisableThreshold<'local>( + mut env: jni::JNIEnv<'local>, + _class: jni::objects::JClass<'local>, + contextId: jni::sys::jlong, + threshold: jni::sys::jint, +) -> jni::sys::jint { + crate::jni::helpers::catch_jni_panic(&mut env, "Java_com_goudengine_internal_GoudGameNative_setShadowAutoDisableThreshold", 0, |env| -> crate::jni::helpers::JniCallResult { + crate::jni::helpers::prepare_call(env)?; + crate::jni::helpers::clear_last_error(); + let result = crate::ffi::renderer3d::goud_renderer3d_set_shadow_auto_disable_threshold(goud_context_id_from_jlong(contextId), threshold as _); + if crate::jni::helpers::last_error_code() != 0 { + let _ = crate::jni::helpers::throw_engine_error(env, "goud_renderer3d_set_shadow_auto_disable_threshold", Some(result as i64)); + return Err(()); + } + Ok(result as i32) + }) +} + +#[allow(non_snake_case)] +#[no_mangle] +pub extern "system" fn Java_com_goudengine_internal_GoudGameNative_getShadowAutoDisableThreshold<'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_getShadowAutoDisableThreshold", 0, |env| -> crate::jni::helpers::JniCallResult { + crate::jni::helpers::prepare_call(env)?; + crate::jni::helpers::clear_last_error(); + let result = crate::ffi::renderer3d::goud_renderer3d_get_shadow_auto_disable_threshold(goud_context_id_from_jlong(contextId)); + if crate::jni::helpers::last_error_code() != 0 { + let _ = crate::jni::helpers::throw_engine_error(env, "goud_renderer3d_get_shadow_auto_disable_threshold", Some(result as i64)); + return Err(()); + } + Ok(result as i32) + }) +} + #[allow(non_snake_case)] #[no_mangle] pub extern "system" fn Java_com_goudengine_internal_GoudGameNative_instantiateModelBatch<'local>( diff --git a/goud_engine/src/libs/graphics/renderer3d/core/mod.rs b/goud_engine/src/libs/graphics/renderer3d/core/mod.rs index 26aec5d8f..8635f766f 100644 --- a/goud_engine/src/libs/graphics/renderer3d/core/mod.rs +++ b/goud_engine/src/libs/graphics/renderer3d/core/mod.rs @@ -150,6 +150,9 @@ pub struct Renderer3D { pub(in crate::libs::graphics::renderer3d) static_batch_groups: Vec, /// Total vertex count in the static batch buffer. pub(in crate::libs::graphics::renderer3d) static_batch_vertex_count: u32, + /// Object IDs that were actually included in the static batch (not overflowed). + /// Used to distinguish batched objects from overflow objects that need individual draws. + pub(in crate::libs::graphics::renderer3d) static_batched_ids: std::collections::HashSet, } // StaticBatchGroup is defined in core_static_batch.rs @@ -342,16 +345,29 @@ impl Renderer3D { static_batch_buffer: None, static_batch_groups: Vec::new(), static_batch_vertex_count: 0, + static_batched_ids: std::collections::HashSet::new(), }) } pub fn set_object_position(&mut self, id: u32, x: f32, y: f32, z: f32) -> bool { - self.mutate_object(id, |obj| obj.position = Vector3::new(x, y, z)) + let ok = self.mutate_object(id, |obj| obj.position = Vector3::new(x, y, z)); + if ok && self.objects.get(&id).is_some_and(|o| o.is_static) { + self.static_batch_dirty = true; + } + ok } pub fn set_object_rotation(&mut self, id: u32, x: f32, y: f32, z: f32) -> bool { - self.mutate_object(id, |obj| obj.rotation = Vector3::new(x, y, z)) + let ok = self.mutate_object(id, |obj| obj.rotation = Vector3::new(x, y, z)); + if ok && self.objects.get(&id).is_some_and(|o| o.is_static) { + self.static_batch_dirty = true; + } + ok } pub fn set_object_scale(&mut self, id: u32, x: f32, y: f32, z: f32) -> bool { - self.mutate_object(id, |obj| obj.scale = Vector3::new(x, y, z)) + let ok = self.mutate_object(id, |obj| obj.scale = Vector3::new(x, y, z)); + if ok && self.objects.get(&id).is_some_and(|o| o.is_static) { + self.static_batch_dirty = true; + } + ok } /// Mark an object as static (transform never changes) or dynamic. diff --git a/goud_engine/src/libs/graphics/renderer3d/core_models/lifecycle.rs b/goud_engine/src/libs/graphics/renderer3d/core_models/lifecycle.rs index 492371a69..6a16ed2f5 100644 --- a/goud_engine/src/libs/graphics/renderer3d/core_models/lifecycle.rs +++ b/goud_engine/src/libs/graphics/renderer3d/core_models/lifecycle.rs @@ -291,11 +291,18 @@ impl Renderer3D { if obj_ids.is_empty() { return false; } + let mut has_static = false; for obj_id in obj_ids { if let Some(obj) = self.objects.get_mut(&obj_id) { f(obj); + if obj.is_static { + has_static = true; + } } } + if has_static { + self.static_batch_dirty = true; + } true } } diff --git a/goud_engine/src/libs/graphics/renderer3d/core_models/mod.rs b/goud_engine/src/libs/graphics/renderer3d/core_models/mod.rs index 8cf8c9bc4..53d787f82 100644 --- a/goud_engine/src/libs/graphics/renderer3d/core_models/mod.rs +++ b/goud_engine/src/libs/graphics/renderer3d/core_models/mod.rs @@ -121,7 +121,7 @@ impl Renderer3D { self.next_object_id = 1; } let tri_vert_count = verts.len() / floats_per_vertex; - let bounds = super::types::compute_bounding_sphere(&verts); + let bounds = super::types::compute_bounding_sphere(&verts, floats_per_vertex); self.objects.insert( object_id, Object3D { diff --git a/goud_engine/src/libs/graphics/renderer3d/core_primitives.rs b/goud_engine/src/libs/graphics/renderer3d/core_primitives.rs index fa77ba8a7..548b5254e 100644 --- a/goud_engine/src/libs/graphics/renderer3d/core_primitives.rs +++ b/goud_engine/src/libs/graphics/renderer3d/core_primitives.rs @@ -35,7 +35,7 @@ impl Renderer3D { self.next_object_id = 1; // Skip 0 which may be used as invalid sentinel } - let bounds = compute_bounding_sphere(&vertices); + let bounds = compute_bounding_sphere(&vertices, 8); self.objects.insert( id, Object3D { diff --git a/goud_engine/src/libs/graphics/renderer3d/core_static_batch.rs b/goud_engine/src/libs/graphics/renderer3d/core_static_batch.rs index 5182b9342..628aad085 100644 --- a/goud_engine/src/libs/graphics/renderer3d/core_static_batch.rs +++ b/goud_engine/src/libs/graphics/renderer3d/core_static_batch.rs @@ -31,6 +31,7 @@ impl Renderer3D { } self.static_batch_groups.clear(); self.static_batch_vertex_count = 0; + self.static_batched_ids.clear(); self.static_batch_dirty = false; // Collect static object IDs with their sort key. @@ -85,11 +86,16 @@ impl Renderer3D { let obj_vert_count = obj.vertices.len() / FPV; if (all_verts.len() / FPV) + obj_vert_count > max_verts { log::warn!( - "Static batch vertex limit ({max_verts}) reached, skipping remaining objects" + "Static batch vertex limit ({max_verts}) reached; \ + {}/{} static objects batched, remainder uses individual draws", + self.static_batched_ids.len(), + entries.len(), ); break; } + self.static_batched_ids.insert(obj_id); + // Build model matrix and bake transform into vertices. let model = Self::create_model_matrix(obj.position, obj.rotation, obj.scale); let normal_matrix = Self::normal_matrix_from_model(&model); @@ -157,6 +163,7 @@ impl Renderer3D { log::error!("Failed to create static batch buffer: {e}"); self.static_batch_groups.clear(); self.static_batch_vertex_count = 0; + self.static_batched_ids.clear(); } } } diff --git a/goud_engine/src/libs/graphics/renderer3d/render/mod.rs b/goud_engine/src/libs/graphics/renderer3d/render/mod.rs index 35bd5fdea..c24aed1a9 100644 --- a/goud_engine/src/libs/graphics/renderer3d/render/mod.rs +++ b/goud_engine/src/libs/graphics/renderer3d/render/mod.rs @@ -202,7 +202,7 @@ impl Renderer3D { if skinned_obj_ids.contains(&id) { continue; } - if has_static_batch && obj.is_static { + if has_static_batch && self.static_batched_ids.contains(&id) { continue; } if let Some(filter) = scene_obj_filter { diff --git a/goud_engine/src/libs/graphics/renderer3d/render_instanced.rs b/goud_engine/src/libs/graphics/renderer3d/render_instanced.rs index 5d9851b07..8458c7de4 100644 --- a/goud_engine/src/libs/graphics/renderer3d/render_instanced.rs +++ b/goud_engine/src/libs/graphics/renderer3d/render_instanced.rs @@ -186,8 +186,8 @@ impl Renderer3D { self.backend .set_uniform_mat4(self.uniforms.model, &identity); - // Snapshot groups to avoid borrow conflict with &mut self. - let groups = self.static_batch_groups.clone(); + // Temporarily take groups to avoid borrow conflict with &mut self. + let groups = std::mem::take(&mut self.static_batch_groups); let batch_buf = match self.static_batch_buffer { Some(buf) => buf, None => { @@ -229,5 +229,6 @@ impl Renderer3D { ); self.stats.draw_calls += 1; } + self.static_batch_groups = groups; } } diff --git a/goud_engine/src/libs/graphics/renderer3d/types.rs b/goud_engine/src/libs/graphics/renderer3d/types.rs index 66834935b..2479b65ea 100644 --- a/goud_engine/src/libs/graphics/renderer3d/types.rs +++ b/goud_engine/src/libs/graphics/renderer3d/types.rs @@ -197,12 +197,13 @@ impl Default for BoundingSphere { } } -/// Compute a bounding sphere from a flat vertex buffer (8 floats per vertex: pos+normal+uv). +/// Compute a bounding sphere from a flat vertex buffer with the given stride. pub(in crate::libs::graphics::renderer3d) fn compute_bounding_sphere( vertices: &[f32], + floats_per_vertex: usize, ) -> BoundingSphere { - const FPV: usize = 8; - let vert_count = vertices.len() / FPV; + let fpv = floats_per_vertex; + let vert_count = vertices.len() / fpv; if vert_count == 0 { return BoundingSphere::default(); } @@ -211,7 +212,7 @@ pub(in crate::libs::graphics::renderer3d) fn compute_bounding_sphere( let mut min = Vector3::new(f32::MAX, f32::MAX, f32::MAX); let mut max = Vector3::new(f32::MIN, f32::MIN, f32::MIN); for i in 0..vert_count { - let base = i * FPV; + let base = i * fpv; let p = Vector3::new(vertices[base], vertices[base + 1], vertices[base + 2]); min.x = min.x.min(p.x); min.y = min.y.min(p.y); @@ -225,7 +226,7 @@ pub(in crate::libs::graphics::renderer3d) fn compute_bounding_sphere( // Compute radius as max distance from center. let mut radius_sq = 0.0f32; for i in 0..vert_count { - let base = i * FPV; + let base = i * fpv; let p = Vector3::new(vertices[base], vertices[base + 1], vertices[base + 2]); let d = p - center; radius_sq = radius_sq.max(d.x * d.x + d.y * d.y + d.z * d.z); 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 39e7ea6ec..3fc30050d 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 @@ -207,11 +207,15 @@ use crate::ffi::renderer3d::goud_renderer3d_get_grid_alpha; use crate::ffi::renderer3d::goud_renderer3d_get_instanced_draw_calls; use crate::ffi::renderer3d::goud_renderer3d_get_instancing_enabled; use crate::ffi::renderer3d::goud_renderer3d_get_material_sorting_enabled; +use crate::ffi::renderer3d::goud_renderer3d_get_max_bones_per_mesh; +use crate::ffi::renderer3d::goud_renderer3d_get_max_static_batch_vertices; use crate::ffi::renderer3d::goud_renderer3d_get_min_instances_for_batching; use crate::ffi::renderer3d::goud_renderer3d_get_model_mesh_count; use crate::ffi::renderer3d::goud_renderer3d_get_object_material; +use crate::ffi::renderer3d::goud_renderer3d_get_shadow_auto_disable_threshold; use crate::ffi::renderer3d::goud_renderer3d_get_shadow_bias; use crate::ffi::renderer3d::goud_renderer3d_get_shadow_map_size; +use crate::ffi::renderer3d::goud_renderer3d_get_shadow_strength; use crate::ffi::renderer3d::goud_renderer3d_get_shadows_enabled; use crate::ffi::renderer3d::goud_renderer3d_get_shared_animation_eval; use crate::ffi::renderer3d::goud_renderer3d_get_skinning_mode; @@ -249,6 +253,8 @@ use crate::ffi::renderer3d::goud_renderer3d_set_grid_alpha; use crate::ffi::renderer3d::goud_renderer3d_set_grid_enabled; use crate::ffi::renderer3d::goud_renderer3d_set_instancing_enabled; use crate::ffi::renderer3d::goud_renderer3d_set_material_sorting_enabled; +use crate::ffi::renderer3d::goud_renderer3d_set_max_bones_per_mesh; +use crate::ffi::renderer3d::goud_renderer3d_set_max_static_batch_vertices; use crate::ffi::renderer3d::goud_renderer3d_set_min_instances_for_batching; use crate::ffi::renderer3d::goud_renderer3d_set_model_material; use crate::ffi::renderer3d::goud_renderer3d_set_model_position; @@ -260,8 +266,10 @@ use crate::ffi::renderer3d::goud_renderer3d_set_object_position; use crate::ffi::renderer3d::goud_renderer3d_set_object_rotation; use crate::ffi::renderer3d::goud_renderer3d_set_object_scale; use crate::ffi::renderer3d::goud_renderer3d_set_object_static; +use crate::ffi::renderer3d::goud_renderer3d_set_shadow_auto_disable_threshold; use crate::ffi::renderer3d::goud_renderer3d_set_shadow_bias; use crate::ffi::renderer3d::goud_renderer3d_set_shadow_map_size; +use crate::ffi::renderer3d::goud_renderer3d_set_shadow_strength; use crate::ffi::renderer3d::goud_renderer3d_set_shadows_enabled; use crate::ffi::renderer3d::goud_renderer3d_set_shared_animation_eval; use crate::ffi::renderer3d::goud_renderer3d_set_skinned_mesh_position; @@ -685,6 +693,46 @@ pub(crate) fn register_goud_game_tools(lua: &Lua, ctx_id: u64) -> LuaResult<()> Ok(goud_renderer3d_get_shadow_bias(ctx) as f64) })?; tbl.set("get_shadow_bias", f_get_shadow_bias)?; + // GoudGame.setMaxStaticBatchVertices + let f_set_max_static_batch_vertices = lua.create_function(move |_, arg0: i64| { + Ok(goud_renderer3d_set_max_static_batch_vertices(ctx, arg0 as u32) as i64) + })?; + tbl.set("set_max_static_batch_vertices", f_set_max_static_batch_vertices)?; + // GoudGame.getMaxStaticBatchVertices + let f_get_max_static_batch_vertices = lua.create_function(move |_, _: ()| { + Ok(goud_renderer3d_get_max_static_batch_vertices(ctx) as i64) + })?; + tbl.set("get_max_static_batch_vertices", f_get_max_static_batch_vertices)?; + // GoudGame.setMaxBonesPerMesh + let f_set_max_bones_per_mesh = lua.create_function(move |_, arg0: i64| { + Ok(goud_renderer3d_set_max_bones_per_mesh(ctx, arg0 as u32) as i64) + })?; + tbl.set("set_max_bones_per_mesh", f_set_max_bones_per_mesh)?; + // GoudGame.getMaxBonesPerMesh + let f_get_max_bones_per_mesh = lua.create_function(move |_, _: ()| { + Ok(goud_renderer3d_get_max_bones_per_mesh(ctx) as i64) + })?; + tbl.set("get_max_bones_per_mesh", f_get_max_bones_per_mesh)?; + // GoudGame.setShadowStrength + let f_set_shadow_strength = lua.create_function(move |_, arg0: f64| { + Ok(goud_renderer3d_set_shadow_strength(ctx, arg0 as f32) as i64) + })?; + tbl.set("set_shadow_strength", f_set_shadow_strength)?; + // GoudGame.getShadowStrength + let f_get_shadow_strength = lua.create_function(move |_, _: ()| { + Ok(goud_renderer3d_get_shadow_strength(ctx) as f64) + })?; + tbl.set("get_shadow_strength", f_get_shadow_strength)?; + // GoudGame.setShadowAutoDisableThreshold + let f_set_shadow_auto_disable_threshold = lua.create_function(move |_, arg0: i64| { + Ok(goud_renderer3d_set_shadow_auto_disable_threshold(ctx, arg0 as u32) as i64) + })?; + tbl.set("set_shadow_auto_disable_threshold", f_set_shadow_auto_disable_threshold)?; + // GoudGame.getShadowAutoDisableThreshold + let f_get_shadow_auto_disable_threshold = lua.create_function(move |_, _: ()| { + Ok(goud_renderer3d_get_shadow_auto_disable_threshold(ctx) as i64) + })?; + tbl.set("get_shadow_auto_disable_threshold", f_get_shadow_auto_disable_threshold)?; // GoudGame.getDrawCalls let f_get_draw_calls = lua.create_function(move |_, _: ()| { Ok(goud_renderer3d_get_draw_calls(ctx) as i64) 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 7f7a2ce29..e5cef2653 100644 --- a/goud_engine/tests/jni/java/com/goudengine/internal/GoudGameNative.java +++ b/goud_engine/tests/jni/java/com/goudengine/internal/GoudGameNative.java @@ -122,6 +122,14 @@ private GoudGameNative() {} public static native int getShadowMapSize(long contextId); public static native int setShadowBias(long contextId, float bias); public static native float getShadowBias(long contextId); + public static native int setMaxStaticBatchVertices(long contextId, int vertices); + public static native int getMaxStaticBatchVertices(long contextId); + public static native int setMaxBonesPerMesh(long contextId, int bones); + public static native int getMaxBonesPerMesh(long contextId); + public static native int setShadowStrength(long contextId, float strength); + public static native float getShadowStrength(long contextId); + public static native int setShadowAutoDisableThreshold(long contextId, int threshold); + public static native int getShadowAutoDisableThreshold(long contextId); public static native int instantiateModelBatch(long contextId, int sourceModelId, int count); public static native int setModelPositionsBatch(long contextId, int count); public static native int addModelsToSceneBatch(long contextId, int sceneId, int count); diff --git a/sdks/csharp/generated/GoudGame.g.cs b/sdks/csharp/generated/GoudGame.g.cs index baed05085..3a6b518b4 100644 --- a/sdks/csharp/generated/GoudGame.g.cs +++ b/sdks/csharp/generated/GoudGame.g.cs @@ -1014,6 +1014,54 @@ public float GetShadowBias() return NativeMethods.goud_renderer3d_get_shadow_bias(_ctx); } + /// Sets the maximum vertex count for static batching + public int SetMaxStaticBatchVertices(uint vertices) + { + return NativeMethods.goud_renderer3d_set_max_static_batch_vertices(_ctx, vertices); + } + + /// Returns the maximum vertex count for static batching + public int GetMaxStaticBatchVertices() + { + return NativeMethods.goud_renderer3d_get_max_static_batch_vertices(_ctx); + } + + /// Sets the maximum bones per mesh for skeletal animation + public int SetMaxBonesPerMesh(uint bones) + { + return NativeMethods.goud_renderer3d_set_max_bones_per_mesh(_ctx, bones); + } + + /// Returns the maximum bones per mesh + public int GetMaxBonesPerMesh() + { + return NativeMethods.goud_renderer3d_get_max_bones_per_mesh(_ctx); + } + + /// Sets the shadow strength (darkness, 0.0-1.0) + public int SetShadowStrength(float strength) + { + return NativeMethods.goud_renderer3d_set_shadow_strength(_ctx, strength); + } + + /// Returns the shadow strength (0.0-1.0) + public float GetShadowStrength() + { + return NativeMethods.goud_renderer3d_get_shadow_strength(_ctx); + } + + /// Sets the vertex threshold at which shadows are automatically disabled + public int SetShadowAutoDisableThreshold(uint threshold) + { + return NativeMethods.goud_renderer3d_set_shadow_auto_disable_threshold(_ctx, threshold); + } + + /// Returns the vertex threshold for shadow auto-disable + public int GetShadowAutoDisableThreshold() + { + return NativeMethods.goud_renderer3d_get_shadow_auto_disable_threshold(_ctx); + } + /// Returns the number of draw calls last frame public int GetDrawCalls() { diff --git a/sdks/csharp/generated/NativeMethods.g.cs b/sdks/csharp/generated/NativeMethods.g.cs index f693fb37e..7ce265de8 100644 --- a/sdks/csharp/generated/NativeMethods.g.cs +++ b/sdks/csharp/generated/NativeMethods.g.cs @@ -936,6 +936,30 @@ public static unsafe class NativeMethods [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] public static extern float goud_renderer3d_get_shadow_bias(GoudContextId context_id); + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern int goud_renderer3d_set_max_static_batch_vertices(GoudContextId context_id, uint vertices); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern int goud_renderer3d_get_max_static_batch_vertices(GoudContextId context_id); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern int goud_renderer3d_set_max_bones_per_mesh(GoudContextId context_id, uint bones); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern int goud_renderer3d_get_max_bones_per_mesh(GoudContextId context_id); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern int goud_renderer3d_set_shadow_strength(GoudContextId context_id, float strength); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern float goud_renderer3d_get_shadow_strength(GoudContextId context_id); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern int goud_renderer3d_set_shadow_auto_disable_threshold(GoudContextId context_id, uint threshold); + + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] + public static extern int goud_renderer3d_get_shadow_auto_disable_threshold(GoudContextId context_id); + [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)] public static extern int goud_renderer3d_instantiate_model_batch(GoudContextId context_id, uint source_model_id, uint count, ref uint out_ids); diff --git a/sdks/csharp/include/goud_engine.h b/sdks/csharp/include/goud_engine.h index 347f45ac7..f5392336d 100644 --- a/sdks/csharp/include/goud_engine.h +++ b/sdks/csharp/include/goud_engine.h @@ -2388,6 +2388,26 @@ int32_t goud_renderer3d_get_shadow_map_size(struct GoudContextId context_id); */ float goud_renderer3d_get_shadow_bias(struct GoudContextId context_id); +/** + * Returns the maximum vertex count for static batching, or -1 on error. + */ +int32_t goud_renderer3d_get_max_static_batch_vertices(struct GoudContextId context_id); + +/** + * Returns the maximum bones per mesh, or -1 on error. + */ +int32_t goud_renderer3d_get_max_bones_per_mesh(struct GoudContextId context_id); + +/** + * Returns the shadow strength (0.0-1.0), or -1.0 on error. + */ +float goud_renderer3d_get_shadow_strength(struct GoudContextId context_id); + +/** + * Returns the vertex threshold for shadow auto-disable, or -1 on error. + */ +int32_t goud_renderer3d_get_shadow_auto_disable_threshold(struct GoudContextId context_id); + /** * Enables or disables frustum culling. */ @@ -2488,6 +2508,26 @@ int32_t goud_renderer3d_set_shadow_map_size(struct GoudContextId context_id, uin */ int32_t goud_renderer3d_set_shadow_bias(struct GoudContextId context_id, float bias); +/** + * Sets the maximum vertex count for static batching. + */ +int32_t goud_renderer3d_set_max_static_batch_vertices(struct GoudContextId context_id, uint32_t vertices); + +/** + * Sets the maximum bones per mesh for skeletal animation. + */ +int32_t goud_renderer3d_set_max_bones_per_mesh(struct GoudContextId context_id, uint32_t bones); + +/** + * Sets the shadow strength (darkness, 0.0 = invisible, 1.0 = fully opaque). + */ +int32_t goud_renderer3d_set_shadow_strength(struct GoudContextId context_id, float strength); + +/** + * Sets the vertex threshold at which shadows are automatically disabled. + */ +int32_t goud_renderer3d_set_shadow_auto_disable_threshold(struct GoudContextId context_id, uint32_t threshold); + /** * Returns the number of draw calls issued during the last `render()` call. */ diff --git a/sdks/go/goud/game.go b/sdks/go/goud/game.go index cfa86396e..8be9ecf49 100644 --- a/sdks/go/goud/game.go +++ b/sdks/go/goud/game.go @@ -719,6 +719,46 @@ func (g *Game) GetShadowBias() float32 { return 0 } +// SetMaxStaticBatchVertices Sets the maximum vertex count for static batching +func (g *Game) SetMaxStaticBatchVertices(vertices uint32) int32 { + return 0 +} + +// GetMaxStaticBatchVertices Returns the maximum vertex count for static batching +func (g *Game) GetMaxStaticBatchVertices() int32 { + return 0 +} + +// SetMaxBonesPerMesh Sets the maximum bones per mesh for skeletal animation +func (g *Game) SetMaxBonesPerMesh(bones uint32) int32 { + return 0 +} + +// GetMaxBonesPerMesh Returns the maximum bones per mesh +func (g *Game) GetMaxBonesPerMesh() int32 { + return 0 +} + +// SetShadowStrength Sets the shadow strength (darkness, 0.0-1.0) +func (g *Game) SetShadowStrength(strength float32) int32 { + return 0 +} + +// GetShadowStrength Returns the shadow strength (0.0-1.0) +func (g *Game) GetShadowStrength() float32 { + return 0 +} + +// SetShadowAutoDisableThreshold Sets the vertex threshold at which shadows are automatically disabled +func (g *Game) SetShadowAutoDisableThreshold(threshold uint32) int32 { + return 0 +} + +// GetShadowAutoDisableThreshold Returns the vertex threshold for shadow auto-disable +func (g *Game) GetShadowAutoDisableThreshold() int32 { + return 0 +} + // InstantiateModelBatch Creates multiple instances of a model in one call func (g *Game) InstantiateModelBatch(sourceModelId uint32, count uint32) int32 { return 0 diff --git a/sdks/go/include/goud_engine.h b/sdks/go/include/goud_engine.h index 347f45ac7..f5392336d 100644 --- a/sdks/go/include/goud_engine.h +++ b/sdks/go/include/goud_engine.h @@ -2388,6 +2388,26 @@ int32_t goud_renderer3d_get_shadow_map_size(struct GoudContextId context_id); */ float goud_renderer3d_get_shadow_bias(struct GoudContextId context_id); +/** + * Returns the maximum vertex count for static batching, or -1 on error. + */ +int32_t goud_renderer3d_get_max_static_batch_vertices(struct GoudContextId context_id); + +/** + * Returns the maximum bones per mesh, or -1 on error. + */ +int32_t goud_renderer3d_get_max_bones_per_mesh(struct GoudContextId context_id); + +/** + * Returns the shadow strength (0.0-1.0), or -1.0 on error. + */ +float goud_renderer3d_get_shadow_strength(struct GoudContextId context_id); + +/** + * Returns the vertex threshold for shadow auto-disable, or -1 on error. + */ +int32_t goud_renderer3d_get_shadow_auto_disable_threshold(struct GoudContextId context_id); + /** * Enables or disables frustum culling. */ @@ -2488,6 +2508,26 @@ int32_t goud_renderer3d_set_shadow_map_size(struct GoudContextId context_id, uin */ int32_t goud_renderer3d_set_shadow_bias(struct GoudContextId context_id, float bias); +/** + * Sets the maximum vertex count for static batching. + */ +int32_t goud_renderer3d_set_max_static_batch_vertices(struct GoudContextId context_id, uint32_t vertices); + +/** + * Sets the maximum bones per mesh for skeletal animation. + */ +int32_t goud_renderer3d_set_max_bones_per_mesh(struct GoudContextId context_id, uint32_t bones); + +/** + * Sets the shadow strength (darkness, 0.0 = invisible, 1.0 = fully opaque). + */ +int32_t goud_renderer3d_set_shadow_strength(struct GoudContextId context_id, float strength); + +/** + * Sets the vertex threshold at which shadows are automatically disabled. + */ +int32_t goud_renderer3d_set_shadow_auto_disable_threshold(struct GoudContextId context_id, uint32_t threshold); + /** * Returns the number of draw calls issued during the last `render()` call. */ diff --git a/sdks/go/internal/ffi/ffi.go b/sdks/go/internal/ffi/ffi.go index 29c7c736e..70e1afd8f 100644 --- a/sdks/go/internal/ffi/ffi.go +++ b/sdks/go/internal/ffi/ffi.go @@ -2255,6 +2255,16 @@ func GoudRenderer3dGetMaterialSortingEnabled(context_id C.GoudContextId) bool { return bool(C.goud_renderer3d_get_material_sorting_enabled(context_id)) } +// GoudRenderer3dGetMaxBonesPerMesh wraps goud_renderer3d_get_max_bones_per_mesh. +func GoudRenderer3dGetMaxBonesPerMesh(context_id C.GoudContextId) int32 { + return int32(C.goud_renderer3d_get_max_bones_per_mesh(context_id)) +} + +// GoudRenderer3dGetMaxStaticBatchVertices wraps goud_renderer3d_get_max_static_batch_vertices. +func GoudRenderer3dGetMaxStaticBatchVertices(context_id C.GoudContextId) int32 { + return int32(C.goud_renderer3d_get_max_static_batch_vertices(context_id)) +} + // GoudRenderer3dGetMinInstancesForBatching wraps goud_renderer3d_get_min_instances_for_batching. func GoudRenderer3dGetMinInstancesForBatching(context_id C.GoudContextId) int32 { return int32(C.goud_renderer3d_get_min_instances_for_batching(context_id)) @@ -2293,6 +2303,11 @@ func GoudRenderer3dGetObjectMaterial(context_id C.GoudContextId, object_id uint3 return uint32(C.goud_renderer3d_get_object_material(context_id, C.uint32_t(object_id))) } +// GoudRenderer3dGetShadowAutoDisableThreshold wraps goud_renderer3d_get_shadow_auto_disable_threshold. +func GoudRenderer3dGetShadowAutoDisableThreshold(context_id C.GoudContextId) int32 { + return int32(C.goud_renderer3d_get_shadow_auto_disable_threshold(context_id)) +} + // GoudRenderer3dGetShadowBias wraps goud_renderer3d_get_shadow_bias. func GoudRenderer3dGetShadowBias(context_id C.GoudContextId) float32 { return float32(C.goud_renderer3d_get_shadow_bias(context_id)) @@ -2303,6 +2318,11 @@ func GoudRenderer3dGetShadowMapSize(context_id C.GoudContextId) int32 { return int32(C.goud_renderer3d_get_shadow_map_size(context_id)) } +// GoudRenderer3dGetShadowStrength wraps goud_renderer3d_get_shadow_strength. +func GoudRenderer3dGetShadowStrength(context_id C.GoudContextId) float32 { + return float32(C.goud_renderer3d_get_shadow_strength(context_id)) +} + // GoudRenderer3dGetShadowsEnabled wraps goud_renderer3d_get_shadows_enabled. func GoudRenderer3dGetShadowsEnabled(context_id C.GoudContextId) bool { return bool(C.goud_renderer3d_get_shadows_enabled(context_id)) @@ -2509,6 +2529,16 @@ func GoudRenderer3dSetMaterialSortingEnabled(context_id C.GoudContextId, enabled return int32(C.goud_renderer3d_set_material_sorting_enabled(context_id, C._Bool(enabled))) } +// GoudRenderer3dSetMaxBonesPerMesh wraps goud_renderer3d_set_max_bones_per_mesh. +func GoudRenderer3dSetMaxBonesPerMesh(context_id C.GoudContextId, bones uint32) int32 { + return int32(C.goud_renderer3d_set_max_bones_per_mesh(context_id, C.uint32_t(bones))) +} + +// GoudRenderer3dSetMaxStaticBatchVertices wraps goud_renderer3d_set_max_static_batch_vertices. +func GoudRenderer3dSetMaxStaticBatchVertices(context_id C.GoudContextId, vertices uint32) int32 { + return int32(C.goud_renderer3d_set_max_static_batch_vertices(context_id, C.uint32_t(vertices))) +} + // GoudRenderer3dSetMinInstancesForBatching wraps goud_renderer3d_set_min_instances_for_batching. func GoudRenderer3dSetMinInstancesForBatching(context_id C.GoudContextId, count uint32) int32 { return int32(C.goud_renderer3d_set_min_instances_for_batching(context_id, C.uint32_t(count))) @@ -2575,6 +2605,11 @@ func GoudRenderer3dSetObjectStatic(context_id C.GoudContextId, object_id uint32, return bool(C.goud_renderer3d_set_object_static(context_id, C.uint32_t(object_id), C._Bool(is_static))) } +// GoudRenderer3dSetShadowAutoDisableThreshold wraps goud_renderer3d_set_shadow_auto_disable_threshold. +func GoudRenderer3dSetShadowAutoDisableThreshold(context_id C.GoudContextId, threshold uint32) int32 { + return int32(C.goud_renderer3d_set_shadow_auto_disable_threshold(context_id, C.uint32_t(threshold))) +} + // GoudRenderer3dSetShadowBias wraps goud_renderer3d_set_shadow_bias. func GoudRenderer3dSetShadowBias(context_id C.GoudContextId, bias float32) int32 { return int32(C.goud_renderer3d_set_shadow_bias(context_id, C.float(bias))) @@ -2585,6 +2620,11 @@ func GoudRenderer3dSetShadowMapSize(context_id C.GoudContextId, size uint32) int return int32(C.goud_renderer3d_set_shadow_map_size(context_id, C.uint32_t(size))) } +// GoudRenderer3dSetShadowStrength wraps goud_renderer3d_set_shadow_strength. +func GoudRenderer3dSetShadowStrength(context_id C.GoudContextId, strength float32) int32 { + return int32(C.goud_renderer3d_set_shadow_strength(context_id, C.float(strength))) +} + // GoudRenderer3dSetShadowsEnabled wraps goud_renderer3d_set_shadows_enabled. func GoudRenderer3dSetShadowsEnabled(context_id C.GoudContextId, enabled bool) int32 { return int32(C.goud_renderer3d_set_shadows_enabled(context_id, C._Bool(enabled))) 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 7f7a2ce29..e5cef2653 100644 --- a/sdks/kotlin/src/main/java/com/goudengine/internal/GoudGameNative.java +++ b/sdks/kotlin/src/main/java/com/goudengine/internal/GoudGameNative.java @@ -122,6 +122,14 @@ private GoudGameNative() {} public static native int getShadowMapSize(long contextId); public static native int setShadowBias(long contextId, float bias); public static native float getShadowBias(long contextId); + public static native int setMaxStaticBatchVertices(long contextId, int vertices); + public static native int getMaxStaticBatchVertices(long contextId); + public static native int setMaxBonesPerMesh(long contextId, int bones); + public static native int getMaxBonesPerMesh(long contextId); + public static native int setShadowStrength(long contextId, float strength); + public static native float getShadowStrength(long contextId); + public static native int setShadowAutoDisableThreshold(long contextId, int threshold); + public static native int getShadowAutoDisableThreshold(long contextId); public static native int instantiateModelBatch(long contextId, int sourceModelId, int count); public static native int setModelPositionsBatch(long contextId, int count); public static native int addModelsToSceneBatch(long contextId, int sceneId, int count); 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 4345ac102..3834b5a31 100644 --- a/sdks/kotlin/src/main/kotlin/com/goudengine/core/GoudGame.kt +++ b/sdks/kotlin/src/main/kotlin/com/goudengine/core/GoudGame.kt @@ -137,17 +137,17 @@ class GoudGame internal constructor(internal val contextId: Long) : AutoCloseabl return com.goudengine.types.Vec2.fromNative(r) } - fun isGamepadButtonPressed(gamepadId: Int, button: com.goudengine.input.GamepadButton): Boolean = - GoudGameNative.isGamepadButtonPressed(contextId, gamepadId, button.value) + fun isGamepadButtonPressed(gamepadId: Int, button: Int): Boolean = + GoudGameNative.isGamepadButtonPressed(contextId, gamepadId, button) - fun isGamepadButtonJustPressed(gamepadId: Int, button: com.goudengine.input.GamepadButton): Boolean = - GoudGameNative.isGamepadButtonJustPressed(contextId, gamepadId, button.value) + fun isGamepadButtonJustPressed(gamepadId: Int, button: Int): Boolean = + GoudGameNative.isGamepadButtonJustPressed(contextId, gamepadId, button) - fun isGamepadButtonJustReleased(gamepadId: Int, button: com.goudengine.input.GamepadButton): Boolean = - GoudGameNative.isGamepadButtonJustReleased(contextId, gamepadId, button.value) + fun isGamepadButtonJustReleased(gamepadId: Int, button: Int): Boolean = + GoudGameNative.isGamepadButtonJustReleased(contextId, gamepadId, button) - fun getGamepadAxis(gamepadId: Int, axis: com.goudengine.input.GamepadAxis): Float = - GoudGameNative.getGamepadAxis(contextId, gamepadId, axis.value) + fun getGamepadAxis(gamepadId: Int, axis: Int): Float = + GoudGameNative.getGamepadAxis(contextId, gamepadId, axis) fun spawnEmpty(): com.goudengine.core.EntityHandle { val r = GoudGameNative.spawnEmpty(contextId) @@ -412,6 +412,30 @@ class GoudGame internal constructor(internal val contextId: Long) : AutoCloseabl fun getShadowBias(): Float = GoudGameNative.getShadowBias(contextId) + fun setMaxStaticBatchVertices(vertices: Int): Int = + GoudGameNative.setMaxStaticBatchVertices(contextId, vertices) + + fun getMaxStaticBatchVertices(): Int = + GoudGameNative.getMaxStaticBatchVertices(contextId) + + fun setMaxBonesPerMesh(bones: Int): Int = + GoudGameNative.setMaxBonesPerMesh(contextId, bones) + + fun getMaxBonesPerMesh(): Int = + GoudGameNative.getMaxBonesPerMesh(contextId) + + fun setShadowStrength(strength: Float): Int = + GoudGameNative.setShadowStrength(contextId, strength) + + fun getShadowStrength(): Float = + GoudGameNative.getShadowStrength(contextId) + + fun setShadowAutoDisableThreshold(threshold: Int): Int = + GoudGameNative.setShadowAutoDisableThreshold(contextId, threshold) + + fun getShadowAutoDisableThreshold(): Int = + GoudGameNative.getShadowAutoDisableThreshold(contextId) + fun getDrawCalls(): Int = GoudGameNative.getDrawCalls(contextId) diff --git a/sdks/python/goudengine/generated/_ffi.py b/sdks/python/goudengine/generated/_ffi.py index 3fb6881d1..43c253b03 100644 --- a/sdks/python/goudengine/generated/_ffi.py +++ b/sdks/python/goudengine/generated/_ffi.py @@ -779,6 +779,22 @@ def _setup(): _lib.goud_renderer3d_set_shadow_bias.restype = ctypes.c_int32 _lib.goud_renderer3d_get_shadow_bias.argtypes = [GoudContextId] _lib.goud_renderer3d_get_shadow_bias.restype = ctypes.c_float + _lib.goud_renderer3d_set_max_static_batch_vertices.argtypes = [GoudContextId, ctypes.c_uint32] + _lib.goud_renderer3d_set_max_static_batch_vertices.restype = ctypes.c_int32 + _lib.goud_renderer3d_get_max_static_batch_vertices.argtypes = [GoudContextId] + _lib.goud_renderer3d_get_max_static_batch_vertices.restype = ctypes.c_int32 + _lib.goud_renderer3d_set_max_bones_per_mesh.argtypes = [GoudContextId, ctypes.c_uint32] + _lib.goud_renderer3d_set_max_bones_per_mesh.restype = ctypes.c_int32 + _lib.goud_renderer3d_get_max_bones_per_mesh.argtypes = [GoudContextId] + _lib.goud_renderer3d_get_max_bones_per_mesh.restype = ctypes.c_int32 + _lib.goud_renderer3d_set_shadow_strength.argtypes = [GoudContextId, ctypes.c_float] + _lib.goud_renderer3d_set_shadow_strength.restype = ctypes.c_int32 + _lib.goud_renderer3d_get_shadow_strength.argtypes = [GoudContextId] + _lib.goud_renderer3d_get_shadow_strength.restype = ctypes.c_float + _lib.goud_renderer3d_set_shadow_auto_disable_threshold.argtypes = [GoudContextId, ctypes.c_uint32] + _lib.goud_renderer3d_set_shadow_auto_disable_threshold.restype = ctypes.c_int32 + _lib.goud_renderer3d_get_shadow_auto_disable_threshold.argtypes = [GoudContextId] + _lib.goud_renderer3d_get_shadow_auto_disable_threshold.restype = ctypes.c_int32 _lib.goud_renderer3d_instantiate_model_batch.argtypes = [GoudContextId, ctypes.c_uint32, ctypes.c_uint32, ctypes.POINTER(ctypes.c_uint32)] _lib.goud_renderer3d_instantiate_model_batch.restype = ctypes.c_int32 _lib.goud_renderer3d_set_model_positions_batch.argtypes = [GoudContextId, ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_float), ctypes.c_uint32] diff --git a/sdks/python/goudengine/generated/_game.py b/sdks/python/goudengine/generated/_game.py index a7236d9b3..6c3bd6aa2 100644 --- a/sdks/python/goudengine/generated/_game.py +++ b/sdks/python/goudengine/generated/_game.py @@ -652,6 +652,38 @@ def get_shadow_bias(self): """Returns the shadow depth bias""" return self._lib.goud_renderer3d_get_shadow_bias(self._ctx) + def set_max_static_batch_vertices(self, vertices): + """Sets the maximum vertex count for static batching""" + return self._lib.goud_renderer3d_set_max_static_batch_vertices(self._ctx, vertices) + + def get_max_static_batch_vertices(self): + """Returns the maximum vertex count for static batching""" + return self._lib.goud_renderer3d_get_max_static_batch_vertices(self._ctx) + + def set_max_bones_per_mesh(self, bones): + """Sets the maximum bones per mesh for skeletal animation""" + return self._lib.goud_renderer3d_set_max_bones_per_mesh(self._ctx, bones) + + def get_max_bones_per_mesh(self): + """Returns the maximum bones per mesh""" + return self._lib.goud_renderer3d_get_max_bones_per_mesh(self._ctx) + + def set_shadow_strength(self, strength): + """Sets the shadow strength (darkness, 0.0-1.0)""" + return self._lib.goud_renderer3d_set_shadow_strength(self._ctx, strength) + + def get_shadow_strength(self): + """Returns the shadow strength (0.0-1.0)""" + return self._lib.goud_renderer3d_get_shadow_strength(self._ctx) + + def set_shadow_auto_disable_threshold(self, threshold): + """Sets the vertex threshold at which shadows are automatically disabled""" + return self._lib.goud_renderer3d_set_shadow_auto_disable_threshold(self._ctx, threshold) + + def get_shadow_auto_disable_threshold(self): + """Returns the vertex threshold for shadow auto-disable""" + return self._lib.goud_renderer3d_get_shadow_auto_disable_threshold(self._ctx) + def instantiate_model_batch(self, source_model_id, count): """Creates multiple instances of a model in one call""" return self._lib.goud_renderer3d_instantiate_model_batch(self._ctx, source_model_id, count) diff --git a/sdks/python/goudengine/include/goud_engine.h b/sdks/python/goudengine/include/goud_engine.h index 347f45ac7..f5392336d 100644 --- a/sdks/python/goudengine/include/goud_engine.h +++ b/sdks/python/goudengine/include/goud_engine.h @@ -2388,6 +2388,26 @@ int32_t goud_renderer3d_get_shadow_map_size(struct GoudContextId context_id); */ float goud_renderer3d_get_shadow_bias(struct GoudContextId context_id); +/** + * Returns the maximum vertex count for static batching, or -1 on error. + */ +int32_t goud_renderer3d_get_max_static_batch_vertices(struct GoudContextId context_id); + +/** + * Returns the maximum bones per mesh, or -1 on error. + */ +int32_t goud_renderer3d_get_max_bones_per_mesh(struct GoudContextId context_id); + +/** + * Returns the shadow strength (0.0-1.0), or -1.0 on error. + */ +float goud_renderer3d_get_shadow_strength(struct GoudContextId context_id); + +/** + * Returns the vertex threshold for shadow auto-disable, or -1 on error. + */ +int32_t goud_renderer3d_get_shadow_auto_disable_threshold(struct GoudContextId context_id); + /** * Enables or disables frustum culling. */ @@ -2488,6 +2508,26 @@ int32_t goud_renderer3d_set_shadow_map_size(struct GoudContextId context_id, uin */ int32_t goud_renderer3d_set_shadow_bias(struct GoudContextId context_id, float bias); +/** + * Sets the maximum vertex count for static batching. + */ +int32_t goud_renderer3d_set_max_static_batch_vertices(struct GoudContextId context_id, uint32_t vertices); + +/** + * Sets the maximum bones per mesh for skeletal animation. + */ +int32_t goud_renderer3d_set_max_bones_per_mesh(struct GoudContextId context_id, uint32_t bones); + +/** + * Sets the shadow strength (darkness, 0.0 = invisible, 1.0 = fully opaque). + */ +int32_t goud_renderer3d_set_shadow_strength(struct GoudContextId context_id, float strength); + +/** + * Sets the vertex threshold at which shadows are automatically disabled. + */ +int32_t goud_renderer3d_set_shadow_auto_disable_threshold(struct GoudContextId context_id, uint32_t threshold); + /** * Returns the number of draw calls issued during the last `render()` call. */ diff --git a/sdks/swift/Sources/CGoudEngine/include/goud_engine.h b/sdks/swift/Sources/CGoudEngine/include/goud_engine.h index 347f45ac7..f5392336d 100644 --- a/sdks/swift/Sources/CGoudEngine/include/goud_engine.h +++ b/sdks/swift/Sources/CGoudEngine/include/goud_engine.h @@ -2388,6 +2388,26 @@ int32_t goud_renderer3d_get_shadow_map_size(struct GoudContextId context_id); */ float goud_renderer3d_get_shadow_bias(struct GoudContextId context_id); +/** + * Returns the maximum vertex count for static batching, or -1 on error. + */ +int32_t goud_renderer3d_get_max_static_batch_vertices(struct GoudContextId context_id); + +/** + * Returns the maximum bones per mesh, or -1 on error. + */ +int32_t goud_renderer3d_get_max_bones_per_mesh(struct GoudContextId context_id); + +/** + * Returns the shadow strength (0.0-1.0), or -1.0 on error. + */ +float goud_renderer3d_get_shadow_strength(struct GoudContextId context_id); + +/** + * Returns the vertex threshold for shadow auto-disable, or -1 on error. + */ +int32_t goud_renderer3d_get_shadow_auto_disable_threshold(struct GoudContextId context_id); + /** * Enables or disables frustum culling. */ @@ -2488,6 +2508,26 @@ int32_t goud_renderer3d_set_shadow_map_size(struct GoudContextId context_id, uin */ int32_t goud_renderer3d_set_shadow_bias(struct GoudContextId context_id, float bias); +/** + * Sets the maximum vertex count for static batching. + */ +int32_t goud_renderer3d_set_max_static_batch_vertices(struct GoudContextId context_id, uint32_t vertices); + +/** + * Sets the maximum bones per mesh for skeletal animation. + */ +int32_t goud_renderer3d_set_max_bones_per_mesh(struct GoudContextId context_id, uint32_t bones); + +/** + * Sets the shadow strength (darkness, 0.0 = invisible, 1.0 = fully opaque). + */ +int32_t goud_renderer3d_set_shadow_strength(struct GoudContextId context_id, float strength); + +/** + * Sets the vertex threshold at which shadows are automatically disabled. + */ +int32_t goud_renderer3d_set_shadow_auto_disable_threshold(struct GoudContextId context_id, uint32_t threshold); + /** * Returns the number of draw calls issued during the last `render()` call. */ diff --git a/sdks/swift/Sources/GoudEngine/generated/GoudGame.g.swift b/sdks/swift/Sources/GoudEngine/generated/GoudGame.g.swift index 5e1cf9fb5..5740568d3 100644 --- a/sdks/swift/Sources/GoudEngine/generated/GoudGame.g.swift +++ b/sdks/swift/Sources/GoudEngine/generated/GoudGame.g.swift @@ -493,6 +493,46 @@ public final class GoudGame { return goud_renderer3d_get_shadow_bias(_ctx) } + /// Sets the maximum vertex count for static batching + public func setMaxStaticBatchVertices(vertices: UInt32) -> Int32 { + return goud_renderer3d_set_max_static_batch_vertices(_ctx, vertices) + } + + /// Returns the maximum vertex count for static batching + public func getMaxStaticBatchVertices() -> Int32 { + return goud_renderer3d_get_max_static_batch_vertices(_ctx) + } + + /// Sets the maximum bones per mesh for skeletal animation + public func setMaxBonesPerMesh(bones: UInt32) -> Int32 { + return goud_renderer3d_set_max_bones_per_mesh(_ctx, bones) + } + + /// Returns the maximum bones per mesh + public func getMaxBonesPerMesh() -> Int32 { + return goud_renderer3d_get_max_bones_per_mesh(_ctx) + } + + /// Sets the shadow strength (darkness, 0.0-1.0) + public func setShadowStrength(strength: Float) -> Int32 { + return goud_renderer3d_set_shadow_strength(_ctx, strength) + } + + /// Returns the shadow strength (0.0-1.0) + public func getShadowStrength() -> Float { + return goud_renderer3d_get_shadow_strength(_ctx) + } + + /// Sets the vertex threshold at which shadows are automatically disabled + public func setShadowAutoDisableThreshold(threshold: UInt32) -> Int32 { + return goud_renderer3d_set_shadow_auto_disable_threshold(_ctx, threshold) + } + + /// Returns the vertex threshold for shadow auto-disable + public func getShadowAutoDisableThreshold() -> Int32 { + return goud_renderer3d_get_shadow_auto_disable_threshold(_ctx) + } + /// Returns the number of draw calls last frame public func getDrawCalls() -> Int32 { return goud_renderer3d_get_draw_calls(_ctx) diff --git a/sdks/typescript/src/generated/node/index.g.ts b/sdks/typescript/src/generated/node/index.g.ts index 8bbbb835c..40ab96e4a 100644 --- a/sdks/typescript/src/generated/node/index.g.ts +++ b/sdks/typescript/src/generated/node/index.g.ts @@ -815,6 +815,46 @@ export class GoudGame implements IGoudGame { return (this.native as any).getShadowBias(); } + /** Sets the maximum vertex count for static batching */ + setMaxStaticBatchVertices(vertices: number): number { + return (this.native as any).setMaxStaticBatchVertices(vertices); + } + + /** Returns the maximum vertex count for static batching */ + getMaxStaticBatchVertices(): number { + return (this.native as any).getMaxStaticBatchVertices(); + } + + /** Sets the maximum bones per mesh for skeletal animation */ + setMaxBonesPerMesh(bones: number): number { + return (this.native as any).setMaxBonesPerMesh(bones); + } + + /** Returns the maximum bones per mesh */ + getMaxBonesPerMesh(): number { + return (this.native as any).getMaxBonesPerMesh(); + } + + /** Sets the shadow strength (darkness, 0.0-1.0) */ + setShadowStrength(strength: number): number { + return (this.native as any).setShadowStrength(strength); + } + + /** Returns the shadow strength (0.0-1.0) */ + getShadowStrength(): number { + return (this.native as any).getShadowStrength(); + } + + /** Sets the vertex threshold at which shadows are automatically disabled */ + setShadowAutoDisableThreshold(threshold: number): number { + return (this.native as any).setShadowAutoDisableThreshold(threshold); + } + + /** Returns the vertex threshold for shadow auto-disable */ + getShadowAutoDisableThreshold(): number { + return (this.native as any).getShadowAutoDisableThreshold(); + } + /** Creates multiple instances of a model in one call */ instantiateModelBatch(sourceModelId: number, count: number): number { return (this.native as any).instantiateModelBatch(sourceModelId, count); diff --git a/sdks/typescript/src/generated/types/engine.g.ts b/sdks/typescript/src/generated/types/engine.g.ts index 791090fd1..9753894c1 100644 --- a/sdks/typescript/src/generated/types/engine.g.ts +++ b/sdks/typescript/src/generated/types/engine.g.ts @@ -410,6 +410,22 @@ export interface IGoudGame { setShadowBias(bias: number): number; /** Returns the shadow depth bias */ getShadowBias(): number; + /** Sets the maximum vertex count for static batching */ + setMaxStaticBatchVertices(vertices: number): number; + /** Returns the maximum vertex count for static batching */ + getMaxStaticBatchVertices(): number; + /** Sets the maximum bones per mesh for skeletal animation */ + setMaxBonesPerMesh(bones: number): number; + /** Returns the maximum bones per mesh */ + getMaxBonesPerMesh(): number; + /** Sets the shadow strength (darkness, 0.0-1.0) */ + setShadowStrength(strength: number): number; + /** Returns the shadow strength (0.0-1.0) */ + getShadowStrength(): number; + /** Sets the vertex threshold at which shadows are automatically disabled */ + setShadowAutoDisableThreshold(threshold: number): number; + /** Returns the vertex threshold for shadow auto-disable */ + getShadowAutoDisableThreshold(): number; /** Creates multiple instances of a model in one call */ instantiateModelBatch(sourceModelId: number, count: number): number; /** Sets positions for multiple models in one call */ From a38e746ddfcb98a8eef5fb96ff06d9f2996d55b9 Mon Sep 17 00:00:00 2001 From: Aram Hammoudeh Date: Fri, 3 Apr 2026 14:32:32 -0600 Subject: [PATCH 2/4] fix(ts-web): add stub methods for new config getters/setters in WASM SDK The IGoudGame interface gained 8 new methods but the web GoudGame class was missing stub implementations, causing a TS2420 compilation error in the WASM build CI lane. Co-Authored-By: Claude Opus 4.6 (1M context) --- sdks/typescript/src/generated/web/index.g.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sdks/typescript/src/generated/web/index.g.ts b/sdks/typescript/src/generated/web/index.g.ts index 99c5b08d8..612f4d38f 100644 --- a/sdks/typescript/src/generated/web/index.g.ts +++ b/sdks/typescript/src/generated/web/index.g.ts @@ -1209,6 +1209,10 @@ export class GoudGame implements IGoudGame { getSharedAnimationEval(): boolean { return false; } getSkinningMode(): number { return 0; } getStaticBatchingEnabled(): boolean { return false; } + getMaxStaticBatchVertices(): number { return 50000; } + getMaxBonesPerMesh(): number { return 128; } + getShadowStrength(): number { return 0.65; } + getShadowAutoDisableThreshold(): number { return 10000; } setAnimationLodDistance(_distance: number): number { return 0; } setAnimationLodSkipDistance(_distance: number): number { return 0; } @@ -1223,7 +1227,11 @@ export class GoudGame implements IGoudGame { setShadowBias(_bias: number): number { return 0; } setShadowMapSize(_size: number): number { return 0; } setShadowsEnabled(_enabled: boolean): number { return 0; } + setShadowStrength(_strength: number): number { return 0; } + setShadowAutoDisableThreshold(_threshold: number): number { return 0; } setStaticBatchingEnabled(_enabled: boolean): number { return 0; } + setMaxStaticBatchVertices(_vertices: number): number { return 0; } + setMaxBonesPerMesh(_bones: number): number { return 0; } // TODO: wasm coordinate origin -- these stub methods satisfy the IGoudGame interface setCoordinateOrigin(_origin: number): boolean { return true; } From 4bf852de1a9aa9a9a618b2d64779638c19e6a545 Mon Sep 17 00:00:00 2001 From: Aram Hammoudeh Date: Fri, 3 Apr 2026 14:43:32 -0600 Subject: [PATCH 3/4] fix(codegen): add WASM web stubs for new config getters/setters Added 8 stub methods to gen_ts_web.py so the web GoudGame class implements the new IGoudGame interface methods. Regenerated web/index.g.ts via codegen pipeline. Co-Authored-By: Claude Opus 4.6 (1M context) --- codegen/gen_ts_web.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/codegen/gen_ts_web.py b/codegen/gen_ts_web.py index 8c11b13bb..88cb42dc5 100644 --- a/codegen/gen_ts_web.py +++ b/codegen/gen_ts_web.py @@ -1326,6 +1326,10 @@ def gen_web_wrapper(): lines.append(" getSharedAnimationEval(): boolean { return false; }") lines.append(" getSkinningMode(): number { return 0; }") lines.append(" getStaticBatchingEnabled(): boolean { return false; }") + lines.append(" getMaxStaticBatchVertices(): number { return 50000; }") + lines.append(" getMaxBonesPerMesh(): number { return 128; }") + lines.append(" getShadowStrength(): number { return 0.65; }") + lines.append(" getShadowAutoDisableThreshold(): number { return 10000; }") lines.append("") lines.append(" setAnimationLodDistance(_distance: number): number { return 0; }") lines.append(" setAnimationLodSkipDistance(_distance: number): number { return 0; }") @@ -1340,7 +1344,11 @@ def gen_web_wrapper(): lines.append(" setShadowBias(_bias: number): number { return 0; }") lines.append(" setShadowMapSize(_size: number): number { return 0; }") lines.append(" setShadowsEnabled(_enabled: boolean): number { return 0; }") + lines.append(" setShadowStrength(_strength: number): number { return 0; }") + lines.append(" setShadowAutoDisableThreshold(_threshold: number): number { return 0; }") lines.append(" setStaticBatchingEnabled(_enabled: boolean): number { return 0; }") + lines.append(" setMaxStaticBatchVertices(_vertices: number): number { return 0; }") + lines.append(" setMaxBonesPerMesh(_bones: number): number { return 0; }") lines.append("") lines.append(" // TODO: wasm coordinate origin -- these stub methods satisfy the IGoudGame interface") lines.append(" setCoordinateOrigin(_origin: number): boolean { return true; }") From c5e0b0982dffbdd74ee780bf3c6febd660629d87 Mon Sep 17 00:00:00 2001 From: Aram Hammoudeh Date: Fri, 3 Apr 2026 15:12:48 -0600 Subject: [PATCH 4/4] fix(android): pass GamepadButton enum value as Int to SDK methods The Kotlin SDK gamepad methods take Int parameters but the Android example was passing GamepadButton enum directly. Append .value to extract the underlying Int. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../app/src/main/java/com/goudengine/flappybird/Bird.kt | 2 +- .../app/src/main/java/com/goudengine/flappybird/GameManager.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/android/flappy_bird/app/src/main/java/com/goudengine/flappybird/Bird.kt b/examples/android/flappy_bird/app/src/main/java/com/goudengine/flappybird/Bird.kt index 9d81b38f4..b44ef4730 100644 --- a/examples/android/flappy_bird/app/src/main/java/com/goudengine/flappybird/Bird.kt +++ b/examples/android/flappy_bird/app/src/main/java/com/goudengine/flappybird/Bird.kt @@ -68,7 +68,7 @@ class Bird(game: GoudGame) { if (game.isKeyJustPressed(Key.Space)) return true // Gamepad input: A/South button on gamepad 0. - if (game.isGamepadButtonJustPressed(0, GamepadButton.South)) return true + if (game.isGamepadButtonJustPressed(0, GamepadButton.South.value)) return true return false } diff --git a/examples/android/flappy_bird/app/src/main/java/com/goudengine/flappybird/GameManager.kt b/examples/android/flappy_bird/app/src/main/java/com/goudengine/flappybird/GameManager.kt index 7d5224e4c..a910f4e6e 100644 --- a/examples/android/flappy_bird/app/src/main/java/com/goudengine/flappybird/GameManager.kt +++ b/examples/android/flappy_bird/app/src/main/java/com/goudengine/flappybird/GameManager.kt @@ -100,7 +100,7 @@ class GameManager(game: GoudGame) { // Keyboard (emulator) if (game.isKeyJustPressed(Key.Space)) return true // Gamepad A/South button - if (game.isGamepadButtonJustPressed(0, GamepadButton.South)) return true + if (game.isGamepadButtonJustPressed(0, GamepadButton.South.value)) return true return false }