rubygl 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/Rakefile +83 -0
- data/RubyGL.gemspec +23 -0
- data/bin/ffi_code_gen.rb +167 -0
- data/bin/gl_code_gen.rb +459 -0
- data/examples/faceted_example.rb +65 -0
- data/examples/instanced_example.rb +128 -0
- data/examples/phong_example.rb +72 -0
- data/ext/windows/RubyGL.so +0 -0
- data/ext/windows/SDL2.dll +0 -0
- data/lib/RubyGL/Native/glcontext.rb +48 -0
- data/lib/RubyGL/Native/include/GLContext.h +37 -0
- data/lib/RubyGL/Native/include/Input.h +16 -0
- data/lib/RubyGL/Native/include/Window.h +28 -0
- data/lib/RubyGL/Native/input.rb +13 -0
- data/lib/RubyGL/Native/opengl.rb +2032 -0
- data/lib/RubyGL/Native/src/GLContext.c +73 -0
- data/lib/RubyGL/Native/src/Input.c +26 -0
- data/lib/RubyGL/Native/src/Window.c +58 -0
- data/lib/RubyGL/Native/window.rb +25 -0
- data/lib/RubyGL/callback.rb +10 -0
- data/lib/RubyGL/geometry.rb +212 -0
- data/lib/RubyGL/math.rb +301 -0
- data/lib/RubyGL/memory.rb +122 -0
- data/lib/RubyGL/setup.rb +51 -0
- data/lib/RubyGL/shader.rb +203 -0
- data/lib/RubyGL/util.rb +77 -0
- data/lib/rubygl.rb +48 -0
- metadata +92 -0
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
|
data/bin/ffi_code_gen.rb
ADDED
@@ -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----------------------------------/
|
data/bin/gl_code_gen.rb
ADDED
@@ -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----------------------------------/
|