budgie 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6d3516e5b17bc0fff22ffdbafb32895370fe69f5
4
+ data.tar.gz: 0abf1c7ebbd29c8695c91e5710be8230f9ec7734
5
+ SHA512:
6
+ metadata.gz: 3913769ac259337174a656ecf92d4aef3776d31468a24a31ba5aadca2d096f136116a3786b3ca70986da54cab32f88362909685db74ea57ee254f2e32642549c
7
+ data.tar.gz: e796383cb30668a77d792d6af97f0e245fe95c1fb59dd0365014c027da108d0f1cfeb8e678f5947adc948dbbe311824d3b69d9701c96207e8d4fd4f1ee479763
data/bin/budgie ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH << File.expand_path('../../lib', __FILE__)
4
+
5
+ require 'budgie/application'
6
+ Budgie::Application.new ARGV.first
@@ -0,0 +1,293 @@
1
+ require 'ruby-prof'
2
+ require 'debugger'
3
+ require 'opengl'
4
+ require 'glu'
5
+ require 'glut'
6
+
7
+ require 'budgie/map.rb'
8
+ require 'budgie/scene.rb'
9
+ require 'budgie/camera.rb'
10
+ require 'budgie/palette.rb'
11
+ require 'budgie/settings.rb'
12
+ require 'budgie/store_map.rb'
13
+ require 'budgie/selection.rb'
14
+
15
+ module Budgie; class Application
16
+ include GL
17
+ include GLU
18
+ include GLUT
19
+ extend Forwardable
20
+
21
+ attr_reader :width, :height, :map, :camera, :palette, :selection
22
+
23
+ def_delegators :@camera, :forward, :backward, :left, :right, :up, :down
24
+
25
+ def initialize(filename, width = 800, height = 600)
26
+ usage unless filename
27
+
28
+ @filename = filename
29
+
30
+ glutInit
31
+ glutInitDisplayMode GLUT_RGB | GLUT_DOUBLE | GLUT_ALPHA | GLUT_DEPTH
32
+ glutInitWindowSize width, height
33
+ glutInitWindowPosition width / 2, height / 2
34
+
35
+ @window = glutCreateWindow "ffxel"
36
+
37
+ glutIdleFunc :idle
38
+ glutMouseFunc :click
39
+ glutDisplayFunc :display
40
+ glutReshapeFunc :reshape
41
+ glutKeyboardFunc :press
42
+ glutKeyboardUpFunc :release
43
+ glutPassiveMotionFunc :mouse
44
+
45
+ glutSetKeyRepeat GLUT_KEY_REPEAT_OFF
46
+
47
+ reshape width, height
48
+
49
+ setup
50
+
51
+ glutMainLoop
52
+ end
53
+
54
+ def setup
55
+ @settings = Settings.new
56
+ @keyboard = {}
57
+ @mouse_x = @mouse_y = 0
58
+
59
+ @camera = Camera.new
60
+ @palette = Palette.new
61
+ @selection = Selection.new
62
+
63
+ @store_map = StoreMap.new @filename, @palette
64
+ @map = @store_map.load
65
+
66
+ @scene = Scene.new self
67
+ end
68
+
69
+ # Event: window size change
70
+ def reshape(width, height)
71
+ @width = width
72
+ @height = height
73
+
74
+ height = 1 if height == 0
75
+
76
+ glViewport 0, 0, width, height
77
+
78
+ glMatrixMode GL_PROJECTION
79
+ glLoadIdentity
80
+ gluPerspective 90.0, width.to_f / height.to_f, 0.001, 100.0
81
+ end
82
+
83
+ # Event: redraw
84
+ def display
85
+ @scene.display
86
+ glutSwapBuffers
87
+ end
88
+
89
+ # Event: no events
90
+ def idle
91
+ time = Time.now.to_f
92
+ @time = time unless @time
93
+ delta = time - @time
94
+ @time = time
95
+
96
+ update_window_title(delta)
97
+ update_camera_step(delta)
98
+
99
+ send_key_presses
100
+ update_selection_at @mouse_x, @mouse_y
101
+
102
+ glutPostRedisplay
103
+ end
104
+
105
+ # Event: key press
106
+ def press(key, x, y)
107
+ @keyboard[key.downcase] = true
108
+ end
109
+
110
+ # Event: key release
111
+ def release(key, x, y)
112
+ @keyboard[key.downcase] = false
113
+ if key == @settings.palette_key
114
+ if @scene.fixed?
115
+ @scene.release
116
+ else
117
+ @scene.fix
118
+ end
119
+ end
120
+ end
121
+
122
+ # Event: mouse move
123
+ def mouse(x, y)
124
+ @mouse_x = x
125
+ @mouse_y = y
126
+
127
+ send mouse_mode
128
+ end
129
+
130
+ # Event: mouse click
131
+ def click(button, state, x, y)
132
+ return unless state == GLUT_UP
133
+
134
+ update_selection_at x, y
135
+
136
+ if button == GLUT_LEFT_BUTTON && glutGetModifiers == GLUT_ACTIVE_SHIFT
137
+ @scene.replace
138
+ elsif button == GLUT_LEFT_BUTTON && glutGetModifiers == GLUT_ACTIVE_CTRL
139
+ @scene.remove
140
+ elsif button == GLUT_LEFT_BUTTON
141
+ @scene.place
142
+ elsif button == GLUT_RIGHT_BUTTON
143
+ update_selection_color_at x, y
144
+ end
145
+ end
146
+
147
+ # User action: exit application
148
+ def escape
149
+ @store_map.save @map
150
+ glutDestroyWindow @window
151
+ exit 0
152
+ end
153
+
154
+ # yields pixel color at (x, y) in window coordinates
155
+ def color_at(x, y, &block)
156
+ y = @height - y
157
+ rgb = glReadPixels x, y, 1, 1, GL_RGB, GL_UNSIGNED_BYTE
158
+ r, g, b = rgb.unpack 'C*'
159
+ yield r, g, b
160
+ end
161
+
162
+ # saves (x, y, z) coordinates of the block under cursor
163
+ def update_selection_at(x, y)
164
+ @scene.picker
165
+ color_at x, y do |r, g, b|
166
+ @selection.from_color r, g, b
167
+ end
168
+ update_selection_normal_at x, y
169
+ end
170
+
171
+ # saves (x, y, z) normal of the block's face under cursor
172
+ def update_selection_normal_at(x, y)
173
+ return unless @selection.selected
174
+ @scene.normal_picker
175
+ color_at x, y do |r, g, b|
176
+ @selection.normal_from_color r, g, b
177
+ end
178
+ end
179
+
180
+ # saves (r, g, b) color of the pixel under cursor
181
+ def update_selection_color_at(x, y)
182
+ @scene.color_picker
183
+ color_at x, y do |r, g, b|
184
+ @selection.r = r
185
+ @selection.g = g
186
+ @selection.b = b
187
+ end
188
+ end
189
+
190
+ # smooth camera movement
191
+ def update_camera_step(delta)
192
+ @camera.step = 30 * delta
193
+ end
194
+
195
+ def send_key_presses
196
+ @keyboard.each do |key, pressed|
197
+ key = @settings.keymap[key]
198
+ send key if key && pressed
199
+ end
200
+ end
201
+
202
+ # draws default cursor
203
+ def free_mouse_mode
204
+ glutSetCursor GLUT_CURSOR_INHERIT
205
+ end
206
+
207
+ # hides cursor image
208
+ # centers cursor
209
+ # rotates camera
210
+ def centered_mouse_mode
211
+ glutSetCursor GLUT_CURSOR_NONE
212
+
213
+ center_x = @width / 2
214
+ center_y = @height / 2
215
+
216
+ dx = center_x - @mouse_x
217
+ dy = center_y - @mouse_y
218
+
219
+ return if dx == 0 && dy == 0
220
+
221
+ glutWarpPointer center_x, center_y
222
+
223
+ quarter_x = center_x / 2
224
+ quarter_y = center_y / 2
225
+
226
+ return if dx.abs > quarter_x || dy.abs > quarter_y
227
+
228
+ dx = 50.0 * dx / @width
229
+ dy = 50.0 * dy / @height
230
+
231
+ @camera.alpha += dx
232
+ @camera.beta += dy
233
+ end
234
+
235
+ def update_window_title(delta)
236
+ @delta = 0 unless @delta
237
+ @frames = 0 unless @frames
238
+
239
+ @delta += delta
240
+ @frames += 1
241
+
242
+ if @delta > 1
243
+ s = @selection
244
+ glutSetWindowTitle "@map.max: #{@map.max}; fps: #{@frames}; selection: #{s.new_x}, #{s.new_y}, #{s.new_z}"
245
+
246
+ @delta = 0
247
+ @frames = 0
248
+ end
249
+ end
250
+
251
+ def mouse_mode
252
+ if @keyboard[@settings.free_mouse_mode_key]
253
+ 'free_mouse_mode'
254
+ else
255
+ 'centered_mouse_mode'
256
+ end
257
+ end
258
+
259
+ def message
260
+ %{
261
+ Usage
262
+ -----
263
+
264
+ $ budgie model-file.png
265
+
266
+
267
+ Keyboard controls
268
+ -----------------
269
+
270
+ WASD horizontal movement
271
+ space move up
272
+ C move down
273
+ E toggle palette
274
+ F free mouse mode
275
+
276
+
277
+ Mouse controls
278
+ --------------
279
+
280
+ Left Click place a block
281
+ Right Click colorpicker
282
+ Shift + Left Click replace current block color
283
+ Control + Left Click remove block
284
+
285
+ }
286
+ end
287
+
288
+ def usage
289
+ puts message.gsub /^ {4}/, ''
290
+ exit 0
291
+ end
292
+
293
+ end; end
@@ -0,0 +1,38 @@
1
+ # Automatically creates array elements by calling
2
+ # the block passed to the constructor when user
3
+ # tries to access the non-existent element
4
+ module Budgie; class AutoArray < Array
5
+ def initialize(&block)
6
+ @block = block
7
+ super
8
+ end
9
+
10
+ def [](index)
11
+ value = super
12
+
13
+ if value.nil?
14
+ populate index
15
+ super
16
+ else
17
+ value
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def populate(index)
24
+ if index > size
25
+ set_upto index
26
+ else
27
+ set index
28
+ end
29
+ end
30
+
31
+ def set_upto(new_size)
32
+ size.upto(new_size) { |index| set index }
33
+ end
34
+
35
+ def set(index)
36
+ self[index] = @block.call(index)
37
+ end
38
+ end; end
@@ -0,0 +1,39 @@
1
+ module Budgie; class Camera
2
+ include Math
3
+
4
+ attr_accessor :x, :y, :z, :alpha, :beta, :step
5
+
6
+ def initialize
7
+ @x = @y = @z = @alpha = @beta = 0
8
+ @step = 0.1
9
+ end
10
+
11
+ def move(step, direction = 0)
12
+ @x += step * cos(PI * (90.0 + @alpha + direction) / 180.0)
13
+ @z += step * sin(PI * (90.0 + @alpha + direction) / 180.0)
14
+ end
15
+
16
+ def forward
17
+ move @step
18
+ end
19
+
20
+ def backward
21
+ move @step, 180
22
+ end
23
+
24
+ def left
25
+ move @step, 90
26
+ end
27
+
28
+ def right
29
+ move @step, -90
30
+ end
31
+
32
+ def up
33
+ @y += @step
34
+ end
35
+
36
+ def down
37
+ @y -= @step
38
+ end
39
+ end; end
@@ -0,0 +1,40 @@
1
+ # Updates map and map views by applying selection to them
2
+ module Budgie; class ChangeMap
3
+ def initialize(scene)
4
+ @map = scene.map
5
+ @palette = scene.palette
6
+ @draw_map = scene.draw_map
7
+ @selection = scene.selection
8
+ @draw_picker_map = scene.draw_picker_map
9
+ end
10
+
11
+ # Places a new block next to the selected one
12
+ def place
13
+ s = @selection
14
+ return unless s.selected
15
+
16
+ @map[s.new_x, s.new_y, s.new_z] = @palette.index(s.r, s.g, s.b)
17
+ @draw_map.update_partial s.new_x, s.new_y, s.new_z
18
+ @draw_picker_map.update_partial s.new_x, s.new_y, s.new_z
19
+ end
20
+
21
+ # Replaces selected block's color
22
+ def replace
23
+ s = @selection
24
+ return unless s.selected
25
+
26
+ @map[s.x, s.y, s.z] = @palette.index(s.r, s.g, s.b)
27
+ @draw_map.update_partial s.x, s.y, s.z
28
+ @draw_picker_map.update_partial s.x, s.y, s.z
29
+ end
30
+
31
+ # Remove selected block from the map
32
+ def remove
33
+ s = @selection
34
+ return unless s.selected
35
+
36
+ @map[s.x, s.y, s.z] = nil
37
+ @draw_map.update_partial s.x, s.y, s.z
38
+ @draw_picker_map.update_partial s.x, s.y, s.z
39
+ end
40
+ end; end
@@ -0,0 +1,42 @@
1
+ require 'opengl'
2
+
3
+ module Budgie; class CheckerTexture
4
+ include GL
5
+
6
+ def initialize
7
+ @id = (glGenTextures 1).first
8
+ bind
9
+
10
+ data = [
11
+ [0xee, 0xee, 0xee], [0xee, 0xee, 0xee], [0xff, 0xff, 0xff], [0xff, 0xff, 0xff],
12
+ [0xee, 0xee, 0xee], [0xee, 0xee, 0xee], [0xff, 0xff, 0xff], [0xff, 0xff, 0xff],
13
+ [0xff, 0xff, 0xff], [0xff, 0xff, 0xff], [0xee, 0xee, 0xee], [0xee, 0xee, 0xee],
14
+ [0xff, 0xff, 0xff], [0xff, 0xff, 0xff], [0xee, 0xee, 0xee], [0xee, 0xee, 0xee]
15
+ ]
16
+
17
+ data = data.flatten.pack 'C*'
18
+
19
+ glTexImage2D GL_TEXTURE_2D,
20
+ level = 0,
21
+ internal_format = 3,
22
+ width = 4,
23
+ height = 4,
24
+ border = 0,
25
+ format = GL_RGB,
26
+ type = GL_UNSIGNED_BYTE,
27
+ data
28
+
29
+ glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST
30
+ glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST
31
+
32
+ unbind
33
+ end
34
+
35
+ def bind
36
+ glBindTexture GL_TEXTURE_2D, @id
37
+ end
38
+
39
+ def unbind
40
+ glBindTexture GL_TEXTURE_2D, 0
41
+ end
42
+ end; end
@@ -0,0 +1,97 @@
1
+ require 'opengl'
2
+ require 'budgie/draw_cube'
3
+ require 'budgie/draw_picker_cube'
4
+
5
+ module Budgie; class Draw
6
+ include GL
7
+
8
+ def initialize(app)
9
+ @app = app
10
+ @cube = DrawCube.new
11
+ @picker_cube = DrawPickerCube.new
12
+ end
13
+
14
+ def ortho(&block)
15
+ glMatrixMode GL_PROJECTION
16
+ glPushMatrix
17
+ glLoadIdentity
18
+ glOrtho 0, @app.width, 0, @app.height, -1, 1
19
+
20
+ glMatrixMode GL_MODELVIEW
21
+ glPushMatrix
22
+ glLoadIdentity
23
+
24
+ yield
25
+
26
+ glMatrixMode GL_PROJECTION
27
+ glPopMatrix
28
+
29
+ glMatrixMode GL_MODELVIEW
30
+ glPopMatrix
31
+ end
32
+
33
+ def crosshair_with_outline
34
+ glColor3ub 0, 0, 0
35
+ crosshair 12, 8
36
+ glColor3ub 0xff, 0xff, 0xff
37
+ crosshair
38
+ end
39
+
40
+ def crosshair(size = 10, width = 4)
41
+ glLineWidth width
42
+ cx = @app.width / 2
43
+ cy = @app.height / 2
44
+
45
+ glBegin GL_LINES
46
+ glVertex2f cx - size, cy
47
+ glVertex2f cx + size, cy
48
+
49
+ glVertex2f cx, cy - size
50
+ glVertex2f cx, cy + size
51
+ glEnd
52
+ end
53
+
54
+ def guides
55
+ glLineWidth 10
56
+ glBegin GL_LINES
57
+
58
+ glColor3f 1, 0, 0
59
+ glVertex3f 0, 0, 0
60
+ glVertex3f 1, 0, 0
61
+
62
+ glColor3f 0, 1, 0
63
+ glVertex3f 0, 0, 0
64
+ glVertex3f 0, 1, 0
65
+
66
+ glColor3f 0, 0, 1
67
+ glVertex3f 0, 0, 0
68
+ glVertex3f 0, 0, 1
69
+
70
+ glEnd
71
+ end
72
+
73
+ def cube_at(x, y, z)
74
+ glPushMatrix
75
+ glTranslatef x, y, z
76
+ @cube.full
77
+ glPopMatrix
78
+ end
79
+
80
+ def picker_cube_at(x, y, z)
81
+ glPushMatrix
82
+ glTranslatef x, y, z
83
+ @picker_cube.full
84
+ glPopMatrix
85
+ end
86
+
87
+ def selection_cube_at(x, y, z)
88
+ glPolygonOffset -1, -1
89
+ cube_at x, y, z
90
+ end
91
+
92
+ def selection(selection)
93
+ return unless selection.selected
94
+ glColor3ub selection.r, selection.g, selection.b
95
+ selection_cube_at selection.x, selection.y, selection.z
96
+ end
97
+ end; end
@@ -0,0 +1,118 @@
1
+ require 'opengl'
2
+
3
+ module Budgie; class DrawCube
4
+ include GL
5
+
6
+ def draw(&block)
7
+ glBegin GL_QUADS
8
+ yield
9
+ glEnd
10
+ end
11
+
12
+ def full
13
+ draw do
14
+ front
15
+ back
16
+ right
17
+ left
18
+ top
19
+ bottom
20
+ end
21
+ end
22
+
23
+ def front
24
+ glNormal3f 0, 0, 1
25
+
26
+ glTexCoord2f 0, 1
27
+ glVertex3f 0, 0, 1
28
+
29
+ glTexCoord2f 1, 1
30
+ glVertex3f 1, 0, 1
31
+
32
+ glTexCoord2f 1, 0
33
+ glVertex3f 1, 1, 1
34
+
35
+ glTexCoord2f 0, 0
36
+ glVertex3f 0, 1, 1
37
+ end
38
+
39
+ def back
40
+ glNormal3f 0, 0, -1
41
+
42
+ glTexCoord2f 0, 1
43
+ glVertex3f 0, 0, 0
44
+
45
+ glTexCoord2f 1, 1
46
+ glVertex3f 0, 1, 0
47
+
48
+ glTexCoord2f 1, 0
49
+ glVertex3f 1, 1, 0
50
+
51
+ glTexCoord2f 0, 0
52
+ glVertex3f 1, 0, 0
53
+ end
54
+
55
+ def top
56
+ glNormal3f 0, 1, 0
57
+
58
+ glTexCoord2f 0, 1
59
+ glVertex3f 0, 1, 0
60
+
61
+ glTexCoord2f 1, 1
62
+ glVertex3f 0, 1, 1
63
+
64
+ glTexCoord2f 1, 0
65
+ glVertex3f 1, 1, 1
66
+
67
+ glTexCoord2f 0, 0
68
+ glVertex3f 1, 1, 0
69
+ end
70
+
71
+ def bottom
72
+ glNormal3f 0, -1, 0
73
+
74
+ glTexCoord2f 1, 1
75
+ glVertex3f 0, 0, 0
76
+
77
+ glTexCoord2f 1, 0
78
+ glVertex3f 1, 0, 0
79
+
80
+ glTexCoord2f 0, 0
81
+ glVertex3f 1, 0, 1
82
+
83
+ glTexCoord2f 0, 1
84
+ glVertex3f 0, 0, 1
85
+ end
86
+
87
+ def right
88
+ glNormal3f 1, 0, 0
89
+
90
+ glTexCoord2f 1, 0
91
+ glVertex3f 1, 0, 0
92
+
93
+ glTexCoord2f 0, 0
94
+ glVertex3f 1, 1, 0
95
+
96
+ glTexCoord2f 0, 1
97
+ glVertex3f 1, 1, 1
98
+
99
+ glTexCoord2f 1, 1
100
+ glVertex3f 1, 0, 1
101
+ end
102
+
103
+ def left
104
+ glNormal3f -1, 0, 0
105
+
106
+ glTexCoord2f 0, 0
107
+ glVertex3f 0, 0, 0
108
+
109
+ glTexCoord2f 0, 1
110
+ glVertex3f 0, 0, 1
111
+
112
+ glTexCoord2f 1, 1
113
+ glVertex3f 0, 1, 1
114
+
115
+ glTexCoord2f 1, 0
116
+ glVertex3f 0, 1, 0
117
+ end
118
+ end; end
@@ -0,0 +1,14 @@
1
+ require 'budgie/draw_picker_map'
2
+
3
+ module Budgie; class DrawMap < DrawPickerMap
4
+ def initialize(map, palette)
5
+ @palette = palette
6
+ super map
7
+ end
8
+
9
+ def cube_color(x, y, z, index)
10
+ @palette.rgb(index) do |r, g, b|
11
+ glColor3ub r, g, b
12
+ end
13
+ end
14
+ end; end