rubygl 0.1.0 → 0.2.1

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