// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
/// Global physics hooks configuration for 2D Rapier integration.
///
/// Rapier's `PhysicsHooks` callbacks are invoked from inside `PhysicsPipeline::step`
/// and do not have access to mgstudio's ECS world. We still provide:
/// - A global place to register callbacks.
/// - A helper to map `ColliderHandle -> Entity` during the current `step`.
///|
pub struct Physics2dHooksConfig {
/// Return false to ignore the contact pair entirely.
filter_contact_pair : ((
@dynamics.RigidBodySet,
@collision.ColliderSet,
@collision.ColliderHandle,
@collision.ColliderHandle,
) -> Bool)?
/// Return false to ignore the intersection (sensor) pair entirely.
filter_intersection_pair : ((
@dynamics.RigidBodySet,
@collision.ColliderSet,
@collision.ColliderHandle,
@collision.ColliderHandle,
) -> Bool)?
/// Return (solve_contacts, friction_dir2) to override solver behavior.
modify_solver_contacts : ((
@dynamics.RigidBodySet,
@collision.ColliderSet,
@collision.ColliderHandle,
@collision.ColliderHandle,
@collision.ContactManifold,
) -> (Bool, @rapier_core.Vec2))?
modify_solver_contacts_with_solver_contacts : ((
@dynamics.RigidBodySet,
@collision.ColliderSet,
@collision.ColliderHandle,
@collision.ColliderHandle,
@collision.ContactManifold,
Array[@collision.SolverContact],
@collision.SolverFlags,
) -> @collision.SolverFlags)?
}
///|
pub fn Physics2dHooksConfig::default() -> Physics2dHooksConfig {
{
filter_contact_pair: None,
filter_intersection_pair: None,
modify_solver_contacts: None,
modify_solver_contacts_with_solver_contacts: None,
}
}
///|
let physics2d_hooks_state : Ref[Physics2dHooksConfig] = Ref::new(
Physics2dHooksConfig::default(),
)
///|
let physics2d_hook_collider_to_entity : Ref[
@hashmap.HashMap[(Int, Int), @core.Entity],
] = Ref::new(@hashmap.HashMap::new())
///|
pub fn physics2d_hooks_config() -> Physics2dHooksConfig {
physics2d_hooks_state.val
}
///|
pub fn physics2d_hooks_set_config(config : Physics2dHooksConfig) -> Unit {
physics2d_hooks_state.val = config
}
///|
pub fn physics2d_set_filter_contact_pair(
f : (
@dynamics.RigidBodySet,
@collision.ColliderSet,
@collision.ColliderHandle,
@collision.ColliderHandle,
) -> Bool,
) -> Unit {
let c = physics2d_hooks_state.val
physics2d_hooks_state.val = Physics2dHooksConfig::{
..c,
filter_contact_pair: Some(f),
}
}
///|
pub fn physics2d_clear_filter_contact_pair() -> Unit {
let c = physics2d_hooks_state.val
physics2d_hooks_state.val = Physics2dHooksConfig::{
..c,
filter_contact_pair: None,
}
}
///|
pub fn physics2d_set_filter_intersection_pair(
f : (
@dynamics.RigidBodySet,
@collision.ColliderSet,
@collision.ColliderHandle,
@collision.ColliderHandle,
) -> Bool,
) -> Unit {
let c = physics2d_hooks_state.val
physics2d_hooks_state.val = Physics2dHooksConfig::{
..c,
filter_intersection_pair: Some(f),
}
}
///|
pub fn physics2d_clear_filter_intersection_pair() -> Unit {
let c = physics2d_hooks_state.val
physics2d_hooks_state.val = Physics2dHooksConfig::{
..c,
filter_intersection_pair: None,
}
}
///|
pub fn physics2d_set_modify_solver_contacts(
f : (
@dynamics.RigidBodySet,
@collision.ColliderSet,
@collision.ColliderHandle,
@collision.ColliderHandle,
@collision.ContactManifold,
) -> (Bool, @rapier_core.Vec2),
) -> Unit {
let c = physics2d_hooks_state.val
physics2d_hooks_state.val = Physics2dHooksConfig::{
..c,
modify_solver_contacts: Some(f),
}
}
///|
pub fn physics2d_clear_modify_solver_contacts() -> Unit {
let c = physics2d_hooks_state.val
physics2d_hooks_state.val = Physics2dHooksConfig::{
..c,
modify_solver_contacts: None,
}
}
///|
pub fn physics2d_set_modify_solver_contacts_with_solver_contacts(
f : (
@dynamics.RigidBodySet,
@collision.ColliderSet,
@collision.ColliderHandle,
@collision.ColliderHandle,
@collision.ContactManifold,
Array[@collision.SolverContact],
@collision.SolverFlags,
) -> @collision.SolverFlags,
) -> Unit {
let c = physics2d_hooks_state.val
physics2d_hooks_state.val = Physics2dHooksConfig::{
..c,
modify_solver_contacts_with_solver_contacts: Some(f),
}
}
///|
pub fn physics2d_clear_modify_solver_contacts_with_solver_contacts() -> Unit {
let c = physics2d_hooks_state.val
physics2d_hooks_state.val = Physics2dHooksConfig::{
..c,
modify_solver_contacts_with_solver_contacts: None,
}
}
///|
/// Map a Rapier collider handle to the owning entity for the current `PhysicsPipeline::step`.
///
/// Note: This is only valid during the time the engine is stepping physics.
pub fn physics2d_hook_entity_from_collider_handle(
h : @collision.ColliderHandle,
) -> @core.Entity? {
physics2d_hook_collider_to_entity.val.get(h.into_raw_parts())
}
///|
pub fn physics2d_hooks_set_current_collider_map(
map : @hashmap.HashMap[(Int, Int), @core.Entity],
) -> Unit {
physics2d_hook_collider_to_entity.val = map
}
///|
pub fn physics2d_hooks_clear_current_collider_map() -> Unit {
physics2d_hook_collider_to_entity.val = @hashmap.HashMap::new()
}
///|
pub fn physics2d_apply_global_hooks(ctx : Physics2dContext) -> Unit {
let c = physics2d_hooks_state.val
let mut hooks = @pipeline.PhysicsHooks::new()
match c.filter_contact_pair {
Some(f) => hooks = hooks.filter_contact_pair(f)
None => ()
}
match c.filter_intersection_pair {
Some(f) => hooks = hooks.filter_intersection_pair(f)
None => ()
}
match c.modify_solver_contacts {
Some(f) => hooks = hooks.modify_solver_contacts(f)
None => ()
}
match c.modify_solver_contacts_with_solver_contacts {
Some(f) => hooks = hooks.modify_solver_contacts_with_solver_contacts(f)
None => ()
}
ctx.hooks = hooks
}