gosling 2.1.0 → 2.3.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.
@@ -14,8 +14,8 @@ module Gosling
14
14
  # Gosu::Image. Otherwise, it loads the image, stores it in the cache, and returns it.
15
15
  #
16
16
  def self.get(filename)
17
- raise ArgumentError.new("File not found: '#{filename}' in '#{Dir.pwd}'") unless File.exists?(filename)
18
17
  unless @@cache.has_key?(filename)
18
+ raise ArgumentError.new("File not found: '#{filename}' in '#{Dir.pwd}'") unless File.exists?(filename)
19
19
  @@cache[filename] = Gosu::Image.new(filename, tileable: true)
20
20
  end
21
21
  @@cache[filename]
@@ -0,0 +1,21 @@
1
+ require_relative 'object_cache.rb'
2
+
3
+ class MatrixCache
4
+ include Singleton
5
+ include ObjectCache
6
+
7
+ def initialize
8
+ @cache = {}
9
+ end
10
+
11
+ protected
12
+
13
+ def create
14
+ Snow::Mat3.new
15
+ end
16
+
17
+ def reset(matrix)
18
+ type_check(matrix, Snow::Mat3)
19
+ matrix.load_identity
20
+ end
21
+ end
@@ -0,0 +1,48 @@
1
+ require 'fiddle'
2
+
3
+ class Object
4
+ FIDDLE_FREEZE_BIT = ~(1 << 3)
5
+
6
+ def unfreeze
7
+ ptr = @fiddle_pointer || Fiddle::Pointer.new(object_id * 2)
8
+ ptr[1] &= FIDDLE_FREEZE_BIT
9
+ @fiddle_pointer = ptr
10
+ end
11
+ end
12
+
13
+ module ObjectCache
14
+ def clear
15
+ @cache.clear
16
+ end
17
+
18
+ def recycle(obj)
19
+ return if @cache.key?(obj.object_id)
20
+ self.reset(obj)
21
+ obj.freeze
22
+ @cache[obj.object_id] = obj
23
+ end
24
+
25
+ def get
26
+ if @cache.empty?
27
+ self.create
28
+ else
29
+ obj = @cache.delete(@cache.keys.first)
30
+ obj.unfreeze
31
+ obj
32
+ end
33
+ end
34
+
35
+ def size
36
+ @cache.size
37
+ end
38
+
39
+ protected
40
+
41
+ def create
42
+ raise "Derived classes must implement create()."
43
+ end
44
+
45
+ def reset(obj)
46
+ raise "Derived classes must implement reset()."
47
+ end
48
+ end
@@ -54,19 +54,52 @@ module Gosling
54
54
  @vertices.pop(@vertices.length - vertices.length)
55
55
  end
56
56
 
57
- vertices.each_index do |i|
58
- @vertices[i].x = vertices[i][0]
59
- @vertices[i].y = vertices[i][1]
60
- @vertices[i].z = 0
57
+ vertices.each_index { |i| @vertices[i].set(vertices[i][0], vertices[i][1], 0) }
58
+ end
59
+
60
+ ##
61
+ # Sets this polygon to a rectangular shape with the given width and height, with its upper left at local [0, 0].
62
+ #
63
+ def set_vertices_rect(width, height)
64
+ raise ArgumentError.new("Expected positive non-zero integer, but received #{width.inspect}!") unless width > 0
65
+ raise ArgumentError.new("Expected positive non-zero integer, but received #{height.inspect}!") unless height > 0
66
+
67
+ if @vertices.length < 4
68
+ @vertices.concat(Array.new(4 - @vertices.length) { Snow::Vec3.new })
69
+ elsif @vertices.length > 4
70
+ @vertices.pop(@vertices.length - 4)
61
71
  end
72
+
73
+ @vertices[0].set( 0, 0, 0)
74
+ @vertices[1].set(width, 0, 0)
75
+ @vertices[2].set(width, height, 0)
76
+ @vertices[3].set( 0, height, 0)
62
77
  end
63
78
 
64
79
  ##
65
80
  # Returns an array containing all of our local vertices transformed to global-space. (See Actor#get_global_transform.)
66
81
  #
67
- def get_global_vertices
68
- tf = get_global_transform
69
- @vertices.map { |v| Transformable.transform_point(tf, v) }
82
+ def get_global_vertices(out = nil)
83
+ type_check(out, Array) unless out.nil?
84
+
85
+ tf = MatrixCache.instance.get
86
+ get_global_transform(tf)
87
+
88
+ if out.nil?
89
+ return @vertices.map { |v| Transformable.transform_point(tf, v, Snow::Vec3.new) }
90
+ end
91
+
92
+ @vertices.each_index do |i|
93
+ v = @vertices[i]
94
+ if out[i]
95
+ Transformable.transform_point(tf, v, out[i])
96
+ else
97
+ out[i] = Transformable.transform_point(tf, v)
98
+ end
99
+ end
100
+ out
101
+ ensure
102
+ MatrixCache.instance.recycle(tf) if tf
70
103
  end
71
104
 
72
105
  ##
@@ -79,20 +112,11 @@ module Gosling
79
112
  private
80
113
 
81
114
  def render(matrix)
115
+ # TODO: write transformed vertices to a reserved list of vertices retained in memory each time
82
116
  type_check(matrix, Snow::Mat3)
83
117
  global_vertices = @vertices.map { |v| Transformable.transform_point(matrix, v) }
84
- i = 2
85
- while i < global_vertices.length
86
- v0 = global_vertices[0]
87
- v1 = global_vertices[i-1]
88
- v2 = global_vertices[i]
89
- @window.draw_triangle(
90
- v0[0].to_f, v0[1].to_f, @color,
91
- v1[0].to_f, v1[1].to_f, @color,
92
- v2[0].to_f, v2[1].to_f, @color,
93
- )
94
- i += 1
95
- end
118
+
119
+ fill_polygon(global_vertices)
96
120
  end
97
121
  end
98
122
  end
data/lib/gosling/rect.rb CHANGED
@@ -33,13 +33,7 @@ module Gosling
33
33
  private
34
34
 
35
35
  def rebuild_vertices
36
- vertices = [
37
- Snow::Vec3[ 0, 0, 0],
38
- Snow::Vec3[@width, 0, 0],
39
- Snow::Vec3[@width, @height, 0],
40
- Snow::Vec3[ 0, @height, 0],
41
- ]
42
- set_vertices(vertices)
36
+ set_vertices_rect(@width, @height)
43
37
  end
44
38
 
45
39
  private :set_vertices
@@ -34,7 +34,8 @@ module Gosling
34
34
  private
35
35
 
36
36
  def render(matrix)
37
- global_vertices = @vertices.map { |v| Transformable.transform_point(matrix, v) }
37
+ # TODO: write transformed vertices to a reserved list of vertices retained in memory each time
38
+ global_vertices = @vertices.map { |v| Transformable.transform_point(matrix, v, Snow::Vec3.new) }
38
39
  @image.draw_as_quad(
39
40
  global_vertices[0][0].to_f, global_vertices[0][1].to_f, @color,
40
41
  global_vertices[1][0].to_f, global_vertices[1][1].to_f, @color,
@@ -43,14 +43,14 @@ module Gosling
43
43
  # Returns the x component of the centerpoint of this Transformable. See Transformable#center.
44
44
  #
45
45
  def center_x
46
- @center.x
46
+ @center[0]
47
47
  end
48
48
 
49
49
  ##
50
50
  # Returns the y component of the centerpoint of this Transformable. See Transformable#center.
51
51
  #
52
52
  def center_y
53
- @center.y
53
+ @center[1]
54
54
  end
55
55
 
56
56
  ##
@@ -64,14 +64,14 @@ module Gosling
64
64
  # Returns the x component of the scaling of this Transformable. See Transformable#scale.
65
65
  #
66
66
  def scale_x
67
- @scale.x
67
+ @scale[0]
68
68
  end
69
69
 
70
70
  ##
71
71
  # Returns the y component of the scaling of this Transformable. See Transformable#scale.
72
72
  #
73
73
  def scale_y
74
- @scale.y
74
+ @scale[1]
75
75
  end
76
76
 
77
77
  ##
@@ -86,14 +86,14 @@ module Gosling
86
86
  # Returns this Transformable's x position in relative space. See Transformable#translation.
87
87
  #
88
88
  def x
89
- @translation.x
89
+ @translation[0]
90
90
  end
91
91
 
92
92
  ##
93
93
  # Returns this Transformable's y position in relative space. See Transformable#translation.
94
94
  #
95
95
  def y
96
- @translation.y
96
+ @translation[1]
97
97
  end
98
98
 
99
99
  ##
@@ -119,21 +119,26 @@ module Gosling
119
119
  # - transform.center = Snow::Vec2[x, y]
120
120
  # - transform.center = Snow::Vec3[x, y, z]
121
121
  # - transform.center = Snow::Vec4[x, y, z, c]
122
+ # - transform.set_center { |c| c.add(-sprite.pos, c) }
122
123
  #
123
- def center=(args)
124
- case args[0]
125
- when Array
126
- self.center = args[0][0], args[0][1]
127
- when Snow::Vec2, Snow::Vec3, Snow::Vec4
128
- @center.x = args[0].x
129
- @center.y = args[0].y
130
- when Numeric
131
- raise ArgumentError.new("Cannot set center from #{args.inspect}: numeric array requires at least two arguments!") unless args.length >= 2
132
- args.each { |arg| type_check(arg, Numeric) }
133
- @center.x = args[0]
134
- @center.y = args[1]
124
+ def center=(args = nil)
125
+ if block_given?
126
+ yield(@center)
135
127
  else
136
- raise ArgumentError.new("Cannot set center from #{args.inspect}: bad type!")
128
+ case args[0]
129
+ when Array
130
+ self.center = args[0][0], args[0][1]
131
+ when Snow::Vec2, Snow::Vec3, Snow::Vec4
132
+ @center[0] = args[0][0]
133
+ @center[1] = args[0][1]
134
+ when Numeric
135
+ raise ArgumentError.new("Cannot set center from #{args.inspect}: numeric array requires at least two arguments!") unless args.length >= 2
136
+ args.each { |arg| type_check(arg, Numeric) }
137
+ @center[0] = args[0]
138
+ @center[1] = args[1]
139
+ else
140
+ raise ArgumentError.new("Cannot set center from #{args.inspect}: bad type!")
141
+ end
137
142
  end
138
143
  @center_is_dirty = @is_dirty = true
139
144
  end
@@ -144,7 +149,7 @@ module Gosling
144
149
  #
145
150
  def center_x=(val)
146
151
  type_check(val, Numeric)
147
- @center.x = val
152
+ @center[0] = val
148
153
  @center_is_dirty = @is_dirty = true
149
154
  end
150
155
 
@@ -153,7 +158,7 @@ module Gosling
153
158
  #
154
159
  def center_y=(val)
155
160
  type_check(val, Numeric)
156
- @center.y = val
161
+ @center[1] = val
157
162
  @center_is_dirty = @is_dirty = true
158
163
  end
159
164
 
@@ -164,26 +169,41 @@ module Gosling
164
169
  # If passed more than two numeric arguments, only the first two are used.
165
170
  #
166
171
  # Usage:
172
+ # - transform.scale = scalar
167
173
  # - transform.scale = x, y
168
174
  # - transform.scale = [x, y]
169
175
  # - transform.scale = Snow::Vec2[x, y]
170
176
  # - transform.scale = Snow::Vec3[x, y, z]
171
177
  # - transform.scale = Snow::Vec4[x, y, z, c]
178
+ # - transform.set_scale { |s| s.multiply(0.5, s) }
172
179
  #
173
- def scale=(args)
174
- case args[0]
175
- when Array
176
- self.scale = args[0][0], args[0][1]
177
- when Snow::Vec2, Snow::Vec3, Snow::Vec4
178
- @scale.x = args[0].x
179
- @scale.y = args[0].y
180
- when Numeric
181
- raise ArgumentError.new("Cannot set scale from #{args.inspect}: numeric array requires at least two arguments!") unless args.length >= 2
182
- args.each { |arg| type_check(arg, Numeric) }
183
- @scale.x = args[0]
184
- @scale.y = args[1]
180
+ def scale=(*args)
181
+ if block_given?
182
+ yield(@scale)
185
183
  else
186
- raise ArgumentError.new("Cannot set scale from #{args.inspect}: bad type!")
184
+ if args.length >= 2
185
+ case args[0]
186
+ when Numeric
187
+ args.each { |arg| type_check(arg, Numeric) }
188
+ @scale[0] = args[0]
189
+ @scale[1] = args[1]
190
+ else
191
+ raise ArgumentError.new("Bad combination of arguments: #{args.inspect}! Please supply a Snow::Vec2, an Array of Numerics, or a single scalar.")
192
+ end
193
+ elsif args.length == 1
194
+ case args[0]
195
+ when Array
196
+ self.set_scale(*(args[0]))
197
+ when Snow::Vec2
198
+ self.set_scale(args[0][0], args[0][1])
199
+ when Numeric
200
+ self.set_scale(args[0], args[0])
201
+ else
202
+ raise ArgumentError.new("Bad combination of arguments: #{args.inspect}! Please supply a Snow::Vec2, an Array of Numerics, or a single scalar.")
203
+ end
204
+ else
205
+ raise ArgumentError.new("Bad combination of arguments: #{args.inspect}! Please supply a Snow::Vec2, an Array of Numerics, or a single scalar.")
206
+ end
187
207
  end
188
208
  @scale_is_dirty = @is_dirty = true
189
209
  end
@@ -194,7 +214,7 @@ module Gosling
194
214
  #
195
215
  def scale_x=(val)
196
216
  type_check(val, Numeric)
197
- @scale.x = val
217
+ @scale[0] = val
198
218
  @scale_is_dirty = @is_dirty = true
199
219
  end
200
220
 
@@ -203,7 +223,7 @@ module Gosling
203
223
  #
204
224
  def scale_y=(val)
205
225
  type_check(val, Numeric)
206
- @scale.y = val
226
+ @scale[1] = val
207
227
  @scale_is_dirty = @is_dirty = true
208
228
  end
209
229
 
@@ -230,27 +250,38 @@ module Gosling
230
250
  #
231
251
  # If passed more than two numeric arguments, only the first two are used.
232
252
  #
253
+ # Optionally, this method can be passed a block and no arguments. A reference to this Transformable's translation
254
+ # will be passed to the block as the first parameter, allowing direct manipulation using all of snow-math's Vec3
255
+ # methods. This is particularly useful when optimizing methods that MUST be as fast as possible, such as animation
256
+ # and game physics, since the result of your mathematics can be written directly to this Transformable's translation
257
+ # without having to instantiate an interim Vector during every physics step.
258
+ #
233
259
  # Usage:
234
260
  # - transform.translation = x, y
235
261
  # - transform.translation = [x, y]
236
262
  # - transform.translation = Snow::Vec2[x, y]
237
263
  # - transform.translation = Snow::Vec3[x, y, z]
238
264
  # - transform.translation = Snow::Vec4[x, y, z, c]
265
+ # - transform.set_translation { |t| t.add(velocity * elapsed, t) }
239
266
  #
240
- def translation=(args)
241
- case args[0]
242
- when Array
243
- self.translation = args[0][0], args[0][1]
244
- when Snow::Vec2, Snow::Vec3, Snow::Vec4
245
- @translation.x = args[0].x
246
- @translation.y = args[0].y
247
- when Numeric
248
- raise ArgumentError.new("Cannot set translation from #{args.inspect}: numeric array requires at least two arguments!") unless args.length >= 2
249
- args.each { |arg| type_check(arg, Numeric) }
250
- @translation.x = args[0]
251
- @translation.y = args[1]
267
+ def translation=(args = nil)
268
+ if block_given?
269
+ yield(@translation)
252
270
  else
253
- raise ArgumentError.new("Cannot set translation from #{args.inspect}: bad type!")
271
+ case args[0]
272
+ when Array
273
+ self.translation = args[0][0], args[0][1]
274
+ when Snow::Vec2, Snow::Vec3, Snow::Vec4
275
+ @translation[0] = args[0][0]
276
+ @translation[1] = args[0][1]
277
+ when Numeric
278
+ raise ArgumentError.new("Cannot set translation from #{args.inspect}: numeric array requires at least two arguments!") unless args.length >= 2
279
+ args.each { |arg| type_check(arg, Numeric) }
280
+ @translation[0] = args[0]
281
+ @translation[1] = args[1]
282
+ else
283
+ raise ArgumentError.new("Cannot set translation from #{args.inspect}: bad type!")
284
+ end
254
285
  end
255
286
  @translate_is_dirty = @is_dirty = true
256
287
  end
@@ -262,7 +293,7 @@ module Gosling
262
293
  #
263
294
  def x=(val)
264
295
  type_check(val, Numeric)
265
- @translation.x = val
296
+ @translation[0] = val
266
297
  @translate_is_dirty = @is_dirty = true
267
298
  end
268
299
 
@@ -271,7 +302,7 @@ module Gosling
271
302
  #
272
303
  def y=(val)
273
304
  type_check(val, Numeric)
274
- @translation.y = val
305
+ @translation[1] = val
275
306
  @translate_is_dirty = @is_dirty = true
276
307
  end
277
308
 
@@ -305,44 +336,43 @@ module Gosling
305
336
  # Transforms a Vec3 using the provided Mat3 transform and returns the result as a new Vec3. This is the
306
337
  # opposite of Transformable.untransform_point.
307
338
  #
308
- def self.transform_point(mat, v)
309
- type_check(mat, Snow::Mat3)
310
- type_check(v, Snow::Vec3)
311
- result = mat * Snow::Vec3[v[0], v[1], 1]
312
- result[2] = 0
313
- result
339
+ @@transformable_point
340
+ def self.transform_point(mat, point, out = nil)
341
+ @@transformable_point ||= Snow::Vec3.new
342
+ @@transformable_point.set(point[0], point[1], 1)
343
+
344
+ out ||= Snow::Vec3.new
345
+ mat.multiply(@@transformable_point, out)
346
+ out[2] = 0
347
+ out
314
348
  end
315
349
 
316
350
  ##
317
351
  # Applies all of our transformations to the point, returning the resulting point as a new Vec3. This is the opposite
318
352
  # of Transformable#untransform_point.
319
353
  #
320
- def transform_point(v)
321
- Transformable.transform_point(to_matrix, v)
354
+ def transform_point(point, out = nil)
355
+ Transformable.transform_point(to_matrix, point, out)
322
356
  end
323
357
 
324
358
  ##
325
359
  # Transforms a Vec3 using the inverse of the provided Mat3 transform and returns the result as a new Vec3. This
326
360
  # is the opposite of Transformable.transform_point.
327
361
  #
328
- def self.untransform_point(mat, v)
329
- type_check(mat, Snow::Mat3)
330
- type_check(v, Snow::Vec3)
331
- inverse_mat = mat.inverse
332
- unless inverse_mat
333
- raise "Unable to invert matrix: #{mat}!"
334
- end
335
- result = mat.inverse * Snow::Vec3[v[0], v[1], 1]
336
- result[2] = 0
337
- result
362
+ def self.untransform_point(mat, point, out = nil)
363
+ inverse_mat = MatrixCache.instance.get
364
+ raise "Unable to invert matrix: #{mat}!" unless mat.inverse(inverse_mat)
365
+ transform_point(inverse_mat, point, out)
366
+ ensure
367
+ MatrixCache.instance.recycle(inverse_mat) if inverse_mat
338
368
  end
339
369
 
340
370
  ##
341
371
  # Applies the inverse of all of our transformations to the point, returning the resulting point as a new Vec3. This
342
372
  # is the opposite of Transformable#transform_point.
343
373
  #
344
- def untransform_point(v)
345
- Transformable.untransform_point(to_matrix, v)
374
+ def untransform_point(point, out = nil)
375
+ Transformable.untransform_point(to_matrix, point, out)
346
376
  end
347
377
 
348
378
  private
@@ -350,8 +380,8 @@ module Gosling
350
380
  def update_center_matrix
351
381
  return unless @center_is_dirty || @center_mat.nil?
352
382
  @center_mat ||= Snow::Mat3.new
353
- @center_mat[2] = -@center.x
354
- @center_mat[5] = -@center.y
383
+ @center_mat[2] = -@center[0]
384
+ @center_mat[5] = -@center[1]
355
385
  @center_is_dirty = false
356
386
  end
357
387
 
@@ -375,8 +405,8 @@ module Gosling
375
405
  def update_translate_matrix
376
406
  return unless @translate_is_dirty || @translate_mat.nil?
377
407
  @translate_mat ||= Snow::Mat3.new
378
- @translate_mat[2] = @translation.x
379
- @translate_mat[5] = @translation.y
408
+ @translate_mat[2] = @translation[0]
409
+ @translate_mat[5] = @translation[1]
380
410
  @translate_is_dirty = false
381
411
  end
382
412
  end