rubygl 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d0db02206c2e8a176817a841bc3ced8cfe60b4af
4
+ data.tar.gz: cc0aee2bc3df4f86b43b151e666b967ad9153b03
5
+ SHA512:
6
+ metadata.gz: c37b401ada50218e49983b17a3e7d8f9486fe1a05f1b5bd99ac1ec67fd83cea4a6254c9febf8fa903a21b1f9364bed9bb8c1f8399a862e7eb7e71c3f220d5911
7
+ data.tar.gz: 7148dd01e893ed00fcfccb884b34d05e24a946ee01d668b637b3defc22a77b0f378784b225512394e6728a2f8210a266e2b247e3c36b3b05204623c3d33d7a86
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Andrew
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,83 @@
1
+ require 'rake/task'
2
+ require 'rake/testtask'
3
+ require 'tmpdir'
4
+
5
+ SDL_INCLUDE = 'C:\Users\GG\Desktop\SDL2-2.0.3\i686-w64-mingw32\include\SDL2'
6
+ SDL_LIB = 'C:\Users\GG\Desktop\SDL2-2.0.3\i686-w64-mingw32\lib'
7
+ SDL_BIN = 'C:\Users\GG\Desktop\SDL2-2.0.3\i686-w64-mingw32\bin'
8
+
9
+ MINGW_LIB = ''
10
+
11
+ PROJ_INCLUDE = 'lib/RubyGL/Native/include'
12
+ PROJ_SRC = 'lib/RubyGL/Native/src/'
13
+
14
+ # OS Detection
15
+ def os
16
+ @os ||= (
17
+ host_os = RbConfig::CONFIG['host_os']
18
+
19
+ case host_os
20
+ when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
21
+ :windows
22
+ when /darwin|mac os/
23
+ :macosx
24
+ when /linux/
25
+ :linux
26
+ when /solaris|bsd/
27
+ :unix
28
+ else
29
+ raise Error::WebDriverError, "unknown os: #{host_os.inspect}"
30
+ end
31
+ )
32
+ end
33
+
34
+ /---------------------------------RAKE JOBS-----------------------------------/
35
+
36
+ task :default do
37
+ files = ['Audio', 'Window', 'GLContext', 'Input', 'OpenGL']
38
+
39
+ # Build Object Files
40
+ obj = "gcc -I#{SDL_INCLUDE} -I#{PROJ_INCLUDE} -c -fPIC REPLACE.c -o REPLACE.o"
41
+ files.each { |file|
42
+ sh (obj.gsub(/REPLACE/, PROJ_SRC + file))
43
+ file = file.prepend PROJ_SRC
44
+ }
45
+
46
+ # Build Shared Object File
47
+ bin = "gcc -L#{SDL_LIB} -L#{MINGW_LIB} #{files.join('.o ') << '.o'} \
48
+ -lmingw32 -lSDL2main -lSDL2 -lopengl32 -shared -o \
49
+ ext/#{os.to_s}/RubyGL.so"
50
+ sh bin
51
+ end
52
+
53
+ task :clean do
54
+ # Clean Out Object Files
55
+ FileUtils.rm Dir.glob(PROJ_SRC + '/*.o')
56
+ end
57
+
58
+ task :bindings do
59
+ shared_lib = "RubyGL.so"
60
+
61
+ Dir.mktmpdir { |dir|
62
+ # Generates Header And Source C Files
63
+ sh "ruby bin/gl_code_gen.rb gl 4.5 OpenGL #{dir}"
64
+
65
+ # Run Preprocessor On Source File
66
+ sh "gcc -E -I#{SDL_INCLUDE} -o #{dir}/OpenGL.o #{dir}/OpenGL.c"
67
+
68
+ # Pipe Preprocessor(ed) File And Header File To Generate Bindings
69
+ sh "ruby bin/ffi_code_gen.rb #{dir}/OpenGL.o #{dir}/OpenGL.h \
70
+ #{dir}/opengl.rb #{shared_lib}"
71
+
72
+ # Move Files To Correct Folders
73
+ mv "#{dir}/OpenGL.h", PROJ_INCLUDE
74
+ mv "#{dir}/OpenGL.c", PROJ_SRC
75
+ mv "#{dir}/opengl.rb", "lib/RubyGL/Native/"
76
+ }
77
+ end
78
+
79
+ Rake::TestTask.new do |t|
80
+ t.libs << "tests"
81
+ t.test_files = FileList['tests/test*.rb']
82
+ t.verbose = true
83
+ end
data/RubyGL.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'rubygl'
5
+ spec.version = '0.1.0'
6
+ spec.authors = ["Andrew Miller"]
7
+ spec.email = ["millera9@seattleu.edu"]
8
+ spec.summary = %q{A Complete Solution For Graphics Programming In Ruby}
9
+ spec.description = %q{This library provides you with all of the essentials for
10
+ doing graphics programming in Ruby including windowing, context management,
11
+ 1:1 mapping of OpenGL calls, and input handling. The backend of this library
12
+ consists of FFI functions with 1:1 mapping to the C code that makes all of this
13
+ possible. The frontend of this library provides abstractions for interacting with
14
+ the backend functionality so that you do not have to deal with the manual memory
15
+ management from within Ruby that the backend requires.}
16
+ spec.homepage = "https://github.com/GGist/RubyGL"
17
+ spec.license = "MIT"
18
+
19
+ spec.files = `git ls-files`.split($/)
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency 'ffi'
23
+ end
@@ -0,0 +1,167 @@
1
+ require 'fileutils'
2
+
3
+ def resolve_typedef(type, type_defs)
4
+ while (type_defs[type]) do
5
+ type = type_defs[type]
6
+ end
7
+
8
+ return type
9
+ end
10
+
11
+ def get_type(type, hash)
12
+ hash[type]
13
+ end
14
+
15
+ def match_type(type, regex_hash)
16
+ regex_hash.select { |key,val|
17
+ type =~ key
18
+ }.values[0]
19
+ end
20
+
21
+ def write_file(name, contents, mode = 'w+')
22
+ File.open(name, mode) { |file|
23
+ file << contents
24
+ }
25
+ end
26
+
27
+ /--------------------------------START SCRIPT---------------------------------/
28
+ # ARGV: [Object File] [Header File] [Ruby Source Name] [Library Name]
29
+
30
+ SOURCE_INDENTS = 4
31
+ FFI_TYPES = { 'char' => 'char', 'signed char' => 'char',
32
+ 'unsigned char' => 'uchar', 'int8_t' => 'int8', 'uint8_t' => 'uint8',
33
+ 'short' => 'short', 'unsigned short' => 'ushort', 'int16_t' => 'int16',
34
+ 'uint16_t' => 'uint16', 'int' => 'int', 'unsigned int' => 'uint',
35
+ 'int32_t' => 'int32', 'uint32_t' => 'uint32', 'long int' => 'long',
36
+ 'unsigned long int' => 'ulong', 'int64_t' => 'int64', 'uint64_t' => 'uint64',
37
+ 'long long int' => 'long_long', 'unsigned long long int' => 'ulong_long',
38
+ 'float' => 'float', 'double' => 'double' }
39
+ FFI_PTR_TYPES = { /\Aconst +(?:unsigned +)?char *\*\Z/ => 'string', /\*/ => 'pointer' }
40
+
41
+ # Open Pre-Processor(ed) Source File
42
+ source_code = ''
43
+ File.open(ARGV[0]) { |file|
44
+ source_code = file.read
45
+ }
46
+
47
+ # Associate Type Definitions With Their Actual Types
48
+ typedef_hash = {}
49
+ source_code.scan(/^typedef (.*?;)$/).flatten.each { |segment|
50
+ segment.gsub!(/\*/, '* ')
51
+
52
+ if (segment =~ /\(.*?\*.*?\)/) then # Function Pointer
53
+ key = segment[/\* *(.*?) *\)/, 1]
54
+
55
+ typedef_hash[key] = segment[0..-2] # Remove Semi-Colon
56
+ else # Other Type
57
+ key = segment[/([^ ]+);/, 1]
58
+
59
+ typedef_hash[key] = segment[/(.*?) +[^ ]+;/, 1]
60
+ end
61
+ }
62
+
63
+ # Open Header File
64
+ source_code = ''
65
+ File.open(ARGV[1]) { |file|
66
+ source_code = file.read
67
+ }
68
+
69
+ # Associate Macros With Their Literal Types (OpenGL Enumerations)
70
+ macro_tuples = []
71
+ source_code.scan(/^#define GL_.+?$/i).each { |macro|
72
+ token = macro[/^.+ +(.+) +.+$/, 1]
73
+ value = macro[/^.+ +.+ +(.+)$/, 1]
74
+ macro_tuples.push([token, value])
75
+ }
76
+
77
+ # Pull Out OpenGL Function Names From Header
78
+ gl_functions = source_code.scan(/^.+?gl.+?\(.*?\);$/)
79
+
80
+ ffi_template = "require 'ffi'
81
+
82
+ module RubyGL::Native
83
+ FUNCTIONS_GO_HERE
84
+
85
+ CONSTANTS_GO_HERE
86
+ end
87
+ "
88
+
89
+ # Append Functions (Param Type -> Typedefs -> Ruby Type)
90
+ buffer = ''
91
+ gl_functions.each { |signature|
92
+ buffer << ' ' * SOURCE_INDENTS + 'attach_function :' +
93
+ signature[/(gl.*?)\(.*?\);/, 1] + ', ['
94
+
95
+ # Get Type List
96
+ param_type_list = signature[/\((.*?)\);/, 1].split(', ').map { |full_param|
97
+ full_param[/(.+) [[:alnum:]]+/, 1]
98
+ }
99
+
100
+ # Type Definition -> Primitive (Actual) Type
101
+ param_type_list.map! { |param_type|
102
+ # Parameter Type Could Use Multiple Type Definitions
103
+
104
+ simple_param_types = param_type.gsub(/(?:\*|const)/, ' ').split(' ')
105
+ # Translate Each Type And Put Back Into param_type
106
+ simple_param_types.each { |simple_param_type|
107
+ primitive_type = resolve_typedef(simple_param_type, typedef_hash)
108
+
109
+ param_type.sub!(/#{simple_param_type}/, primitive_type)
110
+ }
111
+
112
+ param_type
113
+ }
114
+
115
+ # Primitive Type -> Ruby FFI Type
116
+ param_type_list.each { |param_type|
117
+ mutable_type = param_type.gsub(/const/, '').strip
118
+
119
+ if (get_type(mutable_type, FFI_TYPES)) then
120
+ param_type = get_type(mutable_type, FFI_TYPES)
121
+ else
122
+ param_type = match_type(param_type, FFI_PTR_TYPES)
123
+ end
124
+
125
+ # Write Out FFI Type
126
+ buffer << ':' + param_type + ', '
127
+ }
128
+ if (buffer[-1] == '[') then # No Parameters
129
+ buffer << '],'
130
+ else # At Least One Parameter
131
+ buffer[-2..-1] = '],'
132
+ end
133
+
134
+ # Get Return Type
135
+ # We Do Not Care About const If We Can Match Our Return Type With A
136
+ # Primitive Type. Otherwise, We Might Be Able To Match Against A
137
+ # const char* Or const unsigned char* In Which Case We Prefer To Use A
138
+ # string Type.
139
+ return_type = signature[/(.*?) gl[^ ]+\(/, 1]
140
+ mutable_return_type = return_type.gsub(/ *const */, '')
141
+
142
+ mutable_return_type.gsub(/\*/, ' ').split(' ').each { |type|
143
+ resolved_type = resolve_typedef(type, typedef_hash)
144
+
145
+ mutable_return_type.sub!(/#{type}/, resolved_type)
146
+ return_type.sub!(/#{type}/, resolved_type)
147
+ }
148
+
149
+ if (get_type(mutable_return_type, FFI_TYPES)) then
150
+ return_type = get_type(mutable_return_type, FFI_TYPES)
151
+ elsif (match_type(return_type, FFI_PTR_TYPES))
152
+ return_type = match_type(return_type, FFI_PTR_TYPES)
153
+ end
154
+
155
+ buffer << ' :' + return_type + "\n"
156
+ }
157
+ ffi_template.sub!(/FUNCTIONS_GO_HERE/, buffer)
158
+
159
+ # Append OpenGL Enumerations (Macros)
160
+ buffer = ''
161
+ macro_tuples.each { |name,val|
162
+ buffer << ' ' * SOURCE_INDENTS + "#{name} = #{val}\n"
163
+ }
164
+ ffi_template.sub!(/CONSTANTS_GO_HERE/, buffer)
165
+
166
+ write_file(ARGV[2], ffi_template)
167
+ /---------------------------------END SCRIPT----------------------------------/
@@ -0,0 +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
+
459
+ /---------------------------------END SCRIPT----------------------------------/