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.
- checksums.yaml +7 -0
- data/.gitmodules +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +228 -0
- data/Rakefile +180 -0
- data/examples/character_virtual.rb +35 -0
- data/examples/falling_sphere.rb +25 -0
- data/examples/stagecraft_binding.rb +29 -0
- data/ext/jolt_ruby/CMakeLists.txt +42 -0
- data/ext/jolt_ruby/character_helper.cpp +44 -0
- data/ext/jolt_ruby/constraint_helper.cpp +154 -0
- data/ext/jolt_ruby/extconf.rb +56 -0
- data/ext/jolt_ruby/helper.cpp +225 -0
- data/ext/jolt_ruby/helper.h +103 -0
- data/ext/joltc/.github/FUNDING.yml +1 -0
- data/ext/joltc/.github/workflows/build.yml +298 -0
- data/ext/joltc/.gitignore +272 -0
- data/ext/joltc/CMakeLists.txt +431 -0
- data/ext/joltc/LICENSE +21 -0
- data/ext/joltc/README.md +10 -0
- data/ext/joltc/build/cmake_vs2026_arm64.bat +3 -0
- data/ext/joltc/build/cmake_vs2026_clang.bat +10 -0
- data/ext/joltc/build/cmake_vs2026_x64.bat +3 -0
- data/ext/joltc/build/cmake_vs2026_x64_double.bat +3 -0
- data/ext/joltc/include/joltc.h +3166 -0
- data/ext/joltc/samples/01_HelloWorld/main.cpp +170 -0
- data/ext/joltc/samples/CMakeLists.txt +35 -0
- data/ext/joltc/src/joltc.c +4 -0
- data/ext/joltc/src/joltc.cpp +11812 -0
- data/ext/joltc/src/joltc_assert.cpp +271 -0
- data/ext/joltc/tests/CMakeLists.txt +77 -0
- data/ext/joltc/tests/test_character.cpp +149 -0
- data/ext/joltc/tests/test_collision.cpp +146 -0
- data/ext/joltc/tests/test_constraints.cpp +353 -0
- data/ext/joltc/tests/test_core.cpp +94 -0
- data/ext/joltc/tests/test_math.cpp +585 -0
- data/ext/joltc/tests/test_physics_system.cpp +465 -0
- data/ext/joltc/tests/test_shapes.cpp +789 -0
- data/ext/joltc/tests/test_skeleton.cpp +370 -0
- data/ext/joltc/tests/test_vehicle.cpp +319 -0
- data/generator/generate.rb +237 -0
- data/generator/layout_probe.cpp +489 -0
- data/generator/verify_layout.rb +32 -0
- data/lib/jolt/body.rb +147 -0
- data/lib/jolt/body_collection.rb +115 -0
- data/lib/jolt/body_dynamics.rb +93 -0
- data/lib/jolt/character_virtual.rb +162 -0
- data/lib/jolt/constraint.rb +136 -0
- data/lib/jolt/constraint_collection.rb +123 -0
- data/lib/jolt/contact_events.rb +19 -0
- data/lib/jolt/conversions.rb +76 -0
- data/lib/jolt/errors.rb +12 -0
- data/lib/jolt/fixed_stepper.rb +37 -0
- data/lib/jolt/hit.rb +5 -0
- data/lib/jolt/layers.rb +182 -0
- data/lib/jolt/native/character_functions.rb +16 -0
- data/lib/jolt/native/constraint_functions.rb +27 -0
- data/lib/jolt/native/core_functions.rb +17 -0
- data/lib/jolt/native/generated.rb +1995 -0
- data/lib/jolt/native/platform.rb +51 -0
- data/lib/jolt/native/query_functions.rb +43 -0
- data/lib/jolt/native/types.rb +28 -0
- data/lib/jolt/native.rb +94 -0
- data/lib/jolt/shape.rb +90 -0
- data/lib/jolt/shape_builders.rb +155 -0
- data/lib/jolt/system.rb +238 -0
- data/lib/jolt/system_characters.rb +25 -0
- data/lib/jolt/system_constraints.rb +36 -0
- data/lib/jolt/system_contacts.rb +57 -0
- data/lib/jolt/system_queries.rb +111 -0
- data/lib/jolt/transform.rb +5 -0
- data/lib/jolt/version.rb +5 -0
- data/lib/jolt.rb +83 -0
- data/script/smoke_gem.rb +29 -0
- data/script/verify_release.rb +36 -0
- metadata +147 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jolt
|
|
4
|
+
class ConstraintCollection
|
|
5
|
+
include Enumerable
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
def interval(value, name)
|
|
9
|
+
values = value.is_a?(Range) ? [value.begin, value.end] : value.to_a
|
|
10
|
+
raise InvalidArgumentError, "#{name} must contain two values" unless values.length == 2
|
|
11
|
+
|
|
12
|
+
minimum = Conversions.finite_float(values[0], "#{name} minimum")
|
|
13
|
+
maximum = Conversions.finite_float(values[1], "#{name} maximum")
|
|
14
|
+
raise InvalidArgumentError, "#{name} minimum must not exceed maximum" if minimum > maximum
|
|
15
|
+
|
|
16
|
+
[minimum, maximum]
|
|
17
|
+
rescue NoMethodError
|
|
18
|
+
raise InvalidArgumentError, "#{name} must be a Range or two-element array"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def initialize(system)
|
|
23
|
+
@system = system
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def fixed(body_a:, body_b:, anchor: nil)
|
|
27
|
+
create(:fixed, :JR_Constraint_CreateFixed, body_a, body_b, optional_vec(anchor, "anchor"))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def point(body_a:, body_b:, anchor:)
|
|
31
|
+
create(:point, :JR_Constraint_CreatePoint, body_a, body_b, vec(anchor, "anchor"))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def distance(body_a:, body_b:, point_a: nil, point_b: nil, limits: nil)
|
|
35
|
+
validate_body_pair(body_a, body_b)
|
|
36
|
+
point_a ||= body_a.position
|
|
37
|
+
point_b ||= body_b.position
|
|
38
|
+
minimum, maximum = limits ? self.class.interval(limits, "limits") : [-1.0, -1.0]
|
|
39
|
+
create(
|
|
40
|
+
:distance, :JR_Constraint_CreateDistance, body_a, body_b,
|
|
41
|
+
vec(point_a, "point_a"), vec(point_b, "point_b"), minimum, maximum
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def hinge(body_a:, body_b:, anchor:, axis: [0, 1, 0], normal: nil, limits: nil)
|
|
46
|
+
axis = unit_vec(axis, "axis")
|
|
47
|
+
normal = normal ? unit_vec(normal, "normal") : perpendicular(axis)
|
|
48
|
+
has_limits, minimum, maximum = optional_interval(limits)
|
|
49
|
+
create(
|
|
50
|
+
:hinge, :JR_Constraint_CreateHinge, body_a, body_b,
|
|
51
|
+
vec(anchor, "anchor"), axis, normal, has_limits, minimum, maximum
|
|
52
|
+
)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def slider(body_a:, body_b:, anchor:, axis:, limits: nil)
|
|
56
|
+
has_limits, minimum, maximum = optional_interval(limits)
|
|
57
|
+
create(
|
|
58
|
+
:slider, :JR_Constraint_CreateSlider, body_a, body_b,
|
|
59
|
+
vec(anchor, "anchor"), unit_vec(axis, "axis"), has_limits, minimum, maximum
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def each(&block)
|
|
64
|
+
return enum_for(:each) unless block
|
|
65
|
+
|
|
66
|
+
@system.__constraints_snapshot.each(&block)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def size
|
|
70
|
+
@system.__constraints_snapshot.size
|
|
71
|
+
end
|
|
72
|
+
alias length size
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
def create(kind, function, body_a, body_b, *arguments)
|
|
77
|
+
validate_body_pair(body_a, body_b)
|
|
78
|
+
native_arguments = arguments.map { |argument| argument.is_a?(Native::Vec3) ? argument.pointer : argument }
|
|
79
|
+
pointer = Native.public_send(
|
|
80
|
+
function, @system.__native_pointer, body_a.id, body_b.id, *native_arguments
|
|
81
|
+
)
|
|
82
|
+
raise InitializationError, "failed to create #{kind} constraint" if pointer.null?
|
|
83
|
+
|
|
84
|
+
Constraint.new(@system, pointer, kind, body_a, body_b).tap do |constraint|
|
|
85
|
+
@system.__register_constraint(constraint)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def validate_body_pair(body_a, body_b)
|
|
90
|
+
[body_a, body_b].each do |body|
|
|
91
|
+
unless body.is_a?(Body) && body.system.equal?(@system) && !body.destroyed?
|
|
92
|
+
raise InvalidArgumentError, "constraint bodies must be live bodies from this system"
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
raise InvalidArgumentError, "constraint bodies must be different" if body_a.equal?(body_b)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def vec(value, name)
|
|
99
|
+
Conversions.native_vec3(value, name:)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def unit_vec(value, name)
|
|
103
|
+
Conversions.native_unit_vec3(value, name:)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def optional_vec(value, name)
|
|
107
|
+
value.nil? ? nil : vec(value, name)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def optional_interval(value)
|
|
111
|
+
return [false, 0.0, 0.0] if value.nil?
|
|
112
|
+
|
|
113
|
+
[true, *self.class.interval(value, "limits")]
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def perpendicular(axis)
|
|
117
|
+
x, y, z = %i[x y z].map { |component| axis[component] }
|
|
118
|
+
reference = y.abs < 0.9 ? [0.0, 1.0, 0.0] : [1.0, 0.0, 0.0]
|
|
119
|
+
rx, ry, rz = reference
|
|
120
|
+
unit_vec([y * rz - z * ry, z * rx - x * rz, x * ry - y * rx], "normal")
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jolt
|
|
4
|
+
Contact = Data.define(
|
|
5
|
+
:body_a,
|
|
6
|
+
:body_b,
|
|
7
|
+
:sub_shape_a,
|
|
8
|
+
:sub_shape_b,
|
|
9
|
+
:point,
|
|
10
|
+
:normal,
|
|
11
|
+
:penetration
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
ContactEvents = Data.define(:added, :persisted, :removed, :dropped_count) do
|
|
15
|
+
def self.empty
|
|
16
|
+
new(added: [].freeze, persisted: [].freeze, removed: [].freeze, dropped_count: 0)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "larb"
|
|
4
|
+
|
|
5
|
+
module Jolt
|
|
6
|
+
module Conversions
|
|
7
|
+
module_function
|
|
8
|
+
|
|
9
|
+
def native_vec3(value, name: "vector")
|
|
10
|
+
values = components(value, 3, name)
|
|
11
|
+
Native::Vec3.new.tap do |native|
|
|
12
|
+
native[:x], native[:y], native[:z] = values
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def native_quat(value, name: "rotation")
|
|
17
|
+
values = components(value, 4, name)
|
|
18
|
+
length = Math.sqrt(values.sum { |component| component * component })
|
|
19
|
+
raise InvalidArgumentError, "#{name} must not be a zero quaternion" if length <= Float::EPSILON
|
|
20
|
+
|
|
21
|
+
values.map! { |component| component / length }
|
|
22
|
+
Native::Quat.new.tap do |native|
|
|
23
|
+
native[:x], native[:y], native[:z], native[:w] = values
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def native_unit_vec3(value, name:)
|
|
28
|
+
native = native_vec3(value, name:)
|
|
29
|
+
length = Math.sqrt(%i[x y z].sum { |component| native[component]**2 })
|
|
30
|
+
raise InvalidArgumentError, "#{name} must not be zero" if length <= Float::EPSILON
|
|
31
|
+
|
|
32
|
+
%i[x y z].each { |component| native[component] /= length }
|
|
33
|
+
native
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def vec3(native)
|
|
37
|
+
Larb::Vec3.new(native[:x], native[:y], native[:z])
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def quat(native)
|
|
41
|
+
Larb::Quat.new(native[:x], native[:y], native[:z], native[:w])
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def positive_float(value, name)
|
|
45
|
+
number = finite_float(value, name)
|
|
46
|
+
raise InvalidArgumentError, "#{name} must be greater than zero" unless number.positive?
|
|
47
|
+
|
|
48
|
+
number
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def non_negative_float(value, name)
|
|
52
|
+
number = finite_float(value, name)
|
|
53
|
+
raise InvalidArgumentError, "#{name} must be non-negative" if number.negative?
|
|
54
|
+
|
|
55
|
+
number
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def finite_float(value, name)
|
|
59
|
+
number = Float(value)
|
|
60
|
+
raise InvalidArgumentError, "#{name} must be finite" unless number.finite?
|
|
61
|
+
|
|
62
|
+
number
|
|
63
|
+
rescue TypeError, ArgumentError
|
|
64
|
+
raise InvalidArgumentError, "#{name} must be a number"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def components(value, count, name)
|
|
68
|
+
values = value.respond_to?(:to_a) ? value.to_a : nil
|
|
69
|
+
unless values&.length == count
|
|
70
|
+
raise InvalidArgumentError, "#{name} must contain exactly #{count} components"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
values.map.with_index { |component, index| finite_float(component, "#{name}[#{index}]") }
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
data/lib/jolt/errors.rb
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jolt
|
|
4
|
+
class Error < StandardError; end
|
|
5
|
+
class NativeLoadError < Error; end
|
|
6
|
+
class InitializationError < Error; end
|
|
7
|
+
class InvalidArgumentError < Error; end
|
|
8
|
+
class UseAfterDestroyError < Error; end
|
|
9
|
+
class ConcurrentAccessError < Error; end
|
|
10
|
+
class ShapeError < Error; end
|
|
11
|
+
class PhysicsUpdateError < Error; end
|
|
12
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jolt
|
|
4
|
+
class FixedStepper
|
|
5
|
+
attr_reader :step, :max_substeps
|
|
6
|
+
|
|
7
|
+
def initialize(hz: 60, max_substeps: 5)
|
|
8
|
+
frequency = Conversions.positive_float(hz, "hz")
|
|
9
|
+
unless max_substeps.is_a?(Integer) && max_substeps.positive?
|
|
10
|
+
raise InvalidArgumentError, "max_substeps must be a positive integer"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
@step = 1.0 / frequency
|
|
14
|
+
@max_substeps = max_substeps
|
|
15
|
+
@accumulator = 0.0
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def advance(delta_time)
|
|
19
|
+
raise ArgumentError, "a block is required" unless block_given?
|
|
20
|
+
|
|
21
|
+
@accumulator += Conversions.non_negative_float(delta_time, "delta_time")
|
|
22
|
+
substeps = 0
|
|
23
|
+
while @accumulator >= @step && substeps < @max_substeps
|
|
24
|
+
yield @step
|
|
25
|
+
@accumulator -= @step
|
|
26
|
+
substeps += 1
|
|
27
|
+
end
|
|
28
|
+
@accumulator %= @step if @accumulator >= @step
|
|
29
|
+
@accumulator / @step
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def reset
|
|
33
|
+
@accumulator = 0.0
|
|
34
|
+
self
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
data/lib/jolt/hit.rb
ADDED
data/lib/jolt/layers.rb
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "set"
|
|
4
|
+
|
|
5
|
+
module Jolt
|
|
6
|
+
class Layers
|
|
7
|
+
class << self
|
|
8
|
+
def default
|
|
9
|
+
@default ||= define do |layers|
|
|
10
|
+
layers.broad_phase :static_bp, :dynamic_bp
|
|
11
|
+
layers.object :non_moving, broad_phase: :static_bp
|
|
12
|
+
layers.object :moving, broad_phase: :dynamic_bp
|
|
13
|
+
layers.collide :non_moving, with: :moving
|
|
14
|
+
layers.collide :moving, with: :moving
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def define
|
|
19
|
+
builder = Builder.new
|
|
20
|
+
yield builder
|
|
21
|
+
builder.build
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
attr_reader :object_layers, :broad_phase_layers
|
|
26
|
+
|
|
27
|
+
def initialize(object_layers:, broad_phase_layers:, mappings:, collisions:)
|
|
28
|
+
@object_layers = object_layers.freeze
|
|
29
|
+
@broad_phase_layers = broad_phase_layers.freeze
|
|
30
|
+
@mappings = mappings.freeze
|
|
31
|
+
@collisions = collisions.freeze
|
|
32
|
+
freeze
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def object_layer_id(name)
|
|
36
|
+
@object_layers.fetch(name.to_sym)
|
|
37
|
+
rescue KeyError
|
|
38
|
+
raise InvalidArgumentError, "unknown object layer: #{name.inspect}"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def native_resources
|
|
42
|
+
NativeResources.new(self, @mappings, @collisions)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class Builder
|
|
46
|
+
def initialize
|
|
47
|
+
@broad_phase_layers = {}
|
|
48
|
+
@object_layers = {}
|
|
49
|
+
@mappings = {}
|
|
50
|
+
@collisions = Set.new
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def broad_phase(*names)
|
|
54
|
+
names.each { |name| add_named_layer(@broad_phase_layers, name, 256, "broad-phase") }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def object(*names, broad_phase:)
|
|
58
|
+
broad_name = normalize_name(broad_phase)
|
|
59
|
+
names.each do |name|
|
|
60
|
+
object_name = add_named_layer(@object_layers, name, nil, "object")
|
|
61
|
+
@mappings[object_name] = broad_name
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def collide(layer, with:)
|
|
66
|
+
first = normalize_name(layer)
|
|
67
|
+
Array(with).each do |other|
|
|
68
|
+
second = normalize_name(other)
|
|
69
|
+
@collisions << [first, second].sort.freeze
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def build
|
|
74
|
+
raise InvalidArgumentError, "at least one broad-phase layer is required" if @broad_phase_layers.empty?
|
|
75
|
+
raise InvalidArgumentError, "at least one object layer is required" if @object_layers.empty?
|
|
76
|
+
|
|
77
|
+
missing_mappings = @mappings.values.reject { |name| @broad_phase_layers.key?(name) }.uniq
|
|
78
|
+
unless missing_mappings.empty?
|
|
79
|
+
raise InvalidArgumentError, "unknown broad-phase layer(s): #{missing_mappings.join(", ")}"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
collision_names = @collisions.to_a.flatten.uniq
|
|
83
|
+
missing_collisions = collision_names.reject { |name| @object_layers.key?(name) }
|
|
84
|
+
unless missing_collisions.empty?
|
|
85
|
+
raise InvalidArgumentError, "unknown collision layer(s): #{missing_collisions.join(", ")}"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
Layers.new(
|
|
89
|
+
object_layers: @object_layers.dup,
|
|
90
|
+
broad_phase_layers: @broad_phase_layers.dup,
|
|
91
|
+
mappings: @mappings.dup,
|
|
92
|
+
collisions: @collisions.to_a
|
|
93
|
+
)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
def add_named_layer(collection, name, limit, kind)
|
|
99
|
+
normalized = normalize_name(name)
|
|
100
|
+
raise InvalidArgumentError, "duplicate #{kind} layer: #{normalized}" if collection.key?(normalized)
|
|
101
|
+
raise InvalidArgumentError, "too many #{kind} layers" if limit && collection.length >= limit
|
|
102
|
+
|
|
103
|
+
collection[normalized] = collection.length
|
|
104
|
+
normalized
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def normalize_name(name)
|
|
108
|
+
normalized = name.to_sym
|
|
109
|
+
raise InvalidArgumentError, "layer name must not be empty" if normalized.to_s.empty?
|
|
110
|
+
|
|
111
|
+
normalized
|
|
112
|
+
rescue NoMethodError
|
|
113
|
+
raise InvalidArgumentError, "layer name must be convertible to a symbol"
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
class NativeResources
|
|
118
|
+
attr_reader :broad_phase_interface, :object_pair_filter, :object_vs_broad_phase_filter
|
|
119
|
+
|
|
120
|
+
def initialize(layers, mappings, collisions)
|
|
121
|
+
@destroyed = false
|
|
122
|
+
create_filters(layers, mappings, collisions)
|
|
123
|
+
rescue StandardError
|
|
124
|
+
destroy
|
|
125
|
+
raise
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def destroy
|
|
129
|
+
return if @destroyed
|
|
130
|
+
|
|
131
|
+
Native.JPH_ObjectVsBroadPhaseLayerFilter_Destroy(@object_vs_broad_phase_filter) if present?(@object_vs_broad_phase_filter)
|
|
132
|
+
Native.JPH_ObjectLayerPairFilter_Destroy(@object_pair_filter) if present?(@object_pair_filter)
|
|
133
|
+
Native.JPH_BroadPhaseLayerInterface_Destroy(@broad_phase_interface) if present?(@broad_phase_interface)
|
|
134
|
+
@destroyed = true
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def transfer_to_system!
|
|
138
|
+
@destroyed = true
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
private
|
|
142
|
+
|
|
143
|
+
def create_filters(layers, mappings, collisions)
|
|
144
|
+
object_count = layers.object_layers.length
|
|
145
|
+
broad_count = layers.broad_phase_layers.length
|
|
146
|
+
@broad_phase_interface = Native.JPH_BroadPhaseLayerInterfaceTable_Create(object_count, broad_count)
|
|
147
|
+
check_pointer!(@broad_phase_interface, "broad-phase layer interface")
|
|
148
|
+
|
|
149
|
+
mappings.each do |object_name, broad_name|
|
|
150
|
+
Native.JPH_BroadPhaseLayerInterfaceTable_MapObjectToBroadPhaseLayer(
|
|
151
|
+
@broad_phase_interface,
|
|
152
|
+
layers.object_layers.fetch(object_name),
|
|
153
|
+
layers.broad_phase_layers.fetch(broad_name)
|
|
154
|
+
)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
@object_pair_filter = Native.JPH_ObjectLayerPairFilterTable_Create(object_count)
|
|
158
|
+
check_pointer!(@object_pair_filter, "object layer pair filter")
|
|
159
|
+
collisions.each do |first, second|
|
|
160
|
+
Native.JPH_ObjectLayerPairFilterTable_EnableCollision(
|
|
161
|
+
@object_pair_filter,
|
|
162
|
+
layers.object_layers.fetch(first),
|
|
163
|
+
layers.object_layers.fetch(second)
|
|
164
|
+
)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
@object_vs_broad_phase_filter = Native.JPH_ObjectVsBroadPhaseLayerFilterTable_Create(
|
|
168
|
+
@broad_phase_interface, broad_count, @object_pair_filter, object_count
|
|
169
|
+
)
|
|
170
|
+
check_pointer!(@object_vs_broad_phase_filter, "object-vs-broad-phase filter")
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def check_pointer!(pointer, description)
|
|
174
|
+
raise InitializationError, "failed to create #{description}" unless present?(pointer)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def present?(pointer)
|
|
178
|
+
pointer && !pointer.null?
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jolt
|
|
4
|
+
module Native
|
|
5
|
+
module CharacterFunctions
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def attach(native)
|
|
9
|
+
native.attach_function :JR_CharacterVirtual_Create,
|
|
10
|
+
%i[pointer pointer pointer pointer float float], :pointer
|
|
11
|
+
native.attach_function :JR_CharacterVirtual_ExtendedUpdate,
|
|
12
|
+
%i[pointer float uint32 pointer], :void, blocking: true
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jolt
|
|
4
|
+
module Native
|
|
5
|
+
module ConstraintFunctions
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def attach(native)
|
|
9
|
+
attach_constructors(native)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def attach_constructors(native)
|
|
13
|
+
native.attach_function :JR_Constraint_CreateFixed,
|
|
14
|
+
%i[pointer uint32 uint32 pointer], :pointer
|
|
15
|
+
native.attach_function :JR_Constraint_CreatePoint,
|
|
16
|
+
%i[pointer uint32 uint32 pointer], :pointer
|
|
17
|
+
native.attach_function :JR_Constraint_CreateDistance,
|
|
18
|
+
%i[pointer uint32 uint32 pointer pointer float float], :pointer
|
|
19
|
+
native.attach_function :JR_Constraint_CreateHinge,
|
|
20
|
+
%i[pointer uint32 uint32 pointer pointer pointer bool float float], :pointer
|
|
21
|
+
native.attach_function :JR_Constraint_CreateSlider,
|
|
22
|
+
%i[pointer uint32 uint32 pointer pointer bool float float], :pointer
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Jolt
|
|
4
|
+
module Native
|
|
5
|
+
module CoreFunctions
|
|
6
|
+
module_function
|
|
7
|
+
|
|
8
|
+
def attach(native)
|
|
9
|
+
native.attach_function :JR_ContactQueue_Create, [:uint32], :pointer
|
|
10
|
+
native.attach_function :JR_ContactQueue_Destroy, [:pointer], :void
|
|
11
|
+
native.attach_function :JR_ContactQueue_GetListener, [:pointer], :pointer
|
|
12
|
+
native.attach_function :JR_ContactQueue_Pop, %i[pointer pointer], :bool
|
|
13
|
+
native.attach_function :JR_ContactQueue_GetDroppedCount, [:pointer], :uint64
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|