rubygl 0.1.0 → 0.2.1

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 (38) hide show
  1. checksums.yaml +13 -5
  2. data/.travis/push-rdoc-to-gh-pages.sh +22 -0
  3. data/.travis.yml +18 -0
  4. data/Gemfile +7 -0
  5. data/Gemfile.lock +12 -0
  6. data/LICENSE +21 -21
  7. data/README.md +40 -0
  8. data/Rakefile +98 -83
  9. data/bin/ffi_code_gen.rb +166 -166
  10. data/bin/gl_code_gen.rb +458 -458
  11. data/examples/faceted_example.rb +71 -64
  12. data/examples/instanced_example.rb +135 -127
  13. data/examples/phong_example.rb +80 -71
  14. data/ext/windows/RubyGL.so +0 -0
  15. data/lib/rubygl/event.rb +64 -0
  16. data/lib/{RubyGL → rubygl}/geometry.rb +216 -211
  17. data/lib/{RubyGL → rubygl}/math.rb +300 -300
  18. data/lib/{RubyGL → rubygl}/memory.rb +125 -121
  19. data/lib/rubygl/native/all_enums.rb +641 -0
  20. data/lib/{RubyGL/Native → rubygl/native}/glcontext.rb +23 -47
  21. data/lib/{RubyGL/Native → rubygl/native}/include/GLContext.h +36 -36
  22. data/lib/{RubyGL/Native → rubygl/native}/include/Input.h +15 -15
  23. data/lib/{RubyGL/Native → rubygl/native}/include/Window.h +27 -27
  24. data/lib/rubygl/native/input.rb +247 -0
  25. data/lib/{RubyGL/Native → rubygl/native}/opengl.rb +2808 -2032
  26. data/lib/{RubyGL/Native → rubygl/native}/src/GLContext.c +72 -72
  27. data/lib/{RubyGL/Native → rubygl/native}/src/Input.c +25 -25
  28. data/lib/{RubyGL/Native → rubygl/native}/src/Window.c +57 -57
  29. data/lib/{RubyGL/Native → rubygl/native}/window.rb +24 -24
  30. data/lib/{RubyGL → rubygl}/setup.rb +50 -50
  31. data/lib/{RubyGL → rubygl}/shader.rb +203 -203
  32. data/lib/{RubyGL → rubygl}/util.rb +77 -77
  33. data/lib/rubygl.rb +49 -48
  34. data/{RubyGL.gemspec → rubygl.gemspec} +20 -23
  35. data/test/test_util.rb +19 -0
  36. metadata +36 -33
  37. data/lib/RubyGL/Native/input.rb +0 -13
  38. data/lib/RubyGL/callback.rb +0 -10
data/bin/gl_code_gen.rb CHANGED
@@ -1,459 +1,459 @@
1
- require 'net/http'
2
- require 'openssl'
3
- require 'rexml/document'
4
- require 'fileutils'
5
-
6
- # Decorate String Class With Convenience Methods For Parsing
7
- class String
8
- def to_bool
9
- return true if self == true || self =~ (/^(?:true|t|yes|y|1)$/i)
10
- return false if self == false || self.blank? || self =~ (/^(?:false|f|no|n|0)$/i)
11
- raise ArgumentError.new("Invalid Boolean: \"#{self}\"")
12
- end
13
- def to_bool?
14
- begin
15
- self.to_bool
16
- rescue
17
- return false
18
- end
19
- return true
20
- end
21
- def is_i?
22
- !!(self =~ /\A[-+]?[0-9]+\Z/)
23
- end
24
- def replace_tags(sub)
25
- self.gsub(/\<.*?\>/, sub)
26
- end
27
- end
28
-
29
- # Returns The Page Source Of The url And Aborts If Anything Other Than A 200
30
- # Is Received.
31
- def get_page_source(url)
32
- uri = URI.parse url
33
-
34
- http = Net::HTTP.new(uri.host, uri.port)
35
- if (uri.kind_of? URI::HTTPS) then
36
- http.use_ssl = true
37
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
38
- end
39
-
40
- response = http.get(uri.path)
41
- if (response.code != '200') then
42
- abort("\nWas not able to pull xml file from #{uri}" +
43
- "Response Code: #{response.code}")
44
- end
45
-
46
- return response.body
47
- end
48
-
49
- # Returns An API List In The Form Of [[API, Version], ...]
50
- def get_api_list(root)
51
- api_list = []
52
-
53
- root.each_element('feature') { |element|
54
- api = element.attributes['api']
55
- version = element.attributes['number']
56
-
57
- api_list.push [api, version]
58
- }
59
-
60
- return api_list
61
- end
62
-
63
- # Builds An ASCII Grid View From The Tuple List Supplied. The Cell Sizes Will
64
- # Dynamically Scale Based On The Biggest Cell So That Columns Are Aligned.
65
- def get_grid_view(console_width, tuple_list)
66
- formatting_chars = 6
67
-
68
- # Find Size Of Longest String Element (With Formatting)
69
- max_length = tuple_list.sort { |a,b|
70
- string_one = a[0] + a[1]
71
- string_two = b[0] + b[1]
72
-
73
- string_one.size <=> string_two.size
74
- }.last.join('').size + tuple_list.size.to_s.size + formatting_chars
75
-
76
- options_per_row = console_width / max_length
77
-
78
- # Build 1-D Array Of Concatenated Strings
79
- curr_iteraion = -1
80
- concat_list = tuple_list.collect { |a,b|
81
- curr_iteraion += 1
82
- ' ' + curr_iteraion.to_s + ': ' + a + ' ' + b + ' '
83
- }
84
- formatting_chars -= 5
85
-
86
- # Expand Concatenated Strings Into Result
87
- result = '-' * console_width
88
- curr_column = 1
89
- concat_list.each { |concat_item|
90
- num_padding = max_length - concat_item.size - formatting_chars
91
-
92
- result << concat_item << ' ' * num_padding
93
-
94
- if (curr_column == options_per_row) then
95
- result << "\n" + ('-' * console_width)
96
- curr_column = 1
97
- else
98
- result << '|'
99
- curr_column += 1
100
- end
101
- }
102
-
103
- result << "\n" + ('-' * console_width) if result[-1] != '-'
104
-
105
- return result
106
- end
107
-
108
- # Pads The String With pad_char So That It Fills columns Completely
109
- def get_pretty(columns, pad_char, string)
110
- padding = pad_char * ((columns - string.size) / 2)
111
-
112
- result = (padding + string + padding)
113
- result << pad_char if result.size < columns
114
-
115
- return result
116
- end
117
-
118
- # Writes contents Out To path/name Using mode
119
- def write_file(path, name, contents, mode = 'w+')
120
- FileUtils.mkdir_p(path)
121
-
122
- File.open(path + '/' + name, mode) { |file|
123
- file << contents
124
- }
125
- end
126
-
127
- # Verify The Supplied api And version Numbers Against The api_list
128
- def valid_api_values?(api_list, api, version)
129
- api_list.find_index { |api_tuple|
130
- api_tuple[0].casecmp(api) && api_tuple[1].to_f == version
131
- } != nil
132
- end
133
-
134
- # Verify The Supplied "api_index" To Make Sure It Is Valid
135
- def valid_api_index?(api_list, api_index)
136
- api_index.is_i? && (0...api_list.size).include?(api_index.to_i)
137
- end
138
-
139
- # Prompt User With An api_list In A Grid View For An api_index Selection
140
- def prompt_user(api_list)
141
- # Prompt User
142
- print get_pretty(COLUMN_WIDTH, ' ', GRID_TITLE)
143
- print get_grid_view(COLUMN_WIDTH, api_list)
144
- puts "Note: OpenGL 3.2 introduced the notion of a 'core' profile which
145
- essentially means that it is not completely backwards compatible with
146
- earlier versions. To retain this backwards compatibility (not recommended)
147
- append a 'C' to the index number of the API you want.".gsub(/\s/, ' ')
148
-
149
- # User Input
150
- print "\nEnter The Index Number Next To The API You Want: "
151
- api_index = gets.chomp
152
- compatibility_mode = false
153
-
154
- if (api_index[-1].upcase == 'C') then
155
- compatibility_mode = true
156
- api_index = api_index[0..-2]
157
- end
158
-
159
- while (!valid_api_index?(api_list, api_index)) do
160
- print "Please Enter A Valid Input: "
161
- api_index = gets.chomp
162
-
163
- if (api_index[-1].upcase == 'C') then
164
- compatibility_mode = true
165
- api_index = api_index[0..-2]
166
- else
167
- compatibility_mode = false
168
- end
169
- end
170
- api_index = api_index.to_i
171
-
172
- return [api_list[api_index][0], api_list[api_index][1].to_f,
173
- compatibility_mode]
174
- end
175
-
176
- /--------------------------------START SCRIPT---------------------------------/
177
-
178
- # ARGV: (GL_API GL_VERSION) NAME FOLDER
179
- # All Optional, If GL_API Is Specified Then GL_VERSION Must Be As Well
180
-
181
- # Constants
182
- COLUMN_WIDTH = 80
183
- GRID_TITLE = "OpenGL API Versions"
184
- GL_URL = 'https://cvs.khronos.org/svn/repos/ogl/trunk/doc/registry/public/api'
185
- GL_PATH = '/gl.xml'
186
- EGL_URL = 'http://www.khronos.org/registry/egl/api'
187
- EGL_KHR_PATH = '/KHR/khrplatform.h'
188
- SOURCE_INDENTS = 2
189
-
190
- # Execution Variables
191
- gl_header = 'OpenGL.h'
192
- gl_source = 'OpenGL.c'
193
- directory = './'
194
- api, version, compatibility_mode = nil, nil, false
195
-
196
- # Check Command Line Arguments
197
- if (ARGV[0]) then
198
- api = ARGV[0]
199
- version = ARGV[1].to_f
200
- compatibility_mode = ARGV[1][-1].casecmp('C') == 0
201
- end
202
- if (ARGV[2]) then
203
- gl_header = ARGV[2] + '.h'
204
- gl_source = ARGV[2] + '.c'
205
- end
206
- if (ARGV[3]) then
207
- directory = ARGV[3]
208
- end
209
-
210
- print "\n" + get_pretty(COLUMN_WIDTH, '-', "Pulling Data From Web")
211
- puts "- #{GL_URL + GL_PATH}"
212
- source = get_page_source(GL_URL + GL_PATH)
213
- puts get_pretty(COLUMN_WIDTH, '-', "Finished Pulling Data")
214
-
215
- print get_pretty(COLUMN_WIDTH, '-', "Building XML Structure")
216
- puts "Bytes: #{source.size}"
217
- root = REXML::Document.new(source).root
218
- puts get_pretty(COLUMN_WIDTH, '-', "Finished Building XML Structure")
219
-
220
- # Extract API And Version Information
221
- api_list = get_api_list(root) # [[API, Version], ...]
222
-
223
- if (!api || !version) then # Prompt User
224
- api_tuple = prompt_user(api_list)
225
-
226
- api = api_tuple[0]
227
- version = api_tuple[1]
228
- compatibility_mode = api_tuple[3]
229
- else # Verify User Parameters
230
- if (!valid_api_values?(api_list, api, version)) then
231
- abort("Invalid API Or Version Parameters Passed To Script")
232
- end
233
- end
234
-
235
- puts get_pretty(COLUMN_WIDTH, '-', "API/Version/Profile - #{api}/#{version}/" +
236
- (compatibility_mode ? "Compatibility" : "Core"))
237
-
238
- # Parse All Enumerations And Commands
239
- all_enums, all_commands = {}, {}
240
- print get_pretty(COLUMN_WIDTH, '-', "Crawling XML Structure")
241
-
242
- puts "Parsing Enumerations..."
243
- root.each_element('enums/enum') { |element|
244
- all_enums[element.attributes['name']] = element.attributes['value']
245
- }
246
- puts "Parsed All Enumerations\n\n"
247
-
248
- puts "Parsing Commands..."
249
- root.each_element('commands/command') { |element|
250
- comm_name = element.elements['proto/name'].text
251
-
252
- command = element.elements['proto'].to_s.replace_tags(' ').squeeze(' ').strip
253
- command << '('
254
-
255
- element.each_element('param') { |innerElement|
256
- command << innerElement.to_s.replace_tags(' ').squeeze(' ').strip + ', '
257
- }
258
- command[-2..-1] = '' if command.end_with? ', '
259
-
260
- all_commands[comm_name] = command + ');'
261
- }
262
- puts "Parsed All Commands"
263
-
264
- puts get_pretty(COLUMN_WIDTH, '-', "Finished Crawling XML Structure")
265
-
266
- #-----------------------------Build Header File-------------------------------
267
- used_enums, used_commands = {}, {}
268
-
269
- header_template = "#ifndef #{gl_header.upcase.sub(/\./, '_')}
270
- #define #{gl_header.upcase.sub(/\./, '_')}
271
-
272
- #ifdef __cplusplus
273
- extern \"C\" {
274
- #endif
275
-
276
- #if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)
277
- #ifndef WIN32_LEAN_AND_MEAN
278
- #define WIN32_LEAN_AND_MEAN 1
279
- #endif
280
- #include <windows.h>
281
- #undef near // Used As An Identifier In OpenGL
282
- #undef far // Used As An Identifier In OpenGL
283
- #endif
284
-
285
- #ifndef APIENTRY
286
- #define APIENTRY
287
- #endif
288
-
289
- COMMENTS_GO_HERE
290
-
291
- TYPEDEFS_GO_HERE
292
-
293
- DEFINES_GO_HERE
294
-
295
- FUNCTIONS_GO_HERE
296
-
297
- #ifdef __cplusplus
298
- }
299
- #endif
300
-
301
- #endif"
302
-
303
- print get_pretty(COLUMN_WIDTH, '-', "Generating File #{gl_header}")
304
-
305
- puts "Appending Comments To #{gl_header}..."
306
- buffer = "/*\n"
307
- root.elements['comment'].text.each_line { |line|
308
- buffer << '** ' << line
309
- }
310
- buffer << "\n*/"
311
- header_template.sub!(/COMMENTS_GO_HERE/, buffer)
312
- puts "Comments Have Been Appended\n\n"
313
-
314
- # Pull Out All Possible Include Dependencies
315
- puts "Appending Type Definitions To #{gl_header}..."
316
- possible_includes = {}
317
- root.each_element('types/type[@name]') { |element|
318
- possible_includes[element.attributes['name']] = element.text
319
- }
320
-
321
- # Pull Out All Possible Types And Check If They Have Include Dependencies
322
- actual_includes, buffer = {}, ''
323
- attrib_filter = api == 'gl' ? 'not(@api)' : "@api='#{api}'"
324
- root.each_element("types/type[#{attrib_filter} and not(@name)]") { |element|
325
- content = element.to_s.replace_tags(' ').squeeze(' ').strip
326
- content.gsub!(/ +;/, ';')
327
- buffer << content << "\n"
328
-
329
- requires = element.attributes['requires']
330
- if (requires && !actual_includes[requires]) then
331
- actual_includes[requires] = possible_includes[requires]
332
- end
333
- }
334
-
335
- # Any Header Dependencies Found
336
- actual_includes.each { |require,include|
337
- if (require == 'khrplatform') then
338
- print get_pretty(COLUMN_WIDTH, '-', "Pulling Data From Web")
339
- puts "- #{EGL_URL + EGL_KHR_PATH}"
340
-
341
- source = get_page_source(EGL_URL + EGL_KHR_PATH)
342
- write_file("#{directory}/KHR", 'khrplatform.h', source)
343
-
344
- print get_pretty(COLUMN_WIDTH, '-', "Finished Pulling Data")
345
- end
346
-
347
- # Ugh, Khronos Re-Used The Require Attribute For Non-Header Dependencies!!!
348
- if (include) then
349
- buffer.prepend(include + "\n")
350
- end
351
- }
352
- header_template.sub!(/TYPEDEFS_GO_HERE/, buffer)
353
- puts "Appended Type Definitions\n\n"
354
-
355
- puts "Filtering Macros And Commands..."
356
- root.each_element('feature') { |element|
357
- if (element.attributes['api'] == api && element.attributes['number'].to_f <= version) then
358
- element.each_element('require/enum') { |inner_element|
359
- enum_name = inner_element.attributes['name']
360
- used_enums[enum_name] = all_enums[enum_name]
361
- }
362
-
363
- element.each_element('require/command') { |inner_element|
364
- comm_name = inner_element.attributes['name']
365
- used_commands[comm_name] = all_commands[comm_name]
366
- }
367
- if (!compatibility_mode) then
368
- element.each_element('remove/enum') { |inner_element|
369
- enum_name = inner_element.attributes['name']
370
- used_enums.delete(enum_name)
371
- }
372
- element.each_element('remove/command') { |inner_element|
373
- comm_name = inner_element.attributes['name']
374
- used_commands.delete(comm_name)
375
- }
376
- end
377
- end
378
- }
379
- puts "Filtered Macros And Commands\n\n"
380
-
381
- puts "Appending Macro To #{gl_header}..."
382
- used_enums, buffer = used_enums.to_a.sort, ''
383
- used_enums.each { |name,value|
384
- buffer << '#define ' << name << ' ' << value << "\n"
385
- }
386
- header_template.sub!(/DEFINES_GO_HERE/, buffer)
387
- puts "Appended Macros\n\n"
388
-
389
- puts "Appending Commands To #{gl_header}..."
390
- used_commands, buffer = used_commands.to_a.sort, ''
391
- used_commands.each { |name,signature|
392
- buffer << signature << "\n"
393
- }
394
- header_template.sub!(/FUNCTIONS_GO_HERE/, buffer)
395
- puts "Appended Commands"
396
-
397
- write_file(directory, gl_header, header_template)
398
- puts get_pretty(COLUMN_WIDTH, '-', "Finished Generating #{gl_header}")
399
-
400
- #-----------------------------Build Source File-------------------------------
401
- print get_pretty(COLUMN_WIDTH, '-', "Generating File #{gl_source}")
402
-
403
- buffer = "#include \"#{gl_header}\"\n#include \"SDL.h\"\n\n"
404
-
405
- puts "Appending Command Definitions To #{gl_source}..."
406
- used_commands.each { |name,signature|
407
- buffer << signature[0..-2] + " {\n"
408
-
409
- # Break Function Down
410
- func_return_type = signature[/\A(.*?)gl/, 1].strip
411
- typedef_name = 'GL_' + name[2..-1] + '_Func'
412
- func_ptr_name = name + '_ptr'
413
- param_type_list = []
414
- unless (signature =~ /\(\);/) then # Has At Least One Parameter
415
- param_type_list = signature[/\((.*?);/, 1].split(/ *\w+[,\)] */)
416
- end
417
- param_name_list = signature.scan(/(\w+)[,\)]/).flatten
418
-
419
- # Append Typedef
420
- buffer << ' ' * SOURCE_INDENTS + 'typedef ' + func_return_type +
421
- " (APIENTRY * #{typedef_name})("
422
- param_type_list.each { |param_type|
423
- buffer << param_type + ', '
424
- }
425
- if (buffer[-2..-1] == ', ') then
426
- buffer[-2..-1] = ');'
427
- else
428
- buffer << ');'
429
- end
430
- buffer << "\n"
431
-
432
- # Append Pointer Initialization
433
- buffer << ' ' * SOURCE_INDENTS + 'static ' + typedef_name + ' ' +
434
- func_ptr_name + " = NULL;\n\n"
435
-
436
- buffer << ' ' * SOURCE_INDENTS + "if (#{func_ptr_name} == NULL) {\n" +
437
- ' ' * (SOURCE_INDENTS * 2) + func_ptr_name +
438
- " = (#{typedef_name})SDL_GL_GetProcAddress(__func__);\n" +
439
- ' ' * SOURCE_INDENTS + "}\n\n"
440
-
441
- # Append Function Pointer Call
442
- buffer << ' ' * SOURCE_INDENTS + func_ptr_name + '('
443
- param_name_list.each { |param_name|
444
- buffer << param_name + ', '
445
- }
446
- if (buffer[-2..-1] == ', ') then
447
- buffer[-2..-1] = ');'
448
- else
449
- buffer << ');'
450
- end
451
-
452
- buffer << "\n}\n\n"
453
- }
454
- write_file(directory, gl_source, buffer)
455
- puts "Finished Appending Command Definitions"
456
-
457
- print get_pretty(COLUMN_WIDTH, '-', "Finished Generating #{gl_source}")
458
-
1
+ require 'net/http'
2
+ require 'openssl'
3
+ require 'rexml/document'
4
+ require 'fileutils'
5
+
6
+ # Decorate String Class With Convenience Methods For Parsing
7
+ class String
8
+ def to_bool
9
+ return true if self == true || self =~ (/^(?:true|t|yes|y|1)$/i)
10
+ return false if self == false || self.blank? || self =~ (/^(?:false|f|no|n|0)$/i)
11
+ raise ArgumentError.new("Invalid Boolean: \"#{self}\"")
12
+ end
13
+ def to_bool?
14
+ begin
15
+ self.to_bool
16
+ rescue
17
+ return false
18
+ end
19
+ return true
20
+ end
21
+ def is_i?
22
+ !!(self =~ /\A[-+]?[0-9]+\Z/)
23
+ end
24
+ def replace_tags(sub)
25
+ self.gsub(/\<.*?\>/, sub)
26
+ end
27
+ end
28
+
29
+ # Returns The Page Source Of The url And Aborts If Anything Other Than A 200
30
+ # Is Received.
31
+ def get_page_source(url)
32
+ uri = URI.parse url
33
+
34
+ http = Net::HTTP.new(uri.host, uri.port)
35
+ if (uri.kind_of? URI::HTTPS) then
36
+ http.use_ssl = true
37
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
38
+ end
39
+
40
+ response = http.get(uri.path)
41
+ if (response.code != '200') then
42
+ abort("\nWas not able to pull xml file from #{uri}" +
43
+ "Response Code: #{response.code}")
44
+ end
45
+
46
+ return response.body
47
+ end
48
+
49
+ # Returns An API List In The Form Of [[API, Version], ...]
50
+ def get_api_list(root)
51
+ api_list = []
52
+
53
+ root.each_element('feature') { |element|
54
+ api = element.attributes['api']
55
+ version = element.attributes['number']
56
+
57
+ api_list.push [api, version]
58
+ }
59
+
60
+ return api_list
61
+ end
62
+
63
+ # Builds An ASCII Grid View From The Tuple List Supplied. The Cell Sizes Will
64
+ # Dynamically Scale Based On The Biggest Cell So That Columns Are Aligned.
65
+ def get_grid_view(console_width, tuple_list)
66
+ formatting_chars = 6
67
+
68
+ # Find Size Of Longest String Element (With Formatting)
69
+ max_length = tuple_list.sort { |a,b|
70
+ string_one = a[0] + a[1]
71
+ string_two = b[0] + b[1]
72
+
73
+ string_one.size <=> string_two.size
74
+ }.last.join('').size + tuple_list.size.to_s.size + formatting_chars
75
+
76
+ options_per_row = console_width / max_length
77
+
78
+ # Build 1-D Array Of Concatenated Strings
79
+ curr_iteraion = -1
80
+ concat_list = tuple_list.collect { |a,b|
81
+ curr_iteraion += 1
82
+ ' ' + curr_iteraion.to_s + ': ' + a + ' ' + b + ' '
83
+ }
84
+ formatting_chars -= 5
85
+
86
+ # Expand Concatenated Strings Into Result
87
+ result = '-' * console_width
88
+ curr_column = 1
89
+ concat_list.each { |concat_item|
90
+ num_padding = max_length - concat_item.size - formatting_chars
91
+
92
+ result << concat_item << ' ' * num_padding
93
+
94
+ if (curr_column == options_per_row) then
95
+ result << "\n" + ('-' * console_width)
96
+ curr_column = 1
97
+ else
98
+ result << '|'
99
+ curr_column += 1
100
+ end
101
+ }
102
+
103
+ result << "\n" + ('-' * console_width) if result[-1] != '-'
104
+
105
+ return result
106
+ end
107
+
108
+ # Pads The String With pad_char So That It Fills columns Completely
109
+ def get_pretty(columns, pad_char, string)
110
+ padding = pad_char * ((columns - string.size) / 2)
111
+
112
+ result = (padding + string + padding)
113
+ result << pad_char if result.size < columns
114
+
115
+ return result
116
+ end
117
+
118
+ # Writes contents Out To path/name Using mode
119
+ def write_file(path, name, contents, mode = 'w+')
120
+ FileUtils.mkdir_p(path)
121
+
122
+ File.open(path + '/' + name, mode) { |file|
123
+ file << contents
124
+ }
125
+ end
126
+
127
+ # Verify The Supplied api And version Numbers Against The api_list
128
+ def valid_api_values?(api_list, api, version)
129
+ api_list.find_index { |api_tuple|
130
+ api_tuple[0].casecmp(api) && api_tuple[1].to_f == version
131
+ } != nil
132
+ end
133
+
134
+ # Verify The Supplied "api_index" To Make Sure It Is Valid
135
+ def valid_api_index?(api_list, api_index)
136
+ api_index.is_i? && (0...api_list.size).include?(api_index.to_i)
137
+ end
138
+
139
+ # Prompt User With An api_list In A Grid View For An api_index Selection
140
+ def prompt_user(api_list)
141
+ # Prompt User
142
+ print get_pretty(COLUMN_WIDTH, ' ', GRID_TITLE)
143
+ print get_grid_view(COLUMN_WIDTH, api_list)
144
+ puts "Note: OpenGL 3.2 introduced the notion of a 'core' profile which
145
+ essentially means that it is not completely backwards compatible with
146
+ earlier versions. To retain this backwards compatibility (not recommended)
147
+ append a 'C' to the index number of the API you want.".gsub(/\s+/, ' ')
148
+
149
+ # User Input
150
+ print "\nEnter The Index Number Next To The API You Want: "
151
+ api_index = gets.chomp
152
+ compatibility_mode = false
153
+
154
+ if (api_index[-1].upcase == 'C') then
155
+ compatibility_mode = true
156
+ api_index = api_index[0..-2]
157
+ end
158
+
159
+ while (!valid_api_index?(api_list, api_index)) do
160
+ print "Please Enter A Valid Input: "
161
+ api_index = gets.chomp
162
+
163
+ if (api_index[-1].upcase == 'C') then
164
+ compatibility_mode = true
165
+ api_index = api_index[0..-2]
166
+ else
167
+ compatibility_mode = false
168
+ end
169
+ end
170
+ api_index = api_index.to_i
171
+
172
+ return [api_list[api_index][0], api_list[api_index][1].to_f,
173
+ compatibility_mode]
174
+ end
175
+
176
+ /--------------------------------START SCRIPT---------------------------------/
177
+
178
+ # ARGV: (GL_API GL_VERSION) NAME FOLDER
179
+ # All Optional, If GL_API Is Specified Then GL_VERSION Must Be As Well
180
+
181
+ # Constants
182
+ COLUMN_WIDTH = 80
183
+ GRID_TITLE = "OpenGL API Versions"
184
+ GL_URL = 'https://cvs.khronos.org/svn/repos/ogl/trunk/doc/registry/public/api'
185
+ GL_PATH = '/gl.xml'
186
+ EGL_URL = 'https://www.khronos.org/registry/egl/api'
187
+ EGL_KHR_PATH = '/KHR/khrplatform.h'
188
+ SOURCE_INDENTS = 2
189
+
190
+ # Execution Variables
191
+ gl_header = 'OpenGL.h'
192
+ gl_source = 'OpenGL.c'
193
+ directory = './'
194
+ api, version, compatibility_mode = nil, nil, false
195
+
196
+ # Check Command Line Arguments
197
+ if (ARGV[0]) then
198
+ api = ARGV[0]
199
+ version = ARGV[1].to_f
200
+ compatibility_mode = ARGV[1][-1].casecmp('C') == 0
201
+ end
202
+ if (ARGV[2]) then
203
+ gl_header = ARGV[2] + '.h'
204
+ gl_source = ARGV[2] + '.c'
205
+ end
206
+ if (ARGV[3]) then
207
+ directory = ARGV[3]
208
+ end
209
+
210
+ print "\n" + get_pretty(COLUMN_WIDTH, '-', "Pulling Data From Web")
211
+ puts "- #{GL_URL + GL_PATH}"
212
+ source = get_page_source(GL_URL + GL_PATH)
213
+ puts get_pretty(COLUMN_WIDTH, '-', "Finished Pulling Data")
214
+
215
+ print get_pretty(COLUMN_WIDTH, '-', "Building XML Structure")
216
+ puts "Bytes: #{source.size}"
217
+ root = REXML::Document.new(source).root
218
+ puts get_pretty(COLUMN_WIDTH, '-', "Finished Building XML Structure")
219
+
220
+ # Extract API And Version Information
221
+ api_list = get_api_list(root) # [[API, Version], ...]
222
+
223
+ if (!api || !version) then # Prompt User
224
+ api_tuple = prompt_user(api_list)
225
+
226
+ api = api_tuple[0]
227
+ version = api_tuple[1]
228
+ compatibility_mode = api_tuple[2]
229
+ else # Verify User Parameters
230
+ if (!valid_api_values?(api_list, api, version)) then
231
+ abort("Invalid API Or Version Parameters Passed To Script")
232
+ end
233
+ end
234
+
235
+ puts get_pretty(COLUMN_WIDTH, '-', "API/Version/Profile - #{api}/#{version}/" +
236
+ (compatibility_mode ? "Compatibility" : "Core"))
237
+
238
+ # Parse All Enumerations And Commands
239
+ all_enums, all_commands = {}, {}
240
+ print get_pretty(COLUMN_WIDTH, '-', "Crawling XML Structure")
241
+
242
+ puts "Parsing Enumerations..."
243
+ root.each_element('enums/enum') { |element|
244
+ all_enums[element.attributes['name']] = element.attributes['value']
245
+ }
246
+ puts "Parsed All Enumerations\n\n"
247
+
248
+ puts "Parsing Commands..."
249
+ root.each_element('commands/command') { |element|
250
+ comm_name = element.elements['proto/name'].text
251
+
252
+ command = element.elements['proto'].to_s.replace_tags(' ').squeeze(' ').strip
253
+ command << '('
254
+
255
+ element.each_element('param') { |innerElement|
256
+ command << innerElement.to_s.replace_tags(' ').squeeze(' ').strip + ', '
257
+ }
258
+ command[-2..-1] = '' if command.end_with? ', '
259
+
260
+ all_commands[comm_name] = command + ');'
261
+ }
262
+ puts "Parsed All Commands"
263
+
264
+ puts get_pretty(COLUMN_WIDTH, '-', "Finished Crawling XML Structure")
265
+
266
+ #-----------------------------Build Header File-------------------------------
267
+ used_enums, used_commands = {}, {}
268
+
269
+ header_template = "#ifndef #{gl_header.upcase.sub(/\./, '_')}
270
+ #define #{gl_header.upcase.sub(/\./, '_')}
271
+
272
+ #ifdef __cplusplus
273
+ extern \"C\" {
274
+ #endif
275
+
276
+ #if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__)
277
+ #ifndef WIN32_LEAN_AND_MEAN
278
+ #define WIN32_LEAN_AND_MEAN 1
279
+ #endif
280
+ #include <windows.h>
281
+ #undef near // Used As An Identifier In OpenGL
282
+ #undef far // Used As An Identifier In OpenGL
283
+ #endif
284
+
285
+ #ifndef APIENTRY
286
+ #define APIENTRY
287
+ #endif
288
+
289
+ COMMENTS_GO_HERE
290
+
291
+ TYPEDEFS_GO_HERE
292
+
293
+ DEFINES_GO_HERE
294
+
295
+ FUNCTIONS_GO_HERE
296
+
297
+ #ifdef __cplusplus
298
+ }
299
+ #endif
300
+
301
+ #endif"
302
+
303
+ print get_pretty(COLUMN_WIDTH, '-', "Generating File #{gl_header}")
304
+
305
+ puts "Appending Comments To #{gl_header}..."
306
+ buffer = "/*\n"
307
+ root.elements['comment'].text.each_line { |line|
308
+ buffer << '** ' << line
309
+ }
310
+ buffer << "\n*/"
311
+ header_template.sub!(/COMMENTS_GO_HERE/, buffer)
312
+ puts "Comments Have Been Appended\n\n"
313
+
314
+ # Pull Out All Possible Include Dependencies
315
+ puts "Appending Type Definitions To #{gl_header}..."
316
+ possible_includes = {}
317
+ root.each_element('types/type[@name]') { |element|
318
+ possible_includes[element.attributes['name']] = element.text
319
+ }
320
+
321
+ # Pull Out All Possible Types And Check If They Have Include Dependencies
322
+ actual_includes, buffer = {}, ''
323
+ attrib_filter = api == 'gl' ? 'not(@api)' : "@api='#{api}'"
324
+ root.each_element("types/type[#{attrib_filter} and not(@name)]") { |element|
325
+ content = element.to_s.replace_tags(' ').squeeze(' ').strip
326
+ content.gsub!(/ +;/, ';')
327
+ buffer << content << "\n"
328
+
329
+ requires = element.attributes['requires']
330
+ if (requires && !actual_includes[requires]) then
331
+ actual_includes[requires] = possible_includes[requires]
332
+ end
333
+ }
334
+
335
+ # Any Header Dependencies Found
336
+ actual_includes.each { |require,include|
337
+ if (require == 'khrplatform') then
338
+ print get_pretty(COLUMN_WIDTH, '-', "Pulling Data From Web")
339
+ puts "- #{EGL_URL + EGL_KHR_PATH}"
340
+
341
+ source = get_page_source(EGL_URL + EGL_KHR_PATH)
342
+ write_file("#{directory}/KHR", 'khrplatform.h', source)
343
+
344
+ print get_pretty(COLUMN_WIDTH, '-', "Finished Pulling Data")
345
+ end
346
+
347
+ # Ugh, Khronos Re-Used The Require Attribute For Non-Header Dependencies!!!
348
+ if (include) then
349
+ buffer.prepend(include + "\n")
350
+ end
351
+ }
352
+ header_template.sub!(/TYPEDEFS_GO_HERE/, buffer)
353
+ puts "Appended Type Definitions\n\n"
354
+
355
+ puts "Filtering Macros And Commands..."
356
+ root.each_element('feature') { |element|
357
+ if (element.attributes['api'] == api && element.attributes['number'].to_f <= version) then
358
+ element.each_element('require/enum') { |inner_element|
359
+ enum_name = inner_element.attributes['name']
360
+ used_enums[enum_name] = all_enums[enum_name]
361
+ }
362
+
363
+ element.each_element('require/command') { |inner_element|
364
+ comm_name = inner_element.attributes['name']
365
+ used_commands[comm_name] = all_commands[comm_name]
366
+ }
367
+ if (!compatibility_mode) then
368
+ element.each_element('remove/enum') { |inner_element|
369
+ enum_name = inner_element.attributes['name']
370
+ used_enums.delete(enum_name)
371
+ }
372
+ element.each_element('remove/command') { |inner_element|
373
+ comm_name = inner_element.attributes['name']
374
+ used_commands.delete(comm_name)
375
+ }
376
+ end
377
+ end
378
+ }
379
+ puts "Filtered Macros And Commands\n\n"
380
+
381
+ puts "Appending Macro To #{gl_header}..."
382
+ used_enums, buffer = used_enums.to_a.sort, ''
383
+ used_enums.each { |name,value|
384
+ buffer << '#define ' << name << ' ' << value << "\n"
385
+ }
386
+ header_template.sub!(/DEFINES_GO_HERE/, buffer)
387
+ puts "Appended Macros\n\n"
388
+
389
+ puts "Appending Commands To #{gl_header}..."
390
+ used_commands, buffer = used_commands.to_a.sort, ''
391
+ used_commands.each { |name,signature|
392
+ buffer << signature << "\n"
393
+ }
394
+ header_template.sub!(/FUNCTIONS_GO_HERE/, buffer)
395
+ puts "Appended Commands"
396
+
397
+ write_file(directory, gl_header, header_template)
398
+ puts get_pretty(COLUMN_WIDTH, '-', "Finished Generating #{gl_header}")
399
+
400
+ #-----------------------------Build Source File-------------------------------
401
+ print get_pretty(COLUMN_WIDTH, '-', "Generating File #{gl_source}")
402
+
403
+ buffer = "#include \"#{gl_header}\"\n#include \"SDL.h\"\n\n"
404
+
405
+ puts "Appending Command Definitions To #{gl_source}..."
406
+ used_commands.each { |name,signature|
407
+ buffer << signature[0..-2] + " {\n"
408
+
409
+ # Break Function Down
410
+ func_return_type = signature[/\A(.*?)gl/, 1].strip
411
+ typedef_name = 'GL_' + name[2..-1] + '_Func'
412
+ func_ptr_name = name + '_ptr'
413
+ param_type_list = []
414
+ unless (signature =~ /\(\);/) then # Has At Least One Parameter
415
+ param_type_list = signature[/\((.*?);/, 1].split(/ *\w+[,\)] */)
416
+ end
417
+ param_name_list = signature.scan(/(\w+)[,\)]/).flatten
418
+
419
+ # Append Typedef
420
+ buffer << ' ' * SOURCE_INDENTS + 'typedef ' + func_return_type +
421
+ " (APIENTRY * #{typedef_name})("
422
+ param_type_list.each { |param_type|
423
+ buffer << param_type + ', '
424
+ }
425
+ if (buffer[-2..-1] == ', ') then
426
+ buffer[-2..-1] = ');'
427
+ else
428
+ buffer << ');'
429
+ end
430
+ buffer << "\n"
431
+
432
+ # Append Pointer Initialization
433
+ buffer << ' ' * SOURCE_INDENTS + 'static ' + typedef_name + ' ' +
434
+ func_ptr_name + " = NULL;\n\n"
435
+
436
+ buffer << ' ' * SOURCE_INDENTS + "if (#{func_ptr_name} == NULL) {\n" +
437
+ ' ' * (SOURCE_INDENTS * 2) + func_ptr_name +
438
+ " = (#{typedef_name})SDL_GL_GetProcAddress(__func__);\n" +
439
+ ' ' * SOURCE_INDENTS + "}\n\n"
440
+
441
+ # Append Function Pointer Call
442
+ buffer << ' ' * SOURCE_INDENTS + func_ptr_name + '('
443
+ param_name_list.each { |param_name|
444
+ buffer << param_name + ', '
445
+ }
446
+ if (buffer[-2..-1] == ', ') then
447
+ buffer[-2..-1] = ');'
448
+ else
449
+ buffer << ');'
450
+ end
451
+
452
+ buffer << "\n}\n\n"
453
+ }
454
+ write_file(directory, gl_source, buffer)
455
+ puts "Finished Appending Command Definitions"
456
+
457
+ print get_pretty(COLUMN_WIDTH, '-', "Finished Generating #{gl_source}")
458
+
459
459
  /---------------------------------END SCRIPT----------------------------------/