├── .editorconfig ├── .gitignore ├── AUTHORS.md ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake ├── Edyn-config.cmake └── in │ └── build_settings.h.in ├── conanfile.py ├── docs └── Design.md ├── examples ├── CMakeLists.txt ├── current_pos │ └── current_pos.cpp └── hello_world │ └── hello_world.cpp ├── include └── edyn │ ├── collision │ ├── broadphase.hpp │ ├── collide.hpp │ ├── collision_feature.hpp │ ├── collision_result.hpp │ ├── contact_event_emitter.hpp │ ├── contact_manifold.hpp │ ├── contact_manifold_events.hpp │ ├── contact_manifold_map.hpp │ ├── contact_normal_attachment.hpp │ ├── contact_point.hpp │ ├── contact_signal.hpp │ ├── dynamic_tree.hpp │ ├── narrowphase.hpp │ ├── query_aabb.hpp │ ├── query_tree.hpp │ ├── raycast.hpp │ ├── raycast_service.hpp │ ├── should_collide.hpp │ ├── static_tree.hpp │ └── tree_node.hpp │ ├── comp │ ├── aabb.hpp │ ├── action_list.hpp │ ├── angvel.hpp │ ├── center_of_mass.hpp │ ├── child_list.hpp │ ├── collision_exclusion.hpp │ ├── collision_filter.hpp │ ├── delta_angvel.hpp │ ├── delta_linvel.hpp │ ├── graph_edge.hpp │ ├── graph_node.hpp │ ├── gravity.hpp │ ├── inertia.hpp │ ├── island.hpp │ ├── linvel.hpp │ ├── mass.hpp │ ├── material.hpp │ ├── merge_component.hpp │ ├── orientation.hpp │ ├── origin.hpp │ ├── position.hpp │ ├── present_orientation.hpp │ ├── present_position.hpp │ ├── roll_direction.hpp │ ├── rotated_mesh_list.hpp │ ├── scalar_comp.hpp │ ├── shape_index.hpp │ ├── shared_comp.hpp │ ├── tag.hpp │ └── tree_resident.hpp │ ├── config │ ├── config.h │ ├── constants.hpp │ ├── execution_mode.hpp │ └── solver_iteration_config.hpp │ ├── constraints │ ├── cone_constraint.hpp │ ├── constraint.hpp │ ├── constraint_base.hpp │ ├── constraint_body.hpp │ ├── constraint_row.hpp │ ├── constraint_row_friction.hpp │ ├── constraint_row_options.hpp │ ├── constraint_row_spin_friction.hpp │ ├── contact_constraint.hpp │ ├── cvjoint_constraint.hpp │ ├── distance_constraint.hpp │ ├── generic_constraint.hpp │ ├── gravity_constraint.hpp │ ├── hinge_constraint.hpp │ ├── null_constraint.hpp │ ├── point_constraint.hpp │ └── soft_distance_constraint.hpp │ ├── context │ ├── registry_operation_context.hpp │ ├── settings.hpp │ ├── start_thread.hpp │ ├── step_callback.hpp │ ├── task.hpp │ └── task_util.hpp │ ├── core │ ├── entity_graph.hpp │ ├── entity_pair.hpp │ ├── flat_nested_array.hpp │ └── unordered_pair.hpp │ ├── dynamics │ ├── island_constraint_entities.hpp │ ├── island_solver.hpp │ ├── material_mixing.hpp │ ├── moment_of_inertia.hpp │ ├── position_solver.hpp │ ├── restitution_solver.hpp │ ├── row_cache.hpp │ └── solver.hpp │ ├── edyn.hpp │ ├── math │ ├── constants.hpp │ ├── coordinate_axis.hpp │ ├── geom.hpp │ ├── math.hpp │ ├── matrix3x3.hpp │ ├── quaternion.hpp │ ├── scalar.hpp │ ├── shape_volume.hpp │ ├── transform.hpp │ ├── triangle.hpp │ ├── vector2.hpp │ ├── vector2_3_util.hpp │ └── vector3.hpp │ ├── networking │ ├── comp │ │ ├── aabb_of_interest.hpp │ │ ├── aabb_oi_follow.hpp │ │ ├── action_history.hpp │ │ ├── asset_entry.hpp │ │ ├── asset_ref.hpp │ │ ├── discontinuity.hpp │ │ ├── entity_owner.hpp │ │ ├── exporter_modified_components.hpp │ │ ├── network_input.hpp │ │ ├── networked_comp.hpp │ │ └── remote_client.hpp │ ├── context │ │ ├── client_network_context.hpp │ │ └── server_network_context.hpp │ ├── extrapolation │ │ ├── extrapolation_callback.hpp │ │ ├── extrapolation_modified_comp.hpp │ │ ├── extrapolation_operation.hpp │ │ ├── extrapolation_request.hpp │ │ ├── extrapolation_result.hpp │ │ └── extrapolation_worker.hpp │ ├── networking.hpp │ ├── networking_external.hpp │ ├── packet │ │ ├── asset_sync.hpp │ │ ├── client_created.hpp │ │ ├── create_entity.hpp │ │ ├── destroy_entity.hpp │ │ ├── edyn_packet.hpp │ │ ├── entity_entered.hpp │ │ ├── entity_exited.hpp │ │ ├── entity_response.hpp │ │ ├── query_entity.hpp │ │ ├── registry_snapshot.hpp │ │ ├── server_settings.hpp │ │ ├── set_aabb_of_interest.hpp │ │ ├── set_playout_delay.hpp │ │ ├── time_request.hpp │ │ ├── time_response.hpp │ │ └── update_entity_map.hpp │ ├── settings │ │ ├── client_network_settings.hpp │ │ └── server_network_settings.hpp │ ├── sys │ │ ├── accumulate_discontinuities.hpp │ │ ├── assign_previous_transforms.hpp │ │ ├── client_side.hpp │ │ ├── server_side.hpp │ │ └── update_aabbs_of_interest.hpp │ └── util │ │ ├── asset_util.hpp │ │ ├── client_snapshot_exporter.hpp │ │ ├── client_snapshot_importer.hpp │ │ ├── clock_sync.hpp │ │ ├── component_index_type.hpp │ │ ├── import_contact_manifolds.hpp │ │ ├── input_state_history.hpp │ │ ├── pool_snapshot.hpp │ │ ├── pool_snapshot_data.hpp │ │ ├── process_extrapolation_result.hpp │ │ ├── process_update_entity_map_packet.hpp │ │ ├── server_snapshot_exporter.hpp │ │ ├── server_snapshot_importer.hpp │ │ └── snap_to_pool_snapshot.hpp │ ├── parallel │ ├── atomic_counter_sync.hpp │ ├── job.hpp │ ├── job_dispatcher.hpp │ ├── job_queue.hpp │ ├── message.hpp │ ├── message_dispatcher.hpp │ ├── message_queue.hpp │ └── worker.hpp │ ├── replication │ ├── entity_map.hpp │ ├── make_reg_op_builder.hpp │ ├── map_child_entity.hpp │ ├── register_external.hpp │ ├── registry_operation.hpp │ ├── registry_operation_builder.hpp │ └── registry_operation_observer.hpp │ ├── serialization │ ├── entt_s11n.hpp │ ├── file_archive.hpp │ ├── math_s11n.hpp │ ├── memory_archive.hpp │ ├── paged_triangle_mesh_s11n.hpp │ ├── s11n.hpp │ ├── s11n_util.hpp │ ├── static_tree_s11n.hpp │ ├── std_s11n.hpp │ └── triangle_mesh_s11n.hpp │ ├── shapes │ ├── box_shape.hpp │ ├── capsule_shape.hpp │ ├── compound_shape.hpp │ ├── convex_mesh.hpp │ ├── create_paged_triangle_mesh.hpp │ ├── cylinder_shape.hpp │ ├── mesh_shape.hpp │ ├── paged_mesh_shape.hpp │ ├── paged_triangle_mesh.hpp │ ├── plane_shape.hpp │ ├── polyhedron_shape.hpp │ ├── shapes.hpp │ ├── sphere_shape.hpp │ ├── triangle_mesh.hpp │ └── triangle_mesh_page_loader.hpp │ ├── simulation │ ├── island_manager.hpp │ ├── simulation_worker.hpp │ ├── stepper_async.hpp │ └── stepper_sequential.hpp │ ├── sys │ ├── apply_gravity.hpp │ ├── update_aabbs.hpp │ ├── update_inertias.hpp │ ├── update_origins.hpp │ ├── update_presentation.hpp │ └── update_rotated_meshes.hpp │ ├── time │ ├── simulation_time.hpp │ └── time.hpp │ └── util │ ├── aabb_util.hpp │ ├── array_util.hpp │ ├── collision_util.hpp │ ├── constraint_util.hpp │ ├── contact_manifold_util.hpp │ ├── entt_util.hpp │ ├── exclude_collision.hpp │ ├── gravity_util.hpp │ ├── insert_material_mixing.hpp │ ├── island_util.hpp │ ├── paged_mesh_load_reporting.hpp │ ├── polyhedron_shape_initializer.hpp │ ├── ragdoll.hpp │ ├── rigidbody.hpp │ ├── settings_util.hpp │ ├── shape_io.hpp │ ├── shape_util.hpp │ ├── tuple_util.hpp │ ├── vector_util.hpp │ └── visit_component.hpp ├── src └── edyn │ ├── collision │ ├── broadphase.cpp │ ├── collide │ │ ├── collide_box_box.cpp │ │ ├── collide_box_mesh.cpp │ │ ├── collide_box_plane.cpp │ │ ├── collide_capsule_box.cpp │ │ ├── collide_capsule_capsule.cpp │ │ ├── collide_capsule_cylinder.cpp │ │ ├── collide_capsule_mesh.cpp │ │ ├── collide_capsule_plane.cpp │ │ ├── collide_capsule_sphere.cpp │ │ ├── collide_compound_compound.cpp │ │ ├── collide_compound_mesh.cpp │ │ ├── collide_compound_plane.cpp │ │ ├── collide_cylinder_box.cpp │ │ ├── collide_cylinder_cylinder.cpp │ │ ├── collide_cylinder_mesh.cpp │ │ ├── collide_cylinder_plane.cpp │ │ ├── collide_cylinder_sphere.cpp │ │ ├── collide_polyhedron_box.cpp │ │ ├── collide_polyhedron_capsule.cpp │ │ ├── collide_polyhedron_cylinder.cpp │ │ ├── collide_polyhedron_mesh.cpp │ │ ├── collide_polyhedron_plane.cpp │ │ ├── collide_polyhedron_polyhedron.cpp │ │ ├── collide_polyhedron_sphere.cpp │ │ ├── collide_sphere_box.cpp │ │ ├── collide_sphere_mesh.cpp │ │ ├── collide_sphere_plane.cpp │ │ └── collide_sphere_sphere.cpp │ ├── collision_result.cpp │ ├── contact_event_emitter.cpp │ ├── contact_manifold_map.cpp │ ├── contact_signal.cpp │ ├── dynamic_tree.cpp │ ├── narrowphase.cpp │ ├── query_aabb.cpp │ ├── raycast.cpp │ ├── raycast_service.cpp │ └── should_collide.cpp │ ├── config │ └── solver_iteration_config.cpp │ ├── constraints │ ├── cone_constraint.cpp │ ├── constraint_row.cpp │ ├── constraint_row_friction.cpp │ ├── constraint_row_spin_friction.cpp │ ├── contact_constraint.cpp │ ├── cvjoint_constraint.cpp │ ├── distance_constraint.cpp │ ├── generic_constraint.cpp │ ├── gravity_constraint.cpp │ ├── hinge_constraint.cpp │ ├── point_constraint.cpp │ └── soft_distance_constraint.cpp │ ├── context │ ├── registry_operation_context.cpp │ ├── start_thread.cpp │ ├── step_callback.cpp │ └── task.cpp │ ├── core │ └── entity_graph.cpp │ ├── dynamics │ ├── island_solver.cpp │ ├── moment_of_inertia.cpp │ ├── restitution_solver.cpp │ └── solver.cpp │ ├── edyn.cpp │ ├── math │ ├── geom.cpp │ ├── quaternion.cpp │ ├── shape_volume.cpp │ └── triangle.cpp │ ├── networking │ ├── context │ │ ├── client_network_context.cpp │ │ └── server_network_context.cpp │ ├── extrapolation │ │ ├── extrapolation_callback.cpp │ │ └── extrapolation_worker.cpp │ ├── networking.cpp │ ├── sys │ │ ├── client_side.cpp │ │ ├── server_side.cpp │ │ └── update_aabbs_of_interest.cpp │ └── util │ │ ├── clock_sync.cpp │ │ ├── import_contact_manifolds.cpp │ │ ├── pool_snapshot.cpp │ │ ├── process_extrapolation_result.cpp │ │ ├── process_update_entity_map_packet.cpp │ │ └── snap_to_pool_snapshot.cpp │ ├── parallel │ ├── job_dispatcher.cpp │ ├── job_queue.cpp │ └── message_dispatcher.cpp │ ├── replication │ ├── make_reg_op_builder.cpp │ ├── map_child_entity.cpp │ └── register_external.cpp │ ├── serialization │ └── paged_triangle_mesh_s11n.cpp │ ├── shapes │ ├── box_shape.cpp │ ├── compound_shape.cpp │ ├── convex_mesh.cpp │ ├── cylinder_shape.cpp │ ├── paged_triangle_mesh.cpp │ ├── polyhedron_shape.cpp │ └── triangle_mesh.cpp │ ├── simulation │ ├── island_manager.cpp │ ├── simulation_worker.cpp │ ├── stepper_async.cpp │ └── stepper_sequential.cpp │ ├── sys │ ├── update_aabbs.cpp │ ├── update_inertias.cpp │ ├── update_origins.cpp │ ├── update_presentation.cpp │ └── update_rotated_meshes.cpp │ ├── time │ ├── common │ │ └── time.cpp │ ├── simulation_time.cpp │ ├── unix │ │ └── time.cpp │ └── windows │ │ └── time.cpp │ └── util │ ├── aabb_util.cpp │ ├── collision_util.cpp │ ├── constraint_util.cpp │ ├── contact_manifold_util.cpp │ ├── exclude_collision.cpp │ ├── gravity_util.cpp │ ├── insert_material_mixing.cpp │ ├── island_util.cpp │ ├── paged_mesh_load_reporting.cpp │ ├── polyhedron_shape_initializer.cpp │ ├── ragdoll.cpp │ ├── rigidbody.cpp │ ├── settings_util.cpp │ ├── shape_io.cpp │ └── shape_util.cpp └── test ├── CMakeLists.txt └── edyn ├── collision ├── test_broadphase.cpp ├── test_collision.cpp ├── test_exclusion.cpp └── test_raycast.cpp ├── common └── common.hpp ├── issues ├── issue128.cpp ├── issue134.cpp └── issue76.cpp ├── math ├── test_geom.cpp ├── test_math.cpp ├── test_matrix3x3.cpp └── test_vector3.cpp ├── networking ├── test_input_state_history.cpp └── test_net_imp_exp.cpp ├── parallel ├── test_entity_graph.cpp └── test_job_dispatcher.cpp ├── serialization ├── test_std_s11n.cpp └── test_triangle_mesh_s11n.cpp ├── shapes ├── test_centroid.cpp ├── test_paged_trimesh.cpp ├── test_set_shape.cpp ├── test_shape_volume.cpp └── test_trimesh.cpp ├── sys └── test_apply_gravity.cpp └── util ├── test_change_rigidbody_kind.cpp ├── test_clear_rigidbody.cpp ├── test_registry_operation.cpp └── test_tuple_util.cpp /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_size = 4 5 | indent_style = space 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | 9 | [*.yml] 10 | indent_size = 2 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDEs 2 | .vscode 3 | .cache 4 | .vs 5 | 6 | # build 7 | build 8 | out 9 | CMakeUserPresets.json 10 | 11 | # macOS 12 | .DS_Store 13 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | # Author 2 | 3 | [xissburg](https://github.com/xissburg) 4 | 5 | # Contributors 6 | 7 | [StellaSmith](https://github.com/StellaSmith) 8 | 9 | [Anonymous Maarten](https://github.com/madebr) 10 | 11 | [Dvir Azulay](https://github.com/dvir) 12 | 13 | [Quintin](https://github.com/qhdwight) 14 | 15 | [RazielZ](https://github.com/RazielZ) 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019-2023 xissburg 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copy of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copy or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /cmake/Edyn-config.cmake: -------------------------------------------------------------------------------- 1 | include(CMakeFindDependencyMacro) 2 | find_dependency(EnTT) 3 | 4 | include("${CMAKE_CURRENT_LIST_DIR}/Edyn-targets.cmake") 5 | -------------------------------------------------------------------------------- /cmake/in/build_settings.h.in: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_BUILD_SETTINGS_H 2 | #define EDYN_BUILD_SETTINGS_H 3 | 4 | #cmakedefine EDYN_DOUBLE_PRECISION 5 | 6 | #endif // EDYN_BUILD_SETTINGS_H 7 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | macro(SETUP_AND_ADD_EXAMPLE EXAMPLE_NAME EXAMPLE_SOURCES) 2 | add_executable(${EXAMPLE_NAME} ${EXAMPLE_SOURCES}) 3 | target_compile_features(${EXAMPLE_NAME} PUBLIC cxx_std_17) 4 | 5 | target_link_libraries(${EXAMPLE_NAME} 6 | Edyn::Edyn 7 | EnTT::EnTT 8 | ) 9 | 10 | if (UNIX AND NOT APPLE) 11 | target_link_libraries(${EXAMPLE_NAME} 12 | dl 13 | pthread 14 | ) 15 | endif () 16 | 17 | if (WIN32) 18 | target_link_libraries(${EXAMPLE_NAME} winmm Ws2_32) 19 | endif () 20 | 21 | set_property(TARGET ${EXAMPLE_NAME} PROPERTY RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin/examples) 22 | endmacro() 23 | 24 | SETUP_AND_ADD_EXAMPLE(hello_world hello_world/hello_world.cpp) 25 | SETUP_AND_ADD_EXAMPLE(current_pos current_pos/current_pos.cpp) 26 | -------------------------------------------------------------------------------- /examples/current_pos/current_pos.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/math/matrix3x3.hpp" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /** 8 | * Demonstration of the importance of interpolation for smooth real-time 9 | * presentation on screen using `edyn::present_position`. Using the raw 10 | * `edyn::position` will generally cause jitter. The value in 11 | * `edyn::present_position` is the `edyn::position` interpolated or 12 | * extrapolated by an amount of time using the `edyn::linvel` of the entity. 13 | */ 14 | void print_entities(entt::registry& registry) { 15 | printf("================================\n"); 16 | 17 | auto view = registry.view(); 18 | view.each([](auto ent, const auto& pos, const auto& curpos) { 19 | // Compare the physics position to the presentation position and notice how 20 | // the physics position `pos` does not change uniformly after each update. 21 | // The presentation position `curpos` should change linearly with dt (because 22 | // `linvel` is constant in this example). 23 | printf("pos (%d): %.3f, %.3f, %.3f\n", entt::to_integral(ent), pos.x, pos.y, pos.z); 24 | printf("curpos (%d): %.3f, %.3f, %.3f\n", entt::to_integral(ent), curpos.x, curpos.y, curpos.z); 25 | }); 26 | } 27 | 28 | int main(int argc, char** argv) { 29 | entt::registry registry; 30 | edyn::attach(registry); 31 | edyn::set_fixed_dt(registry, 0.041); 32 | 33 | auto def = edyn::rigidbody_def(); 34 | def.presentation = true; 35 | def.mass = 10; 36 | def.inertia = edyn::matrix3x3_identity; 37 | def.linvel = {0, 1/0.041, 0}; 38 | def.gravity = edyn::vector3_zero; 39 | edyn::make_rigidbody(registry, def); 40 | 41 | for (;;) { 42 | edyn::update(registry); 43 | print_entities(registry); 44 | edyn::delay(100); 45 | } 46 | 47 | edyn::detach(registry); 48 | 49 | return 0; 50 | } 51 | -------------------------------------------------------------------------------- /examples/hello_world/hello_world.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | void print_entities(entt::registry& registry) { 7 | printf("================================\n"); 8 | 9 | auto view = registry.view(); 10 | view.each([](auto ent, const auto &pos, const auto &vel) { 11 | printf("pos (%d): %.3f, %.3f, %.3f\n", entt::to_integral(ent), pos.x, pos.y, pos.z); 12 | printf("vel (%d): %.3f, %.3f, %.3f\n", entt::to_integral(ent), vel.x, vel.y, vel.z); 13 | }); 14 | } 15 | 16 | int main(int argc, char** argv) { 17 | entt::registry registry; 18 | edyn::attach(registry); 19 | 20 | auto def = edyn::rigidbody_def(); 21 | def.presentation = true; 22 | def.mass = 10; 23 | def.material->friction = 0.8; 24 | def.material->restitution = 0; 25 | def.position = {0, 3, 0}; 26 | def.orientation = edyn::quaternion_axis_angle({0, 0, 1}, edyn::pi * 0.7); 27 | def.shape = edyn::cylinder_shape{0.2, 0.5}; 28 | edyn::make_rigidbody(registry, def); 29 | 30 | for (;;) { 31 | edyn::update(registry); 32 | print_entities(registry); 33 | edyn::delay(100); 34 | } 35 | 36 | edyn::detach(registry); 37 | 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /include/edyn/collision/collision_feature.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COLLISION_COLLISION_FEATURE_HPP 2 | #define EDYN_COLLISION_COLLISION_FEATURE_HPP 3 | 4 | #include "edyn/shapes/shapes.hpp" 5 | 6 | namespace edyn { 7 | 8 | struct collision_feature { 9 | // Feature type. 10 | shape_feature_t feature; 11 | 12 | // Feature index. 13 | size_t index; 14 | 15 | // Part where the feature is at. Only used by shapes which are made of 16 | // individual parts, such as compound shape (child shape index) and 17 | // paged mesh shape (triangle mesh index). 18 | size_t part; 19 | }; 20 | 21 | template 22 | void serialize(Archive &archive, collision_feature &feature) { 23 | archive(feature.feature); 24 | archive(feature.index); 25 | archive(feature.part); 26 | } 27 | 28 | } 29 | 30 | #endif // EDYN_COLLISION_COLLISION_FEATURE_HPP 31 | -------------------------------------------------------------------------------- /include/edyn/collision/collision_result.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COLLISION_COLLISION_RESULT_HPP 2 | #define EDYN_COLLISION_COLLISION_RESULT_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include "edyn/config/constants.hpp" 8 | #include "edyn/collision/contact_normal_attachment.hpp" 9 | #include "edyn/collision/collision_feature.hpp" 10 | 11 | namespace edyn { 12 | 13 | struct collision_result { 14 | struct collision_point { 15 | vector3 pivotA; 16 | vector3 pivotB; 17 | vector3 normal; 18 | scalar distance; 19 | contact_normal_attachment normal_attachment; 20 | std::optional featureA; 21 | std::optional featureB; 22 | }; 23 | 24 | size_t num_points {0}; 25 | std::array point; 26 | 27 | collision_result & swap() { 28 | for (size_t i = 0; i < num_points; ++i) { 29 | auto &cp = point[i]; 30 | std::swap(cp.pivotA, cp.pivotB); 31 | std::swap(cp.featureA, cp.featureB); 32 | cp.normal *= -1; // Point towards new A. 33 | 34 | if (cp.normal_attachment == contact_normal_attachment::normal_on_A) { 35 | cp.normal_attachment = contact_normal_attachment::normal_on_B; 36 | } else if (cp.normal_attachment == contact_normal_attachment::normal_on_B) { 37 | cp.normal_attachment = contact_normal_attachment::normal_on_A; 38 | } 39 | } 40 | return *this; 41 | } 42 | 43 | void add_point(const collision_point &); 44 | void maybe_add_point(const collision_point &); 45 | }; 46 | 47 | } 48 | 49 | #endif // EDYN_COLLISION_COLLISION_RESULT_HPP 50 | -------------------------------------------------------------------------------- /include/edyn/collision/contact_event_emitter.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COLLISION_CONTACT_EVENT_EMITTER_HPP 2 | #define EDYN_COLLISION_CONTACT_EVENT_EMITTER_HPP 3 | 4 | #include 5 | #include 6 | #include "edyn/collision/contact_manifold.hpp" 7 | 8 | namespace edyn { 9 | 10 | class contact_event_emitter { 11 | public: 12 | contact_event_emitter(entt::registry &); 13 | ~contact_event_emitter(); 14 | 15 | void consume_events(); 16 | 17 | auto contact_started_sink() { 18 | return entt::sink{m_contact_started_signal}; 19 | } 20 | 21 | auto contact_ended_sink() { 22 | return entt::sink{m_contact_ended_signal}; 23 | } 24 | 25 | auto contact_point_created_sink() { 26 | return entt::sink{m_contact_point_created_signal}; 27 | } 28 | 29 | auto contact_point_destroyed_sink() { 30 | return entt::sink{m_contact_point_destroyed_signal}; 31 | } 32 | 33 | void on_destroy_contact_manifold(entt::registry &, entt::entity); 34 | 35 | private: 36 | entt::registry *m_registry; 37 | entt::sigh m_contact_started_signal; 38 | entt::sigh m_contact_ended_signal; 39 | entt::sigh m_contact_point_created_signal; 40 | entt::sigh m_contact_point_destroyed_signal; 41 | }; 42 | 43 | } 44 | 45 | #endif // EDYN_COLLISION_CONTACT_EVENT_EMITTER_HPP 46 | -------------------------------------------------------------------------------- /include/edyn/collision/contact_manifold_events.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COLLISION_CONTACT_MANIFOLD_EVENTS_HPP 2 | #define EDYN_COLLISION_CONTACT_MANIFOLD_EVENTS_HPP 3 | 4 | #include 5 | #include "edyn/config/constants.hpp" 6 | 7 | namespace edyn { 8 | 9 | struct contact_manifold_events { 10 | bool contact_started {false}; 11 | bool contact_ended {false}; 12 | unsigned num_contacts_created {0}; 13 | std::array contacts_created; 14 | unsigned num_contacts_destroyed {0}; 15 | std::array contacts_destroyed; 16 | }; 17 | 18 | } 19 | 20 | #endif // EDYN_COLLISION_CONTACT_MANIFOLD_EVENTS_HPP 21 | -------------------------------------------------------------------------------- /include/edyn/collision/contact_manifold_map.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COLLISION_CONTACT_MANIFOLD_MAP 2 | #define EDYN_COLLISION_CONTACT_MANIFOLD_MAP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "edyn/core/entity_pair.hpp" 9 | 10 | namespace edyn { 11 | 12 | /** 13 | * @brief Maps a pair of entities to their contact manifold. 14 | */ 15 | class contact_manifold_map { 16 | public: 17 | contact_manifold_map(entt::registry &); 18 | 19 | // Cannot be copied because `entt::scoped_connection` deletes its copy ctor. 20 | contact_manifold_map(const contact_manifold_map &) = delete; 21 | contact_manifold_map(contact_manifold_map &&) = default; 22 | 23 | /** 24 | * @brief Checks whether a contact manifold exists for a pair of entities. 25 | * @param pair The pair of entities. 26 | * @return Whether a contact manifold exits between the two entities. 27 | */ 28 | bool contains(entity_pair) const; 29 | 30 | /*! @copydoc contains */ 31 | bool contains(entt::entity, entt::entity) const; 32 | 33 | /** 34 | * @brief Gets the manifold entity joining a pair of entities. 35 | * @param pair The pair of entities. 36 | * @return Entity of manifold connecting the two entities. 37 | */ 38 | entt::entity get(entity_pair) const; 39 | 40 | /*! @copydoc get */ 41 | entt::entity get(entt::entity, entt::entity) const; 42 | 43 | void on_construct_contact_manifold(entt::registry &, entt::entity); 44 | void on_destroy_contact_manifold(entt::registry &, entt::entity); 45 | 46 | void clear(); 47 | 48 | private: 49 | std::map m_pair_map; 50 | std::vector m_connections; 51 | }; 52 | 53 | } 54 | 55 | #endif // EDYN_COLLISION_CONTACT_MANIFOLD_MAP 56 | -------------------------------------------------------------------------------- /include/edyn/collision/contact_normal_attachment.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COLLISION_CONTACT_NORMAL_ATTACHMENT_HPP 2 | #define EDYN_COLLISION_CONTACT_NORMAL_ATTACHMENT_HPP 3 | 4 | namespace edyn { 5 | 6 | /** 7 | * @brief To which body a contact normal is attached in a contact point. 8 | * This is used while solving position contact constraints where the bodies 9 | * are translated and rotated in each iteration, which causes the contact 10 | * points to move. An approximation is made to place the contact points and 11 | * contact normal in a new location where the closest points could be. If 12 | * the closest feature on a body is planar, it is selected as the place 13 | * where the contact normal is attached to, and thus it is rotated with this 14 | * body in each iteration. 15 | */ 16 | enum class contact_normal_attachment : unsigned char { 17 | none, 18 | normal_on_A, 19 | normal_on_B 20 | }; 21 | 22 | template 23 | void serialize(Archive &archive, contact_normal_attachment &attachment) { 24 | serialize_enum(archive, attachment); 25 | } 26 | 27 | } 28 | 29 | #endif // EDYN_COLLISION_CONTACT_NORMAL_ATTACHMENT_HPP 30 | -------------------------------------------------------------------------------- /include/edyn/collision/query_aabb.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COLLISION_QUERY_AABB_HPP 2 | #define EDYN_COLLISION_QUERY_AABB_HPP 3 | 4 | #include "edyn/collision/broadphase.hpp" 5 | #include "edyn/comp/aabb.hpp" 6 | #include 7 | 8 | namespace edyn { 9 | 10 | template 11 | void query_procedural_aabb(entt::registry ®istry, const AABB &aabb, Func func) { 12 | auto &bphase = registry.ctx().get(); 13 | bphase.query_procedural(aabb, func); 14 | } 15 | 16 | template 17 | void query_non_procedural_aabb(entt::registry ®istry, const AABB &aabb, Func func) { 18 | auto &bphase = registry.ctx().get(); 19 | bphase.query_non_procedural(aabb, func); 20 | } 21 | 22 | template 23 | void query_island_aabb(entt::registry ®istry, const AABB &aabb, Func func) { 24 | auto &bphase = registry.ctx().get(); 25 | bphase.query_islands(aabb, func); 26 | } 27 | 28 | struct query_aabb_result { 29 | std::vector procedural_entities; 30 | std::vector non_procedural_entities; 31 | std::vector island_entities; 32 | }; 33 | 34 | using query_aabb_id_type = unsigned; 35 | using query_aabb_delegate_type = entt::delegate; 36 | 37 | query_aabb_id_type query_aabb_async(entt::registry ®istry, const AABB &aabb, 38 | const query_aabb_delegate_type &delegate, 39 | bool query_procedural, 40 | bool query_non_procedural, 41 | bool query_islands); 42 | 43 | query_aabb_id_type query_aabb_of_interest_async(entt::registry ®istry, const AABB &aabb, 44 | const query_aabb_delegate_type &delegate); 45 | 46 | } 47 | 48 | #endif // EDYN_COLLISION_QUERY_AABB_HPP 49 | -------------------------------------------------------------------------------- /include/edyn/collision/query_tree.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COLLISION_QUERY_TREE_HPP 2 | #define EDYN_COLLISION_QUERY_TREE_HPP 3 | 4 | #include "edyn/comp/aabb.hpp" 5 | #include "edyn/math/geom.hpp" 6 | 7 | namespace edyn { 8 | 9 | template 10 | void traverse_tree(const Tree &tree, NodeIdType root_id, NodeIdType null_node_id, 11 | TestFunc test_func, VisitFunc visit_func) { 12 | std::vector stack; 13 | stack.push_back(root_id); 14 | 15 | while (!stack.empty()) { 16 | auto id = stack.back(); 17 | stack.pop_back(); 18 | 19 | if (id == null_node_id) { 20 | continue; 21 | } 22 | 23 | auto &node = tree.get_node(id); 24 | 25 | if (test_func(node)) { 26 | if (node.leaf()) { 27 | visit_func(id); 28 | } else { 29 | stack.push_back(node.child1); 30 | stack.push_back(node.child2); 31 | } 32 | } 33 | } 34 | } 35 | 36 | template 37 | void query_tree(const Tree &tree, NodeIdType root_id, NodeIdType null_node_id, 38 | const AABB &aabb, Func func) { 39 | traverse_tree(tree, root_id, null_node_id, [&](auto &node) { 40 | return intersect(node.aabb, aabb); 41 | }, func); 42 | } 43 | 44 | template 45 | void raycast_tree(const Tree &tree, NodeIdType root_id, NodeIdType null_node_id, 46 | const vector3 &p0, const vector3 &p1, Func func) { 47 | traverse_tree(tree, root_id, null_node_id, [&](auto &node) { 48 | return intersect_segment_aabb(p0, p1, node.aabb.min, node.aabb.max); 49 | }, func); 50 | } 51 | 52 | } 53 | 54 | #endif // EDYN_COLLISION_QUERY_TREE_HPP 55 | -------------------------------------------------------------------------------- /include/edyn/collision/raycast_service.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COLLISION_RAYCAST_SERVICE_HPP 2 | #define EDYN_COLLISION_RAYCAST_SERVICE_HPP 3 | 4 | #include "edyn/collision/raycast.hpp" 5 | #include "edyn/math/vector3.hpp" 6 | #include 7 | #include 8 | 9 | namespace edyn { 10 | 11 | class raycast_service { 12 | struct broadphase_context { 13 | unsigned id; 14 | vector3 p0, p1; 15 | std::vector ignore_entities; 16 | std::vector candidates; 17 | }; 18 | 19 | struct narrowphase_context { 20 | unsigned id; 21 | vector3 p0, p1; 22 | entt::entity entity; 23 | shape_raycast_result result; 24 | }; 25 | 26 | void run_broadphase(bool mt); 27 | void run_narrowphase(bool mt); 28 | void finish_broadphase(); 29 | void finish_narrowphase(); 30 | 31 | public: 32 | raycast_service(entt::registry ®istry); 33 | 34 | void add_ray(vector3 p0, vector3 p1, unsigned id, const std::vector &ignore_entities) { 35 | m_broad_ctx.push_back(broadphase_context{id, p0, p1, ignore_entities}); 36 | } 37 | 38 | void update(bool mt); 39 | 40 | template 41 | void consume_results(Func func) { 42 | for (auto [id, res] : m_results) { 43 | func(id, res); 44 | } 45 | m_results.clear(); 46 | } 47 | 48 | private: 49 | entt::registry *m_registry; 50 | 51 | std::vector m_broad_ctx; 52 | std::vector m_narrow_ctx; 53 | std::unordered_map m_results; 54 | 55 | size_t m_max_raycast_broadphase_sequential_size {4}; 56 | size_t m_max_raycast_narrowphase_sequential_size {4}; 57 | }; 58 | 59 | } 60 | 61 | #endif // EDYN_COLLISION_RAYCAST_SERVICE_HPP 62 | -------------------------------------------------------------------------------- /include/edyn/collision/should_collide.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COLLIDE_SHOULD_COLLIDE_HPP 2 | #define EDYN_COLLIDE_SHOULD_COLLIDE_HPP 3 | 4 | #include 5 | 6 | namespace edyn { 7 | 8 | bool should_collide_default(const entt::registry &, entt::entity, entt::entity); 9 | using should_collide_func_t = decltype(&should_collide_default); 10 | 11 | /** 12 | * @brief Overrides the default collision filtering function, which checks 13 | * collision groups and masks. Remember to return false if both entities 14 | * are the same. 15 | * @param registry Data source. 16 | * @param func The function. 17 | */ 18 | void set_should_collide(entt::registry ®istry, should_collide_func_t func); 19 | 20 | } 21 | 22 | #endif // EDYN_COLLIDE_SHOULD_COLLIDE_HPP 23 | -------------------------------------------------------------------------------- /include/edyn/collision/tree_node.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COLLISION_TREE_NODE_HPP 2 | #define EDYN_COLLISION_TREE_NODE_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include "edyn/comp/aabb.hpp" 8 | 9 | namespace edyn { 10 | 11 | using tree_node_id_t = uint32_t; 12 | constexpr static tree_node_id_t null_tree_node_id = std::numeric_limits::max(); 13 | 14 | struct tree_node { 15 | entt::entity entity; 16 | AABB aabb; 17 | 18 | union { 19 | tree_node_id_t parent; 20 | tree_node_id_t next; 21 | }; 22 | 23 | tree_node_id_t child1; 24 | tree_node_id_t child2; 25 | 26 | // Height from the bottom of the tree, i.e. leaf = 0. If free, -1. 27 | int height; 28 | 29 | bool leaf() const { 30 | return child1 == null_tree_node_id; 31 | } 32 | }; 33 | 34 | } 35 | 36 | #endif // EDYN_COLLISION_TREE_NODE_HPP 37 | -------------------------------------------------------------------------------- /include/edyn/comp/aabb.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_AABB_HPP 2 | #define EDYN_COMP_AABB_HPP 3 | 4 | #include "edyn/math/vector3.hpp" 5 | #include "edyn/math/geom.hpp" 6 | 7 | namespace edyn { 8 | 9 | /** 10 | * @brief Axis-aligned bounding box. 11 | */ 12 | struct AABB { 13 | vector3 min; 14 | vector3 max; 15 | 16 | inline AABB inset(const vector3 &v) const { 17 | return {min + v, max - v}; 18 | } 19 | 20 | inline vector3 center() const { 21 | return (min + max) * scalar(0.5); 22 | } 23 | 24 | inline vector3 size() const { 25 | return max - min; 26 | } 27 | 28 | // Returns this AABB's surface area. 29 | inline scalar area() const { 30 | auto d = size(); 31 | return scalar{2} * (d.x * d.y + d.y * d.z + d.z * d.x); 32 | } 33 | 34 | // Returns whether this AABB contains point `p`. 35 | bool contains(const vector3 &p) const { 36 | return min <= p && p <= max; 37 | } 38 | 39 | // Returns whether `aabb` is contained within this AABB. 40 | bool contains(const AABB &aabb) const { 41 | return contains(aabb.min) && contains(aabb.max); 42 | } 43 | }; 44 | 45 | inline bool intersect(const AABB &b0, const AABB &b1) { 46 | return intersect_aabb(b0.min, b0.max, b1.min, b1.max); 47 | } 48 | 49 | inline AABB enclosing_aabb(const AABB &b0, const AABB &b1) { 50 | return { 51 | min(b0.min, b1.min), 52 | max(b0.max, b1.max) 53 | }; 54 | } 55 | 56 | template 57 | void serialize(Archive &archive, AABB &aabb) { 58 | archive(aabb.min); 59 | archive(aabb.max); 60 | } 61 | 62 | } 63 | 64 | #endif // EDYN_COMP_AABB_HPP 65 | -------------------------------------------------------------------------------- /include/edyn/comp/action_list.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_ACTION_LIST_HPP 2 | #define EDYN_COMP_ACTION_LIST_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace edyn { 10 | 11 | /** 12 | * @brief A list of actions. An action is an one-shot input generated by the 13 | * user which is executed once and then destroyed. It is intended to be used 14 | * for non-idempotent inputs such as firing a gun. 15 | */ 16 | template 17 | struct action_list { 18 | std::vector actions; 19 | }; 20 | 21 | template 22 | void serialize(Archive &archive, action_list &list) { 23 | using size_type = uint8_t; 24 | auto size = static_cast(std::min(list.actions.size(), 25 | static_cast(std::numeric_limits::max()))); 26 | archive(size); 27 | list.actions.resize(size); 28 | 29 | for (size_type i = 0; i < size; ++i) { 30 | archive(list.actions[i]); 31 | } 32 | } 33 | 34 | // Declare custom merge function to accumulate actions instead of replace. 35 | // This will take precedence over the template at edyn/comp/merge_component.hpp. 36 | template 37 | void merge_component(action_list &list, const action_list &new_value) { 38 | // Accumulate received actions. Action updates are only sent from 39 | // main to worker. 40 | list.actions.insert(list.actions.end(), new_value.actions.begin(), new_value.actions.end()); 41 | } 42 | 43 | } 44 | 45 | #endif // EDYN_COMP_ACTION_LIST_HPP 46 | -------------------------------------------------------------------------------- /include/edyn/comp/angvel.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_ANGVEL_HPP 2 | #define EDYN_COMP_ANGVEL_HPP 3 | 4 | #include "edyn/math/vector3.hpp" 5 | 6 | namespace edyn { 7 | /** 8 | * @brief Angular velocity component. 9 | */ 10 | struct angvel : public vector3 { 11 | angvel & operator=(const vector3 &v) { 12 | vector3::operator=(v); 13 | return *this; 14 | } 15 | }; 16 | 17 | template 18 | void serialize(Archive &archive, angvel &v) { 19 | archive(v.x, v.y, v.z); 20 | } 21 | 22 | } 23 | 24 | #endif // EDYN_COMP_ANGVEL_HPP 25 | -------------------------------------------------------------------------------- /include/edyn/comp/center_of_mass.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_CENTER_OF_MASS_HPP 2 | #define EDYN_COMP_CENTER_OF_MASS_HPP 3 | 4 | #include "edyn/math/vector3.hpp" 5 | 6 | namespace edyn { 7 | 8 | /** 9 | * @brief Center of mass offset from the rigid body's origin in object space. 10 | */ 11 | struct center_of_mass : public vector3 { 12 | center_of_mass & operator=(const vector3 &v) { 13 | vector3::operator=(v); 14 | return *this; 15 | } 16 | }; 17 | 18 | template 19 | void serialize(Archive &archive, center_of_mass &com) { 20 | archive(com.x, com.y, com.z); 21 | } 22 | 23 | } 24 | 25 | #endif // EDYN_COMP_CENTER_OF_MASS_HPP 26 | -------------------------------------------------------------------------------- /include/edyn/comp/child_list.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_CHILD_LIST_HPP 2 | #define EDYN_COMP_CHILD_LIST_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace edyn { 8 | 9 | struct parent_comp { 10 | entt::entity child {entt::null}; 11 | }; 12 | 13 | struct child_list { 14 | entt::entity parent {entt::null}; 15 | entt::entity next {entt::null}; 16 | }; 17 | 18 | template 19 | void serialize(Archive &archive, parent_comp &par) { 20 | archive(par.child); 21 | } 22 | 23 | template 24 | void serialize(Archive &archive, child_list &child) { 25 | archive(child.parent); 26 | archive(child.next); 27 | } 28 | 29 | } 30 | 31 | #endif // EDYN_COMP_CHILD_LIST_HPP 32 | -------------------------------------------------------------------------------- /include/edyn/comp/collision_exclusion.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_COLLISION_EXCLUSION_HPP 2 | #define EDYN_COMP_COLLISION_EXCLUSION_HPP 3 | 4 | #include "edyn/util/array_util.hpp" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace edyn { 11 | 12 | /** 13 | * @brief Collision exclusion list. Collisions between a rigid body which has 14 | * one of these and any of the other rigid bodies contained in the entity 15 | * array will be ignored. 16 | */ 17 | struct collision_exclusion { 18 | using size_type = uint8_t; 19 | static constexpr size_type max_exclusions = 16; 20 | std::array entity = 21 | make_array(entt::entity{entt::null}); 22 | 23 | size_type num_entities() const { 24 | size_type i = 0; 25 | for (; i < max_exclusions; ++i) { 26 | if (entity[i] == entt::null) { 27 | break; 28 | } 29 | } 30 | return i; 31 | } 32 | }; 33 | 34 | template 35 | void serialize(Archive &archive, collision_exclusion &excl) { 36 | auto num_entities = excl.num_entities(); 37 | archive(num_entities); 38 | num_entities = std::min(num_entities, collision_exclusion::max_exclusions); 39 | 40 | for (unsigned i = 0; i < num_entities; ++i) { 41 | archive(excl.entity[i]); 42 | } 43 | } 44 | 45 | } 46 | 47 | #endif // EDYN_COMP_COLLISION_EXCLUSION_HPP 48 | -------------------------------------------------------------------------------- /include/edyn/comp/collision_filter.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_COLLISION_FILTER_HPP 2 | #define EDYN_COMP_COLLISION_FILTER_HPP 3 | 4 | #include 5 | 6 | namespace edyn { 7 | 8 | struct collision_filter { 9 | static constexpr uint64_t all_groups = ~0ull; 10 | uint64_t group {all_groups}; 11 | uint64_t mask {all_groups}; 12 | }; 13 | 14 | template 15 | void serialize(Archive &archive, collision_filter &c) { 16 | archive(c.group); 17 | archive(c.mask); 18 | } 19 | 20 | } 21 | 22 | #endif // EDYN_COMP_COLLISION_FILTER_HPP 23 | -------------------------------------------------------------------------------- /include/edyn/comp/delta_angvel.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_DELTA_ANGVEL_HPP 2 | #define EDYN_COMP_DELTA_ANGVEL_HPP 3 | 4 | #include "edyn/math/vector3.hpp" 5 | 6 | namespace edyn { 7 | 8 | struct delta_angvel : public vector3 { 9 | delta_angvel& operator=(const vector3& v) { 10 | vector3::operator=(v); 11 | return *this; 12 | } 13 | }; 14 | 15 | } 16 | 17 | 18 | #endif // EDYN_COMP_DELTA_ANGVEL_HPP -------------------------------------------------------------------------------- /include/edyn/comp/delta_linvel.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_DELTA_LINVEL_HPP 2 | #define EDYN_COMP_DELTA_LINVEL_HPP 3 | 4 | #include "edyn/math/vector3.hpp" 5 | 6 | namespace edyn { 7 | 8 | struct delta_linvel : public vector3 { 9 | delta_linvel& operator=(const vector3& v) { 10 | vector3::operator=(v); 11 | return *this; 12 | } 13 | }; 14 | 15 | } 16 | 17 | #endif // EDYN_COMP_DELTA_LINVEL_HPP -------------------------------------------------------------------------------- /include/edyn/comp/graph_edge.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_GRAPH_EDGE_HPP 2 | #define EDYN_COMP_GRAPH_EDGE_HPP 3 | 4 | #include "edyn/core/entity_graph.hpp" 5 | 6 | namespace edyn { 7 | 8 | struct graph_edge { 9 | entity_graph::index_type edge_index; 10 | }; 11 | 12 | } 13 | 14 | #endif // EDYN_COMP_GRAPH_EDGE_HPP 15 | -------------------------------------------------------------------------------- /include/edyn/comp/graph_node.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_GRAPH_NODE_HPP 2 | #define EDYN_COMP_GRAPH_NODE_HPP 3 | 4 | #include "edyn/core/entity_graph.hpp" 5 | 6 | namespace edyn { 7 | 8 | struct graph_node { 9 | entity_graph::index_type node_index; 10 | }; 11 | 12 | } 13 | 14 | #endif // EDYN_COMP_GRAPH_NODE_HPP 15 | -------------------------------------------------------------------------------- /include/edyn/comp/gravity.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_GRAVITY_HPP 2 | #define EDYN_COMP_GRAVITY_HPP 3 | 4 | #include "edyn/math/vector3.hpp" 5 | 6 | namespace edyn { 7 | 8 | struct gravity : public vector3 { 9 | gravity & operator=(const vector3 &v) { 10 | vector3::operator=(v); 11 | return *this; 12 | } 13 | }; 14 | 15 | template 16 | void serialize(Archive &archive, gravity &g) { 17 | archive(g.x, g.y, g.z); 18 | } 19 | 20 | } 21 | 22 | #endif // EDYN_COMP_GRAVITY_HPP 23 | -------------------------------------------------------------------------------- /include/edyn/comp/inertia.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_INERTIA_HPP 2 | #define EDYN_COMP_INERTIA_HPP 3 | 4 | #include "edyn/math/matrix3x3.hpp" 5 | 6 | namespace edyn { 7 | 8 | struct inertia : public matrix3x3 { 9 | inertia & operator=(const matrix3x3 &m) { 10 | matrix3x3::operator=(m); 11 | return *this; 12 | } 13 | }; 14 | 15 | struct inertia_inv : public matrix3x3 { 16 | inertia_inv & operator=(const matrix3x3 &m) { 17 | matrix3x3::operator=(m); 18 | return *this; 19 | } 20 | }; 21 | 22 | struct inertia_world_inv : public matrix3x3 { 23 | inertia_world_inv & operator=(const matrix3x3 &m) { 24 | matrix3x3::operator=(m); 25 | return *this; 26 | } 27 | }; 28 | 29 | template 30 | void serialize(Archive &archive, inertia &i) { 31 | archive(i.row); 32 | } 33 | 34 | template 35 | void serialize(Archive &archive, inertia_inv &i) { 36 | archive(i.row); 37 | } 38 | 39 | template 40 | void serialize(Archive &archive, inertia_world_inv &i) { 41 | archive(i.row); 42 | } 43 | 44 | } 45 | 46 | #endif // EDYN_COMP_INERTIA_HPP 47 | -------------------------------------------------------------------------------- /include/edyn/comp/island.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_ISLAND_HPP 2 | #define EDYN_COMP_ISLAND_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "edyn/comp/aabb.hpp" 11 | 12 | namespace edyn { 13 | 14 | /** 15 | * @brief An _island_ is a set of entities that can affect one another, 16 | * usually through constraints. 17 | */ 18 | struct island { 19 | entt::sparse_set nodes {}; 20 | entt::sparse_set edges {}; 21 | std::optional sleep_timestamp; 22 | }; 23 | 24 | struct island_AABB : public AABB {}; 25 | 26 | /** 27 | * @brief Component assigned to an entity that resides in an island, i.e. 28 | * procedural entities which can only be present in a single island. 29 | */ 30 | struct island_resident { 31 | entt::entity island_entity {entt::null}; 32 | }; 33 | 34 | /** 35 | * @brief Component assigned to an entity that resides in multiple islands, 36 | * i.e. non-procedural entities which can be present in multiple islands 37 | * simultaneously. 38 | */ 39 | struct multi_island_resident { 40 | entt::sparse_set island_entities {}; 41 | }; 42 | 43 | } 44 | 45 | #endif // EDYN_COMP_ISLAND_HPP 46 | -------------------------------------------------------------------------------- /include/edyn/comp/linvel.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_VELOCITY_HPP 2 | #define EDYN_COMP_VELOCITY_HPP 3 | 4 | #include "edyn/math/vector3.hpp" 5 | 6 | namespace edyn { 7 | 8 | /** 9 | * @brief Linear velocity component. 10 | */ 11 | struct linvel : public vector3 { 12 | linvel & operator=(const vector3 &v) { 13 | vector3::operator=(v); 14 | return *this; 15 | } 16 | }; 17 | 18 | template 19 | void serialize(Archive &archive, linvel &v) { 20 | archive(v.x, v.y, v.z); 21 | } 22 | 23 | } 24 | 25 | #endif // EDYN_COMP_VELOCITY_HPP 26 | -------------------------------------------------------------------------------- /include/edyn/comp/mass.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_MASS_HPP 2 | #define EDYN_COMP_MASS_HPP 3 | 4 | #include "scalar_comp.hpp" 5 | 6 | namespace edyn { 7 | 8 | struct mass : public scalar_comp {}; 9 | 10 | struct mass_inv : public scalar_comp {}; 11 | 12 | template 13 | void serialize(Archive &archive, mass &m) { 14 | archive(m.s); 15 | } 16 | 17 | template 18 | void serialize(Archive &archive, mass_inv &m) { 19 | archive(m.s); 20 | } 21 | 22 | } 23 | 24 | #endif // EDYN_COMP_MASS_HPP 25 | -------------------------------------------------------------------------------- /include/edyn/comp/material.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_MATERIAL_HPP 2 | #define EDYN_COMP_MATERIAL_HPP 3 | 4 | #include 5 | #include 6 | #include "edyn/math/scalar.hpp" 7 | #include "edyn/math/constants.hpp" 8 | 9 | namespace edyn { 10 | 11 | /** 12 | * @brief Base material info which can be set into the material mixing table 13 | * for a pair of material ids. 14 | */ 15 | struct material_base { 16 | scalar restitution {0}; 17 | scalar friction {0.5}; 18 | scalar spin_friction {0}; 19 | scalar roll_friction {0}; 20 | scalar stiffness {large_scalar}; 21 | scalar damping {large_scalar}; 22 | }; 23 | 24 | /** 25 | * @brief Rigid body material component with optional identifier for material mixing. 26 | */ 27 | struct material : public material_base { 28 | using id_type = uint16_t; 29 | static constexpr auto UnassignedID = std::numeric_limits::max(); 30 | id_type id {UnassignedID}; 31 | }; 32 | 33 | template 34 | void serialize(Archive &archive, material &m) { 35 | archive(m.restitution); 36 | archive(m.friction); 37 | archive(m.spin_friction); 38 | archive(m.roll_friction); 39 | archive(m.stiffness); 40 | archive(m.damping); 41 | } 42 | 43 | } 44 | 45 | #endif // EDYN_COMP_MATERIAL_HPP 46 | -------------------------------------------------------------------------------- /include/edyn/comp/merge_component.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_MERGE_COMPONENT_HPP 2 | #define EDYN_COMP_MERGE_COMPONENT_HPP 3 | 4 | namespace edyn { 5 | 6 | /** 7 | * @brief Function which can be specialized to perform custom logic when 8 | * replacing a component with a new value coming from a remote source, such as 9 | * importing components from the main registry into a worker and vice versa, and 10 | * importing remote values for components in client-server communication. 11 | * It simply assigns the new value by default. 12 | */ 13 | template 14 | void merge_component(Component &component, const Component &new_value) { 15 | component = new_value; 16 | } 17 | 18 | } 19 | 20 | #endif // EDYN_COMP_MERGE_COMPONENT_HPP 21 | -------------------------------------------------------------------------------- /include/edyn/comp/orientation.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_ORIENTATION_HPP 2 | #define EDYN_COMP_ORIENTATION_HPP 3 | 4 | #include "edyn/math/quaternion.hpp" 5 | 6 | namespace edyn { 7 | 8 | struct orientation : public quaternion { 9 | orientation & operator=(const quaternion &q) { 10 | quaternion::operator=(q); 11 | return *this; 12 | } 13 | }; 14 | 15 | template 16 | void serialize(Archive &archive, orientation &v) { 17 | archive(v.x, v.y, v.z, v.w); 18 | } 19 | 20 | } 21 | 22 | #endif // EDYN_COMP_ORIENTATION_HPP 23 | -------------------------------------------------------------------------------- /include/edyn/comp/origin.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_ORIGIN_HPP 2 | #define EDYN_COMP_ORIGIN_HPP 3 | 4 | #include "edyn/math/vector3.hpp" 5 | 6 | namespace edyn { 7 | 8 | /** 9 | * @brief Origin of a rigid body in world space. This is present in dynamic 10 | * rigid bodies which have a non-zero center of mass offset. 11 | */ 12 | struct origin : public vector3 { 13 | origin & operator=(const vector3 &v) { 14 | vector3::operator=(v); 15 | return *this; 16 | } 17 | }; 18 | 19 | } 20 | 21 | #endif // EDYN_COMP_ORIGIN_HPP 22 | -------------------------------------------------------------------------------- /include/edyn/comp/position.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_POSITION_HPP 2 | #define EDYN_COMP_POSITION_HPP 3 | 4 | #include "edyn/math/vector3.hpp" 5 | 6 | namespace edyn { 7 | 8 | struct position : public vector3 { 9 | position & operator=(const vector3 &v) { 10 | vector3::operator=(v); 11 | return *this; 12 | } 13 | }; 14 | 15 | template 16 | void serialize(Archive &archive, position &v) { 17 | archive(v.x, v.y, v.z); 18 | } 19 | 20 | } 21 | 22 | #endif // EDYN_COMP_POSITION_HPP 23 | -------------------------------------------------------------------------------- /include/edyn/comp/present_orientation.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_PRESENT_ORIENTATION_HPP 2 | #define EDYN_COMP_PRESENT_ORIENTATION_HPP 3 | 4 | #include "edyn/math/quaternion.hpp" 5 | 6 | namespace edyn { 7 | 8 | struct present_orientation : public quaternion { 9 | present_orientation & operator=(const quaternion &q) { 10 | quaternion::operator=(q); 11 | return *this; 12 | } 13 | }; 14 | 15 | } 16 | 17 | #endif // EDYN_COMP_PRESENT_ORIENTATION_HPP 18 | -------------------------------------------------------------------------------- /include/edyn/comp/present_position.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_PRESENT_POSITION_HPP 2 | #define EDYN_COMP_PRESENT_POSITION_HPP 3 | 4 | #include "edyn/math/vector3.hpp" 5 | 6 | namespace edyn { 7 | 8 | struct present_position : public vector3 { 9 | present_position & operator=(const vector3 &v) { 10 | vector3::operator=(v); 11 | return *this; 12 | } 13 | }; 14 | 15 | } 16 | 17 | #endif // EDYN_COMP_PRESENT_POSITION_HPP 18 | -------------------------------------------------------------------------------- /include/edyn/comp/roll_direction.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_ROLL_DIRECTION_HPP 2 | #define EDYN_COMP_ROLL_DIRECTION_HPP 3 | 4 | #include "edyn/math/vector3.hpp" 5 | 6 | namespace edyn { 7 | 8 | /** 9 | * @brief Assign to shapes that can roll in only one direction, such as 10 | * cylinders. It stores a unit vector in object space which is the 11 | * rotation axis. 12 | */ 13 | struct roll_direction : public vector3 {}; 14 | 15 | template 16 | void serialize(Archive &archive, roll_direction &d) { 17 | archive(d.x, d.y, d.z); 18 | } 19 | 20 | } 21 | 22 | #endif // EDYN_COMP_ROLL_DIRECTION_HPP 23 | -------------------------------------------------------------------------------- /include/edyn/comp/rotated_mesh_list.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_ROTATED_MESH_LIST_HPP 2 | #define EDYN_COMP_ROTATED_MESH_LIST_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include "edyn/math/quaternion.hpp" 8 | #include "edyn/shapes/convex_mesh.hpp" 9 | 10 | namespace edyn { 11 | 12 | /** 13 | * @brief A linked list of rotated meshes. 14 | * This component is assigned to entities holding a `polyhedron_shape` or a 15 | * `compound_shape` which contains polyhedrons. When associated with a 16 | * `polyhedron_shape`, `next` will always be `entt::null`. When associated with 17 | * a `compound_shape` with more than one polyhedron, new entities are created 18 | * to hold other rotated meshes. 19 | */ 20 | struct rotated_mesh_list { 21 | // The original mesh. 22 | std::shared_ptr mesh; 23 | 24 | // The rotated mesh. 25 | std::unique_ptr rotated; 26 | 27 | // Local orientation to be applied for child nodes of a compound. 28 | quaternion orientation {quaternion_identity}; 29 | 30 | // Entity of next rotated mesh in the linked list. 31 | entt::entity next {entt::null}; 32 | }; 33 | 34 | } 35 | 36 | #endif // EDYN_COMP_ROTATED_MESH_LIST_HPP 37 | -------------------------------------------------------------------------------- /include/edyn/comp/scalar_comp.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_SCALAR_COMP_HPP 2 | #define EDYN_COMP_SCALAR_COMP_HPP 3 | 4 | #include "edyn/math/scalar.hpp" 5 | 6 | namespace edyn { 7 | 8 | /** 9 | * Base struct for scalar components (e.g. mass). Provides functions for 10 | * implicit conversion to scalar. 11 | */ 12 | struct scalar_comp { 13 | scalar s; 14 | 15 | operator scalar &() { 16 | return s; 17 | } 18 | 19 | operator scalar() const { 20 | return s; 21 | } 22 | }; 23 | 24 | } 25 | 26 | #endif // EDYN_COMP_SCALAR_COMP_HPP 27 | -------------------------------------------------------------------------------- /include/edyn/comp/shape_index.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_SHAPE_INDEX_HPP 2 | #define EDYN_COMP_SHAPE_INDEX_HPP 3 | 4 | #include 5 | 6 | namespace edyn { 7 | 8 | /** 9 | * @brief Stores the index of the type of shape held by the entity which has 10 | * this component. The actual shape can be obtained using `visit_shape`. Shape 11 | * indices must be generated using `get_shape_index`. 12 | */ 13 | struct shape_index { 14 | using index_type = uint8_t; 15 | index_type value; 16 | }; 17 | 18 | template 19 | void serialize(Archive &archive, shape_index &index) { 20 | archive(index.value); 21 | } 22 | 23 | } 24 | 25 | #endif // EDYN_COMP_SHAPE_INDEX_HPP 26 | -------------------------------------------------------------------------------- /include/edyn/comp/tag.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_TAG_HPP 2 | #define EDYN_COMP_TAG_HPP 3 | 4 | namespace edyn { 5 | 6 | /** 7 | * Rigid body. 8 | */ 9 | struct rigidbody_tag {}; 10 | 11 | /** 12 | * Identifies a constraint. 13 | */ 14 | struct constraint_tag {}; 15 | 16 | /** 17 | * An island. 18 | */ 19 | struct island_tag {}; 20 | 21 | /** 22 | * Dynamic rigid body. 23 | */ 24 | struct dynamic_tag {}; 25 | 26 | /** 27 | * Kinematic rigid body. 28 | */ 29 | struct kinematic_tag {}; 30 | 31 | /** 32 | * Static rigid body. 33 | */ 34 | struct static_tag {}; 35 | 36 | /** 37 | * Procedural entity, which is an entity that has calculated state and can only 38 | * be present in a single island. 39 | */ 40 | struct procedural_tag {}; 41 | 42 | /** 43 | * An entity that is synchronized between client and server. 44 | */ 45 | struct networked_tag {}; 46 | 47 | /** 48 | * An entity that should not be shared between client and server. 49 | */ 50 | struct network_exclude_tag {}; 51 | 52 | /** 53 | * An entity that is currently asleep. 54 | */ 55 | struct sleeping_tag {}; 56 | 57 | /** 58 | * An entity that won't be put to sleep when inactive. 59 | */ 60 | struct sleeping_disabled_tag {}; 61 | 62 | /** 63 | * Disabled entity. 64 | */ 65 | struct disabled_tag {}; 66 | 67 | /** 68 | * A rigid body which holds a shape that can roll. An extra step will be 69 | * performed to help improve contact point persistence when rolling at 70 | * higher speeds. 71 | */ 72 | struct rolling_tag {}; 73 | 74 | /** 75 | * An entity that was created externally and tagged via 76 | * `edyn::tag_external_entity` (i.e. it doesn't represent any of the internal 77 | * Edyn entity types such as rigid bodies and constraints) and is part of the 78 | * entity graph and can be attached to an internal Edyn entity by means of a 79 | * `edyn::null_constraint`. 80 | */ 81 | struct external_tag {}; 82 | 83 | } 84 | 85 | #endif // EDYN_COMP_TAG_HPP 86 | -------------------------------------------------------------------------------- /include/edyn/comp/tree_resident.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_TREE_RESIDENT_HPP 2 | #define EDYN_COMP_TREE_RESIDENT_HPP 3 | 4 | #include "edyn/collision/tree_node.hpp" 5 | 6 | namespace edyn { 7 | 8 | struct tree_resident { 9 | tree_node_id_t id; 10 | bool procedural; 11 | }; 12 | 13 | } 14 | 15 | #endif // EDYN_COMP_TREE_RESIDENT_HPP 16 | -------------------------------------------------------------------------------- /include/edyn/config/config.h: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_CONFIG_CONFIG_H 2 | #define EDYN_CONFIG_CONFIG_H 3 | 4 | #ifndef EDYN_DISABLE_ASSERT 5 | #include 6 | #define EDYN_ASSERT(condition, ...) assert(condition) 7 | #else // EDYN_DISABLE_ASSERT 8 | #undef EDYN_ASSERT 9 | #define EDYN_ASSERT(...) ((void)0) 10 | #endif // EDYN_DISABLE_ASSERT 11 | 12 | #endif // EDYN_CONFIG_CONFIG_H 13 | -------------------------------------------------------------------------------- /include/edyn/config/execution_mode.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_CONFIG_EXECUTION_MODE_HPP 2 | #define EDYN_CONFIG_EXECUTION_MODE_HPP 3 | 4 | namespace edyn { 5 | 6 | /** 7 | * @brief Mode of execution of physics simulation. 8 | */ 9 | enum class execution_mode { 10 | /** 11 | * Run physics simulation in main thread. The steps are performed 12 | * sequentially in every call to `edyn::update`. 13 | */ 14 | sequential, 15 | 16 | /** 17 | * Run physics simulation partially and potentially in worker threads. 18 | * It blocks the main thread until all tasks are finished in the call 19 | * to `edyn::update`. Tasks are only parallelized if large enough. 20 | */ 21 | sequential_multithreaded, 22 | 23 | /** 24 | * Run physics simulation in background threads. The call to `edyn::update` 25 | * does a minimal amount of work, mostly merging the simulation state from 26 | * workers in the main registry. 27 | */ 28 | asynchronous 29 | }; 30 | 31 | } 32 | 33 | #endif // EDYN_CONFIG_EXECUTION_MODE_HPP 34 | -------------------------------------------------------------------------------- /include/edyn/constraints/constraint.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_CONSTRAINTS_CONSTRAINT_HPP 2 | #define EDYN_CONSTRAINTS_CONSTRAINT_HPP 3 | 4 | #include 5 | #include 6 | #include "edyn/constraints/distance_constraint.hpp" 7 | #include "edyn/constraints/soft_distance_constraint.hpp" 8 | #include "edyn/constraints/point_constraint.hpp" 9 | #include "edyn/constraints/contact_constraint.hpp" 10 | #include "edyn/constraints/hinge_constraint.hpp" 11 | #include "edyn/constraints/generic_constraint.hpp" 12 | #include "edyn/constraints/cvjoint_constraint.hpp" 13 | #include "edyn/constraints/cone_constraint.hpp" 14 | #include "edyn/constraints/gravity_constraint.hpp" 15 | 16 | namespace edyn { 17 | 18 | /** 19 | * @brief Tuple of all available constraints. They are solved in this order so 20 | * the more important constraints should be the last in the list. 21 | */ 22 | using constraints_tuple_t = std::tuple< 23 | gravity_constraint, 24 | distance_constraint, 25 | soft_distance_constraint, 26 | hinge_constraint, 27 | generic_constraint, 28 | cvjoint_constraint, 29 | cone_constraint, 30 | point_constraint, 31 | contact_constraint 32 | >; 33 | 34 | static const constraints_tuple_t constraints_tuple = constraints_tuple_t{}; 35 | 36 | } 37 | 38 | #endif // EDYN_CONSTRAINTS_CONSTRAINT_HPP 39 | -------------------------------------------------------------------------------- /include/edyn/constraints/constraint_base.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_CONSTRAINTS_CONSTRAINT_BASE_HPP 2 | #define EDYN_CONSTRAINTS_CONSTRAINT_BASE_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace edyn { 8 | 9 | struct constraint_base { 10 | std::array body; 11 | }; 12 | 13 | } 14 | 15 | #endif // EDYN_CONSTRAINTS_CONSTRAINT_BASE_HPP 16 | -------------------------------------------------------------------------------- /include/edyn/constraints/constraint_body.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_CONSTRAINTS_CONSTRAINT_BODY_HPP 2 | #define EDYN_CONSTRAINTS_CONSTRAINT_BODY_HPP 3 | 4 | #include "edyn/math/matrix3x3.hpp" 5 | #include "edyn/math/quaternion.hpp" 6 | #include "edyn/math/vector3.hpp" 7 | 8 | namespace edyn { 9 | 10 | struct constraint_body { 11 | vector3 origin; 12 | vector3 pos; 13 | quaternion orn; 14 | vector3 linvel; 15 | vector3 angvel; 16 | scalar inv_m; 17 | matrix3x3 inv_I; 18 | }; 19 | 20 | } 21 | 22 | #endif // EDYN_CONSTRAINTS_CONSTRAINT_BODY_HPP 23 | -------------------------------------------------------------------------------- /include/edyn/constraints/constraint_row.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COMP_CONSTRAINT_ROW_HPP 2 | #define EDYN_COMP_CONSTRAINT_ROW_HPP 3 | 4 | #include 5 | #include "edyn/math/vector3.hpp" 6 | #include "edyn/math/matrix3x3.hpp" 7 | #include "edyn/config/constants.hpp" 8 | #include "edyn/comp/delta_linvel.hpp" 9 | #include "edyn/comp/delta_angvel.hpp" 10 | 11 | namespace edyn { 12 | 13 | struct constraint_row_options; 14 | 15 | /** 16 | * `constraint_row` contains all and only the information that's required 17 | * during the constraint solver iterations for better cache use and to avoid 18 | * waste. 19 | */ 20 | struct constraint_row { 21 | // Jacobian diagonals. 22 | std::array J; 23 | 24 | // Effective mass (J M^-1 J^T)^-1. 25 | scalar eff_mass; 26 | 27 | // Right hand side Jv + bias. 28 | scalar rhs; 29 | 30 | // Lower and upper limit of impulses to be applied while solving constraints. 31 | scalar lower_limit; 32 | scalar upper_limit; 33 | 34 | // Last applied impulse. Used for warm-starting and can be used to measure 35 | // strength of impulse applied. 36 | scalar impulse; 37 | 38 | // Inverse masses and inertias used during the solver iterations. 39 | scalar inv_mA, inv_mB; 40 | matrix3x3 inv_IA, inv_IB; 41 | 42 | // Reference to delta velocities used during solver iterations. It is not 43 | // safe to dereference these outside of the solver update context. 44 | delta_linvel *dvA, *dvB; 45 | delta_angvel *dwA, *dwB; 46 | }; 47 | 48 | void prepare_row(constraint_row &row, 49 | const constraint_row_options &options, 50 | const vector3 &linvelA, const vector3 &angvelA, 51 | const vector3 &linvelB, const vector3 &angvelB); 52 | 53 | void apply_row_impulse(scalar impulse, constraint_row &row); 54 | 55 | void warm_start(constraint_row &row); 56 | 57 | scalar solve(constraint_row &row); 58 | 59 | } 60 | 61 | #endif // EDYN_COMP_CONSTRAINT_ROW_HPP 62 | -------------------------------------------------------------------------------- /include/edyn/constraints/constraint_row_friction.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_CONSTRAINTS_CONSTRAINT_ROW_FRICTION_HPP 2 | #define EDYN_CONSTRAINTS_CONSTRAINT_ROW_FRICTION_HPP 3 | 4 | #include "edyn/constraints/constraint_row.hpp" 5 | #include "edyn/math/vector3.hpp" 6 | #include 7 | 8 | namespace edyn { 9 | 10 | struct row_cache; 11 | 12 | struct constraint_row_friction { 13 | struct individual_row { 14 | std::array J; 15 | scalar eff_mass; 16 | scalar rhs; 17 | scalar impulse; 18 | }; 19 | 20 | individual_row row[2]; 21 | scalar friction_coefficient; 22 | unsigned normal_row_index; 23 | }; 24 | 25 | void solve_friction(constraint_row_friction &row, const std::vector &row_cache); 26 | void warm_start(constraint_row_friction &row, const std::vector &row_cache); 27 | 28 | } 29 | 30 | #endif // EDYN_CONSTRAINTS_CONSTRAINT_ROW_FRICTION_HPP 31 | -------------------------------------------------------------------------------- /include/edyn/constraints/constraint_row_options.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_CONSTRAINTS_CONSTRAINT_ROW_OPTIONS_HPP 2 | #define EDYN_CONSTRAINTS_CONSTRAINT_ROW_OPTIONS_HPP 3 | 4 | #include "edyn/math/scalar.hpp" 5 | 6 | namespace edyn { 7 | 8 | /** 9 | * Optional info to be used when setting up a constraint row. 10 | */ 11 | struct constraint_row_options { 12 | scalar error {scalar(0)}; 13 | 14 | // Error reduction parameter. 15 | scalar erp {scalar(0.2)}; 16 | 17 | scalar restitution {scalar(0)}; 18 | }; 19 | 20 | } 21 | 22 | #endif // EDYN_CONSTRAINTS_CONSTRAINT_ROW_OPTIONS_HPP 23 | -------------------------------------------------------------------------------- /include/edyn/constraints/constraint_row_spin_friction.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_CONSTRAINTS_CONSTRAINT_ROW_SPIN_FRICTION_HPP 2 | #define EDYN_CONSTRAINTS_CONSTRAINT_ROW_SPIN_FRICTION_HPP 3 | 4 | #include "edyn/constraints/constraint_row.hpp" 5 | #include "edyn/math/vector3.hpp" 6 | #include 7 | 8 | namespace edyn { 9 | 10 | struct constraint_row_spin_friction { 11 | // Include only angular components in Jacobian since this constraint only 12 | // applies angular impulses. 13 | std::array J; 14 | scalar eff_mass; 15 | scalar rhs; 16 | scalar impulse; 17 | scalar friction_coefficient; 18 | unsigned normal_row_index; 19 | }; 20 | 21 | void solve_spin_friction(constraint_row_spin_friction &row, const std::vector &row_cache); 22 | void warm_start(constraint_row_spin_friction &row, const std::vector &row_cache); 23 | 24 | } 25 | 26 | #endif // EDYN_CONSTRAINTS_CONSTRAINT_ROW_SPIN_FRICTION_HPP 27 | -------------------------------------------------------------------------------- /include/edyn/constraints/contact_constraint.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_CONSTRAINTS_CONTACT_CONSTRAINT_HPP 2 | #define EDYN_CONSTRAINTS_CONTACT_CONSTRAINT_HPP 3 | 4 | #include "edyn/config/constants.hpp" 5 | #include "edyn/math/constants.hpp" 6 | #include "edyn/constraints/constraint_base.hpp" 7 | #include "edyn/constraints/constraint_body.hpp" 8 | 9 | namespace edyn { 10 | 11 | class position_solver; 12 | struct constraint_row_prep_cache; 13 | struct contact_manifold; 14 | 15 | /** 16 | * @brief Non-penetration constraint. 17 | */ 18 | struct contact_constraint : public constraint_base { 19 | void prepare( 20 | const entt::registry &, entt::entity, const contact_manifold &, 21 | constraint_row_prep_cache &cache, scalar dt, 22 | const constraint_body &bodyA, const constraint_body &bodyB); 23 | 24 | void solve_position(position_solver &, contact_manifold &); 25 | 26 | void store_applied_impulse(scalar impulse, unsigned row_index, contact_manifold &); 27 | void store_friction_impulse(scalar impulse0, scalar impulse1, unsigned row_index, contact_manifold &); 28 | void store_rolling_impulse(scalar impulse0, scalar impulse1, unsigned row_index, contact_manifold &); 29 | void store_spinning_impulse(scalar impulse, unsigned row_index, contact_manifold &); 30 | }; 31 | 32 | template 33 | void serialize(Archive &archive, contact_constraint &c) { 34 | archive(c.body); 35 | } 36 | 37 | } 38 | 39 | #endif // EDYN_CONSTRAINTS_CONTACT_CONSTRAINT_HPP 40 | -------------------------------------------------------------------------------- /include/edyn/constraints/distance_constraint.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_CONSTRAINTS_DISTANCE_CONSTRAINT 2 | #define EDYN_CONSTRAINTS_DISTANCE_CONSTRAINT 3 | 4 | #include 5 | #include 6 | #include 7 | #include "edyn/math/vector3.hpp" 8 | #include "edyn/constraints/constraint_base.hpp" 9 | #include "edyn/constraints/constraint_body.hpp" 10 | 11 | namespace edyn { 12 | 13 | struct matrix3x3; 14 | struct quaternion; 15 | struct constraint_row_prep_cache; 16 | 17 | struct distance_constraint : public constraint_base { 18 | std::array pivot; 19 | scalar distance {0}; 20 | scalar applied_impulse {0}; 21 | 22 | void prepare( 23 | const entt::registry &, entt::entity, 24 | constraint_row_prep_cache &cache, scalar dt, 25 | const constraint_body &bodyA, const constraint_body &bodyB); 26 | 27 | void store_applied_impulses(const std::vector &impulses); 28 | }; 29 | 30 | template 31 | void serialize(Archive &archive, distance_constraint &c) { 32 | archive(c.body); 33 | archive(c.pivot); 34 | archive(c.distance); 35 | archive(c.applied_impulse); 36 | } 37 | 38 | } 39 | 40 | #endif // EDYN_CONSTRAINTS_DISTANCE_CONSTRAINT 41 | -------------------------------------------------------------------------------- /include/edyn/constraints/gravity_constraint.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_CONSTRAINTS_GRAVITY_CONSTRAINT_HPP 2 | #define EDYN_CONSTRAINTS_GRAVITY_CONSTRAINT_HPP 3 | 4 | #include 5 | #include 6 | #include "edyn/math/scalar.hpp" 7 | #include "edyn/constraints/constraint_base.hpp" 8 | #include "edyn/constraints/constraint_body.hpp" 9 | 10 | namespace edyn { 11 | 12 | struct vector3; 13 | struct quaternion; 14 | struct matrix3x3; 15 | class position_solver; 16 | struct constraint_row_prep_cache; 17 | 18 | /** 19 | * @brief Applies gravitational attraction forces between two entities. 20 | */ 21 | struct gravity_constraint : public constraint_base { 22 | scalar applied_impulse {scalar(0)}; 23 | 24 | void prepare( 25 | const entt::registry &, entt::entity, 26 | constraint_row_prep_cache &cache, scalar dt, 27 | const constraint_body &bodyA, const constraint_body &bodyB); 28 | 29 | void store_applied_impulses(const std::vector &impulses); 30 | }; 31 | 32 | template 33 | void serialize(Archive &archive, gravity_constraint &con) { 34 | archive(con.body); 35 | archive(con.applied_impulse); 36 | } 37 | 38 | 39 | } 40 | 41 | #endif // EDYN_CONSTRAINTS_GRAVITY_CONSTRAINT_HPP 42 | -------------------------------------------------------------------------------- /include/edyn/constraints/null_constraint.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_CONSTRAINTS_NULL_CONSTRAINT_HPP 2 | #define EDYN_CONSTRAINTS_NULL_CONSTRAINT_HPP 3 | 4 | #include 5 | #include "edyn/constraints/constraint_base.hpp" 6 | 7 | namespace edyn { 8 | 9 | /** 10 | * @brief A constraint that does nothing. It is primarily used to connect 11 | * related or dependent entities together in the entity graph so they'll 12 | * always be present together in the same island. 13 | */ 14 | struct null_constraint : public constraint_base {}; 15 | 16 | template 17 | void serialize(Archive &archive, null_constraint &con) { 18 | archive(con.body); 19 | } 20 | 21 | } 22 | 23 | #endif // EDYN_CONSTRAINTS_NULL_CONSTRAINT_HPP 24 | -------------------------------------------------------------------------------- /include/edyn/constraints/point_constraint.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_CONSTRAINTS_POINT_CONSTRAINT_HPP 2 | #define EDYN_CONSTRAINTS_POINT_CONSTRAINT_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include "edyn/math/vector3.hpp" 8 | #include "edyn/constraints/constraint_base.hpp" 9 | #include "edyn/constraints/constraint_body.hpp" 10 | 11 | namespace edyn { 12 | 13 | struct constraint_row_prep_cache; 14 | class position_solver; 15 | struct quaternion; 16 | struct matrix3x3; 17 | 18 | /** 19 | * @brief Constrains two points on a pair of rigid bodies to match in space. 20 | */ 21 | struct point_constraint : public constraint_base { 22 | // Pivot points in object space. 23 | std::array pivot; 24 | 25 | scalar friction_torque{}; 26 | 27 | std::array applied_impulse {}; 28 | scalar applied_friction_impulse {}; 29 | 30 | void prepare( 31 | const entt::registry &, entt::entity, 32 | constraint_row_prep_cache &cache, scalar dt, 33 | const constraint_body &bodyA, const constraint_body &bodyB); 34 | 35 | void store_applied_impulses(const std::vector &impulses); 36 | }; 37 | 38 | template 39 | void serialize(Archive &archive, point_constraint &c) { 40 | archive(c.body); 41 | archive(c.pivot); 42 | archive(c.friction_torque); 43 | archive(c.applied_impulse); 44 | archive(c.applied_friction_impulse); 45 | } 46 | 47 | } 48 | 49 | #endif // EDYN_CONSTRAINTS_POINT_CONSTRAINT_HPP 50 | -------------------------------------------------------------------------------- /include/edyn/constraints/soft_distance_constraint.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_CONSTRAINTS_SOFT_DISTANCE_CONSTRAINT_HPP 2 | #define EDYN_CONSTRAINTS_SOFT_DISTANCE_CONSTRAINT_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include "edyn/math/vector3.hpp" 8 | #include "edyn/constraints/constraint_base.hpp" 9 | 10 | namespace edyn { 11 | 12 | struct constraint_row_prep_cache; 13 | struct constraint_body; 14 | 15 | struct soft_distance_constraint : public constraint_base { 16 | std::array pivot; 17 | scalar distance {0}; 18 | scalar stiffness {1e10}; 19 | scalar damping {1e10}; 20 | 21 | scalar applied_spring_impulse {}; 22 | scalar applied_damping_impulse {}; 23 | 24 | void prepare( 25 | const entt::registry &, entt::entity, 26 | constraint_row_prep_cache &cache, scalar dt, 27 | const constraint_body &bodyA, const constraint_body &bodyB); 28 | 29 | void store_applied_impulses(const std::vector &impulses); 30 | }; 31 | 32 | template 33 | void serialize(Archive &archive, soft_distance_constraint &c) { 34 | archive(c.body); 35 | archive(c.pivot); 36 | archive(c.distance); 37 | archive(c.stiffness); 38 | archive(c.damping); 39 | archive(c.applied_spring_impulse); 40 | archive(c.applied_damping_impulse); 41 | } 42 | 43 | } 44 | 45 | #endif // EDYN_CONSTRAINTS_SOFT_DISTANCE_CONSTRAINT_HPP 46 | -------------------------------------------------------------------------------- /include/edyn/context/registry_operation_context.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_CONTEXT_REGISTRY_OPERATION_CONTEXT_HPP 2 | #define EDYN_CONTEXT_REGISTRY_OPERATION_CONTEXT_HPP 3 | 4 | #include "edyn/replication/make_reg_op_builder.hpp" 5 | 6 | namespace edyn { 7 | 8 | std::unique_ptr make_reg_op_builder_default(entt::registry &); 9 | std::unique_ptr make_reg_op_observer_default(registry_operation_builder &); 10 | 11 | struct registry_operation_context { 12 | make_reg_op_builder_func_t make_reg_op_builder {&make_reg_op_builder_default}; 13 | make_reg_op_observer_func_t make_reg_op_observer {&make_reg_op_observer_default}; 14 | }; 15 | 16 | } 17 | 18 | #endif // EDYN_CONTEXT_REGISTRY_OPERATION_CONTEXT_HPP 19 | -------------------------------------------------------------------------------- /include/edyn/context/start_thread.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_CONTEXT_START_THREAD_HPP 2 | #define EDYN_CONTEXT_START_THREAD_HPP 3 | 4 | namespace edyn { 5 | 6 | using start_thread_func_t = void(void(*)(void *), void *); 7 | 8 | void start_thread_func_default(void (*func)(void *), void *data); 9 | 10 | } 11 | 12 | #endif // EDYN_CONTEXT_START_THREAD_HPP 13 | -------------------------------------------------------------------------------- /include/edyn/context/task.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_CONTEXT_TASK_HPP 2 | #define EDYN_CONTEXT_TASK_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace edyn { 9 | 10 | // EnTT delegate for background tasks. It receives a range as argument. 11 | using task_delegate_t = entt::delegate; 12 | // EnTT delegate to be called when a background task is finished. 13 | using task_completion_delegate_t = entt::delegate; 14 | // Function to schedule a task to be run in worker threads and return immediately. 15 | // It takes a task delegate to be invoked over the range [0, size) and an 16 | // optional completion to be called when the task is done. 17 | using enqueue_task_t = void (task_delegate_t task, unsigned size, task_completion_delegate_t completion); 18 | // Function to schedule a task to be run in worker threads and wait for 19 | // execution to be finished. 20 | using enqueue_task_wait_t = void (task_delegate_t task, unsigned size); 21 | 22 | void enqueue_task_default(task_delegate_t task, unsigned size, task_completion_delegate_t completion); 23 | void enqueue_task_wait_default(task_delegate_t task, unsigned size); 24 | 25 | } 26 | 27 | #endif // EDYN_CONTEXT_TASK_HPP 28 | -------------------------------------------------------------------------------- /include/edyn/context/task_util.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_CONTEXT_TASK_UTIL_HPP 2 | #define EDYN_CONTEXT_TASK_UTIL_HPP 3 | 4 | #include "edyn/context/settings.hpp" 5 | #include "edyn/context/task.hpp" 6 | #include 7 | #include 8 | 9 | namespace edyn { 10 | 11 | inline void enqueue_task(entt::registry ®istry, task_delegate_t task, unsigned size, task_completion_delegate_t completion) { 12 | auto &settings = registry.ctx().get(); 13 | (*settings.enqueue_task)(task, size, completion); 14 | } 15 | 16 | inline void enqueue_task_wait(entt::registry ®istry, task_delegate_t task, unsigned size) { 17 | auto &settings = registry.ctx().get(); 18 | (*settings.enqueue_task_wait)(task, size); 19 | } 20 | 21 | } 22 | 23 | #endif // EDYN_CONTEXT_TASK_UTIL_HPP 24 | -------------------------------------------------------------------------------- /include/edyn/core/entity_pair.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_CORE_ENTITY_PAIR 2 | #define EDYN_CORE_ENTITY_PAIR 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace edyn { 9 | 10 | using entity_pair = std::pair; 11 | using entity_pair_vector = std::vector; 12 | 13 | } 14 | 15 | #endif // EDYN_CORE_ENTITY_PAIR 16 | -------------------------------------------------------------------------------- /include/edyn/core/unordered_pair.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_CORE_UNORDERED_PAIR_HPP 2 | #define EDYN_CORE_UNORDERED_PAIR_HPP 3 | 4 | #include "edyn/config/config.h" 5 | 6 | namespace edyn { 7 | 8 | /** 9 | * A pair of values of the same type which tests equals independent of order. 10 | */ 11 | template 12 | struct unordered_pair { 13 | T first; 14 | T second; 15 | 16 | unordered_pair() = default; 17 | unordered_pair(T first, T second) 18 | : first(first) 19 | , second(second) 20 | {} 21 | 22 | auto operator[](unsigned idx) const { 23 | EDYN_ASSERT(idx < 2); 24 | return idx == 0 ? first : second; 25 | } 26 | 27 | bool operator==(const unordered_pair &other) const { 28 | return (first == other.first && second == other.second) || 29 | (first == other.second && second == other.first); 30 | } 31 | 32 | bool operator<(const unordered_pair &other) const { 33 | if (first == other.second && second == other.first) { 34 | return false; 35 | } 36 | if (first == other.first) { 37 | return second < other.second; 38 | } 39 | return first < other.first; 40 | } 41 | }; 42 | 43 | } 44 | 45 | #endif // EDYN_CORE_UNORDERED_PAIR_HPP 46 | -------------------------------------------------------------------------------- /include/edyn/dynamics/island_constraint_entities.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_DYNAMICS_ISLAND_CONSTRAINT_ENTITIES_HPP 2 | #define EDYN_DYNAMICS_ISLAND_CONSTRAINT_ENTITIES_HPP 3 | 4 | #include 5 | #include 6 | #include "edyn/constraints/constraint.hpp" 7 | 8 | namespace edyn { 9 | 10 | /** 11 | * Stores the entities of each constraint type that reside in the island to which 12 | * this component is assigned. Each vector of entities is in direct correspondence 13 | * with the type in `constraints_tuple`. This component enables the solver to 14 | * go over constraint entities per constraint type. 15 | */ 16 | struct island_constraint_entities { 17 | std::array, std::tuple_size_v> entities; 18 | }; 19 | 20 | } 21 | 22 | #endif // EDYN_DYNAMICS_ISLAND_CONSTRAINT_ENTITIES_HPP 23 | -------------------------------------------------------------------------------- /include/edyn/dynamics/island_solver.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_DYNAMICS_ISLAND_SOLVER_HPP 2 | #define EDYN_DYNAMICS_ISLAND_SOLVER_HPP 3 | 4 | #include "edyn/math/scalar.hpp" 5 | #include 6 | 7 | namespace edyn { 8 | 9 | class atomic_counter_sync; 10 | 11 | void run_island_solver_seq_mt(entt::registry &, entt::entity island_entity, 12 | unsigned num_iterations, unsigned num_position_iterations, 13 | scalar dt, atomic_counter_sync *counter); 14 | 15 | void run_island_solver_seq(entt::registry &, entt::entity island_entity, 16 | unsigned num_iterations, unsigned num_position_iterations, 17 | scalar dt); 18 | 19 | } 20 | 21 | #endif // EDYN_DYNAMICS_ISLAND_SOLVER_HPP 22 | -------------------------------------------------------------------------------- /include/edyn/dynamics/restitution_solver.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_DYNAMICS_RESTITUTION_SOLVER_HPP 2 | #define EDYN_DYNAMICS_RESTITUTION_SOLVER_HPP 3 | 4 | #include "edyn/math/scalar.hpp" 5 | #include 6 | 7 | namespace edyn { 8 | 9 | void solve_restitution(entt::registry ®istry, scalar dt); 10 | 11 | } 12 | 13 | #endif // EDYN_DYNAMICS_RESTITUTION_SOLVER_HPP 14 | -------------------------------------------------------------------------------- /include/edyn/dynamics/solver.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_DYNAMICS_SOLVER_HPP 2 | #define EDYN_DYNAMICS_SOLVER_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "edyn/math/scalar.hpp" 9 | 10 | namespace edyn { 11 | 12 | struct job; 13 | struct constraint_row; 14 | 15 | scalar solve(constraint_row &row); 16 | 17 | class solver final { 18 | 19 | public: 20 | solver(entt::registry &); 21 | ~solver(); 22 | 23 | void update(bool mt); 24 | 25 | private: 26 | entt::registry *m_registry; 27 | std::vector m_connections; 28 | }; 29 | 30 | } 31 | 32 | #endif // EDYN_DYNAMICS_SOLVER_HPP 33 | -------------------------------------------------------------------------------- /include/edyn/math/constants.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_MATH_CONSTANTS_HPP 2 | #define EDYN_MATH_CONSTANTS_HPP 3 | 4 | #include 5 | #include 6 | #include "vector3.hpp" 7 | 8 | namespace edyn { 9 | 10 | inline constexpr auto pi = scalar(3.1415926535897932384626433832795029); 11 | inline constexpr auto pi2 = pi * scalar(2); 12 | inline constexpr auto half_pi = pi * scalar(0.5); 13 | inline constexpr auto pi_half = half_pi; 14 | inline constexpr auto half_sqrt2 = scalar(0.7071067811865475244008443621048490); 15 | // `large_scalar * large_scalar` < EDYN_SCALAR_MAX` 16 | inline constexpr auto large_scalar = scalar(1e18); 17 | // `small_scalar * small_scalar` > EDYN_SCALAR_MIN` 18 | inline constexpr auto small_scalar = scalar(1e-18); 19 | inline constexpr auto gravitational_constant = scalar(6.674e-11); 20 | 21 | inline constexpr vector3 gravity_sun = vector3_y * scalar(-274); 22 | inline constexpr vector3 gravity_mercury = vector3_y * scalar(-3.7); 23 | inline constexpr vector3 gravity_venus = vector3_y * scalar(-8.87); 24 | inline constexpr vector3 gravity_earth = vector3_y * scalar(-9.8); 25 | inline constexpr vector3 gravity_mars = vector3_y * scalar(-3.721); 26 | inline constexpr vector3 gravity_jupiter = vector3_y * scalar(-24.79); 27 | inline constexpr vector3 gravity_saturn = vector3_y * scalar(-10.44); 28 | inline constexpr vector3 gravity_uranus = vector3_y * scalar(-8.69); 29 | inline constexpr vector3 gravity_neptune = vector3_y * scalar(-11.15); 30 | inline constexpr vector3 gravity_pluto = vector3_y * scalar(-0.62); 31 | inline constexpr vector3 gravity_moon = vector3_y * scalar(-1.625); 32 | 33 | } 34 | 35 | #endif // EDYN_MATH_CONSTANTS_HPP 36 | -------------------------------------------------------------------------------- /include/edyn/math/coordinate_axis.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_SHAPES_COORDINATE_AXIS_HPP 2 | #define EDYN_SHAPES_COORDINATE_AXIS_HPP 3 | 4 | #include "edyn/config/config.h" 5 | #include "edyn/math/quaternion.hpp" 6 | #include "edyn/math/vector3.hpp" 7 | #include "edyn/serialization/s11n_util.hpp" 8 | 9 | namespace edyn { 10 | 11 | /** 12 | * @brief One of the three coordinate axes: X, Y, Z. 13 | */ 14 | enum class coordinate_axis : unsigned char { 15 | x, y, z 16 | }; 17 | 18 | /** 19 | * @brief Vector for a coordinate axis. 20 | * @param axis Axis. 21 | * @return Vector. 22 | */ 23 | constexpr vector3 coordinate_axis_vector(coordinate_axis axis) { 24 | switch (axis) { 25 | case coordinate_axis::x: 26 | return vector3_x; 27 | case coordinate_axis::y: 28 | return vector3_y; 29 | case coordinate_axis::z: 30 | return vector3_z; 31 | } 32 | EDYN_ASSERT(false); 33 | return vector3_zero; 34 | } 35 | 36 | /** 37 | * @brief Rotated vector for a coordinate axis. 38 | * @param axis Axis. 39 | * @param q Rotation. 40 | * @return Rotated vector. 41 | */ 42 | constexpr vector3 coordinate_axis_vector(coordinate_axis axis, const quaternion &q) { 43 | return rotate(q, coordinate_axis_vector(axis)); 44 | } 45 | 46 | template 47 | void serialize(Archive &archive, coordinate_axis &axis) { 48 | serialize_enum(archive, axis); 49 | } 50 | 51 | } 52 | 53 | #endif // EDYN_SHAPES_COORDINATE_AXIS_HPP 54 | -------------------------------------------------------------------------------- /include/edyn/math/scalar.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_MATH_SCALAR_HPP 2 | #define EDYN_MATH_SCALAR_HPP 3 | 4 | #include 5 | #include "edyn/build_settings.h" 6 | 7 | namespace edyn { 8 | 9 | #ifdef EDYN_DOUBLE_PRECISION 10 | using scalar = double; 11 | #define EDYN_EPSILON (DBL_EPSILON) 12 | #define EDYN_SCALAR_MIN (DBL_MIN) 13 | #define EDYN_SCALAR_MAX (DBL_MAX) 14 | #else 15 | using scalar = float; 16 | #define EDYN_EPSILON (FLT_EPSILON) 17 | #define EDYN_SCALAR_MIN (FLT_MIN) 18 | #define EDYN_SCALAR_MAX (FLT_MAX) 19 | #endif 20 | 21 | } 22 | 23 | #endif // EDYN_MATH_SCALAR_HPP 24 | -------------------------------------------------------------------------------- /include/edyn/math/shape_volume.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_MATH_SHAPE_VOLUME_HPP 2 | #define EDYN_MATH_SHAPE_VOLUME_HPP 3 | 4 | #include "edyn/math/vector3.hpp" 5 | 6 | namespace edyn { 7 | 8 | struct box_shape; 9 | struct capsule_shape; 10 | struct compound_shape; 11 | struct cylinder_shape; 12 | struct polyhedron_shape; 13 | struct sphere_shape; 14 | struct convex_mesh; 15 | 16 | /** 17 | * @brief Calculate volume of a cylinder. 18 | * @param radius Radius of cylinder. 19 | * @param length Length of cylinder. 20 | * @return Volume of cylinder. 21 | */ 22 | scalar cylinder_volume(scalar radius, scalar length); 23 | 24 | /** 25 | * @brief Calculate volume of a sphere. 26 | * @param radius Radius of sphere. 27 | * @return Volume of sphere. 28 | */ 29 | scalar sphere_volume(scalar radius); 30 | 31 | /** 32 | * @brief Calculate volume of a box. 33 | * @param extents Size of box in all 3 dimensions. 34 | * @return Volume of box. 35 | */ 36 | scalar box_volume(const vector3 &extents); 37 | 38 | /** 39 | * @brief Calculate volume of a convex mesh. 40 | * @param mesh A convex mesh. 41 | * @return Volume of convex mesh. 42 | */ 43 | scalar mesh_volume(const convex_mesh &mesh); 44 | 45 | // Calculate volume for all relevant shapes. 46 | scalar shape_volume(const box_shape &sh); 47 | scalar shape_volume(const capsule_shape &sh); 48 | scalar shape_volume(const compound_shape &sh); 49 | scalar shape_volume(const cylinder_shape &sh); 50 | scalar shape_volume(const polyhedron_shape &sh); 51 | scalar shape_volume(const sphere_shape &sh); 52 | 53 | } 54 | 55 | #endif // EDYN_MATH_SHAPE_VOLUME_HPP 56 | -------------------------------------------------------------------------------- /include/edyn/math/transform.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_MATH_TRANSFORM_HPP 2 | #define EDYN_MATH_TRANSFORM_HPP 3 | 4 | #include "edyn/math/vector3.hpp" 5 | #include "edyn/math/quaternion.hpp" 6 | #include "edyn/math/matrix3x3.hpp" 7 | 8 | namespace edyn { 9 | 10 | /** 11 | * @brief Converts a point in world space to object space. 12 | * @param p A point in world space. 13 | * @param pos Position in world space. 14 | * @param basis Rotation matrix in world space. 15 | * @return The point `p` in object space. 16 | */ 17 | constexpr 18 | vector3 to_object_space(const vector3 &p, const vector3 &pos, const matrix3x3 &basis) noexcept { 19 | // Multiplying a vector by a matrix on the right is equivalent to multiplying 20 | // by the transpose of the matrix on the left, and the transpose of a rotation 21 | // matrix is its inverse. 22 | return (p - pos) * basis; 23 | } 24 | 25 | /** 26 | * @brief Converts a point in object space to world space. 27 | * @param p A point in object space. 28 | * @param pos Position in world space. 29 | * @param basis Rotation matrix in world space. 30 | * @return The point `p` in world space. 31 | */ 32 | constexpr 33 | vector3 to_world_space(const vector3 &p, const vector3 &pos, const matrix3x3 &basis) noexcept { 34 | return pos + basis * p; 35 | } 36 | 37 | constexpr 38 | vector3 to_object_space(const vector3 &p, const vector3 &pos, const quaternion &orn) noexcept { 39 | return rotate(conjugate(orn), p - pos); 40 | } 41 | 42 | constexpr 43 | vector3 to_world_space(const vector3 &p, const vector3 &pos, const quaternion &orn) noexcept { 44 | return pos + rotate(orn, p); 45 | } 46 | 47 | } 48 | 49 | #endif // EDYN_MATH_TRANSFORM_HPP 50 | -------------------------------------------------------------------------------- /include/edyn/math/vector2_3_util.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_MATH_VECTOR2_3_UTIL_HPP 2 | #define EDYN_MATH_VECTOR2_3_UTIL_HPP 3 | 4 | #include "edyn/math/vector2.hpp" 5 | #include "edyn/math/vector3.hpp" 6 | 7 | namespace edyn { 8 | 9 | // Series of functions to project a `vector3` into a coordinate plane and 10 | // return that as a `vector2`. 11 | 12 | constexpr vector2 to_vector2_xy(const vector3 &v) noexcept { 13 | return {v.x, v.y}; 14 | } 15 | 16 | constexpr vector2 to_vector2_xz(const vector3 &v) noexcept { 17 | return {v.x, v.z}; 18 | } 19 | 20 | constexpr vector2 to_vector2_yz(const vector3 &v) noexcept { 21 | return {v.y, v.z}; 22 | } 23 | 24 | constexpr vector2 to_vector2_zy(const vector3 &v) noexcept { 25 | return {v.z, v.y}; 26 | } 27 | 28 | constexpr vector2 to_vector2_yx(const vector3 &v) noexcept { 29 | return {v.y, v.x}; 30 | } 31 | 32 | constexpr vector2 to_vector2_zx(const vector3 &v) noexcept { 33 | return {v.z, v.x}; 34 | } 35 | 36 | // Series of functions to convert a `vector2` into a `vector3` contained in a 37 | // coordinate plane. 38 | 39 | constexpr vector3 to_vector3_xy(const vector2 &v) noexcept { 40 | return {v.x, v.y, 0}; 41 | } 42 | 43 | constexpr vector3 to_vector3_xz(const vector2 &v) noexcept { 44 | return {v.x, 0, v.y}; 45 | } 46 | 47 | constexpr vector3 to_vector3_yz(const vector2 &v) noexcept { 48 | return {0, v.x, v.y}; 49 | } 50 | 51 | constexpr vector3 to_vector3_zy(const vector2 &v) noexcept { 52 | return {0, v.y, v.x}; 53 | } 54 | 55 | constexpr vector3 to_vector3_yx(const vector2 &v) noexcept { 56 | return {v.y, v.x, 0}; 57 | } 58 | 59 | constexpr vector3 to_vector3_zx(const vector2 &v) noexcept { 60 | return {v.y, 0, v.x}; 61 | } 62 | 63 | } 64 | 65 | #endif // EDYN_MATH_VECTOR2_3_UTIL_HPP 66 | -------------------------------------------------------------------------------- /include/edyn/networking/comp/aabb_of_interest.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_AABB_OF_INTEREST_HPP 2 | #define EDYN_NETWORKING_AABB_OF_INTEREST_HPP 3 | 4 | #include "edyn/comp/aabb.hpp" 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace edyn { 12 | 13 | /** 14 | * @brief Assigned to each client in the server side. Networked entities which 15 | * intersect this AABB will be shared with the client. 16 | */ 17 | struct aabb_of_interest { 18 | // The AABB of interest. 19 | AABB aabb {vector3_one * -500, vector3_one * 500}; 20 | 21 | // Entities in the islands above, including nodes and edges. This is used 22 | // as a way to tell which entities have entered and exited the AABB. 23 | entt::sparse_set entities {}; 24 | 25 | // Entities that entered and exited the AABB in the last update. These 26 | // containers are for temporary data storage in the AABB of interest update 27 | // and so they get cleared up in every update and should not be modified. 28 | std::vector entities_entered; 29 | std::vector entities_exited; 30 | 31 | std::unordered_map asset_entity_count; 32 | 33 | static constexpr auto in_place_delete = true; 34 | }; 35 | 36 | } 37 | 38 | #endif // EDYN_NETWORKING_AABB_OF_INTEREST_HPP 39 | -------------------------------------------------------------------------------- /include/edyn/networking/comp/aabb_oi_follow.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_COMP_AABB_OI_FOLLOW_HPP 2 | #define EDYN_NETWORKING_COMP_AABB_OI_FOLLOW_HPP 3 | 4 | namespace edyn { 5 | 6 | /** 7 | * @brief The center of the AABB of interest of the remote client which has 8 | * this component will be set to match the position of the referred entity. 9 | */ 10 | struct aabb_oi_follow { 11 | entt::entity entity; 12 | }; 13 | 14 | } 15 | 16 | #endif // EDYN_NETWORKING_COMP_AABB_OI_FOLLOW_HPP 17 | -------------------------------------------------------------------------------- /include/edyn/networking/comp/asset_entry.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_COMP_ASSET_ENTRY_HPP 2 | #define EDYN_NETWORKING_COMP_ASSET_ENTRY_HPP 3 | 4 | #include "edyn/networking/util/component_index_type.hpp" 5 | #include 6 | #include 7 | #include 8 | 9 | namespace edyn { 10 | 11 | /** 12 | * @brief An entry contained in an asset. 13 | * @remark When an asset is replicated, the components referenced in `sync_indices` 14 | * will be included in a registry snapshot and be sent to the other end to ensure 15 | * the corresponding entity will be instantiated with the correct initial state. 16 | */ 17 | struct asset_entry { 18 | // Asset it belongs to. 19 | entt::entity asset_entity; 20 | // Unique id inside of asset. 21 | entt::id_type id; 22 | // Components to be synchronized when replicated. 23 | std::vector sync_indices; 24 | }; 25 | 26 | } 27 | 28 | #endif // EDYN_NETWORKING_COMP_ASSET_ENTRY_HPP 29 | -------------------------------------------------------------------------------- /include/edyn/networking/comp/asset_ref.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_COMP_ASSET_REF_HPP 2 | #define EDYN_NETWORKING_COMP_ASSET_REF_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace edyn { 9 | 10 | /** 11 | * @brief Reference to an asset that contains information about a remote entity 12 | * to be instantiated and replicated locally. Sent by the server to clients 13 | * when a new entity enters their AABB of interest thus allowing the client to 14 | * obtain the asset and instantiate the entity with the respective internal id. 15 | */ 16 | struct asset_ref { 17 | // Asset id. 18 | entt::id_type id; 19 | uint32_t version; 20 | // Maps internal asset ids to instantiated entities. 21 | std::map entity_map; 22 | }; 23 | 24 | template 25 | void serialize(Archive &archive, asset_ref &ref) { 26 | archive(ref.id, ref.version); 27 | archive(ref.entity_map); 28 | } 29 | 30 | /** 31 | * @brief Assigned to assets after they're instantiated as a replica of a 32 | * remote asset, thus having the local entities linked to the remote counterparts. 33 | */ 34 | struct asset_linked_tag {}; 35 | 36 | } 37 | 38 | #endif // EDYN_NETWORKING_COMP_ASSET_REF_HPP 39 | -------------------------------------------------------------------------------- /include/edyn/networking/comp/discontinuity.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_COMP_DISCONTINUITY_HPP 2 | #define EDYN_NETWORKING_COMP_DISCONTINUITY_HPP 3 | 4 | #include "edyn/math/quaternion.hpp" 5 | #include "edyn/math/vector3.hpp" 6 | 7 | namespace edyn { 8 | 9 | /** 10 | * @brief A discontinuity introduced by misprediction in the client-side. The 11 | * offsets are added to the present position and orientation to ameliorate 12 | * visual disturbances caused by snapping the physics state to the 13 | * extrapolation result. 14 | */ 15 | struct discontinuity { 16 | vector3 position_offset {vector3_zero}; 17 | quaternion orientation_offset {quaternion_identity}; 18 | }; 19 | 20 | struct discontinuity_accumulator : public discontinuity {}; 21 | 22 | inline void merge_component(discontinuity_accumulator &component, const discontinuity_accumulator &new_value) { 23 | component.position_offset += new_value.position_offset; 24 | component.orientation_offset = edyn::normalize(component.orientation_offset * new_value.orientation_offset); 25 | } 26 | 27 | struct previous_position : public vector3 { 28 | previous_position & operator=(const vector3 &v) { 29 | vector3::operator=(v); 30 | return *this; 31 | } 32 | }; 33 | 34 | struct previous_orientation : public quaternion { 35 | previous_orientation & operator=(const quaternion &q) { 36 | quaternion::operator=(q); 37 | return *this; 38 | } 39 | }; 40 | 41 | } 42 | 43 | #endif // EDYN_NETWORKING_COMP_DISCONTINUITY_HPP 44 | -------------------------------------------------------------------------------- /include/edyn/networking/comp/entity_owner.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_ENTITY_OWNER_HPP 2 | #define EDYN_NETWORKING_ENTITY_OWNER_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace edyn { 9 | 10 | /** 11 | * @brief Component assigned to entities that are owned by a remote client. 12 | */ 13 | struct entity_owner { 14 | entt::entity client_entity {entt::null}; 15 | }; 16 | 17 | template 18 | void serialize(Archive &archive, entity_owner &owner) { 19 | archive(owner.client_entity); 20 | } 21 | 22 | } 23 | 24 | #endif // EDYN_NETWORKING_ENTITY_OWNER_HPP 25 | -------------------------------------------------------------------------------- /include/edyn/networking/comp/exporter_modified_components.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_COMP_EXPORTER_MODIFIED_COMPONENTS_HPP 2 | #define EDYN_NETWORKING_COMP_EXPORTER_MODIFIED_COMPONENTS_HPP 3 | 4 | #include "edyn/config/config.h" 5 | #include 6 | #include 7 | 8 | namespace edyn { 9 | 10 | template 11 | struct exporter_modified_components { 12 | struct comp_index_time { 13 | uint16_t index; 14 | uint16_t remaining; 15 | }; 16 | 17 | std::array entry {}; 18 | uint16_t count {}; 19 | static const uint16_t duration = 400; 20 | 21 | bool empty() const { 22 | for (unsigned i = 0; i < count; ++i) { 23 | if (entry[i].remaining > 0) { 24 | return false; 25 | } 26 | } 27 | return true; 28 | } 29 | 30 | void bump_index(uint16_t index) { 31 | EDYN_ASSERT(count < NumComponents); 32 | bool found = false; 33 | 34 | for (unsigned i = 0; i < count; ++i) { 35 | if (entry[i].index == index) { 36 | entry[i].remaining = duration; 37 | found = true; 38 | } 39 | } 40 | 41 | if (!found) { 42 | entry[count++] = {index, duration}; 43 | } 44 | } 45 | 46 | void decay(unsigned elapsed_ms) { 47 | for (unsigned i = 0; i < count;) { 48 | if (elapsed_ms > entry[i].remaining) { 49 | // Assign value of last and decrement count. 50 | // Note that `i` isn't incremented in this case. 51 | entry[i] = entry[--count]; 52 | } else { 53 | entry[i].remaining -= elapsed_ms; 54 | ++i; 55 | } 56 | } 57 | } 58 | }; 59 | 60 | } 61 | 62 | #endif // EDYN_NETWORKING_COMP_EXPORTER_MODIFIED_COMPONENTS_HPP 63 | -------------------------------------------------------------------------------- /include/edyn/networking/comp/network_input.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_COMP_NETWORK_INPUT_HPP 2 | #define EDYN_NETWORKING_COMP_NETWORK_INPUT_HPP 3 | 4 | namespace edyn { 5 | 6 | /** 7 | * @brief External networked components which are to be handled as input must 8 | * derive from this struct. 9 | */ 10 | struct network_input {}; 11 | 12 | } 13 | 14 | #endif // EDYN_NETWORKING_COMP_NETWORK_INPUT_HPP 15 | -------------------------------------------------------------------------------- /include/edyn/networking/comp/remote_client.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_REMOTE_CLIENT_HPP 2 | #define EDYN_NETWORKING_REMOTE_CLIENT_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include "edyn/replication/entity_map.hpp" 8 | #include "edyn/networking/packet/edyn_packet.hpp" 9 | #include "edyn/networking/util/clock_sync.hpp" 10 | 11 | namespace edyn { 12 | 13 | struct timed_packet { 14 | double timestamp; 15 | packet::edyn_packet packet; 16 | }; 17 | 18 | /** 19 | * @brief Stores data pertaining to a remote client in the server side. 20 | */ 21 | struct remote_client { 22 | // List of entities owned by this client. 23 | entt::sparse_set owned_entities; 24 | 25 | // Maps entities between the client registry and the server registry. 26 | edyn::entity_map entity_map; 27 | 28 | // Client round-trip time in seconds. 29 | double round_trip_time {}; 30 | 31 | // The delay in seconds applied to packet processing. 32 | double playout_delay {}; 33 | 34 | // List of delayed packets pending processing. 35 | std::vector packet_queue; 36 | 37 | // Timestamp of the last registry snapshot that was sent. 38 | double last_snapshot_time {0}; 39 | 40 | // Rate of registry snapshots, i.e. registry snapshots sent per second. 41 | double snapshot_rate {10}; 42 | 43 | // Whether this client will be given temporary ownership of all entities in 44 | // the island where entities owned by it reside, thus allowing the state of 45 | // those entities to be set by the client. 46 | bool allow_full_ownership {true}; 47 | 48 | clock_sync_data clock_sync; 49 | 50 | double last_executed_history_entry_timestamp {0}; 51 | }; 52 | 53 | } 54 | 55 | #endif // EDYN_NETWORKING_REMOTE_CLIENT_HPP 56 | -------------------------------------------------------------------------------- /include/edyn/networking/context/server_network_context.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_SERVER_NETWORK_CONTEXT_HPP 2 | #define EDYN_NETWORKING_SERVER_NETWORK_CONTEXT_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include "edyn/networking/util/server_snapshot_importer.hpp" 8 | #include "edyn/networking/util/server_snapshot_exporter.hpp" 9 | 10 | namespace edyn { 11 | 12 | struct server_network_context { 13 | server_network_context(entt::registry &); 14 | 15 | std::vector pending_created_clients; 16 | 17 | std::shared_ptr snapshot_importer; 18 | std::shared_ptr snapshot_exporter; 19 | 20 | // Packet signals contain the client entity and the packet. 21 | using packet_observer_func_t = void(entt::entity, const packet::edyn_packet &); 22 | entt::sigh packet_signal; 23 | 24 | auto packet_sink() { 25 | return entt::sink{packet_signal}; 26 | } 27 | }; 28 | 29 | } 30 | 31 | #endif // EDYN_NETWORKING_SERVER_NETWORK_CONTEXT_HPP 32 | -------------------------------------------------------------------------------- /include/edyn/networking/extrapolation/extrapolation_operation.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_EXTRAPOLATION_EXTRAPOLATION_OPERATION_HPP 2 | #define EDYN_NETWORKING_EXTRAPOLATION_EXTRAPOLATION_OPERATION_HPP 3 | 4 | #include "edyn/replication/registry_operation.hpp" 5 | 6 | namespace edyn { 7 | 8 | struct extrapolation_operation_create { 9 | registry_operation ops; 10 | std::vector owned_entities; 11 | }; 12 | 13 | struct extrapolation_operation_destroy { 14 | std::vector entities; 15 | }; 16 | 17 | } 18 | 19 | #endif // EDYN_NETWORKING_EXTRAPOLATION_EXTRAPOLATION_OPERATION_HPP 20 | -------------------------------------------------------------------------------- /include/edyn/networking/extrapolation/extrapolation_request.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_EXTRAPOLATION_REQUEST_HPP 2 | #define EDYN_NETWORKING_EXTRAPOLATION_REQUEST_HPP 3 | 4 | #include "edyn/parallel/message_queue.hpp" 5 | #include "edyn/networking/packet/registry_snapshot.hpp" 6 | #include 7 | 8 | namespace edyn { 9 | 10 | struct extrapolation_request { 11 | message_queue_identifier destination; 12 | double start_time; 13 | packet::registry_snapshot snapshot; 14 | double execution_time_limit {0.4}; 15 | bool should_remap {true}; 16 | }; 17 | 18 | } 19 | 20 | #endif // EDYN_NETWORKING_EXTRAPOLATION_REQUEST_HPP 21 | -------------------------------------------------------------------------------- /include/edyn/networking/extrapolation/extrapolation_result.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_EXTRAPOLATION_RESULT_HPP 2 | #define EDYN_NETWORKING_EXTRAPOLATION_RESULT_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include "edyn/collision/contact_manifold.hpp" 8 | #include "edyn/replication/registry_operation.hpp" 9 | 10 | namespace edyn { 11 | 12 | struct extrapolation_result { 13 | registry_operation ops; 14 | std::vector manifolds; 15 | bool terminated_early {false}; 16 | double timestamp; 17 | 18 | void remap(entity_map &emap) { 19 | ops.remap(emap); 20 | 21 | auto remove_it = std::remove_if(manifolds.begin(), manifolds.end(), [&](contact_manifold &manifold) { 22 | if (!emap.contains(manifold.body[0]) || !emap.contains(manifold.body[1])) { 23 | return true; 24 | } 25 | 26 | manifold.body[0] = emap.at(manifold.body[0]); 27 | manifold.body[1] = emap.at(manifold.body[1]); 28 | return false; 29 | }); 30 | manifolds.erase(remove_it, manifolds.end()); 31 | } 32 | }; 33 | 34 | } 35 | 36 | #endif // EDYN_NETWORKING_EXTRAPOLATION_RESULT_HPP 37 | -------------------------------------------------------------------------------- /include/edyn/networking/packet/asset_sync.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_PACKET_ASSET_SYNC_HPP 2 | #define EDYN_NETWORKING_PACKET_ASSET_SYNC_HPP 3 | 4 | #include "edyn/networking/packet/registry_snapshot.hpp" 5 | #include 6 | #include 7 | 8 | namespace edyn::packet { 9 | 10 | struct asset_sync { 11 | uint32_t id; 12 | entt::entity entity; 13 | }; 14 | 15 | template 16 | void serialize(Archive &archive, asset_sync &packet) { 17 | archive(packet.id); 18 | archive(packet.entity); 19 | } 20 | 21 | struct asset_sync_response : public registry_snapshot { 22 | uint32_t id; 23 | entt::entity entity; 24 | }; 25 | 26 | template 27 | void serialize(Archive &archive, asset_sync_response &packet) { 28 | archive(packet.id); 29 | archive(packet.entity); 30 | //archive(packet.timestamp); // Unnecessary in this context. 31 | archive(packet.entities); 32 | archive(packet.pools); 33 | } 34 | 35 | } 36 | 37 | #endif // EDYN_NETWORKING_PACKET_ASSET_SYNC_HPP 38 | -------------------------------------------------------------------------------- /include/edyn/networking/packet/client_created.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_PACKET_CLIENT_CREATED_HPP 2 | #define EDYN_NETWORKING_PACKET_CLIENT_CREATED_HPP 3 | 4 | #include 5 | 6 | namespace edyn::packet { 7 | 8 | /** 9 | * @brief Sent from server to client once the client entity is created after 10 | * connection is established. 11 | */ 12 | struct client_created { 13 | entt::entity client_entity; 14 | }; 15 | 16 | template 17 | void serialize(Archive &archive, client_created &packet) { 18 | archive(packet.client_entity); 19 | } 20 | 21 | } 22 | 23 | #endif // EDYN_NETWORKING_PACKET_CLIENT_CREATED_HPP 24 | -------------------------------------------------------------------------------- /include/edyn/networking/packet/create_entity.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_PACKET_CREATE_ENTITY_HPP 2 | #define EDYN_NETWORKING_PACKET_CREATE_ENTITY_HPP 3 | 4 | #include 5 | #include "edyn/networking/packet/registry_snapshot.hpp" 6 | 7 | namespace edyn::packet { 8 | 9 | /** 10 | * @brief A request to replicate entities and components. 11 | */ 12 | struct create_entity : public registry_snapshot {}; 13 | 14 | template 15 | void serialize(Archive &archive, create_entity &packet) { 16 | archive(packet.timestamp); 17 | archive(packet.entities); 18 | archive(packet.pools); 19 | } 20 | 21 | } 22 | 23 | #endif // EDYN_NETWORKING_PACKET_CREATE_ENTITY_HPP 24 | -------------------------------------------------------------------------------- /include/edyn/networking/packet/destroy_entity.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_PACKET_DESTROY_ENTITY_HPP 2 | #define EDYN_NETWORKING_PACKET_DESTROY_ENTITY_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace edyn::packet { 8 | 9 | /** 10 | * @brief A request to destroy entities. 11 | */ 12 | struct destroy_entity { 13 | double timestamp; 14 | std::vector entities; 15 | }; 16 | 17 | template 18 | void serialize(Archive &archive, destroy_entity &packet) { 19 | archive(packet.timestamp); 20 | archive(packet.entities); 21 | } 22 | 23 | } 24 | 25 | #endif // EDYN_NETWORKING_PACKET_DESTROY_ENTITY_HPP 26 | -------------------------------------------------------------------------------- /include/edyn/networking/packet/entity_entered.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_PACKET_ENTITY_ENTERED_HPP 2 | #define EDYN_NETWORKING_PACKET_ENTITY_ENTERED_HPP 3 | 4 | #include "edyn/networking/comp/asset_ref.hpp" 5 | #include "edyn/networking/packet/registry_snapshot.hpp" 6 | #include "edyn/networking/util/pool_snapshot.hpp" 7 | #include 8 | #include 9 | 10 | namespace edyn::packet { 11 | 12 | struct entity_entered { 13 | struct asset_info : public registry_snapshot { 14 | entt::entity entity; 15 | asset_ref asset; 16 | entt::entity owner; 17 | }; 18 | std::vector entry; 19 | }; 20 | 21 | template 22 | void serialize(Archive &archive, entity_entered::asset_info &info) { 23 | archive(info.entity); 24 | archive(info.asset); 25 | archive(info.owner); 26 | archive(info.entities); 27 | archive(info.pools); 28 | } 29 | 30 | template 31 | void serialize(Archive &archive, entity_entered &packet) { 32 | archive(packet.entry); 33 | } 34 | 35 | } 36 | 37 | #endif // EDYN_NETWORKING_PACKET_ENTITY_ENTERED_HPP 38 | -------------------------------------------------------------------------------- /include/edyn/networking/packet/entity_exited.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_PACKET_ENTITY_EXITED_HPP 2 | #define EDYN_NETWORKING_PACKET_ENTITY_EXITED_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace edyn::packet { 8 | 9 | struct entity_exited { 10 | std::vector entities; 11 | }; 12 | 13 | template 14 | void serialize(Archive &archive, entity_exited &packet) { 15 | archive(packet.entities); 16 | } 17 | 18 | } 19 | 20 | #endif // EDYN_NETWORKING_PACKET_ENTITY_EXITED_HPP 21 | -------------------------------------------------------------------------------- /include/edyn/networking/packet/entity_response.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_PACKET_ENTITY_RESPONSE_HPP 2 | #define EDYN_NETWORKING_PACKET_ENTITY_RESPONSE_HPP 3 | 4 | #include "edyn/networking/packet/registry_snapshot.hpp" 5 | #include 6 | 7 | namespace edyn::packet { 8 | 9 | /** 10 | * @brief A response to an entity query. 11 | */ 12 | struct entity_response : public registry_snapshot { 13 | uint32_t id; 14 | }; 15 | 16 | template 17 | void serialize(Archive &archive, entity_response &packet) { 18 | archive(packet.id); 19 | //archive(packet.timestamp); // Unnecessary in this context. 20 | archive(packet.entities); 21 | archive(packet.pools); 22 | } 23 | 24 | } 25 | 26 | #endif // EDYN_NETWORKING_PACKET_ENTITY_RESPONSE_HPP 27 | -------------------------------------------------------------------------------- /include/edyn/networking/packet/query_entity.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_PACKET_QUERY_ENTITY_HPP 2 | #define EDYN_NETWORKING_PACKET_QUERY_ENTITY_HPP 3 | 4 | #include "edyn/networking/util/component_index_type.hpp" 5 | #include 6 | #include 7 | #include 8 | 9 | namespace edyn::packet { 10 | 11 | struct query_entity { 12 | uint32_t id; 13 | // Vector of pairs of entity and desired components to query. 14 | std::vector>> entities; 15 | }; 16 | 17 | template 18 | void serialize(Archive &archive, query_entity &packet) { 19 | archive(packet.entities); 20 | } 21 | 22 | } 23 | 24 | #endif // EDYN_NETWORKING_PACKET_QUERY_ENTITY_HPP 25 | -------------------------------------------------------------------------------- /include/edyn/networking/packet/server_settings.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_PACKET_SERVER_SETTINGS_HPP 2 | #define EDYN_NETWORKING_PACKET_SERVER_SETTINGS_HPP 3 | 4 | #include 5 | #include "edyn/math/vector3.hpp" 6 | #include "edyn/context/settings.hpp" 7 | 8 | namespace edyn::packet { 9 | 10 | struct server_settings { 11 | scalar fixed_dt; 12 | vector3 gravity; 13 | uint8_t num_solver_velocity_iterations; 14 | uint8_t num_solver_position_iterations; 15 | uint8_t num_restitution_iterations; 16 | uint8_t num_individual_restitution_iterations; 17 | bool allow_full_ownership; 18 | 19 | server_settings() = default; 20 | 21 | server_settings(settings &settings, bool allow_full_ownership) 22 | : fixed_dt(settings.fixed_dt) 23 | , gravity(settings.gravity) 24 | , num_solver_velocity_iterations(settings.num_solver_velocity_iterations) 25 | , num_solver_position_iterations(settings.num_solver_position_iterations) 26 | , num_restitution_iterations(settings.num_restitution_iterations) 27 | , num_individual_restitution_iterations(settings.num_individual_restitution_iterations) 28 | , allow_full_ownership(allow_full_ownership) 29 | {} 30 | }; 31 | 32 | template 33 | void serialize(Archive &archive, server_settings &settings) { 34 | archive(settings.fixed_dt); 35 | archive(settings.gravity); 36 | archive(settings.num_solver_velocity_iterations); 37 | archive(settings.num_solver_position_iterations); 38 | archive(settings.num_restitution_iterations); 39 | archive(settings.num_individual_restitution_iterations); 40 | archive(settings.allow_full_ownership); 41 | } 42 | 43 | } 44 | 45 | #endif // EDYN_NETWORKING_PACKET_SERVER_SETTINGS_HPP 46 | -------------------------------------------------------------------------------- /include/edyn/networking/packet/set_aabb_of_interest.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_PACKET_SET_AABB_OF_INTEREST_HPP 2 | #define EDYN_NETWORKING_PACKET_SET_AABB_OF_INTEREST_HPP 3 | 4 | #include "edyn/math/vector3.hpp" 5 | 6 | namespace edyn::packet { 7 | 8 | struct set_aabb_of_interest { 9 | vector3 min, max; 10 | }; 11 | 12 | template 13 | void serialize(Archive &archive, set_aabb_of_interest &aabb) { 14 | archive(aabb.min, aabb.max); 15 | } 16 | 17 | } 18 | 19 | #endif // EDYN_NETWORKING_PACKET_SET_AABB_OF_INTEREST_HPP 20 | -------------------------------------------------------------------------------- /include/edyn/networking/packet/set_playout_delay.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_PACKET_SET_PLAYOUT_DELAY_HPP 2 | #define EDYN_NETWORKING_PACKET_SET_PLAYOUT_DELAY_HPP 3 | 4 | namespace edyn::packet { 5 | 6 | /** 7 | * @brief Sent to clients when their playout delay changes in the server. 8 | */ 9 | struct set_playout_delay { 10 | double value; 11 | }; 12 | 13 | template 14 | void serialize(Archive &archive, set_playout_delay &delay) { 15 | archive(delay.value); 16 | } 17 | 18 | } 19 | 20 | #endif // EDYN_NETWORKING_PACKET_SET_PLAYOUT_DELAY_HPP 21 | -------------------------------------------------------------------------------- /include/edyn/networking/packet/time_request.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_PACKET_TIME_REQUEST_HPP 2 | #define EDYN_NETWORKING_PACKET_TIME_REQUEST_HPP 3 | 4 | #include 5 | 6 | namespace edyn::packet { 7 | 8 | /** 9 | * @brief Time request for clock synchronization purposes. 10 | */ 11 | struct time_request { 12 | uint32_t id; 13 | }; 14 | 15 | template 16 | void serialize(Archive &archive, time_request &req) { 17 | archive(req.id); 18 | } 19 | 20 | } 21 | 22 | #endif // EDYN_NETWORKING_PACKET_TIME_REQUEST_HPP 23 | -------------------------------------------------------------------------------- /include/edyn/networking/packet/time_response.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_PACKET_TIME_RESPONSE_HPP 2 | #define EDYN_NETWORKING_PACKET_TIME_RESPONSE_HPP 3 | 4 | #include 5 | 6 | namespace edyn::packet { 7 | 8 | /** 9 | * @brief A response to a time request. 10 | */ 11 | struct time_response { 12 | uint32_t id; 13 | double timestamp; 14 | }; 15 | 16 | template 17 | void serialize(Archive &archive, time_response &res) { 18 | archive(res.id); 19 | archive(res.timestamp); 20 | } 21 | 22 | } 23 | 24 | #endif // EDYN_NETWORKING_PACKET_TIME_RESPONSE_HPP 25 | -------------------------------------------------------------------------------- /include/edyn/networking/packet/update_entity_map.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_PACKET_UPDATE_ENTITY_MAP_HPP 2 | #define EDYN_NETWORKING_PACKET_UPDATE_ENTITY_MAP_HPP 3 | 4 | #include "edyn/core/entity_pair.hpp" 5 | 6 | namespace edyn::packet { 7 | 8 | /** 9 | * @brief Produced whenever a local entity is created to correspond to a remote 10 | * entity. It informs the other end into which remote entity their local entity 11 | * has been mapped. 12 | */ 13 | struct update_entity_map { 14 | double timestamp; 15 | entity_pair_vector pairs; // [local -> remote] 16 | }; 17 | 18 | template 19 | void serialize(Archive &archive, update_entity_map &map) { 20 | archive(map.timestamp); 21 | archive(map.pairs); 22 | } 23 | 24 | } 25 | 26 | #endif // EDYN_NETWORKING_PACKET_UPDATE_ENTITY_MAP_HPP 27 | -------------------------------------------------------------------------------- /include/edyn/networking/settings/client_network_settings.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_SETTINGS_CLIENT_NETWORK_SETTINGS_HPP 2 | #define EDYN_NETWORKING_SETTINGS_CLIENT_NETWORK_SETTINGS_HPP 3 | 4 | #include "edyn/math/scalar.hpp" 5 | #include 6 | 7 | namespace edyn { 8 | 9 | using extrapolation_callback_t = void(*)(entt::registry &); 10 | 11 | struct client_network_settings { 12 | double snapshot_rate {20}; 13 | double round_trip_time {0}; 14 | bool extrapolation_enabled {true}; 15 | 16 | // Exponential decay parameter for discontinuity over time. 17 | scalar discontinuity_decay_rate {scalar(6)}; 18 | 19 | // All actions older than this amount are deleted in every update. 20 | // The entire action history is included in every registry snapshot, thus 21 | // it is desirable to keep this low to minimize packet size. Though, a 22 | // longer action history decreases the chances of actions being lost. It 23 | // is sensible to increase it in case packet loss is high. 24 | double action_history_max_age {1.0}; 25 | 26 | extrapolation_callback_t extrapolation_init_callback {nullptr}; 27 | extrapolation_callback_t extrapolation_deinit_callback {nullptr}; 28 | extrapolation_callback_t extrapolation_begin_callback {nullptr}; 29 | extrapolation_callback_t extrapolation_finish_callback {nullptr}; 30 | extrapolation_callback_t extrapolation_pre_step_callback {nullptr}; 31 | extrapolation_callback_t extrapolation_post_step_callback {nullptr}; 32 | }; 33 | 34 | } 35 | 36 | #endif // EDYN_NETWORKING_SETTINGS_CLIENT_NETWORK_SETTINGS_HPP 37 | -------------------------------------------------------------------------------- /include/edyn/networking/settings/server_network_settings.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_SETTINGS_SERVER_NETWORK_SETTINGS_HPP 2 | #define EDYN_NETWORKING_SETTINGS_SERVER_NETWORK_SETTINGS_HPP 3 | 4 | namespace edyn { 5 | 6 | struct server_network_settings { 7 | // Client playout delay buffer length will be calculated as the greatest 8 | // latency among all clients in its AABB of interest multiplied by this 9 | // value, which should be greater than 1, to ensure that all client 10 | // input will be applied with correct relative timing. 11 | double playout_delay_multiplier {1.2}; 12 | 13 | // Prevent playout delay from getting too lengthy. If it gets maxed out due 14 | // to the large RTT of a client, packets received from that client will no 15 | // longer be delayed, they'll be applied immediately instead, which can lead 16 | // to jitter. 17 | double max_playout_delay {2}; 18 | }; 19 | 20 | } 21 | 22 | #endif // EDYN_NETWORKING_SETTINGS_SERVER_NETWORK_SETTINGS_HPP 23 | -------------------------------------------------------------------------------- /include/edyn/networking/sys/accumulate_discontinuities.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_SYS_ACCUMULATE_DISCONTINUITIES_HPP 2 | #define EDYN_NETWORKING_SYS_ACCUMULATE_DISCONTINUITIES_HPP 3 | 4 | #include 5 | #include "edyn/comp/position.hpp" 6 | #include "edyn/comp/orientation.hpp" 7 | #include "edyn/networking/comp/discontinuity.hpp" 8 | #include "edyn/util/island_util.hpp" 9 | 10 | namespace edyn { 11 | 12 | inline void accumulate_discontinuities(entt::registry ®istry) { 13 | auto accum_view = registry.view(exclude_sleeping_disabled); 16 | 17 | for (auto [entity, p_pos, pos, p_orn, orn, accum] : accum_view.each()) { 18 | accum.position_offset += p_pos - pos; 19 | accum.orientation_offset *= p_orn * conjugate(orn); 20 | registry.patch(entity); 21 | } 22 | } 23 | 24 | inline void clear_accumulated_discontinuities_quietly(entt::registry ®istry) { 25 | auto accum_view = registry.view(exclude_sleeping_disabled); 26 | accum_view.each([](discontinuity_accumulator &accum) { 27 | accum.position_offset = edyn::vector3_zero; 28 | accum.orientation_offset = edyn::quaternion_identity; 29 | }); 30 | } 31 | 32 | } 33 | 34 | #endif // EDYN_NETWORKING_SYS_ACCUMULATE_DISCONTINUITIES_HPP 35 | -------------------------------------------------------------------------------- /include/edyn/networking/sys/assign_previous_transforms.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_SYS_ASSIGN_PREVIOUS_TRANSFORMS_HPP 2 | #define EDYN_NETWORKING_SYS_ASSIGN_PREVIOUS_TRANSFORMS_HPP 3 | 4 | #include 5 | #include "edyn/comp/position.hpp" 6 | #include "edyn/comp/orientation.hpp" 7 | #include "edyn/networking/comp/discontinuity.hpp" 8 | 9 | namespace edyn { 10 | 11 | inline void assign_previous_transforms(entt::registry ®istry) { 12 | registry.view().each([](previous_position &p_pos, position &pos) { 13 | p_pos = pos; 14 | }); 15 | 16 | registry.view().each([](previous_orientation &p_orn, orientation &orn) { 17 | p_orn = orn; 18 | }); 19 | } 20 | 21 | } 22 | 23 | #endif // EDYN_NETWORKING_SYS_ASSIGN_PREVIOUS_TRANSFORMS_HPP 24 | -------------------------------------------------------------------------------- /include/edyn/networking/sys/update_aabbs_of_interest.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_UPDATE_AABBS_OF_INTEREST_HPP 2 | #define EDYN_NETWORKING_UPDATE_AABBS_OF_INTEREST_HPP 3 | 4 | #include 5 | 6 | namespace edyn { 7 | 8 | void update_aabbs_of_interest(entt::registry &); 9 | 10 | } 11 | 12 | #endif // EDYN_NETWORKING_UPDATE_AABBS_OF_INTEREST_HPP 13 | -------------------------------------------------------------------------------- /include/edyn/networking/util/asset_util.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_UTIL_ASSET_UTIL_HPP 2 | #define EDYN_NETWORKING_UTIL_ASSET_UTIL_HPP 3 | 4 | #include "edyn/networking/comp/asset_entry.hpp" 5 | #include "edyn/networking/comp/asset_ref.hpp" 6 | #include "edyn/networking/context/client_network_context.hpp" 7 | #include "edyn/networking/context/server_network_context.hpp" 8 | #include "edyn/util/rigidbody.hpp" 9 | 10 | namespace edyn { 11 | 12 | /** 13 | * @brief Assign an entity to an asset. 14 | * @remark When as asset is constructed, the entities that belong to that 15 | * asset must be linked to the asset's internal ids. 16 | * @tparam Component Types of components that must be synchronized when the 17 | * asset is instantiated. These include any component whose value could change 18 | * after instantiation. 19 | * @param registry Data source. 20 | * @param entity Entity that belongs to asset. 21 | * @param asset_entity Asset. 22 | * @param id Unique id of entity inside of asset. 23 | */ 24 | template 25 | void assign_to_asset(entt::registry ®istry, entt::entity entity, entt::entity asset_entity, entt::id_type id) { 26 | registry.get(asset_entity).entity_map[id] = entity; 27 | registry.emplace(entity); 28 | 29 | auto &entry = registry.emplace(entity); 30 | entry.id = id; 31 | entry.asset_entity = asset_entity; 32 | 33 | if constexpr(sizeof...(Component) > 0) { 34 | if (auto *ctx = registry.ctx().find()) { 35 | (entry.sync_indices.push_back(ctx->snapshot_exporter->get_component_index()), ...); 36 | } else if (auto *ctx = registry.ctx().find()) { 37 | (entry.sync_indices.push_back(ctx->snapshot_exporter->get_component_index()), ...); 38 | } 39 | } 40 | } 41 | 42 | } 43 | 44 | #endif // EDYN_NETWORKING_UTIL_ASSET_UTIL_HPP 45 | -------------------------------------------------------------------------------- /include/edyn/networking/util/component_index_type.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_UTIL_COMPONENT_INDEX_TYPE_HPP 2 | #define EDYN_NETWORKING_UTIL_COMPONENT_INDEX_TYPE_HPP 3 | 4 | #include 5 | 6 | namespace edyn { 7 | 8 | // Index type of components stored in registry snapshot packets. Allow for 9 | // up to 2^16 component types. 10 | using component_index_type = uint16_t; 11 | 12 | } 13 | 14 | #endif // EDYN_NETWORKING_UTIL_COMPONENT_INDEX_TYPE_HPP 15 | -------------------------------------------------------------------------------- /include/edyn/networking/util/import_contact_manifolds.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_UTIL_IMPORT_CONTACT_MANIFOLDS_HPP 2 | #define EDYN_NETWORKING_UTIL_IMPORT_CONTACT_MANIFOLDS_HPP 3 | 4 | #include "edyn/collision/contact_manifold.hpp" 5 | #include 6 | #include 7 | 8 | namespace edyn { 9 | 10 | struct contact_manifold; 11 | class entity_map; 12 | 13 | void import_contact_manifolds(entt::registry ®istry, const entity_map &emap, 14 | const std::vector &manifolds); 15 | 16 | void import_contact_manifolds(entt::registry ®istry, 17 | const std::vector &manifolds); 18 | 19 | } 20 | 21 | #endif // EDYN_NETWORKING_UTIL_IMPORT_CONTACT_MANIFOLDS_HPP 22 | -------------------------------------------------------------------------------- /include/edyn/networking/util/pool_snapshot.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_PACKET_POOL_SNAPSHOT_HPP 2 | #define EDYN_NETWORKING_PACKET_POOL_SNAPSHOT_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "edyn/networking/util/pool_snapshot_data.hpp" 9 | #include "edyn/networking/util/component_index_type.hpp" 10 | 11 | namespace edyn { 12 | 13 | struct pool_snapshot { 14 | component_index_type component_index; 15 | std::shared_ptr ptr; 16 | }; 17 | 18 | extern std::unique_ptr(*g_make_pool_snapshot_data)(component_index_type); 19 | 20 | template 21 | void serialize(Archive &archive, pool_snapshot &pool) { 22 | archive(pool.component_index); 23 | std::vector data; 24 | 25 | if constexpr(Archive::is_input::value) { 26 | archive(data); 27 | auto input = memory_input_archive(data.data(), data.size()); 28 | pool.ptr = (*g_make_pool_snapshot_data)(pool.component_index); 29 | pool.ptr->read(input); 30 | } else { 31 | auto output = memory_output_archive(data); 32 | pool.ptr->write(output); 33 | archive(data); 34 | } 35 | } 36 | 37 | template 38 | auto create_make_pool_snapshot_data_function([[maybe_unused]] std::tuple) { 39 | return [](component_index_type component_index) { 40 | std::tuple components; 41 | auto ptr = std::unique_ptr{}; 42 | visit_tuple(components, component_index, [&](auto &&c) { 43 | using CompType = std::decay_t; 44 | ptr.reset(new pool_snapshot_data_impl); 45 | }); 46 | return ptr; 47 | }; 48 | } 49 | 50 | } 51 | 52 | #endif // EDYN_NETWORKING_PACKET_POOL_SNAPSHOT_HPP 53 | -------------------------------------------------------------------------------- /include/edyn/networking/util/process_extrapolation_result.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_UTIL_PROCESS_EXTRAPOLATION_RESULT_HPP 2 | #define EDYN_NETWORKING_UTIL_PROCESS_EXTRAPOLATION_RESULT_HPP 3 | 4 | #include 5 | 6 | namespace edyn { 7 | 8 | class entity_map; 9 | struct extrapolation_result; 10 | 11 | void process_extrapolation_result(entt::registry ®istry, entity_map &emap, 12 | const extrapolation_result &result); 13 | 14 | void process_extrapolation_result(entt::registry ®istry, 15 | const extrapolation_result &result); 16 | 17 | } 18 | 19 | #endif // EDYN_NETWORKING_UTIL_PROCESS_EXTRAPOLATION_RESULT_HPP 20 | -------------------------------------------------------------------------------- /include/edyn/networking/util/process_update_entity_map_packet.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_UTIL_PROCESS_UPDATE_ENTITY_MAP_PACKET_HPP 2 | #define EDYN_NETWORKING_UTIL_PROCESS_UPDATE_ENTITY_MAP_PACKET_HPP 3 | 4 | #include "edyn/networking/packet/update_entity_map.hpp" 5 | #include "edyn/replication/entity_map.hpp" 6 | #include 7 | 8 | namespace edyn { 9 | 10 | void process_update_entity_map_packet(const entt::registry &, const packet::update_entity_map &, entity_map &); 11 | 12 | } 13 | 14 | #endif // EDYN_NETWORKING_UTIL_PROCESS_UPDATE_ENTITY_MAP_PACKET_HPP 15 | -------------------------------------------------------------------------------- /include/edyn/networking/util/snap_to_pool_snapshot.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_NETWORKING_UTIL_SNAP_TO_POOL_SNAPSHOT_HPP 2 | #define EDYN_NETWORKING_UTIL_SNAP_TO_POOL_SNAPSHOT_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace edyn { 8 | 9 | struct pool_snapshot; 10 | class entity_map; 11 | 12 | void snap_to_pool_snapshot(entt::registry ®istry, const entity_map &emap, 13 | const std::vector &entities, 14 | const std::vector &pools, 15 | bool should_accumulate_discontinuities); 16 | 17 | void snap_to_pool_snapshot(entt::registry ®istry, 18 | const std::vector &entities, 19 | const std::vector &pools, 20 | bool should_accumulate_discontinuities); 21 | 22 | } 23 | 24 | #endif // EDYN_NETWORKING_UTIL_SNAP_TO_POOL_SNAPSHOT_HPP 25 | -------------------------------------------------------------------------------- /include/edyn/parallel/atomic_counter_sync.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_PARALLEL_ATOMIC_COUNTER_SYNC_HPP 2 | #define EDYN_PARALLEL_ATOMIC_COUNTER_SYNC_HPP 3 | 4 | #include "edyn/config/config.h" 5 | #include 6 | #include 7 | 8 | namespace edyn { 9 | 10 | class atomic_counter_sync { 11 | public: 12 | atomic_counter_sync(size_t count) 13 | : count(count) 14 | , done(false) 15 | {} 16 | 17 | ~atomic_counter_sync() { 18 | EDYN_ASSERT(count == 0); 19 | } 20 | 21 | void decrement() { 22 | std::lock_guard lock(mutex); 23 | EDYN_ASSERT(count > 0); 24 | --count; 25 | done = count == 0; 26 | 27 | if (done) { 28 | cv.notify_one(); 29 | } 30 | } 31 | 32 | void wait() { 33 | std::unique_lock lock(mutex); 34 | cv.wait(lock, [&] { return done; }); 35 | } 36 | 37 | private: 38 | size_t count; 39 | bool done; 40 | std::mutex mutex; 41 | std::condition_variable cv; 42 | }; 43 | 44 | } 45 | 46 | #endif // EDYN_PARALLEL_ATOMIC_COUNTER_SYNC_HPP 47 | -------------------------------------------------------------------------------- /include/edyn/parallel/job.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_PARALLEL_JOB_HPP 2 | #define EDYN_PARALLEL_JOB_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace edyn { 9 | 10 | struct job { 11 | static constexpr size_t pointer_size = sizeof(void(*)(void)); 12 | static constexpr size_t size = 64; 13 | static constexpr size_t data_size = size - pointer_size; 14 | 15 | using data_type = std::array; 16 | using function_type = void(data_type &); 17 | 18 | data_type data; 19 | function_type *func; 20 | 21 | void operator()() { 22 | (*func)(data); 23 | } 24 | 25 | static auto noop() { 26 | job j; 27 | j.func = [](data_type &) {}; 28 | return j; 29 | } 30 | }; 31 | 32 | } 33 | 34 | #endif // EDYN_PARALLEL_JOB_HPP 35 | -------------------------------------------------------------------------------- /include/edyn/parallel/job_dispatcher.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_PARALLEL_JOB_DISPATCHER_HPP 2 | #define EDYN_PARALLEL_JOB_DISPATCHER_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "edyn/parallel/worker.hpp" 11 | 12 | namespace edyn { 13 | 14 | struct job; 15 | 16 | /** 17 | * Manages a set of worker threads and dispatches jobs to them. 18 | */ 19 | class job_dispatcher { 20 | public: 21 | static job_dispatcher &global(); 22 | 23 | ~job_dispatcher(); 24 | 25 | void start(size_t num_worker_threads); 26 | 27 | void stop(); 28 | 29 | bool running() const; 30 | 31 | /** 32 | * Schedules a job to run asynchronously in a worker thread. 33 | */ 34 | void async(const job &); 35 | 36 | /** 37 | * Number of background workers. 38 | */ 39 | size_t num_workers() const; 40 | 41 | private: 42 | std::vector> m_threads; 43 | std::map> m_workers; 44 | std::atomic m_start; 45 | }; 46 | 47 | } 48 | 49 | #endif // EDYN_PARALLEL_JOB_DISPATCHER_HPP 50 | -------------------------------------------------------------------------------- /include/edyn/parallel/job_queue.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_PARALLEL_JOB_QUEUE_HPP 2 | #define EDYN_PARALLEL_JOB_QUEUE_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include "edyn/parallel/job.hpp" 8 | 9 | namespace edyn { 10 | 11 | /** 12 | * Thread-safe queue of jobs. 13 | */ 14 | class job_queue { 15 | public: 16 | void push(const job &); 17 | 18 | job pop(); 19 | 20 | bool try_pop(job &); 21 | 22 | size_t size() const; 23 | 24 | private: 25 | mutable std::mutex m_mutex; 26 | std::queue m_jobs; 27 | std::condition_variable m_cv; 28 | }; 29 | 30 | } 31 | 32 | #endif // EDYN_PARALLEL_JOB_QUEUE_HPP -------------------------------------------------------------------------------- /include/edyn/parallel/message_queue.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_PARALLEL_MESSAGE_QUEUE_HPP 2 | #define EDYN_PARALLEL_MESSAGE_QUEUE_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace edyn { 10 | 11 | struct message_queue_identifier { 12 | std::string value; 13 | }; 14 | 15 | struct any_message { 16 | message_queue_identifier sender; 17 | entt::any content; 18 | }; 19 | 20 | template 21 | struct message { 22 | message_queue_identifier sender; 23 | T content; 24 | }; 25 | 26 | class message_queue { 27 | public: 28 | template 29 | void push(message_queue_identifier source, Args&& ... args) { 30 | // TODO: create a new single-producer/single-consumer queue for each new source. 31 | auto lock = std::lock_guard(m_mutex); 32 | m_messages.emplace_back(any_message{source, entt::any(std::in_place_type_t{}, std::forward(args)...)}); 33 | m_push_signal.publish(); 34 | } 35 | 36 | template 37 | void consume(Func func) { 38 | auto lock = std::unique_lock(m_mutex); 39 | auto messages = std::move(m_messages); 40 | lock.unlock(); 41 | 42 | for (auto &msg : messages) { 43 | func(msg); 44 | } 45 | } 46 | 47 | auto push_sink() { 48 | return entt::sink{m_push_signal}; 49 | } 50 | 51 | private: 52 | mutable std::mutex m_mutex; 53 | std::vector m_messages; 54 | entt::sigh m_push_signal; 55 | }; 56 | 57 | } 58 | 59 | #endif // EDYN_PARALLEL_MESSAGE_QUEUE_HPP 60 | -------------------------------------------------------------------------------- /include/edyn/parallel/worker.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_PARALLEL_WORKER_HPP 2 | #define EDYN_PARALLEL_WORKER_HPP 3 | 4 | #include 5 | #include 6 | #include "edyn/parallel/job_queue.hpp" 7 | 8 | namespace edyn { 9 | 10 | /** 11 | * A worker that runs jobs in a thread. 12 | */ 13 | class worker { 14 | public: 15 | void push_job(const job &j) { 16 | m_queue.push(j); 17 | ++m_size; 18 | } 19 | 20 | void run() { 21 | m_running = true; 22 | 23 | for (;;) { 24 | auto j = m_queue.pop(); 25 | j(); 26 | --m_size; 27 | 28 | if (!m_running) { 29 | break; 30 | } 31 | } 32 | } 33 | 34 | void once() { 35 | job j; 36 | while (m_queue.try_pop(j)) { 37 | j(); 38 | --m_size; 39 | } 40 | } 41 | 42 | void stop() { 43 | m_running = false; 44 | m_queue.push(job::noop()); // Unblock queue with a no-op job. 45 | } 46 | 47 | size_t size() { 48 | return m_size.load(); 49 | } 50 | 51 | job_queue &get_queue() { 52 | return m_queue; 53 | } 54 | 55 | private: 56 | std::atomic_bool m_running {false}; 57 | job_queue m_queue; 58 | std::atomic m_size {0}; 59 | }; 60 | 61 | } 62 | 63 | #endif // EDYN_PARALLEL_WORKER_HPP 64 | -------------------------------------------------------------------------------- /include/edyn/replication/make_reg_op_builder.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_REPLICATION_MAKE_REG_OP_BUILDER_HPP 2 | #define EDYN_REPLICATION_MAKE_REG_OP_BUILDER_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace edyn { 8 | 9 | class registry_operation_builder; 10 | class registry_operation_observer; 11 | 12 | /** 13 | * @brief Function type of a factory function that creates instances of a 14 | * registry operations builder implementation. 15 | */ 16 | using make_reg_op_builder_func_t = std::unique_ptr(*)(entt::registry &); 17 | using make_reg_op_observer_func_t = std::unique_ptr(*)(registry_operation_builder &); 18 | 19 | /** 20 | * @brief Creates a new registry operation builder. 21 | * 22 | * Returns a builder implementation that supports handling all shared component 23 | * types plus any external component set by the user. 24 | * 25 | * @return Safe pointer to an instance of a builder implementation. 26 | */ 27 | std::unique_ptr make_reg_op_builder(entt::registry ®istry); 28 | std::unique_ptr make_reg_op_observer(registry_operation_builder &builder); 29 | 30 | } 31 | 32 | #endif // EDYN_REPLICATION_MAKE_REG_OP_BUILDER_HPP 33 | -------------------------------------------------------------------------------- /include/edyn/serialization/entt_s11n.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_SERIALIZATION_ENTT_S11N_HPP 2 | #define EDYN_SERIALIZATION_ENTT_S11N_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace edyn { 8 | 9 | template 10 | void serialize(Archive &archive, entt::entity &entity) { 11 | if constexpr(Archive::is_input::value) { 12 | std::underlying_type_t i {}; 13 | archive(i); 14 | entity = entt::entity{i}; 15 | } else { 16 | auto i = entt::to_integral(entity); 17 | archive(i); 18 | } 19 | } 20 | 21 | } 22 | 23 | #endif // EDYN_SERIALIZATION_ENTT_S11N_HPP 24 | -------------------------------------------------------------------------------- /include/edyn/serialization/math_s11n.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_SERIALIZATION_MATH_S11N_HPP 2 | #define EDYN_SERIALIZATION_MATH_S11N_HPP 3 | 4 | #include "edyn/math/vector3.hpp" 5 | #include "edyn/math/quaternion.hpp" 6 | #include "edyn/math/matrix3x3.hpp" 7 | #include "edyn/serialization/s11n_util.hpp" 8 | 9 | namespace edyn { 10 | 11 | template 12 | void serialize(Archive &archive, vector3 &v) { 13 | archive(v.x, v.y, v.z); 14 | } 15 | 16 | template 17 | void serialize(Archive &archive, quaternion &q) { 18 | archive(q.x, q.y, q.z, q.w); 19 | } 20 | 21 | template 22 | void serialize(Archive &archive, matrix3x3 &m) { 23 | archive(m.row); 24 | } 25 | 26 | } 27 | 28 | #endif // EDYN_SERIALIZATION_MATH_S11N_HPP -------------------------------------------------------------------------------- /include/edyn/serialization/s11n.hpp: -------------------------------------------------------------------------------- 1 | #include "edyn/serialization/math_s11n.hpp" 2 | #include "edyn/serialization/std_s11n.hpp" 3 | #include "edyn/serialization/static_tree_s11n.hpp" 4 | #include "edyn/serialization/triangle_mesh_s11n.hpp" 5 | #include "edyn/serialization/paged_triangle_mesh_s11n.hpp" 6 | #include "edyn/serialization/entt_s11n.hpp" 7 | #include "edyn/serialization/file_archive.hpp" 8 | #include "edyn/serialization/memory_archive.hpp" -------------------------------------------------------------------------------- /include/edyn/serialization/s11n_util.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_SERIALIZATION_S11N_UTIL_HPP 2 | #define EDYN_SERIALIZATION_S11N_UTIL_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace edyn { 8 | 9 | template 10 | void serialize_enum(Archive &archive, Enum &value) { 11 | using underlying_type = std::underlying_type_t; 12 | if constexpr(Archive::is_input::value) { 13 | underlying_type i {}; 14 | archive(i); 15 | value = Enum{i}; 16 | } else { 17 | auto i = static_cast(value); 18 | archive(i); 19 | } 20 | } 21 | 22 | template 23 | void serialize_pointer(Archive &archive, T **ptr) { 24 | if constexpr(Archive::is_input::value) { 25 | intptr_t ptr_int {}; 26 | archive(ptr_int); 27 | *ptr = reinterpret_cast(ptr_int); 28 | } else { 29 | auto ptr_int = reinterpret_cast(*ptr); 30 | archive(ptr_int); 31 | } 32 | } 33 | 34 | } 35 | 36 | #endif // EDYN_SERIALIZATION_S11N_UTIL_HPP 37 | -------------------------------------------------------------------------------- /include/edyn/serialization/static_tree_s11n.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_SERIALIZATION_STATIC_TREE_S11N_HPP 2 | #define EDYN_SERIALIZATION_STATIC_TREE_S11N_HPP 3 | 4 | #include "edyn/collision/static_tree.hpp" 5 | #include "edyn/serialization/std_s11n.hpp" 6 | 7 | namespace edyn { 8 | 9 | template 10 | void serialize(Archive &archive, static_tree::tree_node &node) { 11 | archive(node.aabb.min); 12 | archive(node.aabb.max); 13 | archive(node.child1); 14 | archive(node.child2); 15 | } 16 | 17 | template 18 | void serialize(Archive &archive, static_tree &tree) { 19 | archive(tree.m_nodes); 20 | } 21 | 22 | inline 23 | size_t serialization_sizeof(const static_tree::tree_node &node) { 24 | return 25 | sizeof(node.aabb.min) + 26 | sizeof(node.aabb.max) + 27 | sizeof(node.child1) + 28 | sizeof(node.child2); 29 | } 30 | 31 | inline 32 | size_t serialization_sizeof(const static_tree &tree) { 33 | return serialization_sizeof(tree.m_nodes); 34 | } 35 | 36 | } 37 | 38 | #endif // EDYN_SERIALIZATION_STATIC_TREE_S11N_HPP 39 | -------------------------------------------------------------------------------- /include/edyn/shapes/capsule_shape.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_SHAPES_CAPSULE_SHAPE_HPP 2 | #define EDYN_SHAPES_CAPSULE_SHAPE_HPP 3 | 4 | #include 5 | #include 6 | #include "edyn/math/quaternion.hpp" 7 | #include "edyn/math/coordinate_axis.hpp" 8 | 9 | namespace edyn { 10 | 11 | enum class capsule_feature : uint8_t { 12 | hemisphere, 13 | side 14 | }; 15 | 16 | struct capsule_shape { 17 | scalar radius; 18 | scalar half_length; 19 | coordinate_axis axis {coordinate_axis::x}; 20 | 21 | auto get_vertices(const vector3 &pos, const quaternion &orn) const { 22 | const auto dir = coordinate_axis_vector(axis, orn); 23 | return std::array{ 24 | pos + dir * half_length, 25 | pos - dir * half_length 26 | }; 27 | } 28 | }; 29 | 30 | template 31 | void serialize(Archive &archive, capsule_shape &s) { 32 | archive(s.half_length); 33 | archive(s.radius); 34 | archive(s.axis); 35 | } 36 | 37 | } 38 | 39 | #endif // EDYN_SHAPES_CAPSULE_SHAPE_HPP 40 | -------------------------------------------------------------------------------- /include/edyn/shapes/mesh_shape.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_SHAPES_MESH_SHAPE_HPP 2 | #define EDYN_SHAPES_MESH_SHAPE_HPP 3 | 4 | #include 5 | #include "triangle_mesh.hpp" 6 | 7 | namespace edyn { 8 | 9 | /** 10 | * @brief A concave triangle mesh shape. 11 | * @remarks Triangle meshes can only be assigned to static rigid bodies. 12 | * The `collide` functions involving this shape ignore position and 13 | * orientation. If the mesh needs to be transformed, do so while constructing 14 | * or loading it. 15 | */ 16 | struct mesh_shape { 17 | std::shared_ptr trimesh; 18 | }; 19 | 20 | } 21 | 22 | #endif // EDYN_SHAPES_MESH_SHAPE_HPP -------------------------------------------------------------------------------- /include/edyn/shapes/paged_mesh_shape.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_SHAPES_PAGED_MESH_SHAPE_HPP 2 | #define EDYN_SHAPES_PAGED_MESH_SHAPE_HPP 3 | 4 | #include 5 | #include "paged_triangle_mesh.hpp" 6 | 7 | namespace edyn { 8 | 9 | /** 10 | * @brief A concave triangle mesh shape with paging support. 11 | * @remarks Paged triangle meshes can only be assigned to static rigid bodies. 12 | * The `collide` functions involving this shape ignore position and 13 | * orientation. If the mesh needs to be transformed, do so while constructing 14 | * or loading it. 15 | */ 16 | struct paged_mesh_shape { 17 | std::shared_ptr trimesh; 18 | }; 19 | 20 | } 21 | 22 | #endif // EDYN_SHAPES_PAGED_MESH_SHAPE_HPP -------------------------------------------------------------------------------- /include/edyn/shapes/plane_shape.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_SHAPES_PLANE_SHAPE_HPP 2 | #define EDYN_SHAPES_PLANE_SHAPE_HPP 3 | 4 | #include "edyn/math/vector3.hpp" 5 | #include "edyn/serialization/math_s11n.hpp" 6 | 7 | namespace edyn { 8 | 9 | /** 10 | * @brief A plane shape. 11 | * @remarks Planes can only be assigned to static rigid bodies. 12 | * Position and orientation are ignored during collision detection. 13 | */ 14 | struct plane_shape { 15 | vector3 normal; 16 | scalar constant; 17 | }; 18 | 19 | template 20 | void serialize(Archive &archive, plane_shape &s) { 21 | archive(s.normal); 22 | archive(s.constant); 23 | } 24 | 25 | } 26 | 27 | #endif // EDYN_SHAPES_PLANE_SHAPE_HPP 28 | -------------------------------------------------------------------------------- /include/edyn/shapes/sphere_shape.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_COLLISION_SHAPE_SPHERE_SHAPE_HPP 2 | #define EDYN_COLLISION_SHAPE_SPHERE_SHAPE_HPP 3 | 4 | #include "edyn/math/scalar.hpp" 5 | 6 | namespace edyn { 7 | 8 | struct sphere_shape { 9 | scalar radius; 10 | }; 11 | 12 | template 13 | void serialize(Archive &archive, sphere_shape &s) { 14 | archive(s.radius); 15 | } 16 | 17 | } 18 | 19 | #endif // EDYN_COLLISION_SHAPE_SPHERE_SHAPE_HPP 20 | -------------------------------------------------------------------------------- /include/edyn/shapes/triangle_mesh_page_loader.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_SHAPES_TRIANGLE_MESH_PAGE_LOADER_HPP 2 | #define EDYN_SHAPES_TRIANGLE_MESH_PAGE_LOADER_HPP 3 | 4 | #include 5 | 6 | namespace edyn { 7 | 8 | class paged_triangle_mesh; 9 | 10 | class triangle_mesh_page_loader_base { 11 | public: 12 | virtual ~triangle_mesh_page_loader_base() = default; 13 | virtual void load(paged_triangle_mesh *trimesh, size_t index) = 0; 14 | }; 15 | 16 | 17 | } 18 | 19 | #endif // EDYN_SHAPES_TRIANGLE_MESH_PAGE_LOADER_HPP 20 | -------------------------------------------------------------------------------- /include/edyn/simulation/stepper_sequential.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_SIMULATION_STEPPER_SEQUENTIAL_HPP 2 | #define EDYN_SIMULATION_STEPPER_SEQUENTIAL_HPP 3 | 4 | #include 5 | #include "edyn/dynamics/solver.hpp" 6 | #include "edyn/simulation/island_manager.hpp" 7 | #include "edyn/util/polyhedron_shape_initializer.hpp" 8 | 9 | namespace edyn { 10 | 11 | /** 12 | * Steps the simulation sequentially in the main thread into the given 13 | * registry. It can optionally parallelize many steps of the simulation. 14 | */ 15 | class stepper_sequential { 16 | public: 17 | stepper_sequential(entt::registry ®istry, double time, bool multithreaded); 18 | 19 | void update(double time); 20 | void step_simulation(double time); 21 | void set_paused(bool paused); 22 | 23 | bool is_paused() const { 24 | return m_paused; 25 | } 26 | 27 | double get_simulation_timestamp() const { 28 | return m_last_time - m_accumulated_time; 29 | } 30 | 31 | auto & get_island_manager() { 32 | return m_island_manager; 33 | } 34 | 35 | private: 36 | entt::registry *m_registry; 37 | island_manager m_island_manager; 38 | polyhedron_shape_initializer m_poly_initializer; 39 | solver m_solver; 40 | 41 | double m_accumulated_time {}; 42 | double m_last_time {}; 43 | bool m_multithreaded; 44 | bool m_paused; 45 | }; 46 | 47 | } 48 | 49 | #endif // EDYN_SIMULATION_STEPPER_SEQUENTIAL_HPP 50 | -------------------------------------------------------------------------------- /include/edyn/sys/apply_gravity.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_SYS_APPLY_GRAVITY_HPP 2 | #define EDYN_SYS_APPLY_GRAVITY_HPP 3 | 4 | #include 5 | #include "edyn/comp/linvel.hpp" 6 | #include "edyn/comp/gravity.hpp" 7 | #include "edyn/comp/tag.hpp" 8 | #include "edyn/util/island_util.hpp" 9 | 10 | namespace edyn { 11 | 12 | inline void apply_gravity(entt::registry ®istry, scalar dt) { 13 | auto view = registry.view(exclude_sleeping_disabled); 14 | view.each([&](linvel &vel, gravity &g) { 15 | vel += g * dt; 16 | }); 17 | } 18 | 19 | } 20 | 21 | #endif // EDYN_SYS_APPLY_GRAVITY_HPP 22 | -------------------------------------------------------------------------------- /include/edyn/sys/update_aabbs.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_SYS_UPDATE_AABBS_HPP 2 | #define EDYN_SYS_UPDATE_AABBS_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace edyn { 8 | 9 | /** 10 | * @brief Update AABBs of all entities that contain a shape. 11 | * @remark It's important to call this after the rotated meshes of all 12 | * polyhedrons are updated because they will be used to calculate the AABBs of 13 | * polyhedrons. 14 | * @param registry The registry to be updated. 15 | */ 16 | void update_aabbs(entt::registry ®istry); 17 | 18 | 19 | /** 20 | * @brief Update AABBs of given entities. 21 | * @remark This must be called after transforms are changed manually. 22 | * @param registry Data source. 23 | * @param entities Entities to be updated. 24 | */ 25 | void update_aabbs(entt::registry ®istry, const entt::sparse_set &entities); 26 | 27 | /*! @copydoc update_aabbs */ 28 | void update_aabbs(entt::registry ®istry, const std::vector &entities); 29 | 30 | /** 31 | * @brief Update AABB of a single entity. 32 | * @param registry The registry to be updated. 33 | * @param entity Entity to be updated. 34 | */ 35 | void update_aabb(entt::registry ®istry, entt::entity entity); 36 | 37 | void update_island_aabbs(entt::registry ®istry); 38 | void update_island_aabbs(entt::registry ®istry, const entt::sparse_set &entities); 39 | void update_island_aabbs(entt::registry ®istry, const std::vector &entities); 40 | 41 | } 42 | 43 | #endif // EDYN_SYS_UPDATE_AABBS_HPP 44 | -------------------------------------------------------------------------------- /include/edyn/sys/update_inertias.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_SYS_UPDATE_INERTIA_HPP 2 | #define EDYN_SYS_UPDATE_INERTIA_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace edyn { 8 | 9 | /** 10 | * @brief Updates all `inertia_world_inv` by rotating their inertia tensor 11 | * to world space. 12 | * @param registry Data source. 13 | */ 14 | void update_inertias(entt::registry &); 15 | 16 | /** 17 | * @brief Updates a single `inertia_world_inv`. 18 | * @param registry Data source. 19 | * @param entity Entity to be updated. 20 | */ 21 | void update_inertia(entt::registry ®istry, entt::entity entity); 22 | 23 | /** 24 | * @brief Updates `inertia_world_inv` for the given entities. 25 | * @param registry Data source. 26 | * @param entities Entities to be updated. 27 | */ 28 | void update_inertias(entt::registry ®istry, const entt::sparse_set &entities); 29 | 30 | /*! @copydoc update_inertias */ 31 | void update_inertias(entt::registry ®istry, const std::vector &entities); 32 | 33 | } 34 | 35 | #endif // EDYN_SYS_UPDATE_INERTIA_HPP 36 | -------------------------------------------------------------------------------- /include/edyn/sys/update_origins.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_SYS_UPDATE_ORIGINS_HPP 2 | #define EDYN_SYS_UPDATE_ORIGINS_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace edyn { 8 | 9 | /** 10 | * @brief Recalculates cached origin of bodies that have a non-zero 11 | * center of mass offset. 12 | * @param registry Data source. 13 | */ 14 | void update_origins(entt::registry &); 15 | 16 | 17 | /** 18 | * @brief Recalculates cached origin of given entities. 19 | * @param registry Data source. 20 | * @param entities Entities to be updated. 21 | */ 22 | void update_origins(entt::registry &, const entt::sparse_set &); 23 | 24 | /*! @copydoc update_origins */ 25 | void update_origins(entt::registry &, const std::vector &); 26 | 27 | } 28 | 29 | #endif // EDYN_SYS_UPDATE_ORIGINS_HPP 30 | -------------------------------------------------------------------------------- /include/edyn/sys/update_presentation.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_SYS_UPDATE_PRESENTATION_HPP 2 | #define EDYN_SYS_UPDATE_PRESENTATION_HPP 3 | 4 | #include 5 | 6 | namespace edyn { 7 | 8 | void update_presentation(entt::registry ®istry, double sim_time, double current_time, 9 | double delta_time, double presentation_delay); 10 | 11 | void snap_presentation(entt::registry ®istry); 12 | 13 | } 14 | 15 | #endif // EDYN_SYS_UPDATE_PRESENTATION_HPP 16 | -------------------------------------------------------------------------------- /include/edyn/sys/update_rotated_meshes.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_SYS_UPDATE_ROTATED_MESHES_HPP 2 | #define EDYN_SYS_UPDATE_ROTATED_MESHES_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace edyn { 8 | 9 | struct rotated_mesh; 10 | struct convex_mesh; 11 | struct quaternion; 12 | 13 | /** 14 | * @brief Updates the rotated mesh of all polyhedron shapes, including the ones 15 | * in compound shapes. 16 | * @param registry Source of shapes. 17 | */ 18 | void update_rotated_meshes(entt::registry ®istry); 19 | 20 | /** 21 | * @brief Updates the rotated mesh of the given entities. 22 | * @param registry Data source. 23 | * @param entities Entities to be updated. 24 | */ 25 | void update_rotated_meshes(entt::registry ®istry, const entt::sparse_set &entities); 26 | 27 | /*! @copydoc update_rotated_meshes */ 28 | void update_rotated_meshes(entt::registry ®istry, const std::vector &entities); 29 | 30 | /** 31 | * @brief Updates the rotated mesh of a single entity, which is assumed to have 32 | * either a polyhedron or a compound shape. 33 | * @param registry Data source. 34 | * @param entity Entity to be updated. 35 | */ 36 | void update_rotated_mesh(entt::registry ®istry, entt::entity entity); 37 | 38 | /** 39 | * @brief Updates rotated mesh by appliying a rotation to the vertex positions 40 | * and face normals of a mesh. 41 | * @param rotated The rotated mesh to be updated. 42 | * @param mesh The source convex mesh. 43 | * @param orn Rotation to be applied. 44 | */ 45 | void update_rotated_mesh(rotated_mesh &rotated, const convex_mesh &mesh, 46 | const quaternion &orn); 47 | 48 | } 49 | 50 | #endif // EDYN_SYS_UPDATE_ROTATED_MESHES_HPP 51 | -------------------------------------------------------------------------------- /include/edyn/time/simulation_time.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_TIME_SIMULATION_TIME_HPP 2 | #define EDYN_TIME_SIMULATION_TIME_HPP 3 | 4 | #include 5 | 6 | namespace edyn { 7 | 8 | /** 9 | * @brief Get timestamp of the last simulation step. 10 | * @param registry Data source. 11 | * @return Simulation timestamp. 12 | */ 13 | double get_simulation_timestamp(entt::registry ®istry); 14 | 15 | } 16 | 17 | #endif // EDYN_TIME_SIMULATION_TIME_HPP 18 | -------------------------------------------------------------------------------- /include/edyn/time/time.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_TIME_TIME_HPP 2 | #define EDYN_TIME_TIME_HPP 3 | 4 | #include 5 | 6 | namespace edyn { 7 | 8 | /** 9 | * @brief Get the number of milliseconds since library initialization. 10 | * @return Milliseconds since the library initialized. 11 | */ 12 | uint32_t ticks(); 13 | 14 | /** 15 | * @brief Wait a specified number of milliseconds before returning. 16 | * @param ms The number of milliseconds to delay. 17 | */ 18 | void delay(uint32_t ms); 19 | 20 | /** 21 | * @brief Get the current value of the high resolution counter. 22 | * @return Counter value. 23 | */ 24 | uint64_t performance_counter(); 25 | 26 | /** 27 | * @brief Get the count per second of the high resolution counter. 28 | * @return Count per second. 29 | */ 30 | uint64_t performance_frequency(); 31 | 32 | /** 33 | * @brief Returns the current time in seconds using the performance counter. 34 | * I.e. `performance_counter` divided by `performance_frequency`. 35 | * @return Current time in seconds. 36 | */ 37 | double performance_time(); 38 | 39 | } 40 | 41 | #endif // EDYN_TIME_TIME_HPP 42 | -------------------------------------------------------------------------------- /include/edyn/util/array_util.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_UTIL_ARRAY_UTIL_HPP 2 | #define EDYN_UTIL_ARRAY_UTIL_HPP 3 | 4 | #include 5 | 6 | namespace edyn { 7 | 8 | // Create array filled with given value. From https://stackoverflow.com/a/41259045 9 | namespace detail { 10 | template 11 | constexpr std::array make_array(const T& value, std::index_sequence) { 12 | return {{(static_cast(Is), value)...}}; 13 | } 14 | } 15 | 16 | template 17 | constexpr std::array make_array(const T& value) { 18 | return detail::make_array(value, std::make_index_sequence()); 19 | } 20 | 21 | } 22 | 23 | #endif // EDYN_UTIL_ARRAY_UTIL_HPP 24 | -------------------------------------------------------------------------------- /include/edyn/util/contact_manifold_util.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_UTIL_CONTACT_MANIFOLD_UTIL_HPP 2 | #define EDYN_UTIL_CONTACT_MANIFOLD_UTIL_HPP 3 | 4 | #include "edyn/core/entity_pair.hpp" 5 | #include 6 | 7 | namespace edyn { 8 | 9 | /** 10 | * @brief Checks whether there is a contact manifold connecting the two entities. 11 | * @param registry Data source. 12 | * @param first One entity. 13 | * @param second Another entity. 14 | * @return Whether a contact manifold exists between the two entities. 15 | */ 16 | bool manifold_exists(entt::registry ®istry, entt::entity first, entt::entity second); 17 | 18 | /*! @copydoc manifold_exists */ 19 | bool manifold_exists(entt::registry ®istry, entity_pair entities); 20 | 21 | /** 22 | * @brief Get contact manifold entity for a pair of entities. 23 | * Asserts if the manifold does not exist. 24 | * @param registry Data source. 25 | * @param first One entity. 26 | * @param second Another entity. 27 | * @return Contact manifold entity. 28 | */ 29 | entt::entity get_manifold_entity(const entt::registry ®istry, entt::entity first, entt::entity second); 30 | 31 | /*! @copydoc get_manifold_entity */ 32 | entt::entity get_manifold_entity(const entt::registry ®istry, entity_pair entities); 33 | 34 | } 35 | 36 | #endif // EDYN_UTIL_CONTACT_MANIFOLD_UTIL_HPP 37 | -------------------------------------------------------------------------------- /include/edyn/util/entt_util.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_UTIL_ENTT_UTIL_HPP 2 | #define EDYN_UTIL_ENTT_UTIL_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace edyn { 9 | 10 | // Get a view with component types from a tuple. 11 | template 12 | auto get_view_from_tuple(entt::registry ®istry, [[maybe_unused]] std::tuple) { 13 | return registry.view(); 14 | } 15 | 16 | // Get a tuple containing a view of each given type. 17 | template 18 | inline auto get_tuple_of_views(entt::registry ®istry) { 19 | return std::make_tuple(registry.view()...); 20 | } 21 | 22 | // Get a tuple containing a view of each type in the given tuple. 23 | template 24 | inline auto get_tuple_of_views(entt::registry ®istry, [[maybe_unused]] std::tuple) { 25 | return get_tuple_of_views(registry); 26 | } 27 | 28 | template 29 | struct map_to_tuple_of_views; 30 | 31 | // Declares a tuple type which holds single component views for each of the 32 | // types in the given tuple. 33 | template 34 | struct map_to_tuple_of_views> { 35 | using type = std::tuple>, entt::exclude_t<>>...>; 36 | }; 37 | 38 | inline void entity_vector_erase_invalid(std::vector &vec, 39 | const entt::registry ®istry) { 40 | auto predicate = [&](entt::entity entity) { 41 | return !registry.valid(entity); 42 | }; 43 | 44 | vec.erase(std::remove_if(vec.begin(), vec.end(), predicate), vec.end()); 45 | } 46 | 47 | template 48 | size_t calculate_view_size(const View &view) { 49 | return static_cast(std::distance(view.begin(), view.end())); 50 | } 51 | 52 | } 53 | 54 | #endif // EDYN_UTIL_ENTT_UTIL_HPP 55 | -------------------------------------------------------------------------------- /include/edyn/util/exclude_collision.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_UTIL_EXCLUDE_COLLISION_HPP 2 | #define EDYN_UTIL_EXCLUDE_COLLISION_HPP 3 | 4 | #include 5 | #include "edyn/core/entity_pair.hpp" 6 | 7 | namespace edyn { 8 | 9 | /** 10 | * @brief Excludes collisions between a pair of entities. 11 | * Use this when collision filters are not enough. 12 | * @remark Must be removed with `clear_collision_exclusion` when the entity is 13 | * destroyed to avoid having an invalid entity in the exclusion list of others. 14 | * This won't cause UB but will occupy valuable space in the fixed-size array 15 | * of exclusions. 16 | * @param registry Data source. 17 | * @param first The entity that should not collide with `second`. 18 | * @param second The entity that should not collide with `first`. 19 | */ 20 | void exclude_collision(entt::registry ®istry, entt::entity first, entt::entity second); 21 | 22 | /*! @copydoc exclude_collision */ 23 | void exclude_collision(entt::registry ®istry, entity_pair entities); 24 | 25 | /** 26 | * @brief Remove a collision exclusion between two entities. 27 | * @param registry Data source. 28 | * @param first Entity that has an exclusion with `second`. 29 | * @param second Entity that has an exclusion with `first`. 30 | */ 31 | void remove_collision_exclusion(entt::registry ®istry, entt::entity first, entt::entity second); 32 | 33 | /** 34 | * @brief Remove all collision exclusions for the given entity. 35 | * @param registry Data source. 36 | * @param entity Entity to be cleared of exclusions. 37 | */ 38 | void clear_collision_exclusion(entt::registry ®istry, entt::entity entity); 39 | 40 | } 41 | 42 | #endif // EDYN_UTIL_EXCLUDE_COLLISION_HPP 43 | -------------------------------------------------------------------------------- /include/edyn/util/gravity_util.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_UTIL_GRAVITY_UTIL_HPP 2 | #define EDYN_UTIL_GRAVITY_UTIL_HPP 3 | 4 | #include "edyn/math/vector3.hpp" 5 | #include 6 | 7 | namespace edyn { 8 | 9 | /** 10 | * @brief Get default gravity. 11 | * This value is assigned as the gravitational acceleration for all new rigid bodies. 12 | * @param registry Data source. 13 | * @return Gravity acceleration vector. 14 | */ 15 | vector3 get_gravity(const entt::registry ®istry); 16 | 17 | /** 18 | * @brief Changes the default gravity acceleration. 19 | * This value is assigned as the gravitational acceleration for all new rigid bodies. 20 | * @param registry Data source. 21 | * @param gravity The new default gravity acceleration. 22 | */ 23 | void set_gravity(entt::registry ®istry, vector3 gravity); 24 | 25 | } 26 | 27 | #endif // EDYN_UTIL_GRAVITY_UTIL_HPP 28 | -------------------------------------------------------------------------------- /include/edyn/util/insert_material_mixing.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_UTIL_INSERT_MATERIAL_MIXING_HPP 2 | #define EDYN_UTIL_INSERT_MATERIAL_MIXING_HPP 3 | 4 | #include 5 | #include "edyn/comp/material.hpp" 6 | 7 | namespace edyn { 8 | 9 | /** 10 | * @brief Use the provided material when two rigid bodies with the given 11 | * material ids collide. 12 | * @param registry Data source. 13 | * @param material_id0 ID of a material. 14 | * @param material_id1 ID of another material (could be equal to material_id0). 15 | * @param material Material info. 16 | */ 17 | void insert_material_mixing(entt::registry ®istry, material::id_type material_id0, 18 | material::id_type material_id1, const material_base &material); 19 | 20 | } 21 | 22 | #endif // EDYN_UTIL_INSERT_MATERIAL_MIXING_HPP 23 | -------------------------------------------------------------------------------- /include/edyn/util/island_util.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_UTIL_ISLAND_UTIL_HPP 2 | #define EDYN_UTIL_ISLAND_UTIL_HPP 3 | 4 | #include 5 | #include "edyn/comp/island.hpp" 6 | #include "edyn/comp/tag.hpp" 7 | #include "edyn/replication/entity_map.hpp" 8 | 9 | namespace edyn { 10 | 11 | static constexpr auto exclude_sleeping_disabled = entt::exclude_t{}; 12 | 13 | void remove_sleeping_tag_from_island(entt::registry ®istry, 14 | entt::entity island_entity, 15 | const edyn::island &island); 16 | 17 | entt::sparse_set collect_islands_from_residents(entt::registry ®istry, const std::vector &entities); 18 | entt::sparse_set collect_islands_from_residents(entt::registry ®istry, const entt::sparse_set &entities); 19 | 20 | void wake_up_island(entt::registry ®istry, entt::entity island_entity); 21 | 22 | void wake_up_island_residents(entt::registry ®istry, const std::vector &entities); 23 | 24 | void wake_up_island_residents(entt::registry ®istry, const entt::sparse_set &entities); 25 | 26 | void wake_up_island_resident(entt::registry ®istry, entt::entity entity); 27 | 28 | void wake_up_island_residents(entt::registry ®istry, const std::vector &entities, 29 | const entity_map &emap); 30 | 31 | void wake_up_island_residents(entt::registry ®istry, const entt::sparse_set &entities, 32 | const entity_map &emap); 33 | 34 | } 35 | 36 | #endif // EDYN_UTIL_ISLAND_UTIL_HPP 37 | -------------------------------------------------------------------------------- /include/edyn/util/paged_mesh_load_reporting.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_UTIL_PAGED_MESH_LOAD_REPORTING_HPP 2 | #define EDYN_UTIL_PAGED_MESH_LOAD_REPORTING_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace edyn { 8 | 9 | /** 10 | * @brief Triggers when a new page is loaded or unloaded on a paged mesh shape. 11 | * @param registry Data source. 12 | * @return Sink which allows observing page load events. It provides the shape 13 | * entity and the index of page that was loaded or unloaded. 14 | */ 15 | entt::sink> on_paged_mesh_page_loaded(entt::registry &); 16 | 17 | } 18 | 19 | namespace edyn::internal { 20 | 21 | inline constexpr auto paged_mesh_load_queue_identifier = "paged_triangle_mesh_page_load"; 22 | 23 | void init_paged_mesh_load_reporting(entt::registry ®istry); 24 | void update_paged_mesh_load_reporting(entt::registry ®istry); 25 | void deinit_paged_mesh_load_reporting(entt::registry ®istry); 26 | 27 | } 28 | 29 | #endif // EDYN_UTIL_PAGED_MESH_LOAD_REPORTING_HPP 30 | -------------------------------------------------------------------------------- /include/edyn/util/polyhedron_shape_initializer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_UTIL_POLYHEDRON_SHAPE_INITIALIZER_HPP 2 | #define EDYN_UTIL_POLYHEDRON_SHAPE_INITIALIZER_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace edyn { 9 | 10 | /** 11 | * @brief Sets up rotated meshes for polyhedrons that have been recently 12 | * created, including polyhedrons which reside in compound shapes. 13 | */ 14 | class polyhedron_shape_initializer { 15 | public: 16 | polyhedron_shape_initializer(entt::registry &); 17 | ~polyhedron_shape_initializer(); 18 | 19 | void on_construct_polyhedron_shape(entt::registry &, entt::entity); 20 | void on_construct_compound_shape(entt::registry &, entt::entity); 21 | void on_destroy_rotated_mesh_list(entt::registry &, entt::entity); 22 | 23 | void init_new_shapes(); 24 | 25 | private: 26 | entt::registry *m_registry; 27 | std::vector m_new_polyhedron_shapes; 28 | std::vector m_new_compound_shapes; 29 | std::vector m_connections; 30 | }; 31 | 32 | } 33 | 34 | #endif // EDYN_UTIL_POLYHEDRON_SHAPE_INITIALIZER_HPP 35 | -------------------------------------------------------------------------------- /include/edyn/util/settings_util.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_UTIL_SETTINGS_UTIL_HPP 2 | #define EDYN_UTIL_SETTINGS_UTIL_HPP 3 | 4 | #include 5 | 6 | namespace edyn { 7 | 8 | void refresh_settings(entt::registry &); 9 | 10 | } 11 | 12 | #endif // EDYN_UTIL_SETTINGS_UTIL_HPP 13 | -------------------------------------------------------------------------------- /include/edyn/util/vector_util.hpp: -------------------------------------------------------------------------------- 1 | #ifndef EDYN_UTIL_VECTOR_UTIL_HPP 2 | #define EDYN_UTIL_VECTOR_UTIL_HPP 3 | 4 | #include 5 | #include 6 | 7 | namespace edyn { 8 | 9 | template 10 | bool vector_contains(const std::vector &vec, const T &val) { 11 | return std::find(vec.begin(), vec.end(), val) != vec.end(); 12 | } 13 | 14 | template 15 | void vector_erase(std::vector &vec, const T &val) { 16 | vec.erase(std::remove(vec.begin(), vec.end(), val), vec.end()); 17 | } 18 | 19 | } 20 | 21 | #endif // EDYN_UTIL_VECTOR_UTIL_HPP 22 | -------------------------------------------------------------------------------- /src/edyn/collision/collide/collide_capsule_plane.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/collision/collide.hpp" 2 | #include "edyn/math/transform.hpp" 3 | 4 | namespace edyn { 5 | 6 | void collide(const capsule_shape &shA, const plane_shape &shB, 7 | const collision_context &ctx, collision_result &result) { 8 | auto center = shB.normal * shB.constant; 9 | auto capsule_vertices = shA.get_vertices(ctx.posA, ctx.ornA); 10 | 11 | scalar proj_capsule_vertices[] = { 12 | dot(capsule_vertices[0] - center, shB.normal), 13 | dot(capsule_vertices[1] - center, shB.normal) 14 | }; 15 | 16 | collision_feature featureA; 17 | auto is_capsule_edge = std::abs(proj_capsule_vertices[0] - 18 | proj_capsule_vertices[1]) < support_feature_tolerance; 19 | 20 | if (is_capsule_edge) { 21 | featureA.feature = capsule_feature::side; 22 | } else { 23 | featureA.feature = capsule_feature::hemisphere; 24 | featureA.index = proj_capsule_vertices[0] < proj_capsule_vertices[1] ? 0 : 1; 25 | } 26 | 27 | for (auto i = 0; i < 2; ++i) { 28 | auto distance = proj_capsule_vertices[i] - shA.radius; 29 | 30 | if (distance > ctx.threshold) continue; 31 | 32 | auto vertex = capsule_vertices[i]; 33 | auto pivotA_world = vertex - shB.normal * shA.radius; 34 | auto pivotA = to_object_space(pivotA_world, ctx.posA, ctx.ornA); 35 | auto pivotB = project_plane(vertex, center, shB.normal); 36 | result.add_point({pivotA, pivotB, shB.normal, distance, contact_normal_attachment::normal_on_B, featureA}); 37 | } 38 | } 39 | 40 | void collide(const plane_shape &shA, const capsule_shape &shB, 41 | const collision_context &ctx, collision_result &result) { 42 | swap_collide(shA, shB, ctx, result); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/edyn/collision/collide/collide_compound_compound.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/collision/collide.hpp" 2 | #include "edyn/math/transform.hpp" 3 | 4 | namespace edyn { 5 | 6 | void collide(const compound_shape &shA, const compound_shape &shB, 7 | const collision_context &ctx, collision_result &result) { 8 | // Collide every node of B with A. Swap it if B has more nodes than A. 9 | if (shA.nodes.size() < shB.nodes.size()) { 10 | swap_collide(shA, shB, ctx, result); 11 | return; 12 | } 13 | 14 | for (size_t node_idx = 0; node_idx < shB.nodes.size(); ++node_idx) { 15 | auto &nodeB = shB.nodes[node_idx]; 16 | 17 | // Create a new collision context with the position of the child of B 18 | // in world space. 19 | auto child_ctx = ctx; 20 | child_ctx.posB = to_world_space(nodeB.position, ctx.posB, ctx.ornB); 21 | child_ctx.ornB = ctx.ornB * nodeB.orientation; 22 | collision_result child_result; 23 | 24 | // Collide child shape with compound. 25 | std::visit([&](auto &&sh) { 26 | collide(shA, sh, child_ctx, child_result); 27 | }, nodeB.shape_var); 28 | 29 | // Transform the B elements of the result points from child shape space 30 | // into B's space. 31 | for (size_t i = 0; i < child_result.num_points; ++i) { 32 | auto &child_point = child_result.point[i]; 33 | child_point.pivotB = to_world_space(child_point.pivotB, nodeB.position, nodeB.orientation); 34 | 35 | // Assign the part index for the second compound. The part index 36 | // for featureA was already assigned in the call to `collide` above. 37 | if (!child_point.featureB) { 38 | child_point.featureB = collision_feature{}; 39 | } 40 | 41 | child_point.featureB->part = node_idx; 42 | 43 | result.maybe_add_point(child_point); 44 | } 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/edyn/collision/collide/collide_compound_mesh.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/collision/collide.hpp" 2 | #include "edyn/util/aabb_util.hpp" 3 | #include "edyn/math/triangle.hpp" 4 | #include "edyn/math/transform.hpp" 5 | 6 | namespace edyn { 7 | 8 | void collide(const compound_shape &compound, const triangle_mesh &mesh, 9 | const collision_context &ctx, collision_result &result) { 10 | // TODO Possible optimization: find the triangle mesh node which encompasses 11 | // the compound's AABB and start the tree queries from that node in the 12 | // child collision tests. 13 | 14 | for (size_t node_idx = 0; node_idx < compound.nodes.size(); ++node_idx) { 15 | auto &node = compound.nodes[node_idx]; 16 | 17 | // New collision context with child shape in world space. 18 | auto child_ctx = ctx; 19 | child_ctx.posA = to_world_space(node.position, ctx.posA, ctx.ornA); 20 | child_ctx.ornA = ctx.ornA * node.orientation; 21 | child_ctx.aabbA = aabb_to_world_space(node.aabb, ctx.posA, ctx.ornA); 22 | 23 | collision_result child_result; 24 | 25 | std::visit([&](auto &&sh) { 26 | collide(sh, mesh, child_ctx, child_result); 27 | }, node.shape_var); 28 | 29 | // The elements of A in the collision points must be transformed from 30 | // the child node's space into the compound's space. 31 | for (size_t i = 0; i < child_result.num_points; ++i) { 32 | auto &child_point = child_result.point[i]; 33 | child_point.pivotA = to_world_space(child_point.pivotA, node.position, node.orientation); 34 | 35 | // Assign part index for the closest feature in the compound shape. 36 | if (!child_point.featureA) { 37 | child_point.featureA = collision_feature{}; 38 | } 39 | 40 | child_point.featureA->part = node_idx; 41 | 42 | result.maybe_add_point(child_point); 43 | } 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/edyn/collision/collide/collide_compound_plane.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/collision/collide.hpp" 2 | #include "edyn/util/aabb_util.hpp" 3 | #include "edyn/math/transform.hpp" 4 | 5 | namespace edyn { 6 | 7 | void collide(const compound_shape &shA, const plane_shape &shB, 8 | const collision_context &ctx, collision_result &result) { 9 | for (size_t node_idx = 0; node_idx < shA.nodes.size(); ++node_idx) { 10 | auto &node = shA.nodes[node_idx]; 11 | 12 | const auto inset = vector3_one * -contact_breaking_threshold; 13 | auto aabbA = aabb_to_world_space(node.aabb, ctx.posA, ctx.ornA).inset(inset); 14 | 15 | if (!intersect(aabbA, ctx.aabbB)) { 16 | continue; 17 | } 18 | 19 | auto child_ctx = ctx; 20 | child_ctx.posA = to_world_space(node.position, ctx.posA, ctx.ornA); 21 | child_ctx.ornA *= node.orientation; 22 | collision_result child_result; 23 | 24 | std::visit([&](auto &&sh) { 25 | collide(sh, shB, child_ctx, child_result); 26 | }, node.shape_var); 27 | 28 | for (size_t i = 0; i < child_result.num_points; ++i) { 29 | auto &child_point = child_result.point[i]; 30 | child_point.pivotA = to_world_space(child_point.pivotA, node.position, node.orientation); 31 | 32 | if (!child_point.featureA) { 33 | child_point.featureA = collision_feature{}; 34 | } 35 | 36 | child_point.featureA->part = node_idx; 37 | 38 | result.maybe_add_point(child_point); 39 | } 40 | } 41 | } 42 | 43 | // Plane-Compound 44 | void collide(const plane_shape &shA, const compound_shape &shB, 45 | const collision_context &ctx, collision_result &result) { 46 | swap_collide(shA, shB, ctx, result); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/edyn/collision/collide/collide_polyhedron_plane.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/collision/collide.hpp" 2 | #include "edyn/collision/collision_result.hpp" 3 | #include "edyn/math/constants.hpp" 4 | #include "edyn/math/quaternion.hpp" 5 | #include "edyn/math/transform.hpp" 6 | #include "edyn/util/shape_util.hpp" 7 | 8 | namespace edyn { 9 | 10 | void collide(const polyhedron_shape &shA, const plane_shape &shB, 11 | const collision_context &ctx, collision_result &result) { 12 | auto &posA = ctx.posA; 13 | auto &rmeshA = *shA.rotated; 14 | 15 | auto normal = shB.normal; 16 | auto center = shB.normal * shB.constant - posA; 17 | 18 | auto proj_poly = -polyhedron_support_projection(rmeshA.vertices, shA.mesh->neighbors_start, shA.mesh->neighbor_indices, -normal); 19 | auto proj_plane = dot(center, normal); 20 | scalar distance = proj_poly - proj_plane; 21 | 22 | if (distance > ctx.threshold) return; 23 | 24 | auto polygon = point_cloud_support_polygon( 25 | rmeshA.vertices.begin(), rmeshA.vertices.end(), vector3_zero, 26 | normal, proj_poly, true, support_feature_tolerance); 27 | auto normal_attachment = contact_normal_attachment::normal_on_B; 28 | 29 | for (auto idxA : polygon.hull) { 30 | auto &pointA = polygon.vertices[idxA]; 31 | 32 | auto pivotA = rotate(conjugate(ctx.ornA), pointA); 33 | auto local_distance = dot(pointA - center, normal); 34 | auto pivotB = pointA - normal * local_distance + posA; // Project onto plane. 35 | result.maybe_add_point({pivotA, pivotB, normal, local_distance, normal_attachment}); 36 | } 37 | } 38 | 39 | void collide(const plane_shape &shA, const polyhedron_shape &shB, 40 | const collision_context &ctx, collision_result &result) { 41 | swap_collide(shA, shB, ctx, result); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/edyn/collision/collide/collide_sphere_plane.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/collision/collide.hpp" 2 | 3 | namespace edyn { 4 | 5 | void collide(const sphere_shape &sphere, const plane_shape &plane, 6 | const collision_context &ctx, collision_result &result) { 7 | auto normal = plane.normal; 8 | auto center = plane.normal * plane.constant; 9 | auto d = ctx.posA - center; 10 | auto l = dot(normal, d); 11 | 12 | if (l > sphere.radius) { 13 | return; 14 | } 15 | 16 | auto pivotA = rotate(conjugate(ctx.ornA), -normal * sphere.radius); 17 | auto pivotB = rotate(conjugate(ctx.ornB), d - normal * l - center); 18 | auto distance = l - sphere.radius; 19 | result.add_point({pivotA, pivotB, plane.normal, distance, contact_normal_attachment::normal_on_B}); 20 | } 21 | 22 | void collide(const plane_shape &shA, const sphere_shape &shB, 23 | const collision_context &ctx, collision_result &result) { 24 | swap_collide(shA, shB, ctx, result); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/edyn/collision/collide/collide_sphere_sphere.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/collision/collide.hpp" 2 | 3 | namespace edyn { 4 | 5 | void collide(const sphere_shape &shA, const sphere_shape &shB, 6 | const collision_context &ctx, collision_result &result) { 7 | auto d = ctx.posA - ctx.posB; 8 | auto dist_sqr = length_sqr(d); 9 | auto r = shA.radius + shB.radius + ctx.threshold; 10 | 11 | if (dist_sqr > r * r) { 12 | return; 13 | } 14 | 15 | auto dist = std::sqrt(dist_sqr); 16 | auto dn = dist > EDYN_EPSILON ? d / dist : vector3_x; 17 | auto rA = -dn * shA.radius; 18 | rA = rotate(conjugate(ctx.ornA), rA); 19 | auto rB = dn * shB.radius; 20 | rB = rotate(conjugate(ctx.ornB), rB); 21 | 22 | auto pivotA = rA; 23 | auto pivotB = rB; 24 | auto normal = dn; 25 | auto distance = dist - shA.radius - shB.radius; 26 | result.add_point({pivotA, pivotB, normal, distance, contact_normal_attachment::none}); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/edyn/collision/collision_result.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/collision/collision_result.hpp" 2 | #include "edyn/math/geom.hpp" 3 | 4 | namespace edyn { 5 | 6 | void collision_result::add_point(const collision_result::collision_point &new_point) { 7 | EDYN_ASSERT(num_points < max_contacts); 8 | auto idx = num_points++; 9 | point[idx] = new_point; 10 | } 11 | 12 | void collision_result::maybe_add_point(const collision_result::collision_point &new_point) { 13 | std::array pivots; 14 | std::array distances; 15 | for (size_t i = 0; i < num_points; ++i) { 16 | pivots[i] = point[i].pivotA; 17 | distances[i] = point[i].distance; 18 | } 19 | 20 | auto res = insertion_point_index(pivots, distances, num_points, new_point.pivotA); 21 | 22 | // No closest point found for pivotA, try pivotB. 23 | if (res.type == point_insertion_type::none) { 24 | for (size_t i = 0; i < num_points; ++i) { 25 | pivots[i] = point[i].pivotB; 26 | } 27 | 28 | res = insertion_point_index(pivots, distances, num_points, new_point.pivotB); 29 | } 30 | 31 | if (res.type != point_insertion_type::none) { 32 | point[res.index] = new_point; 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/edyn/collision/contact_signal.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/collision/contact_signal.hpp" 2 | #include "edyn/collision/contact_event_emitter.hpp" 3 | 4 | namespace edyn { 5 | 6 | entt::sink> on_contact_started(entt::registry ®istry) { 7 | return registry.ctx().get().contact_started_sink(); 8 | } 9 | 10 | entt::sink> on_contact_ended(entt::registry ®istry) { 11 | return registry.ctx().get().contact_ended_sink(); 12 | } 13 | 14 | entt::sink> on_contact_point_created(entt::registry ®istry) { 15 | return registry.ctx().get().contact_point_created_sink(); 16 | } 17 | 18 | entt::sink> on_contact_point_destroyed(entt::registry ®istry) { 19 | return registry.ctx().get().contact_point_destroyed_sink(); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/edyn/collision/query_aabb.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/collision/query_aabb.hpp" 2 | #include "edyn/simulation/stepper_async.hpp" 3 | 4 | namespace edyn { 5 | 6 | query_aabb_id_type query_island_aabb_async(entt::registry ®istry, const AABB &aabb, 7 | const query_aabb_delegate_type &delegate, 8 | bool query_procedural, 9 | bool query_non_procedural, 10 | bool query_islands) { 11 | auto &stepper = registry.ctx().get(); 12 | return stepper.query_aabb(aabb, delegate, query_procedural, query_non_procedural, query_islands); 13 | } 14 | 15 | 16 | query_aabb_id_type query_aabb_of_interest_async(entt::registry ®istry, const AABB &aabb, 17 | const query_aabb_delegate_type &delegate) { 18 | auto &stepper = registry.ctx().get(); 19 | return stepper.query_aabb_of_interest(aabb, delegate); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/edyn/constraints/constraint_row_spin_friction.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/constraints/constraint_row_spin_friction.hpp" 2 | 3 | namespace edyn { 4 | 5 | void solve_spin_friction(constraint_row_spin_friction &row, const std::vector &row_cache) { 6 | auto &normal_row = row_cache[row.normal_row_index]; 7 | auto max_impulse_len = row.friction_coefficient * normal_row.impulse; 8 | 9 | auto delta_relvel = dot(row.J[0], *normal_row.dwA) + dot(row.J[1], *normal_row.dwB); 10 | auto delta_impulse = (row.rhs - delta_relvel) * row.eff_mass; 11 | auto impulse = row.impulse + delta_impulse; 12 | auto lower_limit = -max_impulse_len; 13 | auto upper_limit = max_impulse_len; 14 | 15 | if (impulse < lower_limit) { 16 | delta_impulse = lower_limit - row.impulse; 17 | row.impulse = lower_limit; 18 | } else if (impulse > upper_limit) { 19 | delta_impulse = upper_limit - row.impulse; 20 | row.impulse = upper_limit; 21 | } else { 22 | row.impulse = impulse; 23 | } 24 | 25 | // Apply angular impulse. 26 | *normal_row.dwA += normal_row.inv_IA * row.J[0] * delta_impulse; 27 | *normal_row.dwB += normal_row.inv_IB * row.J[1] * delta_impulse; 28 | } 29 | 30 | void warm_start(constraint_row_spin_friction &row, const std::vector &row_cache) { 31 | auto &normal_row = row_cache[row.normal_row_index]; 32 | // Apply angular impulse. 33 | *normal_row.dwA += normal_row.inv_IA * row.J[0] * row.impulse; 34 | *normal_row.dwB += normal_row.inv_IB * row.J[1] * row.impulse; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/edyn/constraints/distance_constraint.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/constraints/distance_constraint.hpp" 2 | #include "edyn/dynamics/row_cache.hpp" 3 | #include "edyn/math/transform.hpp" 4 | 5 | namespace edyn { 6 | 7 | void distance_constraint::prepare( 8 | const entt::registry &, entt::entity, 9 | constraint_row_prep_cache &cache, scalar dt, 10 | const constraint_body &bodyA, const constraint_body &bodyB) { 11 | 12 | auto pivotA = to_world_space(pivot[0], bodyA.origin, bodyA.orn); 13 | auto pivotB = to_world_space(pivot[1], bodyB.origin, bodyB.orn); 14 | auto rA = pivotA - bodyA.pos; 15 | auto rB = pivotB - bodyB.pos; 16 | 17 | auto d = pivotA - pivotB; 18 | auto dist_sqr = length_sqr(d); 19 | 20 | if (!(dist_sqr > EDYN_EPSILON)) { 21 | d = vector3_x; 22 | } 23 | 24 | auto &row = cache.add_row(); 25 | row.J = {d, cross(rA, d), -d, -cross(rB, d)}; 26 | row.lower_limit = -large_scalar; 27 | row.upper_limit = large_scalar; 28 | row.impulse = applied_impulse; 29 | 30 | auto &options = cache.get_options(); 31 | options.error = scalar(0.5) * (dist_sqr - distance * distance) / dt; 32 | } 33 | 34 | void distance_constraint::store_applied_impulses(const std::vector &impulses) { 35 | applied_impulse = impulses[0]; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/edyn/constraints/gravity_constraint.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/constraints/gravity_constraint.hpp" 2 | #include "edyn/dynamics/row_cache.hpp" 3 | 4 | namespace edyn { 5 | 6 | void gravity_constraint::prepare( 7 | const entt::registry &, entt::entity, 8 | constraint_row_prep_cache &cache, scalar dt, 9 | const constraint_body &bodyA, const constraint_body &bodyB) { 10 | 11 | auto d = bodyA.pos - bodyB.pos; 12 | auto l2 = length_sqr(d); 13 | l2 = std::max(l2, EDYN_EPSILON); 14 | 15 | auto l = std::sqrt(l2); 16 | auto dn = d / l; 17 | 18 | auto F = gravitational_constant / (l2 * bodyA.inv_m * bodyB.inv_m); 19 | auto P = F * dt; 20 | 21 | auto &row = cache.add_row(); 22 | row.J = {dn, vector3_zero, -dn, -vector3_zero}; 23 | row.lower_limit = -P; 24 | row.upper_limit = P; 25 | row.impulse = applied_impulse; 26 | 27 | auto &options = cache.get_options(); 28 | options.error = large_scalar; 29 | } 30 | 31 | void gravity_constraint::store_applied_impulses(const std::vector &impulses) { 32 | applied_impulse = impulses[0]; 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/edyn/context/registry_operation_context.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/context/registry_operation_context.hpp" 2 | #include "edyn/comp/shared_comp.hpp" 3 | #include "edyn/replication/registry_operation_builder.hpp" 4 | #include "edyn/replication/registry_operation_observer.hpp" 5 | 6 | namespace edyn { 7 | 8 | std::unique_ptr make_reg_op_builder_default(entt::registry ®istry) { 9 | return std::unique_ptr( 10 | new registry_operation_builder_impl(registry, shared_components_t{})); 11 | } 12 | 13 | std::unique_ptr make_reg_op_observer_default(registry_operation_builder &builder) { 14 | return std::unique_ptr( 15 | new registry_operation_observer_impl(builder, shared_components_t{})); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/edyn/context/start_thread.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/context/start_thread.hpp" 2 | #include 3 | 4 | namespace edyn { 5 | 6 | void start_thread_func_default(void (*func)(void *), void *data) { 7 | std::thread(func, data).detach(); 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/edyn/context/step_callback.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/context/step_callback.hpp" 2 | #include "edyn/context/settings.hpp" 3 | #include "edyn/networking/context/client_network_context.hpp" 4 | #include "edyn/simulation/stepper_async.hpp" 5 | #include 6 | 7 | namespace edyn { 8 | 9 | namespace internal { 10 | void notify_settings(entt::registry ®istry, const edyn::settings &settings) { 11 | if (auto *stepper = registry.ctx().find()) { 12 | stepper->settings_changed(); 13 | } 14 | 15 | if (auto *ctx = registry.ctx().find()) { 16 | ctx->extrapolator->set_settings(settings); 17 | } 18 | } 19 | } 20 | 21 | void set_pre_step_callback(entt::registry ®istry, step_callback_t func) { 22 | auto &settings = registry.ctx().get(); 23 | settings.pre_step_callback = func; 24 | internal::notify_settings(registry, settings); 25 | } 26 | 27 | void set_post_step_callback(entt::registry ®istry, step_callback_t func) { 28 | auto &settings = registry.ctx().get(); 29 | settings.post_step_callback = func; 30 | internal::notify_settings(registry, settings); 31 | } 32 | 33 | void set_init_callback(entt::registry ®istry, init_callback_t func) { 34 | auto &settings = registry.ctx().get(); 35 | settings.init_callback = func; 36 | internal::notify_settings(registry, settings); 37 | } 38 | 39 | void set_deinit_callback(entt::registry ®istry, init_callback_t func) { 40 | auto &settings = registry.ctx().get(); 41 | settings.deinit_callback = func; 42 | internal::notify_settings(registry, settings); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/edyn/math/quaternion.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/math/quaternion.hpp" 2 | #include "edyn/math/geom.hpp" 3 | 4 | namespace edyn { 5 | 6 | // "Practical Parameterization of Rotations Using the Exponential Map", F. Sebastian Grassia 7 | quaternion integrate(const quaternion &q, const vector3 &w, scalar dt) noexcept { 8 | const auto ws = length(w); 9 | const auto min_ws = scalar(0.001); 10 | constexpr auto half = scalar(0.5); 11 | scalar t; 12 | 13 | if (ws < min_ws) { 14 | constexpr auto k = scalar(1) / scalar(48); 15 | t = half * dt - dt * dt * dt * k * ws * ws; 16 | } else { 17 | t = std::sin(half * ws * dt) / ws; 18 | } 19 | 20 | auto r = quaternion {w.x * t, w.y * t, w.z * t, std::cos(half * ws * dt)}; 21 | return normalize(r * q); 22 | } 23 | 24 | // Bullet Physics (btQuaternion.h), Game Programming Gems 2.10. 25 | quaternion shortest_arc(const vector3 &v0, const vector3 &v1) noexcept { 26 | auto c = cross(v0, v1); 27 | auto d = dot(v0, v1); 28 | 29 | if (d <= -1 + EDYN_EPSILON) { 30 | vector3 n, m; 31 | plane_space(v0, n, m); 32 | return {n.x, n.y, n.z, 0}; 33 | } 34 | 35 | auto s = std::sqrt((1 + d) * 2); 36 | auto rs = 1 / s; 37 | return normalize(quaternion{c.x * rs, c.y * rs, c.z * rs, s * scalar(0.5)}); 38 | } 39 | 40 | scalar angle_between(const quaternion &q0, const quaternion &q1) noexcept { 41 | auto s = std::sqrt(length_sqr(q0) * length_sqr(q1)); 42 | EDYN_ASSERT(std::abs(s) > EDYN_EPSILON); 43 | auto d = dot(q0, q1); 44 | return std::acos(d / s) * scalar(2); 45 | } 46 | 47 | } -------------------------------------------------------------------------------- /src/edyn/networking/context/client_network_context.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/networking/context/client_network_context.hpp" 2 | #include "edyn/networking/comp/networked_comp.hpp" 3 | #include "edyn/serialization/entt_s11n.hpp" 4 | #include "edyn/serialization/std_s11n.hpp" 5 | #include "edyn/serialization/math_s11n.hpp" 6 | 7 | namespace edyn { 8 | 9 | static std::unique_ptr 10 | make_extrapolation_modified_comp_default(entt::registry ®istry) { 11 | return std::unique_ptr( 12 | new extrapolation_modified_comp_impl(registry, networked_components)); 13 | } 14 | 15 | client_network_context::client_network_context(entt::registry ®istry) 16 | : snapshot_importer(new client_snapshot_importer_impl(networked_components)) 17 | , snapshot_exporter(new client_snapshot_exporter_impl(registry, networked_components, {})) 18 | , input_history{} 19 | , make_extrapolation_modified_comp(&make_extrapolation_modified_comp_default) 20 | { 21 | clock_sync.send_packet.connect<&entt::sigh::publish>(packet_signal); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/edyn/networking/context/server_network_context.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/networking/context/server_network_context.hpp" 2 | #include "edyn/networking/comp/networked_comp.hpp" 3 | #include "edyn/serialization/entt_s11n.hpp" 4 | #include "edyn/serialization/std_s11n.hpp" 5 | #include "edyn/serialization/math_s11n.hpp" 6 | 7 | namespace edyn { 8 | 9 | server_network_context::server_network_context(entt::registry ®istry) 10 | : snapshot_importer(new server_snapshot_importer_impl(networked_components, {})) 11 | , snapshot_exporter(new server_snapshot_exporter_impl(registry, networked_components)) 12 | {} 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/edyn/networking/util/pool_snapshot.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/networking/util/pool_snapshot.hpp" 2 | #include "edyn/networking/comp/networked_comp.hpp" 3 | #include "edyn/serialization/entt_s11n.hpp" 4 | #include "edyn/serialization/math_s11n.hpp" 5 | #include "edyn/serialization/std_s11n.hpp" 6 | 7 | namespace edyn { 8 | 9 | std::unique_ptr(*g_make_pool_snapshot_data)(component_index_type) = 10 | create_make_pool_snapshot_data_function(networked_components); 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/edyn/networking/util/process_update_entity_map_packet.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/networking/util/process_update_entity_map_packet.hpp" 2 | #include 3 | 4 | namespace edyn { 5 | 6 | void process_update_entity_map_packet(const entt::registry ®istry, const packet::update_entity_map &packet, entity_map &emap) { 7 | for (auto &pair : packet.pairs) { 8 | auto local_entity = pair.first; 9 | auto remote_entity = pair.second; 10 | 11 | if (registry.valid(local_entity)) { 12 | emap.insert(remote_entity, local_entity); 13 | } 14 | } 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/edyn/parallel/job_queue.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/parallel/job_queue.hpp" 2 | #include "edyn/config/config.h" 3 | 4 | namespace edyn { 5 | 6 | void job_queue::push(const job &j) { 7 | { 8 | std::lock_guard lock(m_mutex); 9 | m_jobs.push(j); 10 | } 11 | m_cv.notify_one(); 12 | } 13 | 14 | job job_queue::pop() { 15 | std::unique_lock lock(m_mutex); 16 | m_cv.wait(lock, [&]() { 17 | return !m_jobs.empty(); 18 | }); 19 | 20 | auto j = m_jobs.front(); 21 | m_jobs.pop(); 22 | return j; 23 | } 24 | 25 | bool job_queue::try_pop(job &j) { 26 | std::lock_guard lock(m_mutex); 27 | if (m_jobs.empty()) { 28 | return false; 29 | } 30 | 31 | j = m_jobs.front(); 32 | m_jobs.pop(); 33 | return true; 34 | } 35 | 36 | size_t job_queue::size() const { 37 | std::lock_guard lock(m_mutex); 38 | return m_jobs.size(); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/edyn/parallel/message_dispatcher.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/parallel/message_dispatcher.hpp" 2 | 3 | namespace edyn { 4 | 5 | message_dispatcher &message_dispatcher::global() { 6 | static message_dispatcher instance; 7 | return instance; 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /src/edyn/replication/make_reg_op_builder.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/replication/make_reg_op_builder.hpp" 2 | #include "edyn/replication/registry_operation_builder.hpp" 3 | #include "edyn/replication/registry_operation_observer.hpp" 4 | #include "edyn/context/registry_operation_context.hpp" 5 | #include "edyn/config/config.h" 6 | #include 7 | 8 | namespace edyn { 9 | 10 | std::unique_ptr make_reg_op_builder(entt::registry ®istry) { 11 | auto &ctx = registry.ctx().get(); 12 | EDYN_ASSERT(ctx.make_reg_op_builder != nullptr); 13 | return (*ctx.make_reg_op_builder)(registry); 14 | } 15 | 16 | std::unique_ptr make_reg_op_observer(registry_operation_builder &builder) { 17 | auto ®istry = builder.get_registry(); 18 | auto &ctx = registry.ctx().get(); 19 | EDYN_ASSERT(ctx.make_reg_op_observer != nullptr); 20 | return (*ctx.make_reg_op_observer)(builder); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/edyn/shapes/compound_shape.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/shapes/compound_shape.hpp" 2 | #include "edyn/shapes/polyhedron_shape.hpp" 3 | #include "edyn/util/shape_util.hpp" 4 | #include "edyn/util/aabb_util.hpp" 5 | 6 | namespace edyn { 7 | 8 | void compound_shape::finish() { 9 | EDYN_ASSERT(!nodes.empty()); 10 | 11 | // Calculate node aabbs. 12 | auto aabbs = std::vector{}; 13 | aabbs.reserve(nodes.size()); 14 | 15 | for (auto &node : nodes) { 16 | std::visit([&node](auto &&shape) { 17 | node.aabb = shape_aabb(shape, node.position, node.orientation); 18 | }, node.shape_var); 19 | aabbs.push_back(node.aabb); 20 | } 21 | 22 | auto report_leaf = [](static_tree::tree_node &node, auto ids_begin, auto ids_end) { 23 | node.id = *ids_begin; 24 | }; 25 | tree.build(aabbs.begin(), aabbs.end(), report_leaf); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/edyn/shapes/polyhedron_shape.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/shapes/polyhedron_shape.hpp" 2 | #include "edyn/util/shape_util.hpp" 3 | #include "edyn/util/shape_util.hpp" 4 | 5 | namespace edyn { 6 | 7 | polyhedron_shape::polyhedron_shape(std::shared_ptr mesh) 8 | : mesh(mesh) 9 | {} 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/edyn/sys/update_inertias.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/sys/update_inertias.hpp" 2 | #include "edyn/comp/orientation.hpp" 3 | #include "edyn/comp/inertia.hpp" 4 | #include "edyn/comp/tag.hpp" 5 | #include "edyn/util/island_util.hpp" 6 | #include 7 | #include 8 | #include 9 | 10 | namespace edyn { 11 | 12 | template 13 | void update_inertia(entt::entity entity, OrnInertiaView &view) { 14 | auto [orn, inv_I, inv_IW] = view.template get(entity); 15 | auto basis = to_matrix3x3(orn); 16 | inv_IW = basis * inv_I * transpose(basis); 17 | } 18 | 19 | void update_inertias(entt::registry ®istry) { 20 | auto view = registry.view(exclude_sleeping_disabled); 21 | for (auto entity : view) { 22 | update_inertia(entity, view); 23 | } 24 | } 25 | 26 | void update_inertia(entt::registry ®istry, entt::entity entity) { 27 | auto view = registry.view(); 28 | update_inertia(entity, view); 29 | } 30 | 31 | template 32 | void update_inertias(entt::registry ®istry, It first, It last) { 33 | auto view = registry.view(); 34 | 35 | for (; first != last; ++first) { 36 | auto entity = *first; 37 | 38 | if (view.contains(entity)) { 39 | update_inertia(entity, view); 40 | } 41 | } 42 | } 43 | 44 | void update_inertias(entt::registry ®istry, const entt::sparse_set &entities) { 45 | update_inertias(registry, entities.begin(), entities.end()); 46 | } 47 | 48 | void update_inertias(entt::registry ®istry, const std::vector &entities) { 49 | update_inertias(registry, entities.begin(), entities.end()); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/edyn/sys/update_origins.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/sys/update_origins.hpp" 2 | #include "edyn/comp/center_of_mass.hpp" 3 | #include "edyn/comp/origin.hpp" 4 | #include "edyn/comp/position.hpp" 5 | #include "edyn/comp/orientation.hpp" 6 | #include "edyn/comp/tag.hpp" 7 | #include "edyn/math/transform.hpp" 8 | #include "edyn/util/island_util.hpp" 9 | #include 10 | 11 | namespace edyn { 12 | 13 | void update_origin(const position &pos, const orientation &orn, const center_of_mass &com, origin &orig) { 14 | orig = to_world_space(-com, pos, orn); 15 | } 16 | 17 | void update_origins(entt::registry ®istry) { 18 | registry.view(exclude_sleeping_disabled).each(update_origin); 19 | } 20 | 21 | template 22 | void update_origins(entt::registry ®istry, It first, It last) { 23 | auto view = registry.view(); 24 | 25 | for (; first != last; ++first) { 26 | auto entity = *first; 27 | 28 | if (view.contains(entity)) { 29 | auto [pos, orn, com, orig] = view.get(entity); 30 | update_origin(pos, orn, com, orig); 31 | } 32 | } 33 | } 34 | 35 | void update_origins(entt::registry ®istry, const entt::sparse_set &entities) { 36 | update_origins(registry, entities.begin(), entities.end()); 37 | } 38 | 39 | void update_origins(entt::registry ®istry, const std::vector &entities) { 40 | update_origins(registry, entities.begin(), entities.end()); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/edyn/time/common/time.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/time/time.hpp" 2 | 3 | namespace edyn { 4 | 5 | double performance_time() 6 | { 7 | return static_cast(performance_counter()) / 8 | static_cast(performance_frequency()); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/edyn/time/simulation_time.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/simulation/stepper_sequential.hpp" 2 | #include "edyn/util/island_util.hpp" 3 | #include "edyn/simulation/stepper_async.hpp" 4 | 5 | namespace edyn { 6 | 7 | double get_simulation_timestamp(entt::registry ®istry) { 8 | if (auto *stepper = registry.ctx().find()) { 9 | return stepper->get_simulation_timestamp(); 10 | } 11 | 12 | if (auto *stepper = registry.ctx().find()) { 13 | return stepper->get_simulation_timestamp(); 14 | } 15 | 16 | return 0; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/edyn/time/windows/time.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/time/time.hpp" 2 | 3 | #define WIN32_LEAN_AND_MEAN 4 | #include 5 | #include 6 | 7 | namespace edyn { 8 | 9 | uint32_t ticks() { 10 | return static_cast(timeGetTime()); 11 | } 12 | 13 | void delay(uint32_t ms) { 14 | Sleep(static_cast(ms)); 15 | } 16 | 17 | uint64_t performance_counter() { 18 | LARGE_INTEGER ticks; 19 | if (QueryPerformanceCounter(&ticks)) { 20 | return ticks.QuadPart; 21 | } 22 | return 0; 23 | } 24 | 25 | uint64_t performance_frequency() { 26 | LARGE_INTEGER freq; 27 | if (QueryPerformanceFrequency(&freq)) { 28 | return freq.QuadPart; 29 | } 30 | return 0; 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /src/edyn/util/contact_manifold_util.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/util/contact_manifold_util.hpp" 2 | #include "edyn/collision/contact_manifold_map.hpp" 3 | #include 4 | 5 | namespace edyn { 6 | 7 | bool manifold_exists(entt::registry ®istry, entt::entity first, entt::entity second) { 8 | return manifold_exists(registry, entity_pair{first, second}); 9 | } 10 | 11 | bool manifold_exists(entt::registry ®istry, entity_pair entities) { 12 | auto &manifold_map = registry.ctx().get(); 13 | return manifold_map.contains(entities); 14 | } 15 | 16 | entt::entity get_manifold_entity(const entt::registry ®istry, entt::entity first, entt::entity second) { 17 | return get_manifold_entity(registry, entity_pair{first, second}); 18 | } 19 | 20 | entt::entity get_manifold_entity(const entt::registry ®istry, entity_pair entities) { 21 | auto &manifold_map = registry.ctx().get(); 22 | return manifold_map.get(entities); 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/edyn/util/gravity_util.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/util/gravity_util.hpp" 2 | #include "edyn/comp/gravity.hpp" 3 | #include "edyn/context/settings.hpp" 4 | #include 5 | 6 | namespace edyn { 7 | 8 | vector3 get_gravity(const entt::registry ®istry) { 9 | return registry.ctx().get().gravity; 10 | } 11 | 12 | void set_gravity(entt::registry ®istry, vector3 gravity) { 13 | registry.ctx().get().gravity = gravity; 14 | 15 | auto view = registry.view(); 16 | 17 | for (auto entity : view) { 18 | registry.replace(entity, gravity); 19 | } 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/edyn/util/insert_material_mixing.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/util/insert_material_mixing.hpp" 2 | #include "edyn/dynamics/material_mixing.hpp" 3 | #include "edyn/networking/context/client_network_context.hpp" 4 | #include "edyn/simulation/stepper_async.hpp" 5 | #include 6 | 7 | namespace edyn { 8 | 9 | void insert_material_mixing(entt::registry ®istry, material::id_type material_id0, 10 | material::id_type material_id1, const material_base &material) { 11 | auto &material_table = registry.ctx().get(); 12 | material_table.insert({material_id0, material_id1}, material); 13 | 14 | if (auto *stepper = registry.ctx().find()) { 15 | stepper->material_table_changed(); 16 | } 17 | 18 | if (auto *ctx = registry.ctx().find()) { 19 | ctx->extrapolator->set_material_table(material_table); 20 | } 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/edyn/util/paged_mesh_load_reporting.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/util/paged_mesh_load_reporting.hpp" 2 | #include "edyn/parallel/message.hpp" 3 | #include "edyn/parallel/message_dispatcher.hpp" 4 | #include "edyn/shapes/paged_mesh_shape.hpp" 5 | 6 | namespace edyn::internal { 7 | 8 | struct paged_mesh_page_load_context { 9 | message_queue_handle queue; 10 | entt::sigh load_signal; 11 | }; 12 | 13 | void on_paged_triangle_mesh_load_page(entt::registry ®istry, message &msg) { 14 | auto &ctx = registry.ctx().get(); 15 | 16 | for (auto [entity, shape] : registry.view().each()) { 17 | if (shape.trimesh.get() == msg.content.trimesh) { 18 | ctx.load_signal.publish(entity, msg.content.mesh_index); 19 | break; 20 | } 21 | } 22 | } 23 | 24 | void init_paged_mesh_load_reporting(entt::registry ®istry) { 25 | auto &dispatcher = message_dispatcher::global(); 26 | auto &ctx = registry.ctx().emplace(dispatcher.make_queue(paged_mesh_load_queue_identifier)); 27 | ctx.queue.sink().connect<&on_paged_triangle_mesh_load_page>(registry); 28 | } 29 | 30 | void update_paged_mesh_load_reporting(entt::registry ®istry) { 31 | auto &ctx = registry.ctx().get(); 32 | ctx.queue.update(); 33 | } 34 | 35 | void deinit_paged_mesh_load_reporting(entt::registry ®istry) { 36 | registry.ctx().erase(); 37 | } 38 | 39 | } 40 | 41 | namespace edyn { 42 | 43 | entt::sink> on_paged_mesh_page_loaded(entt::registry ®istry) { 44 | auto &ctx = registry.ctx().get(); 45 | return {ctx.load_signal}; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/edyn/util/settings_util.cpp: -------------------------------------------------------------------------------- 1 | #include "edyn/util/settings_util.hpp" 2 | #include "edyn/networking/context/client_network_context.hpp" 3 | #include "edyn/simulation/stepper_async.hpp" 4 | #include 5 | 6 | namespace edyn { 7 | 8 | void refresh_settings(entt::registry ®istry) 9 | { 10 | if (auto *stepper = registry.ctx().find()) { 11 | stepper->settings_changed(); 12 | } 13 | 14 | if (auto *ctx = registry.ctx().find()) { 15 | auto &settings = registry.ctx().get(); 16 | ctx->extrapolator->set_settings(settings); 17 | } 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /test/edyn/collision/test_broadphase.cpp: -------------------------------------------------------------------------------- 1 | #include "../common/common.hpp" 2 | #include "edyn/collision/should_collide.hpp" 3 | 4 | TEST(test_broadphase, collision_filtering) { 5 | entt::registry registry; 6 | auto config = edyn::init_config{}; 7 | config.num_worker_threads = 2; 8 | edyn::attach(registry, config); 9 | 10 | auto def = edyn::rigidbody_def{}; 11 | def.shape = edyn::box_shape{0.5, 0.5, 0.5}; 12 | auto first = edyn::make_rigidbody(registry, def); 13 | auto second = edyn::make_rigidbody(registry, def); 14 | auto third = edyn::make_rigidbody(registry, def); 15 | 16 | ASSERT_TRUE(edyn::should_collide_default(registry, first, second)); 17 | ASSERT_TRUE(edyn::should_collide_default(registry, first, third)); 18 | ASSERT_TRUE(edyn::should_collide_default(registry, second, third)); 19 | 20 | registry.emplace(first, /*group*/0x1, /*mask*/~0x2); 21 | registry.emplace(second, /*group*/0x2, /*mask*/~0x1); 22 | 23 | ASSERT_FALSE(edyn::should_collide_default(registry, first, second)); 24 | ASSERT_TRUE(edyn::should_collide_default(registry, first, third)); 25 | ASSERT_TRUE(edyn::should_collide_default(registry, second, third)); 26 | 27 | edyn::exclude_collision(registry, first, third); 28 | 29 | ASSERT_FALSE(edyn::should_collide_default(registry, first, second)); 30 | ASSERT_FALSE(edyn::should_collide_default(registry, first, third)); 31 | ASSERT_TRUE(edyn::should_collide_default(registry, second, third)); 32 | 33 | edyn::detach(registry); 34 | } 35 | -------------------------------------------------------------------------------- /test/edyn/collision/test_raycast.cpp: -------------------------------------------------------------------------------- 1 | #include "../common/common.hpp" 2 | 3 | TEST(test_raycast, raycast_box) { 4 | entt::registry registry; 5 | auto config = edyn::init_config{}; 6 | config.execution_mode = edyn::execution_mode::sequential; 7 | config.num_worker_threads = 2; 8 | edyn::attach(registry, config); 9 | 10 | auto def = edyn::rigidbody_def{}; 11 | def.shape = edyn::box_shape{0.5, 0.5, 0.5}; 12 | def.position = {0.5, 0.5, 0.5}; 13 | def.kind = edyn::rigidbody_kind::rb_static; 14 | auto box_entity = edyn::make_rigidbody(registry, def); 15 | edyn::update(registry); 16 | 17 | auto p0 = edyn::vector3{2, 2, 2}; 18 | auto p1 = edyn::vector3{0, 0, 0}; 19 | auto result = edyn::raycast(registry, p0, p1); 20 | 21 | ASSERT_EQ(result.entity, box_entity); 22 | ASSERT_SCALAR_EQ(result.fraction, edyn::scalar(0.5)); 23 | ASSERT_TRUE(std::holds_alternative(result.info_var)); 24 | 25 | // Try another one with a vertical ray coming from above. 26 | p0 = {0.5, 2, 0.5}; 27 | p1 = {0.5, 0, 0.5}; 28 | result = edyn::raycast(registry, p0, p1); 29 | ASSERT_SCALAR_EQ(result.fraction, edyn::scalar(0.5)); 30 | ASSERT_TRUE(std::holds_alternative(result.info_var)); 31 | 32 | auto &info = std::get(result.info_var); 33 | ASSERT_EQ(info.face_index, 2); 34 | } 35 | -------------------------------------------------------------------------------- /test/edyn/common/common.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TEST_EDYN_COMMON_COMMON_HPP 2 | #define TEST_EDYN_COMMON_COMMON_HPP 3 | 4 | #include 5 | #include 6 | 7 | #ifdef EDYN_DOUBLE_PRECISION 8 | #define ASSERT_SCALAR_EQ ASSERT_DOUBLE_EQ 9 | #else 10 | #define ASSERT_SCALAR_EQ ASSERT_FLOAT_EQ 11 | #endif 12 | 13 | inline void ASSERT_VECTOR3_EQ(edyn::vector3 v0, edyn::vector3 v1) { 14 | ASSERT_SCALAR_EQ(v0.x, v1.x); 15 | ASSERT_SCALAR_EQ(v0.y, v1.y); 16 | ASSERT_SCALAR_EQ(v0.z, v1.z); 17 | } 18 | 19 | #endif // TEST_EDYN_COMMON_COMMON_HPP 20 | -------------------------------------------------------------------------------- /test/edyn/issues/issue134.cpp: -------------------------------------------------------------------------------- 1 | #include "../common/common.hpp" 2 | #include "edyn/collision/broadphase.hpp" 3 | 4 | TEST(issue_test, test_issue_134) { 5 | // https://github.com/xissburg/edyn/issues/134 6 | entt::registry registry; 7 | 8 | auto config = edyn::init_config{}; 9 | edyn::attach(registry, config); 10 | edyn::detach(registry); 11 | 12 | ASSERT_FALSE(registry.ctx().contains()); 13 | } 14 | -------------------------------------------------------------------------------- /test/edyn/issues/issue76.cpp: -------------------------------------------------------------------------------- 1 | #include "../common/common.hpp" 2 | #include "edyn/edyn.hpp" 3 | 4 | TEST(issue_test, test_issue_76) { 5 | // https://github.com/xissburg/edyn/issues/76 6 | entt::registry registry; 7 | 8 | auto config = edyn::init_config{}; 9 | config.num_worker_threads = 2; 10 | edyn::attach(registry, config); 11 | 12 | // Create floor 13 | auto floor_def = edyn::rigidbody_def(); 14 | floor_def.kind = edyn::rigidbody_kind::rb_static; 15 | floor_def.shape = edyn::plane_shape{ {0, 1, 0}, 0 }; 16 | auto entity = edyn::make_rigidbody(registry, floor_def); 17 | 18 | registry.destroy(entity); 19 | 20 | auto entity2 = edyn::make_rigidbody(registry, floor_def); 21 | edyn::update(registry); 22 | edyn::detach(registry); 23 | } 24 | -------------------------------------------------------------------------------- /test/edyn/math/test_math.cpp: -------------------------------------------------------------------------------- 1 | #include "../common/common.hpp" 2 | #include "edyn/math/math.hpp" 3 | 4 | TEST(math_test, average) { 5 | auto vertices = std::array{ 6 | edyn::vector3{1, 1, 0}, 7 | edyn::vector3{-1, 1, 0}, 8 | edyn::vector3{0, -1, 0} 9 | }; 10 | auto center = edyn::average(vertices); 11 | ASSERT_SCALAR_EQ(center.x, 0); 12 | ASSERT_SCALAR_EQ(center.y, 1.f / 3.f); 13 | ASSERT_SCALAR_EQ(center.z, 0); 14 | } 15 | -------------------------------------------------------------------------------- /test/edyn/math/test_matrix3x3.cpp: -------------------------------------------------------------------------------- 1 | #include "../common/common.hpp" 2 | #include 3 | 4 | class matrix3x3_test: public ::testing::Test { 5 | protected: 6 | std::random_device rd; 7 | std::mt19937 gen; 8 | std::uniform_real_distribution dist; 9 | 10 | matrix3x3_test() : 11 | gen(rd()), 12 | dist(-1e4, 1e4) 13 | {} 14 | 15 | public: 16 | edyn::scalar random() { 17 | return dist(gen); 18 | } 19 | edyn::matrix3x3 random_mat() { 20 | return { 21 | edyn::vector3{random(), random(), random()}, 22 | edyn::vector3{random(), random(), random()}, 23 | edyn::vector3{random(), random(), random()} 24 | }; 25 | } 26 | }; 27 | 28 | TEST_F(matrix3x3_test, fundamental) { 29 | auto m = random_mat(); 30 | auto n = random_mat(); 31 | auto p = m + n; 32 | 33 | for (auto i = 0; i < 3; ++i) { 34 | for (auto j = 0; j < 3; ++j) { 35 | ASSERT_SCALAR_EQ(p[i][j], m[i][j] + n[i][j]); 36 | } 37 | } 38 | 39 | m += n; 40 | 41 | for (auto i = 0; i < 3; ++i) { 42 | for (auto j = 0; j < 3; ++j) { 43 | ASSERT_SCALAR_EQ(m[i][j], p[i][j]); 44 | } 45 | } 46 | } 47 | 48 | TEST_F(matrix3x3_test, comparison) { 49 | auto m = random_mat(); 50 | ASSERT_EQ(m, m); 51 | 52 | auto n = m; 53 | n[0][0] += 1; 54 | ASSERT_NE(n, m); 55 | ASSERT_GE(n, m); 56 | ASSERT_LE(m, n); 57 | 58 | n += edyn::matrix3x3_one; 59 | ASSERT_GT(n, m); 60 | ASSERT_LT(m, n); 61 | } 62 | -------------------------------------------------------------------------------- /test/edyn/serialization/test_std_s11n.cpp: -------------------------------------------------------------------------------- 1 | #include "../common/common.hpp" 2 | 3 | TEST(std_serialization_test, test_variant) { 4 | auto var = std::variant{1.2}; 5 | auto buffer = edyn::memory_output_archive::buffer_type{}; 6 | auto output = edyn::memory_output_archive(buffer); 7 | serialize(output, var); 8 | auto input = edyn::memory_input_archive(buffer.data(), buffer.size()); 9 | auto var_in = std::variant{}; 10 | serialize(input, var_in); 11 | ASSERT_TRUE(std::holds_alternative(var_in)); 12 | ASSERT_DOUBLE_EQ(std::get(var_in), 1.2); 13 | } 14 | 15 | TEST(std_serialization_test, test_map) { 16 | auto map = std::map{}; 17 | map["one"] = 1; 18 | map["twelve"] = 12; 19 | 20 | auto buffer = edyn::memory_output_archive::buffer_type{}; 21 | auto output = edyn::memory_output_archive(buffer); 22 | serialize(output, map); 23 | auto input = edyn::memory_input_archive(buffer.data(), buffer.size()); 24 | auto map_in = std::map{}; 25 | serialize(input, map_in); 26 | ASSERT_EQ(map_in["one"], 1); 27 | ASSERT_EQ(map_in["twelve"], 12); 28 | } 29 | -------------------------------------------------------------------------------- /test/edyn/shapes/test_shape_volume.cpp: -------------------------------------------------------------------------------- 1 | #include "../common/common.hpp" 2 | #include "edyn/util/shape_util.hpp" 3 | #include "edyn/math/shape_volume.hpp" 4 | 5 | TEST(test_shape_volume, polyhedron_volume) { 6 | auto mesh = std::make_shared(); 7 | edyn::make_box_mesh({0.5, 0.5, 0.5}, mesh->vertices, 8 | mesh->indices, mesh->faces); 9 | mesh->initialize(); 10 | auto polyhedron = edyn::polyhedron_shape(mesh); 11 | 12 | ASSERT_SCALAR_EQ(edyn::shape_volume(polyhedron), edyn::scalar(1)); 13 | 14 | // Rotate mesh arbitrarily. 15 | auto q = edyn::quaternion_axis_angle(edyn::normalize(edyn::vector3{3, 8, -1}), edyn::pi * 1.34); 16 | for (auto &v : mesh->vertices) { 17 | v = edyn::rotate(q, v); 18 | } 19 | for (auto &n : mesh->normals) { 20 | n = edyn::rotate(q, n); 21 | } 22 | 23 | // Precision suffers after rotation. 24 | ASSERT_LE(std::abs(edyn::shape_volume(polyhedron) - edyn::scalar(1)), 0.00001); 25 | 26 | // Move mesh away from the origin. 27 | for (auto &v : mesh->vertices) { 28 | v += edyn::vector3{10, -12, 20.889}; 29 | } 30 | 31 | ASSERT_LE(std::abs(edyn::shape_volume(polyhedron) - edyn::scalar(1)), 0.00001); 32 | 33 | // Scale vertices. 34 | for (auto &v : mesh->vertices) { 35 | v *= 2; 36 | } 37 | 38 | ASSERT_LE(std::abs(edyn::shape_volume(polyhedron) - edyn::scalar(8)), 0.0001); 39 | } 40 | -------------------------------------------------------------------------------- /test/edyn/shapes/test_trimesh.cpp: -------------------------------------------------------------------------------- 1 | #include "../common/common.hpp" 2 | 3 | TEST(test_trimesh, voronoi_regions) { 4 | auto vertices = std::vector{}; 5 | vertices.push_back({1, 0, 1}); 6 | vertices.push_back({1, 0, -1}); 7 | vertices.push_back({-1, 0, -1}); 8 | vertices.push_back({-1, 0, 1}); 9 | vertices.push_back({0, 1, 0}); 10 | vertices.push_back({2, 0, 0}); 11 | 12 | auto indices = std::vector{}; 13 | indices.insert(indices.end(), {0, 1, 4}); 14 | indices.insert(indices.end(), {1, 2, 4}); 15 | indices.insert(indices.end(), {2, 3, 4}); 16 | indices.insert(indices.end(), {3, 0, 4}); 17 | indices.insert(indices.end(), {0, 5, 1}); 18 | 19 | auto trimesh = edyn::triangle_mesh{}; 20 | trimesh.insert_vertices(vertices.begin(), vertices.end()); 21 | trimesh.insert_indices(indices.begin(), indices.end()); 22 | trimesh.initialize(); 23 | 24 | ASSERT_EQ(trimesh.num_edges(), 10); 25 | 26 | ASSERT_FALSE(trimesh.is_convex_edge(trimesh.get_face_edge_index(4, 2))); 27 | ASSERT_TRUE(trimesh.is_convex_edge(trimesh.get_face_edge_index(0, 1))); 28 | ASSERT_TRUE(trimesh.is_convex_edge(trimesh.get_face_edge_index(2, 1))); 29 | 30 | ASSERT_TRUE(trimesh.is_boundary_edge(trimesh.get_face_edge_index(1, 0))); 31 | ASSERT_TRUE(trimesh.is_boundary_edge(trimesh.get_face_edge_index(2, 0))); 32 | ASSERT_TRUE(trimesh.is_boundary_edge(trimesh.get_face_edge_index(3, 0))); 33 | ASSERT_TRUE(trimesh.is_boundary_edge(trimesh.get_face_edge_index(4, 0))); 34 | ASSERT_TRUE(trimesh.is_boundary_edge(trimesh.get_face_edge_index(4, 1))); 35 | ASSERT_FALSE(trimesh.is_boundary_edge(trimesh.get_face_edge_index(4, 2))); 36 | 37 | ASSERT_VECTOR3_EQ(trimesh.get_adjacent_face_normal(0, 0), {0, 1, 0}); 38 | 39 | ASSERT_VECTOR3_EQ(trimesh.get_aabb().min, {-1, 0, -1}); 40 | ASSERT_VECTOR3_EQ(trimesh.get_aabb().max, {2, 1, 1}); 41 | } 42 | -------------------------------------------------------------------------------- /test/edyn/sys/test_apply_gravity.cpp: -------------------------------------------------------------------------------- 1 | #include "../common/common.hpp" 2 | #include 3 | 4 | TEST(apply_gravity, test) { 5 | entt::registry registry; 6 | auto ent = registry.create(); 7 | registry.emplace(ent, edyn::gravity_earth); 8 | registry.emplace(ent, edyn::vector3_zero); 9 | 10 | // Only dynamic entities have their velocity updated. 11 | registry.emplace(ent); 12 | 13 | const edyn::scalar dt = 0.1666; 14 | const size_t n = 10; 15 | 16 | for (size_t i = 0; i < n; ++i) { 17 | edyn::apply_gravity(registry, dt); 18 | } 19 | 20 | auto& linvel = registry.get(ent); 21 | ASSERT_SCALAR_EQ(linvel.x, (edyn::gravity_earth * dt * n).x); 22 | ASSERT_SCALAR_EQ(linvel.y, (edyn::gravity_earth * dt * n).y); 23 | ASSERT_SCALAR_EQ(linvel.z, (edyn::gravity_earth * dt * n).z); 24 | } -------------------------------------------------------------------------------- /test/edyn/util/test_clear_rigidbody.cpp: -------------------------------------------------------------------------------- 1 | #include "../common/common.hpp" 2 | #include "edyn/util/rigidbody.hpp" 3 | 4 | TEST(test_clear_rigidbody, clear_rigidbody) { 5 | entt::registry registry; 6 | auto config = edyn::init_config{}; 7 | config.execution_mode = edyn::execution_mode::sequential; 8 | edyn::attach(registry, config); 9 | 10 | auto def = edyn::rigidbody_def{}; 11 | def.shape = edyn::box_shape{0.5, 0.5, 0.5}; 12 | auto rb = edyn::make_rigidbody(registry, def); 13 | edyn::clear_rigidbody(registry, rb); 14 | 15 | ASSERT_TRUE(registry.valid(rb)); 16 | ASSERT_FALSE(edyn::validate_rigidbody(registry, rb)); 17 | ASSERT_FALSE((registry.any_of(rb))); 18 | 19 | edyn::update(registry); 20 | edyn::make_rigidbody(rb, registry, def); 21 | 22 | edyn::update(registry); 23 | 24 | ASSERT_TRUE(edyn::validate_rigidbody(registry, rb)); 25 | edyn::clear_rigidbody(registry, rb); 26 | 27 | ASSERT_TRUE(registry.valid(rb)); 28 | ASSERT_FALSE(edyn::validate_rigidbody(registry, rb)); 29 | ASSERT_FALSE((registry.any_of(rb))); 30 | 31 | registry.destroy(rb); 32 | 33 | edyn::detach(registry); 34 | } 35 | -------------------------------------------------------------------------------- /test/edyn/util/test_tuple_util.cpp: -------------------------------------------------------------------------------- 1 | #include "../common/common.hpp" 2 | #include 3 | 4 | TEST(test_tuple_util, test_visit_tuple) { 5 | auto tuple = std::make_tuple(std::string("str"), 3.14f, 'c', 667); 6 | 7 | for (size_t i = 0; i < std::tuple_size_v; ++i) { 8 | edyn::visit_tuple(tuple, i, [](auto &&value) { 9 | using ValueType = std::decay_t; 10 | auto count = 0; 11 | 12 | if constexpr(std::is_same_v) { 13 | ASSERT_EQ(value, "str"); 14 | ++count; 15 | } 16 | 17 | if constexpr(std::is_same_v) { 18 | ASSERT_EQ(value, 3.14f); 19 | ++count; 20 | } 21 | 22 | if constexpr(std::is_same_v) { 23 | ASSERT_EQ(value, 'c'); 24 | ++count; 25 | } 26 | 27 | if constexpr(std::is_same_v) { 28 | ASSERT_EQ(value, 667); 29 | ++count; 30 | } 31 | 32 | ASSERT_EQ(count, 1); 33 | }); 34 | } 35 | } 36 | --------------------------------------------------------------------------------