shattered_pack 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,11 @@
1
+ module ShatteredPack
2
+ class Configuration #:nodoc:
3
+ def self.environment
4
+ @@environment={} unless defined? @@environment
5
+ return @@environment
6
+ end
7
+ def self.environment=(environment)
8
+ @@environment=environment
9
+ end
10
+ end
11
+ end
@@ -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,7 @@
1
+ module ShatteredView
2
+ class Camera < Node
3
+ def initialize( scene )
4
+ @scene_node = scene.getCamera
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ module ShatteredOgre #:nodoc:
2
+ class Vector3 #:nodoc:
3
+ def to_a
4
+ [x, y, z]
5
+ end
6
+ def to_v
7
+ Vector.new x, y, z
8
+ end
9
+ end
10
+ end
@@ -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