jolt-ruby 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/.gitmodules +3 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +228 -0
  5. data/Rakefile +180 -0
  6. data/examples/character_virtual.rb +35 -0
  7. data/examples/falling_sphere.rb +25 -0
  8. data/examples/stagecraft_binding.rb +29 -0
  9. data/ext/jolt_ruby/CMakeLists.txt +42 -0
  10. data/ext/jolt_ruby/character_helper.cpp +44 -0
  11. data/ext/jolt_ruby/constraint_helper.cpp +154 -0
  12. data/ext/jolt_ruby/extconf.rb +56 -0
  13. data/ext/jolt_ruby/helper.cpp +225 -0
  14. data/ext/jolt_ruby/helper.h +103 -0
  15. data/ext/joltc/.github/FUNDING.yml +1 -0
  16. data/ext/joltc/.github/workflows/build.yml +298 -0
  17. data/ext/joltc/.gitignore +272 -0
  18. data/ext/joltc/CMakeLists.txt +431 -0
  19. data/ext/joltc/LICENSE +21 -0
  20. data/ext/joltc/README.md +10 -0
  21. data/ext/joltc/build/cmake_vs2026_arm64.bat +3 -0
  22. data/ext/joltc/build/cmake_vs2026_clang.bat +10 -0
  23. data/ext/joltc/build/cmake_vs2026_x64.bat +3 -0
  24. data/ext/joltc/build/cmake_vs2026_x64_double.bat +3 -0
  25. data/ext/joltc/include/joltc.h +3166 -0
  26. data/ext/joltc/samples/01_HelloWorld/main.cpp +170 -0
  27. data/ext/joltc/samples/CMakeLists.txt +35 -0
  28. data/ext/joltc/src/joltc.c +4 -0
  29. data/ext/joltc/src/joltc.cpp +11812 -0
  30. data/ext/joltc/src/joltc_assert.cpp +271 -0
  31. data/ext/joltc/tests/CMakeLists.txt +77 -0
  32. data/ext/joltc/tests/test_character.cpp +149 -0
  33. data/ext/joltc/tests/test_collision.cpp +146 -0
  34. data/ext/joltc/tests/test_constraints.cpp +353 -0
  35. data/ext/joltc/tests/test_core.cpp +94 -0
  36. data/ext/joltc/tests/test_math.cpp +585 -0
  37. data/ext/joltc/tests/test_physics_system.cpp +465 -0
  38. data/ext/joltc/tests/test_shapes.cpp +789 -0
  39. data/ext/joltc/tests/test_skeleton.cpp +370 -0
  40. data/ext/joltc/tests/test_vehicle.cpp +319 -0
  41. data/generator/generate.rb +237 -0
  42. data/generator/layout_probe.cpp +489 -0
  43. data/generator/verify_layout.rb +32 -0
  44. data/lib/jolt/body.rb +147 -0
  45. data/lib/jolt/body_collection.rb +115 -0
  46. data/lib/jolt/body_dynamics.rb +93 -0
  47. data/lib/jolt/character_virtual.rb +162 -0
  48. data/lib/jolt/constraint.rb +136 -0
  49. data/lib/jolt/constraint_collection.rb +123 -0
  50. data/lib/jolt/contact_events.rb +19 -0
  51. data/lib/jolt/conversions.rb +76 -0
  52. data/lib/jolt/errors.rb +12 -0
  53. data/lib/jolt/fixed_stepper.rb +37 -0
  54. data/lib/jolt/hit.rb +5 -0
  55. data/lib/jolt/layers.rb +182 -0
  56. data/lib/jolt/native/character_functions.rb +16 -0
  57. data/lib/jolt/native/constraint_functions.rb +27 -0
  58. data/lib/jolt/native/core_functions.rb +17 -0
  59. data/lib/jolt/native/generated.rb +1995 -0
  60. data/lib/jolt/native/platform.rb +51 -0
  61. data/lib/jolt/native/query_functions.rb +43 -0
  62. data/lib/jolt/native/types.rb +28 -0
  63. data/lib/jolt/native.rb +94 -0
  64. data/lib/jolt/shape.rb +90 -0
  65. data/lib/jolt/shape_builders.rb +155 -0
  66. data/lib/jolt/system.rb +238 -0
  67. data/lib/jolt/system_characters.rb +25 -0
  68. data/lib/jolt/system_constraints.rb +36 -0
  69. data/lib/jolt/system_contacts.rb +57 -0
  70. data/lib/jolt/system_queries.rb +111 -0
  71. data/lib/jolt/transform.rb +5 -0
  72. data/lib/jolt/version.rb +5 -0
  73. data/lib/jolt.rb +83 -0
  74. data/script/smoke_gem.rb +29 -0
  75. data/script/verify_release.rb +36 -0
  76. metadata +147 -0
data/lib/jolt/body.rb ADDED
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jolt
4
+ class Body
5
+ include BodyDynamics
6
+
7
+ attr_reader :id, :system
8
+
9
+ def initialize(system, id)
10
+ @system = system
11
+ @id = id
12
+ @destroyed = false
13
+ transform = current_transform
14
+ @previous_transform = transform
15
+ @current_transform = transform
16
+ end
17
+
18
+ def position
19
+ read_vec3(:JPH_BodyInterface_GetPosition)
20
+ end
21
+
22
+ def position=(value)
23
+ write_vec3(:JPH_BodyInterface_SetPosition, value, activation)
24
+ end
25
+
26
+ def rotation
27
+ check_alive!
28
+ native = Native::Quat.new
29
+ Native.JPH_BodyInterface_GetRotation(body_interface, @id, native.pointer)
30
+ Conversions.quat(native)
31
+ end
32
+
33
+ def rotation=(value)
34
+ check_alive!
35
+ native = Conversions.native_quat(value)
36
+ Native.JPH_BodyInterface_SetRotation(body_interface, @id, native.pointer, activation)
37
+ end
38
+
39
+ def user_data
40
+ check_alive!
41
+ @system.__user_data(@id)
42
+ end
43
+
44
+ def user_data=(value)
45
+ check_alive!
46
+ @system.__set_user_data(@id, value)
47
+ end
48
+
49
+ def interpolated(alpha)
50
+ check_alive!
51
+ alpha = Conversions.finite_float(alpha, "alpha")
52
+ raise InvalidArgumentError, "alpha must be between 0 and 1" unless alpha.between?(0.0, 1.0)
53
+
54
+ Transform.new(
55
+ position: @previous_transform.position.lerp(@current_transform.position, alpha),
56
+ rotation: @previous_transform.rotation.slerp(@current_transform.rotation, alpha)
57
+ )
58
+ end
59
+
60
+ def destroy
61
+ @system.__destroy_body(self)
62
+ end
63
+
64
+ def destroyed?
65
+ @destroyed
66
+ end
67
+
68
+ def hash
69
+ [@system.object_id, @id].hash
70
+ end
71
+
72
+ def eql?(other)
73
+ other.is_a?(Body) && other.system.equal?(@system) && other.id == @id
74
+ end
75
+ alias == eql?
76
+
77
+ def __capture_before_step
78
+ check_alive!
79
+ @previous_transform = @current_transform
80
+ end
81
+
82
+ def __capture_after_step
83
+ check_alive!
84
+ @current_transform = current_transform
85
+ end
86
+
87
+ def __mark_destroyed
88
+ @destroyed = true
89
+ end
90
+
91
+ private
92
+
93
+ def current_transform
94
+ Transform.new(position: position, rotation: rotation)
95
+ end
96
+
97
+ def read_vec3(function)
98
+ check_alive!
99
+ native = Native::Vec3.new
100
+ Native.public_send(function, body_interface, @id, native.pointer)
101
+ Conversions.vec3(native)
102
+ end
103
+
104
+ def write_vec3(function, value, *extra)
105
+ check_alive!
106
+ native = Conversions.native_vec3(value)
107
+ Native.public_send(function, body_interface, @id, native.pointer, *extra)
108
+ value
109
+ end
110
+
111
+ def apply_vector_at_point(center_function, point_function, value, point)
112
+ check_alive!
113
+ native_value = Conversions.native_vec3(value)
114
+ if point
115
+ native_point = Conversions.native_vec3(point, name: "point")
116
+ Native.public_send(point_function, body_interface, @id, native_value.pointer, native_point.pointer)
117
+ else
118
+ Native.public_send(center_function, body_interface, @id, native_value.pointer)
119
+ end
120
+ self
121
+ end
122
+
123
+ def scalar_property(function)
124
+ check_alive!
125
+ Native.public_send(function, body_interface, @id)
126
+ end
127
+
128
+ def write_scalar(function, value, name)
129
+ check_alive!
130
+ number = Conversions.non_negative_float(value, name)
131
+ Native.public_send(function, body_interface, @id, number)
132
+ end
133
+
134
+ def activation
135
+ 0
136
+ end
137
+
138
+ def body_interface
139
+ @system.__body_interface
140
+ end
141
+
142
+ def check_alive!
143
+ raise UseAfterDestroyError, "body #{@id} has been destroyed" if @destroyed
144
+ @system.__check_alive!
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jolt
4
+ class BodyCollection
5
+ include Enumerable
6
+
7
+ MOTION_TYPES = {static: 0, kinematic: 1, dynamic: 2}.freeze
8
+ INVALID_BODY_ID = 0xffff_ffff
9
+
10
+ def initialize(system)
11
+ @system = system
12
+ end
13
+
14
+ def create(shape:, position: [0, 0, 0], rotation: [0, 0, 0, 1],
15
+ motion: :dynamic, layer: nil, activate: nil, friction: 0.2,
16
+ restitution: 0.0, linear_damping: 0.05, angular_damping: 0.05,
17
+ gravity_factor: 1.0, mass: nil, ccd: false, sensor: false,
18
+ user_data: nil)
19
+ @system.__check_alive!
20
+ validate_shape!(shape)
21
+ motion_type = MOTION_TYPES.fetch(motion.respond_to?(:to_sym) ? motion.to_sym : nil)
22
+ if !motion_type.zero? && shape.must_be_static?
23
+ raise InvalidArgumentError, "#{shape.kind} shapes can only be used by static bodies"
24
+ end
25
+ layer ||= motion_type.zero? ? :non_moving : :moving
26
+ layer_id = @system.layers.object_layer_id(layer)
27
+ settings = create_settings(shape, position, rotation, motion_type, layer_id)
28
+ configure_settings(
29
+ settings,
30
+ friction:, restitution:, linear_damping:, angular_damping:,
31
+ gravity_factor:, mass:, ccd:, sensor:, motion_type:
32
+ )
33
+ id = Native.JPH_BodyInterface_CreateAndAddBody(
34
+ @system.__body_interface, settings, activation(activate, motion_type)
35
+ )
36
+ raise InitializationError, "failed to create body" if id == INVALID_BODY_ID
37
+
38
+ Body.new(@system, id).tap do |body|
39
+ @system.__register_body(body, shape)
40
+ body.user_data = user_data unless user_data.nil?
41
+ end
42
+ rescue KeyError
43
+ raise InvalidArgumentError, "motion must be one of: #{MOTION_TYPES.keys.join(", ")}"
44
+ ensure
45
+ Native.JPH_BodyCreationSettings_Destroy(settings) if settings && !settings.null?
46
+ end
47
+
48
+ def each(&block)
49
+ return enum_for(:each) unless block
50
+
51
+ @system.__bodies_snapshot.each(&block)
52
+ end
53
+
54
+ def [](id)
55
+ @system.__body(id)
56
+ end
57
+
58
+ def size
59
+ @system.__bodies_snapshot.size
60
+ end
61
+ alias length size
62
+
63
+ private
64
+
65
+ def create_settings(shape, position, rotation, motion_type, layer_id)
66
+ position = Conversions.native_vec3(position, name: "position")
67
+ rotation = Conversions.native_quat(rotation)
68
+ settings = Native.JPH_BodyCreationSettings_Create3(
69
+ shape.native_pointer, position.pointer, rotation.pointer, motion_type, layer_id
70
+ )
71
+ raise InitializationError, "failed to allocate body creation settings" if settings.null?
72
+
73
+ settings
74
+ end
75
+
76
+ def configure_settings(settings, friction:, restitution:, linear_damping:, angular_damping:,
77
+ gravity_factor:, mass:, ccd:, sensor:, motion_type:)
78
+ {
79
+ Friction: [friction, "friction"],
80
+ Restitution: [restitution, "restitution"],
81
+ LinearDamping: [linear_damping, "linear_damping"],
82
+ AngularDamping: [angular_damping, "angular_damping"],
83
+ GravityFactor: [gravity_factor, "gravity_factor"]
84
+ }.each do |name, (value, label)|
85
+ number = Conversions.non_negative_float(value, label)
86
+ Native.public_send(:"JPH_BodyCreationSettings_Set#{name}", settings, number)
87
+ end
88
+ Native.JPH_BodyCreationSettings_SetIsSensor(settings, !!sensor)
89
+ Native.JPH_BodyCreationSettings_SetMotionQuality(settings, ccd ? 1 : 0)
90
+ configure_mass(settings, mass, motion_type) unless mass.nil?
91
+ end
92
+
93
+ def configure_mass(settings, mass, motion_type)
94
+ raise InvalidArgumentError, "mass is only valid for dynamic bodies" unless motion_type == MOTION_TYPES[:dynamic]
95
+
96
+ mass = Conversions.positive_float(mass, "mass")
97
+ properties = Native::MassProperties.new
98
+ Native.JPH_BodyCreationSettings_GetMassPropertiesOverride(settings, properties.pointer)
99
+ properties[:mass] = mass
100
+ Native.JPH_BodyCreationSettings_SetMassPropertiesOverride(settings, properties.pointer)
101
+ Native.JPH_BodyCreationSettings_SetOverrideMassProperties(settings, 1)
102
+ end
103
+
104
+ def activation(value, motion_type)
105
+ activate = value.nil? ? motion_type == MOTION_TYPES[:dynamic] : !!value
106
+ activate ? 0 : 1
107
+ end
108
+
109
+ def validate_shape!(shape)
110
+ raise InvalidArgumentError, "shape must be a Jolt::Shape" unless shape.is_a?(Shape)
111
+
112
+ shape.native_pointer
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jolt
4
+ module BodyDynamics
5
+ def linear_velocity
6
+ read_vec3(:JPH_BodyInterface_GetLinearVelocity)
7
+ end
8
+
9
+ def linear_velocity=(value)
10
+ write_vec3(:JPH_BodyInterface_SetLinearVelocity, value)
11
+ end
12
+
13
+ def angular_velocity
14
+ read_vec3(:JPH_BodyInterface_GetAngularVelocity)
15
+ end
16
+
17
+ def angular_velocity=(value)
18
+ write_vec3(:JPH_BodyInterface_SetAngularVelocity, value)
19
+ end
20
+
21
+ def apply_impulse(impulse, point: nil)
22
+ apply_vector_at_point(:JPH_BodyInterface_AddImpulse, :JPH_BodyInterface_AddImpulse2, impulse, point)
23
+ end
24
+
25
+ def apply_angular_impulse(impulse)
26
+ write_vec3(:JPH_BodyInterface_AddAngularImpulse, impulse)
27
+ self
28
+ end
29
+
30
+ def add_force(force, point: nil)
31
+ apply_vector_at_point(:JPH_BodyInterface_AddForce, :JPH_BodyInterface_AddForce2, force, point)
32
+ end
33
+
34
+ def add_torque(torque)
35
+ write_vec3(:JPH_BodyInterface_AddTorque, torque)
36
+ self
37
+ end
38
+
39
+ def active?
40
+ check_alive!
41
+ Native.JPH_BodyInterface_IsActive(body_interface, @id)
42
+ end
43
+
44
+ def activate
45
+ check_alive!
46
+ Native.JPH_BodyInterface_ActivateBody(body_interface, @id)
47
+ self
48
+ end
49
+
50
+ def deactivate
51
+ check_alive!
52
+ Native.JPH_BodyInterface_DeactivateBody(body_interface, @id)
53
+ self
54
+ end
55
+
56
+ def kinematic_move_to(position, rotation, delta_time)
57
+ check_alive!
58
+ position = Conversions.native_vec3(position, name: "position")
59
+ rotation = Conversions.native_quat(rotation)
60
+ delta_time = Conversions.positive_float(delta_time, "delta_time")
61
+ Native.JPH_BodyInterface_MoveKinematic(
62
+ body_interface, @id, position.pointer, rotation.pointer, delta_time
63
+ )
64
+ self
65
+ end
66
+
67
+ def friction
68
+ scalar_property(:JPH_BodyInterface_GetFriction)
69
+ end
70
+
71
+ def friction=(value)
72
+ write_scalar(:JPH_BodyInterface_SetFriction, value, "friction")
73
+ end
74
+
75
+ def restitution
76
+ scalar_property(:JPH_BodyInterface_GetRestitution)
77
+ end
78
+
79
+ def restitution=(value)
80
+ write_scalar(:JPH_BodyInterface_SetRestitution, value, "restitution")
81
+ end
82
+
83
+ def sensor?
84
+ check_alive!
85
+ Native.JPH_BodyInterface_IsSensor(body_interface, @id)
86
+ end
87
+
88
+ def sensor=(value)
89
+ check_alive!
90
+ Native.JPH_BodyInterface_SetIsSensor(body_interface, @id, !!value)
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,162 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jolt
4
+ class CharacterVirtual
5
+ GROUND_STATES = {
6
+ 0 => :on_ground,
7
+ 1 => :sliding,
8
+ 2 => :in_air,
9
+ 3 => :in_air
10
+ }.freeze
11
+
12
+ attr_reader :system, :shape, :layer
13
+
14
+ def initialize(system, shape:, position: [0, 0, 0], rotation: [0, 0, 0, 1],
15
+ max_slope: Math::PI / 4, mass: 70, layer: :moving)
16
+ unless system.is_a?(System) && !system.destroyed?
17
+ raise InvalidArgumentError, "system must be a live Jolt::System"
18
+ end
19
+ unless shape.is_a?(Shape) && !shape.released?
20
+ raise InvalidArgumentError, "shape must be a live Jolt::Shape"
21
+ end
22
+
23
+ @system = system
24
+ @shape = shape
25
+ layer_id = @system.layers.object_layer_id(layer)
26
+ @layer = layer.to_sym
27
+ native_position = Conversions.native_vec3(position, name: "position")
28
+ native_rotation = Conversions.native_quat(rotation)
29
+ slope = Conversions.positive_float(max_slope, "max_slope")
30
+ unless slope <= Math::PI / 2
31
+ raise InvalidArgumentError, "max_slope must not exceed pi / 2"
32
+ end
33
+ character_mass = Conversions.positive_float(mass, "mass")
34
+ @pointer = Native.JR_CharacterVirtual_Create(
35
+ @system.__native_pointer,
36
+ @shape.native_pointer,
37
+ native_position.pointer,
38
+ native_rotation.pointer,
39
+ slope,
40
+ character_mass
41
+ )
42
+ raise InitializationError, "failed to create virtual character" if @pointer.null?
43
+
44
+ @layer_id = layer_id
45
+ @destroyed = false
46
+ @system.__register_character(self, @shape)
47
+ rescue StandardError
48
+ Native.JPH_CharacterBase_Destroy(@pointer) if @pointer && !@pointer.null?
49
+ raise
50
+ end
51
+
52
+ def position
53
+ read_vec3(:JPH_CharacterVirtual_GetPosition)
54
+ end
55
+
56
+ def position=(value)
57
+ write_vec3(:JPH_CharacterVirtual_SetPosition, value, "position")
58
+ end
59
+
60
+ def rotation
61
+ check_alive!
62
+ native = Native::Quat.new
63
+ Native.JPH_CharacterVirtual_GetRotation(@pointer, native.pointer)
64
+ Conversions.quat(native)
65
+ end
66
+
67
+ def rotation=(value)
68
+ check_alive!
69
+ native = Conversions.native_quat(value)
70
+ Native.JPH_CharacterVirtual_SetRotation(@pointer, native.pointer)
71
+ value
72
+ end
73
+
74
+ def velocity
75
+ read_vec3(:JPH_CharacterVirtual_GetLinearVelocity)
76
+ end
77
+ alias linear_velocity velocity
78
+
79
+ def velocity=(value)
80
+ write_vec3(:JPH_CharacterVirtual_SetLinearVelocity, value, "velocity")
81
+ end
82
+ alias linear_velocity= velocity=
83
+
84
+ def mass
85
+ check_alive!
86
+ Native.JPH_CharacterVirtual_GetMass(@pointer)
87
+ end
88
+
89
+ def mass=(value)
90
+ check_alive!
91
+ Native.JPH_CharacterVirtual_SetMass(
92
+ @pointer,
93
+ Conversions.positive_float(value, "mass")
94
+ )
95
+ value
96
+ end
97
+
98
+ def update(delta_time)
99
+ check_alive!
100
+ delta_time = Conversions.positive_float(delta_time, "delta_time")
101
+ @system.__with_native_operation("recursive virtual character update is not allowed") do
102
+ Native.JR_CharacterVirtual_ExtendedUpdate(
103
+ @pointer,
104
+ delta_time,
105
+ @layer_id,
106
+ @system.__native_pointer
107
+ )
108
+ self
109
+ end
110
+ end
111
+
112
+ def ground_state
113
+ check_alive!
114
+ GROUND_STATES.fetch(Native.JPH_CharacterBase_GetGroundState(@pointer))
115
+ end
116
+
117
+ def supported?
118
+ check_alive!
119
+ Native.JPH_CharacterBase_IsSupported(@pointer)
120
+ end
121
+
122
+ def destroy
123
+ @system.__destroy_character(self)
124
+ end
125
+
126
+ def destroyed?
127
+ @destroyed
128
+ end
129
+
130
+ def __native_pointer
131
+ check_alive!
132
+ @pointer
133
+ end
134
+
135
+ def __destroy_native
136
+ Native.JPH_CharacterBase_Destroy(@pointer) if @pointer && !@pointer.null?
137
+ @pointer = nil
138
+ @destroyed = true
139
+ end
140
+
141
+ private
142
+
143
+ def read_vec3(function)
144
+ check_alive!
145
+ native = Native::Vec3.new
146
+ Native.public_send(function, @pointer, native.pointer)
147
+ Conversions.vec3(native)
148
+ end
149
+
150
+ def write_vec3(function, value, name)
151
+ check_alive!
152
+ native = Conversions.native_vec3(value, name:)
153
+ Native.public_send(function, @pointer, native.pointer)
154
+ value
155
+ end
156
+
157
+ def check_alive!
158
+ raise UseAfterDestroyError, "virtual character has been destroyed" if @destroyed
159
+ @system.__check_alive!
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Jolt
4
+ class Constraint
5
+ attr_reader :kind, :body_a, :body_b, :system
6
+
7
+ def initialize(system, pointer, kind, body_a, body_b)
8
+ @system = system
9
+ @pointer = pointer
10
+ @kind = kind
11
+ @body_a = body_a
12
+ @body_b = body_b
13
+ @destroyed = false
14
+ end
15
+
16
+ def enabled?
17
+ check_alive!
18
+ Native.JPH_Constraint_GetEnabled(@pointer)
19
+ end
20
+
21
+ def enabled=(value)
22
+ check_alive!
23
+ Native.JPH_Constraint_SetEnabled(@pointer, !!value)
24
+ end
25
+
26
+ def limits
27
+ check_alive!
28
+ functions = limit_functions
29
+ [
30
+ Native.public_send(functions.fetch(:min), @pointer),
31
+ Native.public_send(functions.fetch(:max), @pointer)
32
+ ]
33
+ end
34
+
35
+ def limits=(value)
36
+ check_alive!
37
+ minimum, maximum = ConstraintCollection.interval(value, "limits")
38
+ Native.public_send(limit_functions.fetch(:set), @pointer, minimum, maximum)
39
+ value
40
+ end
41
+
42
+ def current_position
43
+ check_alive!
44
+ function = case @kind
45
+ when :hinge then :JPH_HingeConstraint_GetCurrentAngle
46
+ when :slider then :JPH_SliderConstraint_GetCurrentPosition
47
+ else
48
+ raise InvalidArgumentError, "#{@kind} constraints do not have a scalar position"
49
+ end
50
+ Native.public_send(function, @pointer)
51
+ end
52
+
53
+ def motor_speed
54
+ check_alive!
55
+ Native.public_send(motor_functions.fetch(:get), @pointer)
56
+ end
57
+
58
+ def motor_speed=(value)
59
+ check_alive!
60
+ speed = Conversions.finite_float(value, "motor_speed")
61
+ functions = motor_functions
62
+ Native.public_send(functions.fetch(:state), @pointer, 1)
63
+ Native.public_send(functions.fetch(:set), @pointer, speed)
64
+ value
65
+ end
66
+
67
+ def disable_motor
68
+ check_alive!
69
+ Native.public_send(motor_functions.fetch(:state), @pointer, 0)
70
+ self
71
+ end
72
+
73
+ def destroy
74
+ @system.__destroy_constraint(self)
75
+ end
76
+
77
+ def destroyed?
78
+ @destroyed
79
+ end
80
+
81
+ def __native_pointer
82
+ check_alive!
83
+ @pointer
84
+ end
85
+
86
+ def __involves?(body)
87
+ @body_a.equal?(body) || @body_b.equal?(body)
88
+ end
89
+
90
+ def __mark_destroyed
91
+ @destroyed = true
92
+ @pointer = nil
93
+ end
94
+
95
+ private
96
+
97
+ def limit_functions
98
+ prefix = case @kind
99
+ when :distance then "Distance"
100
+ when :hinge then "Hinge"
101
+ when :slider then "Slider"
102
+ else
103
+ raise InvalidArgumentError, "#{@kind} constraints do not have limits"
104
+ end
105
+ {
106
+ set: :"JPH_#{prefix}Constraint_Set#{prefix == "Distance" ? "Distance" : "Limits"}",
107
+ min: :"JPH_#{prefix}Constraint_Get#{prefix == "Distance" ? "MinDistance" : "LimitsMin"}",
108
+ max: :"JPH_#{prefix}Constraint_Get#{prefix == "Distance" ? "MaxDistance" : "LimitsMax"}"
109
+ }
110
+ end
111
+
112
+ def motor_functions
113
+ case @kind
114
+ when :hinge
115
+ {
116
+ state: :JPH_HingeConstraint_SetMotorState,
117
+ set: :JPH_HingeConstraint_SetTargetAngularVelocity,
118
+ get: :JPH_HingeConstraint_GetTargetAngularVelocity
119
+ }
120
+ when :slider
121
+ {
122
+ state: :JPH_SliderConstraint_SetMotorState,
123
+ set: :JPH_SliderConstraint_SetTargetVelocity,
124
+ get: :JPH_SliderConstraint_GetTargetVelocity
125
+ }
126
+ else
127
+ raise InvalidArgumentError, "#{@kind} constraints do not have a motor"
128
+ end
129
+ end
130
+
131
+ def check_alive!
132
+ raise UseAfterDestroyError, "#{@kind} constraint has been destroyed" if @destroyed
133
+ @system.__check_alive!
134
+ end
135
+ end
136
+ end