shattered_pack 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/shattered_controller.rb +3 -0
- data/lib/shattered_controller/actor/actor.rb +107 -0
- data/lib/shattered_controller/base.rb +108 -0
- data/lib/shattered_controller/keyboard_input/key_converter.rb +43 -0
- data/lib/shattered_controller/keyboard_input/keyboard_input.rb +106 -0
- data/lib/shattered_controller/mock_camera.rb +9 -0
- data/lib/shattered_controller/runner.rb +33 -0
- data/lib/shattered_controller/state.rb +59 -0
- data/lib/shattered_model.rb +3 -0
- data/lib/shattered_model/base.rb +24 -0
- data/lib/shattered_model/fuzzy_logic.rb +188 -0
- data/lib/shattered_model/linear_interpolator.rb +29 -0
- data/lib/shattered_pack.rb +11 -0
- data/lib/shattered_pack/base.rb +127 -0
- data/lib/shattered_pack/pre_initialize/pre_initialize.rb +105 -0
- data/lib/shattered_pack/runner.rb +11 -0
- data/lib/shattered_pack/timer/timed_event.rb +77 -0
- data/lib/shattered_pack/timer/timer.rb +75 -0
- data/lib/shattered_view.rb +7 -0
- data/lib/shattered_view/base.rb +151 -0
- data/lib/shattered_view/camera.rb +7 -0
- data/lib/shattered_view/extensions.rb +10 -0
- data/lib/shattered_view/light.rb +28 -0
- data/lib/shattered_view/mesh/animation.rb +20 -0
- data/lib/shattered_view/mesh/mesh.rb +135 -0
- data/lib/shattered_view/node.rb +113 -0
- data/lib/shattered_view/resources.rb +47 -0
- data/lib/shattered_view/rmaterial.rb +43 -0
- data/lib/shattered_view/runner.rb +48 -0
- data/lib/shattered_view/utilities.rb +7 -0
- data/lib/shattered_view/vector.rb +242 -0
- metadata +75 -0
@@ -0,0 +1,77 @@
|
|
1
|
+
module ShatteredPack
|
2
|
+
module Timer #:nodoc:all
|
3
|
+
|
4
|
+
class Timer
|
5
|
+
attr_reader :events_remaining
|
6
|
+
def initialize
|
7
|
+
@events_remaining = []
|
8
|
+
end
|
9
|
+
def in(seconds, &block)
|
10
|
+
@events_remaining << TimedEvent.new(seconds, &block)
|
11
|
+
end
|
12
|
+
def every(seconds, &block)
|
13
|
+
@events_remaining << ContinuousTimedEvent.new(seconds,&block)
|
14
|
+
end
|
15
|
+
def update(time_elapsed)
|
16
|
+
events_for_update = @events_remaining.dup
|
17
|
+
events_for_update.each do |event|
|
18
|
+
event.update(time_elapsed)
|
19
|
+
@events_remaining.delete(event) if event.processed?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class TimedEvent
|
25
|
+
attr_accessor :time_remaining
|
26
|
+
def initialize(seconds, &block)
|
27
|
+
@event = block
|
28
|
+
if(seconds != :frame)
|
29
|
+
@time_remaining = seconds
|
30
|
+
@initial_time = @time_remaining
|
31
|
+
else
|
32
|
+
@time_remaining = 0
|
33
|
+
@initial_time = 0
|
34
|
+
end
|
35
|
+
end
|
36
|
+
def update(time_elapsed)
|
37
|
+
return if processed?
|
38
|
+
@time_remaining -= time_elapsed
|
39
|
+
if time_up?
|
40
|
+
process_event
|
41
|
+
@processed=true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
def processed?
|
45
|
+
return @processed == true
|
46
|
+
end
|
47
|
+
def reset
|
48
|
+
@time_remaining = @initial_time
|
49
|
+
end
|
50
|
+
private
|
51
|
+
def process_event
|
52
|
+
@event.call(@initial_time - @time_remaining)
|
53
|
+
end
|
54
|
+
def time_up?
|
55
|
+
return @time_remaining <= 0
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class ContinuousTimedEvent < TimedEvent
|
60
|
+
def update(time_elapsed)
|
61
|
+
@time_remaining -= time_elapsed
|
62
|
+
if(time_up?)
|
63
|
+
process_event
|
64
|
+
reset
|
65
|
+
end
|
66
|
+
end
|
67
|
+
def reset
|
68
|
+
@time_remaining += @initial_time
|
69
|
+
@time_remaining = 0 if @time_remaining < 0
|
70
|
+
end
|
71
|
+
def processed?
|
72
|
+
false
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/timed_event'
|
2
|
+
|
3
|
+
module ShatteredPack
|
4
|
+
module Timer #:nodoc:
|
5
|
+
def self.append_features(base)
|
6
|
+
super
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
base.send(:include, InstanceMethods)
|
9
|
+
end
|
10
|
+
module ClassMethods
|
11
|
+
# Timers are controller events that occur after a specified period of time.
|
12
|
+
# The time values are seconds by default.
|
13
|
+
#
|
14
|
+
# timer :in
|
15
|
+
# If you want an event to occur once after a specified amount of time, use
|
16
|
+
# timer :in => 3.seconds, :action => :method_to_call
|
17
|
+
#
|
18
|
+
# timer :every
|
19
|
+
# If you want an event to occur once every specified amount of time, use
|
20
|
+
# timer :every => 3.seconds, :action => :method_to_call
|
21
|
+
# If you want to be notified of every frame update, use
|
22
|
+
# timer :every => :frame, :action => :method_to_call
|
23
|
+
#
|
24
|
+
# Inside the instance, you can still create timer events, using the timer object:
|
25
|
+
# timer.in( 3.seconds ) { ... }
|
26
|
+
# timer.every( 3.seconds ) { ... }
|
27
|
+
def timer(options = {})
|
28
|
+
if options[:action].nil?
|
29
|
+
throw ShatteredPack::Error,
|
30
|
+
"Timer event must specify an :action => :method"
|
31
|
+
end
|
32
|
+
if options[:in].nil? && options[:every].nil?
|
33
|
+
throw ShatteredPack::Error,
|
34
|
+
"Timer event must specify a time. (timer :in or timer :every)"
|
35
|
+
end
|
36
|
+
time = options[:in] || options[:every]
|
37
|
+
action = options[:action]
|
38
|
+
before_init_call(:timer_in, time, action) unless options[:in].nil?
|
39
|
+
before_init_call(:timer_every, time, action) unless options[:every].nil?
|
40
|
+
end
|
41
|
+
end
|
42
|
+
module InstanceMethods
|
43
|
+
def timer
|
44
|
+
@timer ||= Timer.new
|
45
|
+
end
|
46
|
+
private
|
47
|
+
def timer_in(time, action)
|
48
|
+
timer.in(time) do |time_elapsed|
|
49
|
+
timer_enactment(action, time_elapsed)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
def timer_every(time, action)
|
53
|
+
timer.every(time) do |time_elapsed|
|
54
|
+
timer_enactment(action, time_elapsed)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
def timer_enactment(action, time_elapsed)
|
58
|
+
begin
|
59
|
+
if(action.is_a? Hash)
|
60
|
+
if(action.values[0].is_a?(Array))
|
61
|
+
send(action.keys[0], *(action.values[0]))
|
62
|
+
else
|
63
|
+
send(action.keys[0], action.values[0])
|
64
|
+
end
|
65
|
+
else
|
66
|
+
send(action.to_sym)
|
67
|
+
end
|
68
|
+
rescue ArgumentError => argument_error
|
69
|
+
raise argument_error unless argument_error.message.to_sym == :"wrong number of arguments (0 for 1)"
|
70
|
+
send(action.to_sym,time_elapsed)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
require 'shattered_ogre'
|
2
|
+
require 'shattered_pack'
|
3
|
+
raise( ShatteredPack::Error, "Could not find ShatteredOgre (a ShatteredView dependency)" ) unless defined? ShatteredOgre
|
4
|
+
|
5
|
+
%w(vector utilities node camera extensions runner rmaterial base resources).each do |dependency|
|
6
|
+
require "shattered_view/#{dependency}"
|
7
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
|
2
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
3
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
4
|
+
|
5
|
+
require 'mesh/mesh'
|
6
|
+
|
7
|
+
module ShatteredView #:nodoc:
|
8
|
+
|
9
|
+
class Error < StandardError; end
|
10
|
+
|
11
|
+
def self.append_features(base)
|
12
|
+
super
|
13
|
+
base.extend(ClassMethods)
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
# Materials are parsed through erb as .rmaterials and parsed when a material
|
18
|
+
# metafunction is called.
|
19
|
+
#
|
20
|
+
# Example:
|
21
|
+
# material :wall_material, :template => 'normal_map', :texture => 'wall.png', :normal => 'brick_normals.png', :tangent_space => true
|
22
|
+
# mesh :wall, :material => :wall_material
|
23
|
+
#
|
24
|
+
# You must make sure that your material name is unique, or Ogre will fail.
|
25
|
+
#
|
26
|
+
# All rmaterial files are included within media/templates, you can add your own there.
|
27
|
+
# TODO: ruby script/generate material (?)
|
28
|
+
def material(name, options = {})
|
29
|
+
before_init_set "self", {:new_material => [name]}
|
30
|
+
before_init_set name, options
|
31
|
+
before_init_set name, {:create_ogre_material => []}
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
|
36
|
+
# Any view can create a light.
|
37
|
+
# There are 4 types of possible lights.
|
38
|
+
# Ambient Light - this type of light lights everything in the scene equally.
|
39
|
+
# Example: light :sun, :ambient => [1,1,1]
|
40
|
+
#
|
41
|
+
# Point Light - this type of light emanates from a point in space and lights all
|
42
|
+
# directions equally.
|
43
|
+
# Example: light :fireplace, :position => [0,-5,20], :diffuse => [1,0.5,0.5], :specular => [1,0,0], :attenuation => [8000,0,4,0.3]
|
44
|
+
# Parameters:
|
45
|
+
# :position: the x,y,z position or vector position
|
46
|
+
# :diffuse: the r,g,b value of the diffuse color
|
47
|
+
# :specular: the r,g,b value of the specular color
|
48
|
+
# :attenuation: arguments are range, constant, linear, quadratic
|
49
|
+
# range is how far the light will extend
|
50
|
+
# constant The constant factor in the attenuation formula: 1.0 means never attenuate, 0.0 is complete attenuation
|
51
|
+
# linear The linear factor in the attenuation formula: 1 means attenuate evenly over the distance
|
52
|
+
# quadratic The quadratic factor in the attenuation formula: adds a curvature to the attenuation formula.
|
53
|
+
#
|
54
|
+
# Directional Light - this light is like the positional light, except instead of position it has only direction
|
55
|
+
# Example: light :sun, :direction => [0,-1,-1], :diffuse => [1,1,1], :specular => [1,1,1]
|
56
|
+
# :direction: the direction light emanates from
|
57
|
+
# : other : same as point light above.
|
58
|
+
#
|
59
|
+
# Spotlight - this light has both a direction and a position, as well as other factors.
|
60
|
+
# Example: light :flashlight...
|
61
|
+
# TODO: This type of light is not currently implemented in shattered.
|
62
|
+
|
63
|
+
def light(name, options = {})
|
64
|
+
before_init_set "self", {:new_light => [name]}
|
65
|
+
before_init_set name, options
|
66
|
+
end
|
67
|
+
|
68
|
+
# Particle systems are defined in .particle files
|
69
|
+
# - This works for first order particle systems --
|
70
|
+
# - We need to look into second and third order particle systems
|
71
|
+
#
|
72
|
+
def particle_system(name, options = {})
|
73
|
+
raise Error, "Must supply a template for your particle system." if options[:template].nil?
|
74
|
+
before_init_set "self", {:new_particle_system, [name, options[:template]]}
|
75
|
+
options.delete :template
|
76
|
+
before_init_set name, options
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class Base < ShatteredPack::Base
|
81
|
+
attr_reader :meshes
|
82
|
+
attr_accessor :model
|
83
|
+
|
84
|
+
def rotate(vector, amount)
|
85
|
+
node.rotate(vector, amount)
|
86
|
+
end
|
87
|
+
|
88
|
+
def new_material=(name)
|
89
|
+
@materials ||= {}
|
90
|
+
name = name.to_sym
|
91
|
+
@materials[name] = RMaterial.new
|
92
|
+
@materials[name].name=name
|
93
|
+
self.class.send(:define_method, name) do
|
94
|
+
@materials[name]
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def define_accessor(array, new_obj, name)
|
99
|
+
instance_eval("#{array} ||= {}")
|
100
|
+
instance_eval("#{array}[\"#{name}\"] = new_obj")
|
101
|
+
|
102
|
+
self.class.class_eval <<-EOF
|
103
|
+
define_method(:#{name}) do
|
104
|
+
#{array}[\"#{name}\"]
|
105
|
+
end
|
106
|
+
EOF
|
107
|
+
end
|
108
|
+
|
109
|
+
def new_light=(name)
|
110
|
+
define_accessor("@lights", ShatteredView::Light.new, name)
|
111
|
+
end
|
112
|
+
|
113
|
+
def new_particle_system=(name, template)
|
114
|
+
define_accessor("@particle_systems", ShatteredView::ParticleSystem.new(template), name)
|
115
|
+
end
|
116
|
+
|
117
|
|
118
|
+
#Return all of the children attached to this view's scene node
|
119
|
+
def children
|
120
|
+
node.children
|
121
|
+
end
|
122
|
+
|
123
|
+
def position
|
124
|
+
node.position
|
125
|
+
end
|
126
|
+
|
127
|
+
def position=(val)
|
128
|
+
node.position = val
|
129
|
+
end
|
130
|
+
|
131
|
+
def translate(v)
|
132
|
+
node.translate(v)
|
133
|
+
end
|
134
|
+
|
135
|
+
def node
|
136
|
+
@scene_node ||= Node.new(Node.root)
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
140
|
+
#This is called when a view is removed from the scene
|
141
|
+
def unload!
|
142
|
+
children.each { |child| child.remove_from_scene }
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
ShatteredView::Base.class_eval do
|
150
|
+
include ShatteredView::Mesh
|
151
|
+
end
|
152
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module ShatteredView
|
2
|
+
class Light
|
3
|
+
def light
|
4
|
+
@light ||= Scene.getSingleton().createLight
|
5
|
+
end
|
6
|
+
def direction=(*direction)
|
7
|
+
light.setType(ShatteredOgre::Light::LT_DIRECTIONAL)
|
8
|
+
light.setDirection(direction.to_v)
|
9
|
+
end
|
10
|
+
def diffuse=(r,g,b)
|
11
|
+
light.setDiffuseColour(r,g,b)
|
12
|
+
end
|
13
|
+
def specular=(r,g,b)
|
14
|
+
light.setSpecularColour(r,g,b)
|
15
|
+
end
|
16
|
+
def position=(pos)
|
17
|
+
light.setType(ShatteredOgre::Light::LT_POINT)
|
18
|
+
light.setPosition(pos.to_v)
|
19
|
+
end
|
20
|
+
def ambient=(r,g,b)
|
21
|
+
Scene.getSingleton().setAmbientLight(r,g,b);
|
22
|
+
end
|
23
|
+
def attenuation=(range, const, linear, quad)
|
24
|
+
light.setType(ShatteredOgre::Light::LT_POINT)
|
25
|
+
light.setAttenuation(range, const, linear, quad)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ShatteredView
|
2
|
+
class Animation
|
3
|
+
def initialize(mesh, name)
|
4
|
+
@animation = mesh.getAnimation name
|
5
|
+
@animation.addAsFrameListener
|
6
|
+
end
|
7
|
+
def play
|
8
|
+
@animation.play
|
9
|
+
end
|
10
|
+
def stop
|
11
|
+
@animation.stop
|
12
|
+
end
|
13
|
+
def loop=(looping)
|
14
|
+
@animation.setLoop(looping);
|
15
|
+
end
|
16
|
+
def time_scale=(scale)
|
17
|
+
@animation.setTimeScale(scale);
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require 'animation'
|
5
|
+
module ShatteredView
|
6
|
+
module Mesh
|
7
|
+
def self.included(base)
|
8
|
+
base.extend(ClassMethods)
|
9
|
+
base.send(:include, InstanceMethods)
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
# Meshes are the foundation for your world.
|
14
|
+
#
|
15
|
+
# They tie in to Ogre's .mesh file format, and display a 3d object onto the screen.
|
16
|
+
#
|
17
|
+
# Shattered has support for loading meshes, creating materials for those meshes (using RMaterials)
|
18
|
+
# and skeletal animation support.
|
19
|
+
#
|
20
|
+
# class DirtyRubyView
|
21
|
+
# mesh "dirty_ruby", :position => v(0,0,1)
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# When a Mesh is created, all animations found in that mesh are loaded, and added
|
25
|
+
# as functions to that mesh.
|
26
|
+
#
|
27
|
+
# example: Tim.mesh has animations kick and punch
|
28
|
+
#
|
29
|
+
# after loading Tim.mesh, View has the Mesh object tim
|
30
|
+
# tim.kick.play
|
31
|
+
# tim.animation(:kick).play
|
32
|
+
#
|
33
|
+
# See Rmaterials and Animation for more details.
|
34
|
+
def mesh(file, options = {})
|
35
|
+
before_init_call( :mesh, file, options )
|
36
|
+
end
|
37
|
+
|
38
|
+
# Animations declare helper functions for mesh animations.
|
39
|
+
#
|
40
|
+
# class AnimatedRubyView < ...
|
41
|
+
# mesh :animated_ruby, ...
|
42
|
+
# animation "shatter"
|
43
|
+
#
|
44
|
+
# def initialize
|
45
|
+
# shatter # same as animated_ruby.shatter
|
46
|
+
# end
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# options can include any function in animation.rb
|
50
|
+
# and :alias => :alias_name
|
51
|
+
def animation(name, options = {})
|
52
|
+
play_alias = options.delete(:alias)
|
53
|
+
before_init_call(:animation, name, options)
|
54
|
+
unless play_alias.nil?
|
55
|
+
before_init_call(:animation_alias, name, play_alias)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
module InstanceMethods
|
61
|
+
|
62
|
+
private
|
63
|
+
def mesh(file, options={})
|
64
|
+
mesh = Mesh.new(file)
|
65
|
+
name = mesh.name
|
66
|
+
attr_reader name.to_sym, mesh
|
67
|
+
|
68
|
+
call_object_function_for_each_key(name, options)
|
69
|
+
#keep track of the meshes we create
|
70
|
+
mesh.attach_to(node)
|
71
|
+
end
|
72
|
+
|
73
|
+
def meshes
|
74
|
+
children.collect { |child| child.is_a? Mesh }
|
75
|
+
end
|
76
|
+
|
77
|
+
def animation(name, options={})
|
78
|
+
meshes.each do |mesh|
|
79
|
+
mesh.instance_eval <<-EOF
|
80
|
+
class << self
|
81
|
+
define_method(:#{name}) do
|
82
|
+
animation(:#{name})
|
83
|
+
end
|
84
|
+
end
|
85
|
+
EOF
|
86
|
+
call_object_function_for_each_key(:"#{mesh.name}.#{name}", options)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def animation_alias(name, renamed)
|
91
|
+
define_method(renamed.to_sym) do
|
92
|
+
meshes.each do |mesh|
|
93
|
+
mesh.animation(name.to_sym).play
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# This Mesh class corresponds (almost 1 to 1) with ShatteredOgre's mesh class
|
100
|
+
class Mesh < ShatteredView::Node
|
101
|
+
attr_reader :name
|
102
|
+
|
103
|
+
def initialize( file, options = {} )
|
104
|
+
super( )
|
105
|
+
@name = file.underscore
|
106
|
+
@scene_node = ShatteredOgre::Scene.getSingleton.createMesh( [0,0,0].to_v3, "#{file}.mesh" )
|
107
|
+
# TODO Log this
|
108
|
+
# puts "Loading Mesh: #{scene_node} from #{file}.mesh"
|
109
|
+
populate_animations
|
110
|
+
end
|
111
|
+
|
112
|
+
# Access a given animation. Returns nil on failure.
|
113
|
+
def animation( animation )
|
114
|
+
return @animations[animation]
|
115
|
+
end
|
116
|
+
|
117
|
+
# Remove the Ogre Mesh from the scene
|
118
|
+
def remove_from_scene
|
119
|
+
scene_node.removeFromScene
|
120
|
+
end
|
121
|
+
private
|
122
|
+
|
123
|
+
# populate_animations queries ShatteredOgre and adds the found animations to the mesh.
|
124
|
+
def populate_animations
|
125
|
+
@animations = {}
|
126
|
+
(0...scene_node.getNumberOfAnimations).each do |idx|
|
127
|
+
name = scene_node.getAnimationName(idx)
|
128
|
+
@animations[name.downcase.to_sym] = ShatteredView::Animation.new(scene_node, name)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|