bevy 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 (93) hide show
  1. checksums.yaml +7 -0
  2. data/Cargo.lock +4279 -0
  3. data/Cargo.toml +36 -0
  4. data/README.md +226 -0
  5. data/crates/bevy/Cargo.toml +52 -0
  6. data/crates/bevy/src/app.rs +43 -0
  7. data/crates/bevy/src/component.rs +111 -0
  8. data/crates/bevy/src/entity.rs +30 -0
  9. data/crates/bevy/src/error.rs +32 -0
  10. data/crates/bevy/src/event.rs +190 -0
  11. data/crates/bevy/src/input_bridge.rs +300 -0
  12. data/crates/bevy/src/lib.rs +42 -0
  13. data/crates/bevy/src/mesh_renderer.rs +328 -0
  14. data/crates/bevy/src/query.rs +53 -0
  15. data/crates/bevy/src/render_app.rs +689 -0
  16. data/crates/bevy/src/resource.rs +28 -0
  17. data/crates/bevy/src/schedule.rs +186 -0
  18. data/crates/bevy/src/sprite_renderer.rs +355 -0
  19. data/crates/bevy/src/system.rs +44 -0
  20. data/crates/bevy/src/text_renderer.rs +258 -0
  21. data/crates/bevy/src/types/color.rs +114 -0
  22. data/crates/bevy/src/types/dynamic.rs +131 -0
  23. data/crates/bevy/src/types/math.rs +260 -0
  24. data/crates/bevy/src/types/mod.rs +9 -0
  25. data/crates/bevy/src/types/transform.rs +166 -0
  26. data/crates/bevy/src/world.rs +163 -0
  27. data/crates/bevy_ruby_render/Cargo.toml +22 -0
  28. data/crates/bevy_ruby_render/src/asset.rs +360 -0
  29. data/crates/bevy_ruby_render/src/audio.rs +511 -0
  30. data/crates/bevy_ruby_render/src/camera.rs +365 -0
  31. data/crates/bevy_ruby_render/src/gamepad.rs +398 -0
  32. data/crates/bevy_ruby_render/src/lib.rs +26 -0
  33. data/crates/bevy_ruby_render/src/material.rs +310 -0
  34. data/crates/bevy_ruby_render/src/mesh.rs +491 -0
  35. data/crates/bevy_ruby_render/src/sprite.rs +289 -0
  36. data/ext/bevy/Cargo.toml +20 -0
  37. data/ext/bevy/extconf.rb +6 -0
  38. data/ext/bevy/src/conversions.rs +137 -0
  39. data/ext/bevy/src/lib.rs +29 -0
  40. data/ext/bevy/src/ruby_app.rs +65 -0
  41. data/ext/bevy/src/ruby_color.rs +149 -0
  42. data/ext/bevy/src/ruby_component.rs +189 -0
  43. data/ext/bevy/src/ruby_entity.rs +33 -0
  44. data/ext/bevy/src/ruby_math.rs +384 -0
  45. data/ext/bevy/src/ruby_query.rs +64 -0
  46. data/ext/bevy/src/ruby_render_app.rs +779 -0
  47. data/ext/bevy/src/ruby_system.rs +122 -0
  48. data/ext/bevy/src/ruby_world.rs +107 -0
  49. data/lib/bevy/animation.rb +597 -0
  50. data/lib/bevy/app.rb +675 -0
  51. data/lib/bevy/asset.rb +613 -0
  52. data/lib/bevy/audio.rb +545 -0
  53. data/lib/bevy/audio_effects.rb +224 -0
  54. data/lib/bevy/camera.rb +412 -0
  55. data/lib/bevy/component.rb +91 -0
  56. data/lib/bevy/diagnostics.rb +227 -0
  57. data/lib/bevy/ecs_advanced.rb +296 -0
  58. data/lib/bevy/event.rb +199 -0
  59. data/lib/bevy/gizmos.rb +158 -0
  60. data/lib/bevy/gltf.rb +227 -0
  61. data/lib/bevy/hierarchy.rb +444 -0
  62. data/lib/bevy/input.rb +514 -0
  63. data/lib/bevy/lighting.rb +369 -0
  64. data/lib/bevy/material.rb +248 -0
  65. data/lib/bevy/mesh.rb +257 -0
  66. data/lib/bevy/navigation.rb +344 -0
  67. data/lib/bevy/networking.rb +335 -0
  68. data/lib/bevy/particle.rb +337 -0
  69. data/lib/bevy/physics.rb +396 -0
  70. data/lib/bevy/plugins/default_plugins.rb +34 -0
  71. data/lib/bevy/plugins/input_plugin.rb +49 -0
  72. data/lib/bevy/reflect.rb +361 -0
  73. data/lib/bevy/render_graph.rb +210 -0
  74. data/lib/bevy/resource.rb +185 -0
  75. data/lib/bevy/scene.rb +254 -0
  76. data/lib/bevy/shader.rb +319 -0
  77. data/lib/bevy/shape.rb +195 -0
  78. data/lib/bevy/skeletal.rb +248 -0
  79. data/lib/bevy/sprite.rb +152 -0
  80. data/lib/bevy/sprite_sheet.rb +444 -0
  81. data/lib/bevy/state.rb +277 -0
  82. data/lib/bevy/system.rb +206 -0
  83. data/lib/bevy/text.rb +99 -0
  84. data/lib/bevy/text_advanced.rb +455 -0
  85. data/lib/bevy/timer.rb +147 -0
  86. data/lib/bevy/transform.rb +158 -0
  87. data/lib/bevy/ui.rb +454 -0
  88. data/lib/bevy/ui_advanced.rb +568 -0
  89. data/lib/bevy/version.rb +5 -0
  90. data/lib/bevy/visibility.rb +250 -0
  91. data/lib/bevy/window.rb +302 -0
  92. data/lib/bevy.rb +390 -0
  93. metadata +150 -0
data/lib/bevy/mesh.rb ADDED
@@ -0,0 +1,257 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bevy
4
+ module Mesh
5
+ SHAPE_RECTANGLE = 0
6
+ SHAPE_CIRCLE = 1
7
+ SHAPE_REGULAR_POLYGON = 2
8
+ SHAPE_LINE = 3
9
+ SHAPE_ELLIPSE = 4
10
+
11
+ class Rectangle
12
+ attr_accessor :width, :height, :color, :fill, :thickness, :transform
13
+
14
+ def initialize(width:, height:, color: Color.white, fill: true, thickness: 2.0)
15
+ @width = width.to_f
16
+ @height = height.to_f
17
+ @color = color
18
+ @fill = fill
19
+ @thickness = thickness.to_f
20
+ @transform = Transform.identity
21
+ end
22
+
23
+ def shape_type
24
+ SHAPE_RECTANGLE
25
+ end
26
+
27
+ def to_mesh_data
28
+ {
29
+ shape_type: shape_type,
30
+ color_r: @color.r,
31
+ color_g: @color.g,
32
+ color_b: @color.b,
33
+ color_a: @color.a,
34
+ width: @width,
35
+ height: @height,
36
+ radius: 0.0,
37
+ sides: 0,
38
+ line_start_x: 0.0,
39
+ line_start_y: 0.0,
40
+ line_end_x: 0.0,
41
+ line_end_y: 0.0,
42
+ thickness: @thickness,
43
+ fill: @fill
44
+ }
45
+ end
46
+
47
+ def type_name
48
+ 'Mesh::Rectangle'
49
+ end
50
+ end
51
+
52
+ class Circle
53
+ attr_accessor :radius, :color, :fill, :thickness, :transform
54
+
55
+ def initialize(radius:, color: Color.white, fill: true, thickness: 2.0)
56
+ @radius = radius.to_f
57
+ @color = color
58
+ @fill = fill
59
+ @thickness = thickness.to_f
60
+ @transform = Transform.identity
61
+ end
62
+
63
+ def diameter
64
+ @radius * 2.0
65
+ end
66
+
67
+ def shape_type
68
+ SHAPE_CIRCLE
69
+ end
70
+
71
+ def to_mesh_data
72
+ {
73
+ shape_type: shape_type,
74
+ color_r: @color.r,
75
+ color_g: @color.g,
76
+ color_b: @color.b,
77
+ color_a: @color.a,
78
+ width: 0.0,
79
+ height: 0.0,
80
+ radius: @radius,
81
+ sides: 0,
82
+ line_start_x: 0.0,
83
+ line_start_y: 0.0,
84
+ line_end_x: 0.0,
85
+ line_end_y: 0.0,
86
+ thickness: @thickness,
87
+ fill: @fill
88
+ }
89
+ end
90
+
91
+ def type_name
92
+ 'Mesh::Circle'
93
+ end
94
+ end
95
+
96
+ class RegularPolygon
97
+ attr_accessor :radius, :sides, :color, :fill, :thickness, :transform
98
+
99
+ def initialize(radius:, sides:, color: Color.white, fill: true, thickness: 2.0)
100
+ @radius = radius.to_f
101
+ @sides = [sides.to_i, 3].max
102
+ @color = color
103
+ @fill = fill
104
+ @thickness = thickness.to_f
105
+ @transform = Transform.identity
106
+ end
107
+
108
+ def shape_type
109
+ SHAPE_REGULAR_POLYGON
110
+ end
111
+
112
+ def to_mesh_data
113
+ {
114
+ shape_type: shape_type,
115
+ color_r: @color.r,
116
+ color_g: @color.g,
117
+ color_b: @color.b,
118
+ color_a: @color.a,
119
+ width: 0.0,
120
+ height: 0.0,
121
+ radius: @radius,
122
+ sides: @sides,
123
+ line_start_x: 0.0,
124
+ line_start_y: 0.0,
125
+ line_end_x: 0.0,
126
+ line_end_y: 0.0,
127
+ thickness: @thickness,
128
+ fill: @fill
129
+ }
130
+ end
131
+
132
+ def type_name
133
+ 'Mesh::RegularPolygon'
134
+ end
135
+ end
136
+
137
+ class Triangle < RegularPolygon
138
+ def initialize(radius:, color: Color.white, fill: true, thickness: 2.0)
139
+ super(radius: radius, sides: 3, color: color, fill: fill, thickness: thickness)
140
+ end
141
+
142
+ def type_name
143
+ 'Mesh::Triangle'
144
+ end
145
+ end
146
+
147
+ class Hexagon < RegularPolygon
148
+ def initialize(radius:, color: Color.white, fill: true, thickness: 2.0)
149
+ super(radius: radius, sides: 6, color: color, fill: fill, thickness: thickness)
150
+ end
151
+
152
+ def type_name
153
+ 'Mesh::Hexagon'
154
+ end
155
+ end
156
+
157
+ class Line
158
+ attr_accessor :start_point, :end_point, :color, :thickness, :transform
159
+
160
+ def initialize(start_point:, end_point:, color: Color.white, thickness: 2.0)
161
+ @start_point = start_point
162
+ @end_point = end_point
163
+ @color = color
164
+ @thickness = thickness.to_f
165
+ @transform = Transform.identity
166
+ end
167
+
168
+ def length
169
+ dx = @end_point.x - @start_point.x
170
+ dy = @end_point.y - @start_point.y
171
+ Math.sqrt(dx * dx + dy * dy)
172
+ end
173
+
174
+ def angle
175
+ dx = @end_point.x - @start_point.x
176
+ dy = @end_point.y - @start_point.y
177
+ Math.atan2(dy, dx)
178
+ end
179
+
180
+ def center
181
+ Vec2.new(
182
+ (@start_point.x + @end_point.x) / 2.0,
183
+ (@start_point.y + @end_point.y) / 2.0
184
+ )
185
+ end
186
+
187
+ def shape_type
188
+ SHAPE_LINE
189
+ end
190
+
191
+ def to_mesh_data
192
+ {
193
+ shape_type: shape_type,
194
+ color_r: @color.r,
195
+ color_g: @color.g,
196
+ color_b: @color.b,
197
+ color_a: @color.a,
198
+ width: 0.0,
199
+ height: 0.0,
200
+ radius: 0.0,
201
+ sides: 0,
202
+ line_start_x: @start_point.x,
203
+ line_start_y: @start_point.y,
204
+ line_end_x: @end_point.x,
205
+ line_end_y: @end_point.y,
206
+ thickness: @thickness,
207
+ fill: false
208
+ }
209
+ end
210
+
211
+ def type_name
212
+ 'Mesh::Line'
213
+ end
214
+ end
215
+
216
+ class Ellipse
217
+ attr_accessor :width, :height, :color, :fill, :thickness, :transform
218
+
219
+ def initialize(width:, height:, color: Color.white, fill: true, thickness: 2.0)
220
+ @width = width.to_f
221
+ @height = height.to_f
222
+ @color = color
223
+ @fill = fill
224
+ @thickness = thickness.to_f
225
+ @transform = Transform.identity
226
+ end
227
+
228
+ def shape_type
229
+ SHAPE_ELLIPSE
230
+ end
231
+
232
+ def to_mesh_data
233
+ {
234
+ shape_type: shape_type,
235
+ color_r: @color.r,
236
+ color_g: @color.g,
237
+ color_b: @color.b,
238
+ color_a: @color.a,
239
+ width: @width,
240
+ height: @height,
241
+ radius: 0.0,
242
+ sides: 0,
243
+ line_start_x: 0.0,
244
+ line_start_y: 0.0,
245
+ line_end_x: 0.0,
246
+ line_end_y: 0.0,
247
+ thickness: @thickness,
248
+ fill: @fill
249
+ }
250
+ end
251
+
252
+ def type_name
253
+ 'Mesh::Ellipse'
254
+ end
255
+ end
256
+ end
257
+ end
@@ -0,0 +1,344 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bevy
4
+ class NavMesh
5
+ attr_reader :vertices, :polygons
6
+
7
+ def initialize
8
+ @vertices = []
9
+ @polygons = []
10
+ end
11
+
12
+ def add_vertex(position)
13
+ index = @vertices.size
14
+ @vertices << position
15
+ index
16
+ end
17
+
18
+ def add_polygon(vertex_indices)
19
+ @polygons << NavPolygon.new(indices: vertex_indices, mesh: self)
20
+ self
21
+ end
22
+
23
+ def find_path(start_pos, end_pos)
24
+ start_poly = find_polygon_at(start_pos)
25
+ end_poly = find_polygon_at(end_pos)
26
+
27
+ return nil unless start_poly && end_poly
28
+
29
+ a_star(start_poly, end_poly, start_pos, end_pos)
30
+ end
31
+
32
+ def find_polygon_at(position)
33
+ @polygons.find { |poly| poly.contains?(position) }
34
+ end
35
+
36
+ def polygon_count
37
+ @polygons.size
38
+ end
39
+
40
+ def type_name
41
+ 'NavMesh'
42
+ end
43
+
44
+ private
45
+
46
+ def a_star(start_poly, end_poly, start_pos, end_pos)
47
+ return [start_pos, end_pos] if start_poly == end_poly
48
+
49
+ open_set = [start_poly]
50
+ came_from = {}
51
+ g_score = { start_poly => 0 }
52
+ f_score = { start_poly => heuristic(start_pos, end_pos) }
53
+
54
+ until open_set.empty?
55
+ current = open_set.min_by { |p| f_score[p] || Float::INFINITY }
56
+ return reconstruct_path(came_from, current, start_pos, end_pos) if current == end_poly
57
+
58
+ open_set.delete(current)
59
+
60
+ current.neighbors(@polygons).each do |neighbor|
61
+ tentative_g = (g_score[current] || Float::INFINITY) + current.distance_to(neighbor)
62
+
63
+ next unless tentative_g < (g_score[neighbor] || Float::INFINITY)
64
+
65
+ came_from[neighbor] = current
66
+ g_score[neighbor] = tentative_g
67
+ f_score[neighbor] = tentative_g + heuristic(neighbor.center, end_pos)
68
+ open_set << neighbor unless open_set.include?(neighbor)
69
+ end
70
+ end
71
+
72
+ nil
73
+ end
74
+
75
+ def heuristic(a, b)
76
+ Math.sqrt((b.x - a.x)**2 + (b.y - a.y)**2)
77
+ end
78
+
79
+ def reconstruct_path(came_from, current, start_pos, end_pos)
80
+ path = [end_pos]
81
+ while came_from[current]
82
+ path.unshift(current.center)
83
+ current = came_from[current]
84
+ end
85
+ path.unshift(start_pos)
86
+ path
87
+ end
88
+ end
89
+
90
+ class NavPolygon
91
+ attr_reader :indices
92
+
93
+ def initialize(indices:, mesh:)
94
+ @indices = indices
95
+ @mesh = mesh
96
+ end
97
+
98
+ def vertices
99
+ @indices.map { |i| @mesh.vertices[i] }
100
+ end
101
+
102
+ def center
103
+ verts = vertices
104
+ return Vec3.zero if verts.empty?
105
+
106
+ sum_x = verts.sum(&:x)
107
+ sum_y = verts.sum(&:y)
108
+ sum_z = verts.sum(&:z)
109
+ count = verts.size.to_f
110
+ Vec3.new(sum_x / count, sum_y / count, sum_z / count)
111
+ end
112
+
113
+ def contains?(point)
114
+ verts = vertices
115
+ return false if verts.size < 3
116
+
117
+ n = verts.size
118
+ inside = false
119
+ j = n - 1
120
+ n.times do |i|
121
+ if ((verts[i].y > point.y) != (verts[j].y > point.y)) &&
122
+ (point.x < (verts[j].x - verts[i].x) * (point.y - verts[i].y) / (verts[j].y - verts[i].y) + verts[i].x)
123
+ inside = !inside
124
+ end
125
+ j = i
126
+ end
127
+ inside
128
+ end
129
+
130
+ def neighbors(all_polygons)
131
+ all_polygons.select { |other| other != self && shares_edge?(other) }
132
+ end
133
+
134
+ def shares_edge?(other)
135
+ shared = @indices & other.indices
136
+ shared.size >= 2
137
+ end
138
+
139
+ def distance_to(other)
140
+ Math.sqrt((other.center.x - center.x)**2 + (other.center.y - center.y)**2)
141
+ end
142
+
143
+ def type_name
144
+ 'NavPolygon'
145
+ end
146
+ end
147
+
148
+ class NavAgent
149
+ attr_accessor :position, :target, :speed, :radius, :path
150
+
151
+ def initialize(position: nil, speed: 5.0, radius: 0.5)
152
+ @position = position || Vec3.zero
153
+ @target = nil
154
+ @speed = speed.to_f
155
+ @radius = radius.to_f
156
+ @path = []
157
+ @current_waypoint = 0
158
+ end
159
+
160
+ def set_destination(target)
161
+ @target = target
162
+ @current_waypoint = 0
163
+ end
164
+
165
+ def update(delta, nav_mesh)
166
+ return unless @target
167
+ return if @path.empty? && !calculate_path(nav_mesh)
168
+
169
+ move_along_path(delta)
170
+ end
171
+
172
+ def reached_destination?
173
+ return false unless @target
174
+
175
+ distance_to(@target) < @radius
176
+ end
177
+
178
+ def type_name
179
+ 'NavAgent'
180
+ end
181
+
182
+ private
183
+
184
+ def calculate_path(nav_mesh)
185
+ @path = nav_mesh.find_path(@position, @target) || []
186
+ @current_waypoint = 0
187
+ @path.any?
188
+ end
189
+
190
+ def move_along_path(delta)
191
+ return if @current_waypoint >= @path.size
192
+
193
+ waypoint = @path[@current_waypoint]
194
+ direction = Vec3.new(
195
+ waypoint.x - @position.x,
196
+ waypoint.y - @position.y,
197
+ waypoint.z - @position.z
198
+ )
199
+ distance = Math.sqrt(direction.x**2 + direction.y**2 + direction.z**2)
200
+
201
+ if distance < @radius
202
+ @current_waypoint += 1
203
+ return
204
+ end
205
+
206
+ move_distance = @speed * delta
207
+ if move_distance >= distance
208
+ @position = waypoint
209
+ @current_waypoint += 1
210
+ else
211
+ ratio = move_distance / distance
212
+ @position = Vec3.new(
213
+ @position.x + direction.x * ratio,
214
+ @position.y + direction.y * ratio,
215
+ @position.z + direction.z * ratio
216
+ )
217
+ end
218
+ end
219
+
220
+ def distance_to(point)
221
+ Math.sqrt((@position.x - point.x)**2 + (@position.y - point.y)**2 + (@position.z - point.z)**2)
222
+ end
223
+ end
224
+
225
+ class SteeringBehavior
226
+ attr_accessor :weight
227
+
228
+ def initialize(weight: 1.0)
229
+ @weight = weight.to_f
230
+ end
231
+
232
+ def calculate(_agent)
233
+ Vec3.zero
234
+ end
235
+
236
+ def type_name
237
+ 'SteeringBehavior'
238
+ end
239
+ end
240
+
241
+ class SeekBehavior < SteeringBehavior
242
+ attr_accessor :target
243
+
244
+ def initialize(target: nil, **kwargs)
245
+ super(**kwargs)
246
+ @target = target
247
+ end
248
+
249
+ def calculate(agent)
250
+ return Vec3.zero unless @target
251
+
252
+ desired = Vec3.new(
253
+ @target.x - agent.position.x,
254
+ @target.y - agent.position.y,
255
+ @target.z - agent.position.z
256
+ )
257
+ normalize(desired)
258
+ end
259
+
260
+ def type_name
261
+ 'SeekBehavior'
262
+ end
263
+
264
+ private
265
+
266
+ def normalize(v)
267
+ length = Math.sqrt(v.x**2 + v.y**2 + v.z**2)
268
+ return Vec3.zero if length == 0
269
+
270
+ Vec3.new(v.x / length, v.y / length, v.z / length)
271
+ end
272
+ end
273
+
274
+ class FleeBehavior < SteeringBehavior
275
+ attr_accessor :target, :panic_distance
276
+
277
+ def initialize(target: nil, panic_distance: 10.0, **kwargs)
278
+ super(**kwargs)
279
+ @target = target
280
+ @panic_distance = panic_distance.to_f
281
+ end
282
+
283
+ def calculate(agent)
284
+ return Vec3.zero unless @target
285
+
286
+ diff = Vec3.new(
287
+ agent.position.x - @target.x,
288
+ agent.position.y - @target.y,
289
+ agent.position.z - @target.z
290
+ )
291
+ distance = Math.sqrt(diff.x**2 + diff.y**2 + diff.z**2)
292
+ return Vec3.zero if distance > @panic_distance
293
+
294
+ normalize(diff)
295
+ end
296
+
297
+ def type_name
298
+ 'FleeBehavior'
299
+ end
300
+
301
+ private
302
+
303
+ def normalize(v)
304
+ length = Math.sqrt(v.x**2 + v.y**2 + v.z**2)
305
+ return Vec3.zero if length == 0
306
+
307
+ Vec3.new(v.x / length, v.y / length, v.z / length)
308
+ end
309
+ end
310
+
311
+ class WanderBehavior < SteeringBehavior
312
+ attr_accessor :radius, :distance, :jitter
313
+
314
+ def initialize(radius: 1.0, distance: 2.0, jitter: 0.5, **kwargs)
315
+ super(**kwargs)
316
+ @radius = radius.to_f
317
+ @distance = distance.to_f
318
+ @jitter = jitter.to_f
319
+ @wander_target = Vec3.new(1.0, 0.0, 0.0)
320
+ end
321
+
322
+ def calculate(_agent)
323
+ @wander_target = Vec3.new(
324
+ @wander_target.x + (rand - 0.5) * @jitter,
325
+ @wander_target.y + (rand - 0.5) * @jitter,
326
+ 0.0
327
+ )
328
+ normalize(@wander_target)
329
+ end
330
+
331
+ def type_name
332
+ 'WanderBehavior'
333
+ end
334
+
335
+ private
336
+
337
+ def normalize(v)
338
+ length = Math.sqrt(v.x**2 + v.y**2 + v.z**2)
339
+ return Vec3.zero if length == 0
340
+
341
+ Vec3.new(v.x / length, v.y / length, v.z / length)
342
+ end
343
+ end
344
+ end