gosling 2.1.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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