shattered_pack 0.3.3

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.
@@ -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