rage 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/COPYING +8 -0
- data/LICENSE +674 -0
- data/README.rdoc +48 -0
- data/Rakefile +46 -0
- data/bin/mesh-viewer.rb +91 -0
- data/lib/rage.rb +30 -0
- data/lib/rage/camera.rb +59 -0
- data/lib/rage/color.rb +18 -0
- data/lib/rage/common.rb +52 -0
- data/lib/rage/dsl.rb +36 -0
- data/lib/rage/game.rb +64 -0
- data/lib/rage/input.rb +91 -0
- data/lib/rage/loader.rb +33 -0
- data/lib/rage/location.rb +141 -0
- data/lib/rage/mesh.rb +147 -0
- data/lib/rage/resource.rb +49 -0
- data/lib/rage/viewport.rb +50 -0
- data/lib/rage/window.rb +58 -0
- data/spec/location_spec.rb +112 -0
- data/spec/mesh_spec.rb +84 -0
- data/spec/spec_helper.rb +13 -0
- metadata +75 -0
data/lib/rage/input.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# Input handler constructs and methods
|
2
|
+
#
|
3
|
+
# Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
|
4
|
+
# Licensed under the GPLv3+ http://www.gnu.org/licenses/gpl.txt
|
5
|
+
|
6
|
+
module RAGE
|
7
|
+
|
8
|
+
# FIXME turn into callback interface
|
9
|
+
|
10
|
+
# Handles and redirects user events to registered callbacks.
|
11
|
+
class InputHandler
|
12
|
+
|
13
|
+
# Invoked during main game execution cycle to handle any SDL input events
|
14
|
+
def self.handle(event)
|
15
|
+
|
16
|
+
@@left_button_pressed = false unless defined? @@left_button_pressed
|
17
|
+
@@right_button_pressed = false unless defined? @@right_button_pressed
|
18
|
+
|
19
|
+
case event
|
20
|
+
when SDL::Event2::Quit
|
21
|
+
exit
|
22
|
+
|
23
|
+
when SDL::Event2::KeyDown
|
24
|
+
case event.sym
|
25
|
+
when SDL::Key::Q, SDL::Key::ESCAPE
|
26
|
+
exit
|
27
|
+
when SDL::Key::W
|
28
|
+
Game.wireframe_mode= !Game.wireframe_mode
|
29
|
+
end
|
30
|
+
|
31
|
+
when SDL::Event::MouseMotion
|
32
|
+
# get state of mouse buttons
|
33
|
+
left_pressed = ((event.state & SDL::Mouse::BUTTON_LMASK) != 0)
|
34
|
+
right_pressed = ((event.state & SDL::Mouse::BUTTON_RMASK) != 0)
|
35
|
+
|
36
|
+
# if left button is down, rotate camera
|
37
|
+
if left_pressed
|
38
|
+
xpos, ypos, zpos = to_3d_coordinates(event.x, event.y)
|
39
|
+
oxpos, oypos, ozpos = to_3d_coordinates(event.x-event.xrel, event.y-event.yrel)
|
40
|
+
|
41
|
+
if xpos > oxpos
|
42
|
+
Game.current_viewport.camera.xrotate += 1
|
43
|
+
elsif xpos < oxpos
|
44
|
+
Game.current_viewport.camera.xrotate -= 1
|
45
|
+
end
|
46
|
+
|
47
|
+
if ypos > oypos
|
48
|
+
Game.current_viewport.camera.yrotate += 1
|
49
|
+
elsif ypos < oypos
|
50
|
+
Game.current_viewport.camera.yrotate -= 1
|
51
|
+
end
|
52
|
+
|
53
|
+
if zpos > ozpos
|
54
|
+
Game.current_viewport.camera.zrotate += 1
|
55
|
+
elsif zpos < ozpos
|
56
|
+
Game.current_viewport.camera.zrotate -= 1
|
57
|
+
end
|
58
|
+
|
59
|
+
# if right is down pan camera
|
60
|
+
elsif right_pressed
|
61
|
+
if event.xrel > 0
|
62
|
+
Game.current_viewport.camera.pos[0] -= 1
|
63
|
+
elsif event.xrel < 0
|
64
|
+
Game.current_viewport.camera.pos[0] += 1
|
65
|
+
end
|
66
|
+
|
67
|
+
if event.yrel > 0
|
68
|
+
Game.current_viewport.camera.pos[1] += 1
|
69
|
+
elsif event.yrel < 0
|
70
|
+
Game.current_viewport.camera.pos[1] -= 1
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
when SDL::Event::MouseButtonDown
|
76
|
+
# zoom out the camera
|
77
|
+
if event.button == 5 # wheeldown
|
78
|
+
Game.current_viewport.camera.pos[2] += 1
|
79
|
+
end
|
80
|
+
|
81
|
+
when SDL::Event::MouseButtonUp
|
82
|
+
# zoom in the camera
|
83
|
+
if event.button == 4 # wheel up
|
84
|
+
Game.current_viewport.camera.pos[2] -= 1
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
data/lib/rage/loader.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# RAGE resource loader
|
2
|
+
#
|
3
|
+
# Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
|
4
|
+
# Licensed under the GPLv3+ http://www.gnu.org/licenses/gpl.txt
|
5
|
+
|
6
|
+
require 'uri' # use uri to parse sources
|
7
|
+
require 'net/http' # get http:// based resources
|
8
|
+
|
9
|
+
module RAGE
|
10
|
+
|
11
|
+
# Loads resources from uris
|
12
|
+
class Loader
|
13
|
+
|
14
|
+
# Loads and return text resource from specified source uri
|
15
|
+
def self.load(source_uri)
|
16
|
+
Logger.info "loading resource from uri #{source_uri}"
|
17
|
+
data = nil
|
18
|
+
uri = URI.parse(source_uri)
|
19
|
+
if uri.scheme == "file"
|
20
|
+
data = File.read_all uri.path
|
21
|
+
elsif uri.scheme == "http"
|
22
|
+
data = Net::HTTP.get_response(uri.host, uri.path).body
|
23
|
+
# elsif FIXME support other uri types
|
24
|
+
end
|
25
|
+
|
26
|
+
return data
|
27
|
+
|
28
|
+
rescue URI::InvalidURIError
|
29
|
+
raise Exceptions::InvalidResourceUri
|
30
|
+
end
|
31
|
+
|
32
|
+
end # class loader
|
33
|
+
end # module RXSD
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# Location related constructs
|
2
|
+
#
|
3
|
+
# Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
|
4
|
+
# Licensed under the GPLv3+ http://www.gnu.org/licenses/gpl.txt
|
5
|
+
|
6
|
+
module RAGE
|
7
|
+
|
8
|
+
# Location w/ coordinates in the 3D system.
|
9
|
+
# Various entities may be associated with Location to be drawn.
|
10
|
+
class Location
|
11
|
+
# x, y, z coordinates of the location,
|
12
|
+
attr_reader :x, :y, :z
|
13
|
+
|
14
|
+
# Mesh which to draw at location
|
15
|
+
attr_reader :mesh
|
16
|
+
|
17
|
+
# TODO lights, other entities to draw at locations
|
18
|
+
|
19
|
+
# Initialize Location with argument hash, which may include
|
20
|
+
# * :x location coordinate
|
21
|
+
# * :y location coordinate
|
22
|
+
# * :z location coordinate
|
23
|
+
# * :mesh resource to draw at location
|
24
|
+
def initialize(args = {})
|
25
|
+
@x = args.has_key?(:x) ? args[:x] : 0
|
26
|
+
@y = args.has_key?(:y) ? args[:y] : 0
|
27
|
+
@z = args.has_key?(:z) ? args[:z] : 0
|
28
|
+
@mesh = args[:mesh]
|
29
|
+
end
|
30
|
+
|
31
|
+
# Update location w/ args, which may include
|
32
|
+
# * :x location coordinate
|
33
|
+
# * :y location coordinate
|
34
|
+
# * :z location coordinate
|
35
|
+
def update(args = {})
|
36
|
+
@x = args[:x] if args.has_key?(:x)
|
37
|
+
@y = args[:y] if args.has_key?(:y)
|
38
|
+
@z = args[:z] if args.has_key?(:z)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Return location boundaries. This is generated by determining maxima/minima
|
42
|
+
# local coordinates of entity associated w/ location and adding those to
|
43
|
+
# the location's coordinates. Return value is an array of six values as follows
|
44
|
+
# max_x, max_y, max_z, min_x, min_y, min_z
|
45
|
+
def boundaries
|
46
|
+
max_x = max_y = max_z = min_x = min_y = min_z = 0
|
47
|
+
unless mesh.nil?
|
48
|
+
max_x, max_y, max_z, min_x, min_y, min_z = mesh.boundaries
|
49
|
+
end
|
50
|
+
return x + max_x, y + max_y, z + max_z, x + min_x, y + min_y, z + min_z
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
# Invoked during draw cycle to draw location & entity associated w/ it
|
55
|
+
def draw
|
56
|
+
Gl.glPushMatrix();
|
57
|
+
|
58
|
+
# translate coordinate system to location's coordinates
|
59
|
+
Gl.glTranslatef(x, y, z)
|
60
|
+
|
61
|
+
# draw mesh
|
62
|
+
mesh.draw
|
63
|
+
|
64
|
+
Gl.glPopMatrix();
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Singleton class managing all locations
|
69
|
+
class LocationsManager
|
70
|
+
include Singleton
|
71
|
+
|
72
|
+
# Array of locations being managed
|
73
|
+
attr_accessor :locations
|
74
|
+
|
75
|
+
def initialize
|
76
|
+
@locations = []
|
77
|
+
end
|
78
|
+
|
79
|
+
# Add location to be managed
|
80
|
+
def add(location)
|
81
|
+
@locations.push location unless @locations.include? location
|
82
|
+
end
|
83
|
+
|
84
|
+
# Empty the array of managed locations
|
85
|
+
def clear
|
86
|
+
@locations.clear
|
87
|
+
end
|
88
|
+
|
89
|
+
# Return the maxima and minima of the x,y,z coordinate values for all the
|
90
|
+
# locations being managed. Return value is an array of six values as follows
|
91
|
+
# max_x, max_y, max_z, min_x, min_y, min_z
|
92
|
+
# This method essentially gives you the "box" in which all locations being managed are in
|
93
|
+
def boundaries
|
94
|
+
max_x = max_y = max_z = min_x = min_y = min_z = 0
|
95
|
+
@locations.each { |loc|
|
96
|
+
locb = loc.boundaries
|
97
|
+
max_x = locb[0] if locb[0] > max_x
|
98
|
+
max_y = locb[1] if locb[1] > max_y
|
99
|
+
max_z = locb[2] if locb[2] > max_z
|
100
|
+
min_x = locb[3] if locb[3] < min_x
|
101
|
+
min_y = locb[4] if locb[4] < min_y
|
102
|
+
min_z = locb[5] if locb[5] < min_z
|
103
|
+
}
|
104
|
+
return max_x, max_y, max_z, min_x, min_y, min_z
|
105
|
+
end
|
106
|
+
|
107
|
+
# Return the center coordinate of location system.
|
108
|
+
# Must specify the metric which to generate center, which may be either
|
109
|
+
# * :avg - compute and return bounaries averages
|
110
|
+
# * :mean - compute and return mean of all coords
|
111
|
+
def center(metric = :mean)
|
112
|
+
if metric == :mean
|
113
|
+
ls = @locations.size
|
114
|
+
mx = my = mz = 0
|
115
|
+
@locations.each { |lm|
|
116
|
+
mx += lm.x / ls
|
117
|
+
my += lm.y / ls
|
118
|
+
mz += lm.z / ls
|
119
|
+
}
|
120
|
+
return mx,my,mz
|
121
|
+
|
122
|
+
elsif metric == :avg
|
123
|
+
b = self.boundaries
|
124
|
+
return (b[0] + b[3]) / 2, (b[1] + b[4]) / 2, (b[2] + b[5]) / 2
|
125
|
+
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
# Helper method to get 3d coords cooresponding to 2d ones via OPENGL unprojection
|
132
|
+
def to_3d_coordinates(x2, y2)
|
133
|
+
model_view = Gl.glGetDoublev Gl::GL_MODELVIEW_MATRIX
|
134
|
+
projection = Gl.glGetDoublev Gl::GL_PROJECTION_MATRIX
|
135
|
+
viewport = Gl.glGetIntegerv Gl::GL_VIEWPORT
|
136
|
+
depth = Gl.glReadPixels x2, y2, 1, 1, Gl::GL_DEPTH_COMPONENT, Gl::GL_FLOAT
|
137
|
+
x3, y3, z3 = Glu.gluUnProject x2, y2, depth[0], model_view, projection, viewport
|
138
|
+
return [x3, y3, z3]
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
data/lib/rage/mesh.rb
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
# Mesh Resource
|
2
|
+
#
|
3
|
+
# Currently manages X3D meshes
|
4
|
+
#
|
5
|
+
# Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
|
6
|
+
# Licensed under the GPLv3+ http://www.gnu.org/licenses/gpl.txt
|
7
|
+
|
8
|
+
# use rxsd to load x3d xsd schema, declare xsd classes, instantiate scenes
|
9
|
+
require 'rxsd'
|
10
|
+
|
11
|
+
module RAGE
|
12
|
+
|
13
|
+
# 3D mesh to be displayed on the screen.
|
14
|
+
# Contains geometry and visual properties.
|
15
|
+
# Currently implements certain parts of the X3D graphics schema.
|
16
|
+
# http://en.wikipedia.org/wiki/X3D
|
17
|
+
class Mesh
|
18
|
+
# Optional transformation params, set these to
|
19
|
+
# transformations to be factored in if needed
|
20
|
+
attr_accessor :op_translation, :op_scale, :op_rotation
|
21
|
+
|
22
|
+
# X3D schema for shared access
|
23
|
+
class_attr :x3d_schema
|
24
|
+
|
25
|
+
# X3D schema classes for shared access
|
26
|
+
class_attr :x3d_schema_classes
|
27
|
+
|
28
|
+
|
29
|
+
# Create new mesh from raw X3D mesh
|
30
|
+
def initialize(data)
|
31
|
+
# set default attributes
|
32
|
+
@op_translation = [0,0,0]
|
33
|
+
@op_scale = [1,1,1]
|
34
|
+
|
35
|
+
# load the x3d xsd schema and classes if not already done so
|
36
|
+
unless defined? @@x3d_schema
|
37
|
+
@@x3d_schema = RXSD::Parser.parse_xsd :uri => 'http://www.web3d.org/specifications/x3d-3.0.xsd' # TODO allow location which this is pulled from to be configured
|
38
|
+
@@x3d_schema_classes = @@x3d_schema.to :ruby_classes
|
39
|
+
end
|
40
|
+
|
41
|
+
# load the scene from the x3d schema instance
|
42
|
+
objs = RXSD::Parser.parse_xml(:raw => data).to(:ruby_objects, :schema => @@x3d_schema)
|
43
|
+
@scene = objs[0].scene
|
44
|
+
|
45
|
+
# FIXME get all transformations (and groups of) under scene
|
46
|
+
@tf = @scene.collision.transform
|
47
|
+
@translation = @tf.translation.to_a :type => XSDFloat
|
48
|
+
@scale = @tf.scale.to_a :type => XSDFloat
|
49
|
+
#diffuse = tf.shape.appearance.material.diffuse_color.split.collect { |c| c.to_f }
|
50
|
+
#specular = tf.shape.appearance.material.specular_color.split.collect { |c| c.to_f }
|
51
|
+
#emissive = tf.shape.appearance.material.emissive_color.split.collect { |c| c.to_f }
|
52
|
+
@coord_index = @tf.shape.indexed_face_set.coord_index.
|
53
|
+
to_a(:type => String, :delim => ",").
|
54
|
+
collect { |coords| a = coords.to_a(:type => XSDInteger); a[0...a.size-1]}.flatten # cut off the last -1
|
55
|
+
@coord_point = @tf.shape.indexed_face_set.coordinate.point.
|
56
|
+
to_a(:type => String, :delim => ",").
|
57
|
+
collect { |coords| coords.to_a :type => XSDFloat }.flatten
|
58
|
+
end
|
59
|
+
|
60
|
+
# Create location w/ specified args, associated mesh w/ it and add it to the LocationsManager
|
61
|
+
def show_at(args = {})
|
62
|
+
# XXX not a huge fan of storing location interally,
|
63
|
+
# but this is best way to do this for now
|
64
|
+
if ! defined? @location
|
65
|
+
args[:mesh] = self
|
66
|
+
@location = Location.new(args)
|
67
|
+
LocationsManager.instance.add @location
|
68
|
+
else
|
69
|
+
@location.update args
|
70
|
+
end
|
71
|
+
return @location
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
# Return center point of mesh from mesh translation provided in mesh
|
76
|
+
# and op_translation
|
77
|
+
def center
|
78
|
+
return @op_translation[0] + @translation[0],
|
79
|
+
@op_translation[1] + @translation[1],
|
80
|
+
@op_translation[2] + @translation[2]
|
81
|
+
end
|
82
|
+
|
83
|
+
# Return factor which mesh should scale to
|
84
|
+
def scale
|
85
|
+
return @scale[0] * @op_scale[0],
|
86
|
+
@scale[1] * @op_scale[1],
|
87
|
+
@scale[2] * @op_scale[2]
|
88
|
+
end
|
89
|
+
|
90
|
+
# Return mesh boundaries. This is generated by determining the maxima/minima
|
91
|
+
# coordinate points. Return value is an array of six values as follows
|
92
|
+
# max_x, max_y, max_z, min_x, min_y, min_z
|
93
|
+
def boundaries
|
94
|
+
centerx, centery, centerz = *center
|
95
|
+
max_x = max_y = max_z = min_x = min_y = min_z = 0
|
96
|
+
@coord_point.each_index { |cpi|
|
97
|
+
if cpi % 3 == 0
|
98
|
+
adjx = centerx + @coord_point[cpi]
|
99
|
+
if adjx > max_x
|
100
|
+
max_x = adjx
|
101
|
+
elsif adjx < min_x
|
102
|
+
min_x = adjx
|
103
|
+
end
|
104
|
+
elsif cpi % 3 == 1
|
105
|
+
adjy = centery + @coord_point[cpi]
|
106
|
+
if adjy > max_y
|
107
|
+
max_y = adjy
|
108
|
+
elsif adjy < min_y
|
109
|
+
min_y = adjy
|
110
|
+
end
|
111
|
+
else
|
112
|
+
adjz = centerz + @coord_point[cpi]
|
113
|
+
if adjz > max_z
|
114
|
+
max_z = adjz
|
115
|
+
elsif adjz < min_z
|
116
|
+
min_z = adjz
|
117
|
+
end
|
118
|
+
end
|
119
|
+
}
|
120
|
+
return max_x, max_y, max_z, min_x, min_y, min_z
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
# Invoked during draw cycle to draw mesh
|
125
|
+
def draw
|
126
|
+
# FIXME mesh material
|
127
|
+
#Gl.glMaterial(Gl::GL_FRONT, Gl::GL_DIFFUSE, diffuse)
|
128
|
+
#Gl.glMaterial(Gl::GL_FRONT, Gl::GL_SPECULAR, specular)
|
129
|
+
#Gl.glMaterial(Gl::GL_FRONT, Gl::GL_EMISSIVE, emissive)
|
130
|
+
|
131
|
+
# translate coordinate system to center position of mesh
|
132
|
+
Gl.glTranslatef(*center)
|
133
|
+
|
134
|
+
# scale as specified
|
135
|
+
Gl.glScalef(*scale)
|
136
|
+
|
137
|
+
# FIXME rotate according to mesh and optional params
|
138
|
+
|
139
|
+
# draw mesh
|
140
|
+
Gl.glEnableClientState(Gl::GL_VERTEX_ARRAY);
|
141
|
+
Gl.glVertexPointer(3, Gl::GL_FLOAT, 0, @coord_point);
|
142
|
+
Gl.glDrawElements(Gl::GL_QUADS, @coord_index.size, Gl::GL_UNSIGNED_INT, @coord_index);
|
143
|
+
Gl.glDisableClientState(Gl::GL_VERTEX_ARRAY);
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# Resources constructs and methods
|
2
|
+
#
|
3
|
+
# Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
|
4
|
+
# Licensed under the GPLv3+ http://www.gnu.org/licenses/gpl.txt
|
5
|
+
|
6
|
+
require 'singleton'
|
7
|
+
|
8
|
+
module RAGE
|
9
|
+
|
10
|
+
# Singleton class managing all RAGE resources.
|
11
|
+
# Resources should be loaded here instead as opposed to creating them manually
|
12
|
+
# so as to mantain a central management repository during game execution.
|
13
|
+
class ResourcesManager
|
14
|
+
include Singleton
|
15
|
+
|
16
|
+
# Hash of ids -> resources we are managing
|
17
|
+
attr_accessor :resources
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@resources = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
# Load resource from specified args and add to manager. Args may include
|
24
|
+
# * :type type of resource to load
|
25
|
+
# * :id unique identifier to give resource
|
26
|
+
# * :uri uri of resource if appropriate
|
27
|
+
# * :color resource color if appropriate
|
28
|
+
def load_resource(args = {})
|
29
|
+
type = args[:type]
|
30
|
+
id = args[:id]
|
31
|
+
|
32
|
+
resource = nil
|
33
|
+
|
34
|
+
if type == :mesh
|
35
|
+
uri = args[:uri]
|
36
|
+
data = Loader.load uri
|
37
|
+
resource = Mesh.new(data)
|
38
|
+
elsif type == :color
|
39
|
+
color = args[:color]
|
40
|
+
resource = Color.new(:rgb => color)
|
41
|
+
# elsif other resource types
|
42
|
+
end
|
43
|
+
|
44
|
+
@resources[id] = resource
|
45
|
+
return resource
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|