rubygl 0.1.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +13 -5
- data/.travis/push-rdoc-to-gh-pages.sh +22 -0
- data/.travis.yml +18 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +12 -0
- data/LICENSE +21 -21
- data/README.md +40 -0
- data/Rakefile +98 -83
- data/bin/ffi_code_gen.rb +166 -166
- data/bin/gl_code_gen.rb +458 -458
- data/examples/faceted_example.rb +71 -64
- data/examples/instanced_example.rb +135 -127
- data/examples/phong_example.rb +80 -71
- data/ext/windows/RubyGL.so +0 -0
- data/lib/rubygl/event.rb +64 -0
- data/lib/{RubyGL → rubygl}/geometry.rb +216 -211
- data/lib/{RubyGL → rubygl}/math.rb +300 -300
- data/lib/{RubyGL → rubygl}/memory.rb +125 -121
- data/lib/rubygl/native/all_enums.rb +641 -0
- data/lib/{RubyGL/Native → rubygl/native}/glcontext.rb +23 -47
- data/lib/{RubyGL/Native → rubygl/native}/include/GLContext.h +36 -36
- data/lib/{RubyGL/Native → rubygl/native}/include/Input.h +15 -15
- data/lib/{RubyGL/Native → rubygl/native}/include/Window.h +27 -27
- data/lib/rubygl/native/input.rb +247 -0
- data/lib/{RubyGL/Native → rubygl/native}/opengl.rb +2808 -2032
- data/lib/{RubyGL/Native → rubygl/native}/src/GLContext.c +72 -72
- data/lib/{RubyGL/Native → rubygl/native}/src/Input.c +25 -25
- data/lib/{RubyGL/Native → rubygl/native}/src/Window.c +57 -57
- data/lib/{RubyGL/Native → rubygl/native}/window.rb +24 -24
- data/lib/{RubyGL → rubygl}/setup.rb +50 -50
- data/lib/{RubyGL → rubygl}/shader.rb +203 -203
- data/lib/{RubyGL → rubygl}/util.rb +77 -77
- data/lib/rubygl.rb +49 -48
- data/{RubyGL.gemspec → rubygl.gemspec} +20 -23
- data/test/test_util.rb +19 -0
- metadata +36 -33
- data/lib/RubyGL/Native/input.rb +0 -13
- 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 = '
|
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[
|
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----------------------------------/
|