teien 0.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/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +34 -0
- data/Rakefile +1 -0
- data/bin/teien +49 -0
- data/lib/teien/animation_operator.rb +142 -0
- data/lib/teien/camera.rb +25 -0
- data/lib/teien/camera_mover.rb +193 -0
- data/lib/teien/garden.rb +203 -0
- data/lib/teien/garden_object.rb +314 -0
- data/lib/teien/light_object.rb +36 -0
- data/lib/teien/object_factory.rb +302 -0
- data/lib/teien/object_info.rb +171 -0
- data/lib/teien/physics.rb +150 -0
- data/lib/teien/physics_info.rb +23 -0
- data/lib/teien/smooth_mover.rb +108 -0
- data/lib/teien/tools.rb +261 -0
- data/lib/teien/ui_listener.rb +68 -0
- data/lib/teien/user_interface.rb +201 -0
- data/lib/teien/version.rb +3 -0
- data/lib/teien/view.rb +244 -0
- data/lib/teien.rb +40 -0
- data/sample/standalone/hello_garden/hello_garden.rb +155 -0
- data/sample/standalone/hello_garden/plugins.cfg +17 -0
- data/sample/standalone/hello_garden/resources.cfg +21 -0
- data/sample/standalone/hello_garden/run +5 -0
- data/teien.gemspec +22 -0
- metadata +107 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 abexsoft@gmail.com
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# Teien
|
2
|
+
|
3
|
+
Teien will be a platform to make 3D world easily with Ruby.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'teien'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install ruby-ogre
|
18
|
+
$ gem install ruby-bullet
|
19
|
+
$ gem install teienlib
|
20
|
+
$ gem install pkg/teien-<version>.gem
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
Run a sample application.
|
25
|
+
|
26
|
+
$ /var/lib/gems/1.9.1/gems/teien-<version>/sample/standalone/hello_garden/run
|
27
|
+
|
28
|
+
## Contributing
|
29
|
+
|
30
|
+
1. Fork it
|
31
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
32
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
33
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
34
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/teien
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# This script is a environment setting helper on the startup.
|
4
|
+
# 1. set a LD_LIBRARY_PATH(or PATH) value for dynamic libraries.
|
5
|
+
# 2. restart with above value.
|
6
|
+
# 3. set a LOAD_PATH value for ruby libraries.
|
7
|
+
|
8
|
+
if (ARGV[0] == "-h" || ARGV[0] == "--help")
|
9
|
+
puts "Usage: teien file"
|
10
|
+
exit
|
11
|
+
end
|
12
|
+
|
13
|
+
#
|
14
|
+
# require this file directly at first line, unless you set
|
15
|
+
# RUBYLIB and LD_LIBRARY_PATH. (check sample code)
|
16
|
+
#
|
17
|
+
require 'rbconfig'
|
18
|
+
require 'OgreConfig'
|
19
|
+
|
20
|
+
$topDir = File.dirname(File.dirname(File.expand_path(__FILE__)))
|
21
|
+
libPath = $topDir + "/lib"
|
22
|
+
extPath = OgreConfig::getDepsLibPath
|
23
|
+
|
24
|
+
# needed by dynamic library.
|
25
|
+
if (/mingw/ =~ RUBY_PLATFORM)
|
26
|
+
ldLibPath = extPath + ";" + OgreConfig::getLib + ";" + libPath
|
27
|
+
if (ENV["PATH"] == nil)
|
28
|
+
ENV["PATH"] = ldLibPath
|
29
|
+
exec("ruby #{$0} #{ARGV.join(" ")}")
|
30
|
+
elsif (!ENV["PATH"].include?(ldLibPath))
|
31
|
+
ENV["PATH"] = ldLibPath + ";" +ENV["PATH"]
|
32
|
+
exec("ruby #{$0} #{ARGV.join(" ")}")
|
33
|
+
end
|
34
|
+
else
|
35
|
+
ldLibPath = extPath + ":" + libPath
|
36
|
+
if (ENV["LD_LIBRARY_PATH"] == nil)
|
37
|
+
ENV["LD_LIBRARY_PATH"] = ldLibPath
|
38
|
+
exec($0, *ARGV)
|
39
|
+
elsif(!ENV["LD_LIBRARY_PATH"].include?(ldLibPath))
|
40
|
+
ENV["LD_LIBRARY_PATH"] = ldLibPath + ":" + ENV["LD_LIBRARY_PATH"]
|
41
|
+
exec($0, *ARGV)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
$LOAD_PATH.push(libPath)
|
46
|
+
$LOAD_PATH.push("./")
|
47
|
+
|
48
|
+
load ARGV[0]
|
49
|
+
|
@@ -0,0 +1,142 @@
|
|
1
|
+
class AnimationOperator
|
2
|
+
BL_MODE_SWITCH = 0 # stop current animation and start next animation.
|
3
|
+
BL_MODE_CONCURRENT = 1 # cross fade, blend current animation out while blending next animation in.
|
4
|
+
BL_MODE_FIRSTFRAME = 2 # blend current animation to first frame of next animation, when done, start next animation.
|
5
|
+
|
6
|
+
def initialize(entity)
|
7
|
+
@entity = entity
|
8
|
+
@state = nil
|
9
|
+
@nextState = nil
|
10
|
+
@mode = BL_MODE_CONCURRENT
|
11
|
+
@duration = 0.2
|
12
|
+
@timeLeft = 0
|
13
|
+
@complete = false
|
14
|
+
@loop = false
|
15
|
+
|
16
|
+
# @blender = Ogrelet::AnimationBlender.new(entity)
|
17
|
+
end
|
18
|
+
|
19
|
+
def init(name, loop)
|
20
|
+
initializeAllAnimations()
|
21
|
+
play(name, loop)
|
22
|
+
end
|
23
|
+
|
24
|
+
def initializeAllAnimations()
|
25
|
+
set = @entity.getAllAnimationStates()
|
26
|
+
set.each_AnimationState {|state|
|
27
|
+
state.setEnabled(false)
|
28
|
+
state.setWeight(0)
|
29
|
+
state.setTimePosition(0)
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
def setEnabled(bl)
|
34
|
+
# @blender.getSource().setEnabled(bl)
|
35
|
+
end
|
36
|
+
|
37
|
+
def setBlendingMode(mode)
|
38
|
+
@mode = mode
|
39
|
+
end
|
40
|
+
|
41
|
+
def setBlengingDuration(duration)
|
42
|
+
@duration = duration
|
43
|
+
end
|
44
|
+
|
45
|
+
def play(name, loop)
|
46
|
+
@loop = loop
|
47
|
+
|
48
|
+
unless @state
|
49
|
+
@state = @entity.getAnimationState(name)
|
50
|
+
@state.setEnabled(true)
|
51
|
+
@state.setWeight(1)
|
52
|
+
@state.setTimePosition(0)
|
53
|
+
@state.setLoop(loop)
|
54
|
+
return
|
55
|
+
end
|
56
|
+
|
57
|
+
case @mode
|
58
|
+
when BL_MODE_SWITCH
|
59
|
+
@state.setEnabled(false)
|
60
|
+
@state = @entity.getAnimationState(name)
|
61
|
+
@state.setEnabled(true)
|
62
|
+
@state.setWeight(1)
|
63
|
+
@state.setTimePosition(0)
|
64
|
+
@timeLeft = 0
|
65
|
+
else
|
66
|
+
newState = @entity.getAnimationState(name)
|
67
|
+
if @timeLeft > 0
|
68
|
+
if newState.getAnimationName == @nextState.getAnimationName
|
69
|
+
return
|
70
|
+
elsif newState.getAnimationName == @state.getAnimationName
|
71
|
+
# going back to the source state
|
72
|
+
@state = @nextState
|
73
|
+
@nextState = newState
|
74
|
+
@timeLeft = @duration - @timeLeft
|
75
|
+
else
|
76
|
+
if @timeLeft < @duration * 0.5
|
77
|
+
# simply replace the target with this one
|
78
|
+
@nextState.setEnabled(false)
|
79
|
+
@nextState.setWeight(0)
|
80
|
+
else
|
81
|
+
# old target becomes new source
|
82
|
+
@state.setEnabled(false)
|
83
|
+
@state.setWeight(0)
|
84
|
+
@state = @nextState
|
85
|
+
end
|
86
|
+
|
87
|
+
@nextState = newState
|
88
|
+
@nextState.setEnabled(true)
|
89
|
+
@nextState.setWeight( 1.0 - @timeLeft / @duration )
|
90
|
+
@nextState.setTimePosition(0)
|
91
|
+
end
|
92
|
+
else
|
93
|
+
return if newState.getAnimationName == @state.getAnimationName
|
94
|
+
|
95
|
+
# assert( target == 0, "target should be 0 when not blending" )
|
96
|
+
# @state.setEnabled(true)
|
97
|
+
# @state.setWeight(1)
|
98
|
+
# mTransition = transition;
|
99
|
+
@timeLeft = @duration
|
100
|
+
@nextState = newState
|
101
|
+
@nextState.setEnabled(true)
|
102
|
+
@nextState.setWeight(0)
|
103
|
+
@nextState.setTimePosition(0)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
# @blender.blend(name, Ogrelet::AnimationBlender::BlendWhileAnimating, 0.2, loop)
|
107
|
+
end
|
108
|
+
|
109
|
+
def addTime(delta)
|
110
|
+
if @state
|
111
|
+
if @timeLeft > 0
|
112
|
+
@timeLeft -= delta
|
113
|
+
|
114
|
+
if @timeLeft < 0
|
115
|
+
@state.setEnabled(false)
|
116
|
+
@state.setWeight(0)
|
117
|
+
@state = @nextState
|
118
|
+
@state.setEnabled(true)
|
119
|
+
@state.setWeight(1)
|
120
|
+
@nextState = nil
|
121
|
+
else
|
122
|
+
# still blending, advance weights
|
123
|
+
@state.setWeight(@timeLeft / @duration)
|
124
|
+
@nextState.setWeight(1.0 - @timeLeft / @duration)
|
125
|
+
if(@mode == BL_MODE_CONCURRENT)
|
126
|
+
@nextState.addTime(delta)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
if @state.getTimePosition() >= @state.getLength()
|
132
|
+
@complete = true
|
133
|
+
else
|
134
|
+
@complete = false
|
135
|
+
end
|
136
|
+
|
137
|
+
@state.addTime(delta)
|
138
|
+
@state.setLoop(@loop)
|
139
|
+
end
|
140
|
+
# @blender.addTime(delta)
|
141
|
+
end
|
142
|
+
end
|
data/lib/teien/camera.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'teien/camera_mover'
|
2
|
+
|
3
|
+
module Teien
|
4
|
+
|
5
|
+
class Camera
|
6
|
+
def initialize(cam)
|
7
|
+
# Ogre::Camera
|
8
|
+
@camera = cam
|
9
|
+
@mover = CameraMover.new(cam)
|
10
|
+
end
|
11
|
+
|
12
|
+
def get_mover()
|
13
|
+
return @mover
|
14
|
+
end
|
15
|
+
|
16
|
+
def get_position
|
17
|
+
return Vector3D.to_self(@camera.getPosition())
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_direction
|
21
|
+
return Vector3D.to_self(@camera.getDirection())
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
module Teien
|
2
|
+
|
3
|
+
class CameraMover
|
4
|
+
CS_FREELOOK = 0
|
5
|
+
CS_ORBIT = 1
|
6
|
+
CS_MANUAL = 2
|
7
|
+
CS_TPS = 3
|
8
|
+
|
9
|
+
CAM_HEIGHT = 5.0
|
10
|
+
|
11
|
+
attr_accessor :height
|
12
|
+
attr_accessor :camera_pivot
|
13
|
+
attr_accessor :camera
|
14
|
+
|
15
|
+
def initialize(cam)
|
16
|
+
@camera = cam
|
17
|
+
@camera.setPosition(0, 0, 0)
|
18
|
+
@camera.setNearClipDistance(0.1)
|
19
|
+
|
20
|
+
@style = CS_FREELOOK
|
21
|
+
|
22
|
+
# CS_FREELOOK, CS_ORBIT, CS_MANUAL
|
23
|
+
@sdk_camera_man = OgreBites::SdkCameraMan.new(@camera)
|
24
|
+
@evt_frame = Ogre::FrameEvent.new
|
25
|
+
|
26
|
+
# CS_TPS
|
27
|
+
@height = CAM_HEIGHT
|
28
|
+
@camera_pivot = cam.getSceneManager().getRootSceneNode().createChildSceneNode()
|
29
|
+
@camera_goal = @camera_pivot.createChildSceneNode(Ogre::Vector3.new(0, 0, 5))
|
30
|
+
|
31
|
+
@camera_pivot.setFixedYawAxis(true)
|
32
|
+
@camera_goal.setFixedYawAxis(true)
|
33
|
+
|
34
|
+
@pivot_pitch = 0
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
def set_style(style)
|
39
|
+
@style = style
|
40
|
+
case @style
|
41
|
+
when CS_FREELOOK
|
42
|
+
@sdk_camera_man.setStyle(OgreBites::CS_FREELOOK)
|
43
|
+
when CS_ORBIT
|
44
|
+
@sdk_camera_man.setStyle(OgreBites::CS_ORBIT)
|
45
|
+
else # CS_MANUAL, CS_TPS
|
46
|
+
@sdk_camera_man.setStyle(OgreBites::CS_MANUAL)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def set_target(target)
|
51
|
+
@target = target
|
52
|
+
if @style == CS_TPS
|
53
|
+
@camera.setAutoTracking(false)
|
54
|
+
@camera.moveRelative(Ogre::Vector3.new(0, 0, 0))
|
55
|
+
update_camera(1.0)
|
56
|
+
else
|
57
|
+
@sdk_camera_man.setTarget(target.pivotSceneNode)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def set_position(pos)
|
62
|
+
@camera.setPosition(Vector3D.to_ogre(pos)) if @style == CS_FREELOOK
|
63
|
+
end
|
64
|
+
|
65
|
+
def look_at(pos)
|
66
|
+
@camera.lookAt(Vector3D.to_ogre(pos)) if @style == CS_FREELOOK
|
67
|
+
end
|
68
|
+
|
69
|
+
def set_yaw_pitch_dist(yaw, pitch, dist)
|
70
|
+
@sdk_camera_man.setYawPitchDist(yaw, pitch, dist) if @style == CS_ORBIT
|
71
|
+
end
|
72
|
+
|
73
|
+
def move_forward(bl)
|
74
|
+
evt = OIS::KeyEvent.new(nil, OIS::KC_W, 0)
|
75
|
+
if bl
|
76
|
+
@sdk_camera_man.injectKeyDown(evt)
|
77
|
+
else
|
78
|
+
@sdk_camera_man.injectKeyUp(evt)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def move_backward(bl)
|
83
|
+
evt = OIS::KeyEvent.new(nil, OIS::KC_S, 0)
|
84
|
+
if bl
|
85
|
+
@sdk_camera_man.injectKeyDown(evt)
|
86
|
+
else
|
87
|
+
@sdk_camera_man.injectKeyUp(evt)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def move_left(bl)
|
92
|
+
evt = OIS::KeyEvent.new(nil, OIS::KC_A, 0)
|
93
|
+
if bl
|
94
|
+
@sdk_camera_man.injectKeyDown(evt)
|
95
|
+
else
|
96
|
+
@sdk_camera_man.injectKeyUp(evt)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def move_right(bl)
|
101
|
+
evt = OIS::KeyEvent.new(nil, OIS::KC_D, 0)
|
102
|
+
if bl
|
103
|
+
@sdk_camera_man.injectKeyDown(evt)
|
104
|
+
else
|
105
|
+
@sdk_camera_man.injectKeyUp(evt)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
def update(delta)
|
111
|
+
if (@style == CS_TPS)
|
112
|
+
update_camera(delta)
|
113
|
+
else
|
114
|
+
@evt_frame.timeSinceLastFrame = delta
|
115
|
+
@sdk_camera_man.frameRenderingQueued(@evt_frame)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
#
|
120
|
+
# This method moves this camera position to the goal position smoothly.
|
121
|
+
# In general, should be called in the frameRenderingQueued handler.
|
122
|
+
#
|
123
|
+
def update_camera(deltaTime)
|
124
|
+
# place the camera pivot roughly at the character's shoulder
|
125
|
+
@camera_pivot.setPosition(Vector3D::to_ogre(@target.get_position()) + Ogre::Vector3.UNIT_Y * @height)
|
126
|
+
# move the camera smoothly to the goal
|
127
|
+
goalOffset = @camera_goal._getDerivedPosition() - @camera.getPosition()
|
128
|
+
@camera.move(goalOffset * deltaTime * 9.0)
|
129
|
+
# always look at the pivot
|
130
|
+
@camera.lookAt(@camera_pivot._getDerivedPosition())
|
131
|
+
end
|
132
|
+
|
133
|
+
def mouse_moved(evt)
|
134
|
+
if @style == CS_TPS
|
135
|
+
|
136
|
+
# deal with a warp.
|
137
|
+
if evt.state.X.rel.abs > 300
|
138
|
+
#puts "#{evt.state.X.rel}, #{evt.state.X.abs}"
|
139
|
+
return true
|
140
|
+
end
|
141
|
+
|
142
|
+
update_camera_goal(-0.05 * evt.state.X.rel,
|
143
|
+
-0.05 * evt.state.Y.rel,
|
144
|
+
-0.0005 * evt.state.Z.rel)
|
145
|
+
else
|
146
|
+
@sdk_camera_man.injectMouseMove(evt)
|
147
|
+
end
|
148
|
+
return true
|
149
|
+
end
|
150
|
+
|
151
|
+
#
|
152
|
+
# This method updates the goal position, which this camera should be placed finally.
|
153
|
+
# In general, should be called when the mouse is moved.
|
154
|
+
# *deltaYaw*::_float_, degree value.
|
155
|
+
# *deltaPitch*::_float_, degree value.
|
156
|
+
# *deltaZoom*::_float_, zoom
|
157
|
+
#
|
158
|
+
def update_camera_goal(deltaYaw, deltaPitch, deltaZoom)
|
159
|
+
|
160
|
+
@camera_pivot.yaw(Ogre::Radian.new(Ogre::Degree.new(deltaYaw)), Ogre::Node::TS_WORLD);
|
161
|
+
|
162
|
+
# bound the pitch
|
163
|
+
if (!(@pivot_pitch + deltaPitch > 25 && deltaPitch > 0) &&
|
164
|
+
!(@pivot_pitch + deltaPitch < -60 && deltaPitch < 0))
|
165
|
+
@camera_pivot.pitch(Ogre::Radian.new(Ogre::Degree.new(deltaPitch)), Ogre::Node::TS_LOCAL)
|
166
|
+
@pivot_pitch += deltaPitch;
|
167
|
+
end
|
168
|
+
dist = @camera_goal._getDerivedPosition().distance(@camera_pivot._getDerivedPosition())
|
169
|
+
distChange = deltaZoom * dist;
|
170
|
+
|
171
|
+
# puts "dist: #{dist}:#{distChange}"
|
172
|
+
|
173
|
+
# bound the zoom
|
174
|
+
if (!(dist + distChange < 8 && distChange < 0) &&
|
175
|
+
!(dist + distChange > 25 && distChange > 0))
|
176
|
+
|
177
|
+
@camera_goal.translate(Ogre::Vector3.new(0, 0, distChange), Ogre::Node::TS_LOCAL)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def mouse_pressed(mouseEvent, mouseButtonID)
|
182
|
+
@sdk_camera_man.injectMouseDown(mouseEvent, mouseButtonID) if @style == CS_ORBIT
|
183
|
+
return true
|
184
|
+
end
|
185
|
+
|
186
|
+
def mouse_released(mouseEvent, mouseButtonID)
|
187
|
+
@sdk_camera_man.injectMouseUp(mouseEvent, mouseButtonID) if @style == CS_ORBIT
|
188
|
+
return true
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
data/lib/teien/garden.rb
ADDED
@@ -0,0 +1,203 @@
|
|
1
|
+
require "teien/garden_object.rb"
|
2
|
+
require "teien/light_object.rb"
|
3
|
+
require "teien/object_factory.rb"
|
4
|
+
require "teien/view.rb"
|
5
|
+
#require "teien/server_view.rb"
|
6
|
+
require "teien/physics.rb"
|
7
|
+
require "teien/user_interface.rb"
|
8
|
+
#require "teien/server_user_interface.rb"
|
9
|
+
|
10
|
+
module Teien
|
11
|
+
|
12
|
+
# This is a top object of 3D world.
|
13
|
+
class Garden
|
14
|
+
attr_accessor :view
|
15
|
+
attr_accessor :physics
|
16
|
+
attr_accessor :resources_cfg
|
17
|
+
attr_accessor :plugins_cfg
|
18
|
+
attr_accessor :objects
|
19
|
+
attr_accessor :object_factory
|
20
|
+
attr_accessor :is_server
|
21
|
+
|
22
|
+
#
|
23
|
+
# _script_klass_:: : set a user define class.
|
24
|
+
#
|
25
|
+
def initialize(script_klass)
|
26
|
+
@view = nil
|
27
|
+
@physics = nil
|
28
|
+
@script_klass = script_klass
|
29
|
+
|
30
|
+
@resources_cfg = nil
|
31
|
+
@plugins_cfg = nil
|
32
|
+
@objects = {}
|
33
|
+
@object_num = 0
|
34
|
+
|
35
|
+
@object_factory = ObjectFactory.new(self)
|
36
|
+
|
37
|
+
@is_server = false
|
38
|
+
@debug_draw = false
|
39
|
+
@quit = false
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# set a title name on the window title bar.
|
44
|
+
#
|
45
|
+
# _title_ :: : set a name(String).
|
46
|
+
#
|
47
|
+
def set_window_title(title)
|
48
|
+
@view.window_title = title unless @is_server
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
# set the gravity of the world.
|
53
|
+
#
|
54
|
+
# _grav_ :: : set a vector(Vector3D) as the gravity.
|
55
|
+
#
|
56
|
+
def set_gravity(grav)
|
57
|
+
@physics.set_gravity(grav)
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
# set the ambient light of the world.
|
62
|
+
#
|
63
|
+
# _color_:: : set a color(Color).
|
64
|
+
#
|
65
|
+
def set_ambient_light(color)
|
66
|
+
@view.scene_mgr.setAmbientLight(color)
|
67
|
+
end
|
68
|
+
|
69
|
+
def set_sky_dome(enable, materialName, curvature = 10, tiling = 8, distance = 4000)
|
70
|
+
@view.scene_mgr.setSkyDome(enable, materialName, curvature, tiling, distance)
|
71
|
+
end
|
72
|
+
|
73
|
+
def set_debug_draw(bl)
|
74
|
+
if (bl)
|
75
|
+
@debug_draw = bl
|
76
|
+
@debug_drawer = Ogrelet::DebugDrawer.new(@view.scene_mgr)
|
77
|
+
@physics.dynamicsWorld.setDebugDrawer(@debug_drawer)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def create_user_interface()
|
82
|
+
if @is_server
|
83
|
+
return ServerUserInterface.new(@view)
|
84
|
+
else
|
85
|
+
return UserInterface.new(@view)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def create_object(name, objectInfo, physicsInfo)
|
90
|
+
return @object_factory.create_object(name, objectInfo, physicsInfo)
|
91
|
+
end
|
92
|
+
|
93
|
+
def create_light(name)
|
94
|
+
return LightObject.new(self, name)
|
95
|
+
end
|
96
|
+
|
97
|
+
def add_object(obj, collision_filter = nil)
|
98
|
+
if (@objects[obj.name] == nil)
|
99
|
+
if (obj.rigid_body != nil)
|
100
|
+
if (collision_filter)
|
101
|
+
@physics.add_rigid_body(obj.rigid_body, collision_filter)
|
102
|
+
else
|
103
|
+
@physics.add_rigid_body(obj.rigid_body)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
@objects[obj.name] = obj
|
107
|
+
obj.id = @object_num
|
108
|
+
@object_num += 1
|
109
|
+
else
|
110
|
+
raise RuntimeError, "There is a object with the same name (#{obj.name})"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def check_collision(objectA, objectB)
|
115
|
+
result = @physics.contact_pair_test(objectA.rigid_body, objectB.rigid_body)
|
116
|
+
return result.isCollided
|
117
|
+
end
|
118
|
+
|
119
|
+
def setup()
|
120
|
+
if @is_server
|
121
|
+
@view = ServerView.new(self)
|
122
|
+
else
|
123
|
+
@view = View.new(self)
|
124
|
+
end
|
125
|
+
|
126
|
+
@physics = Physics.new(self)
|
127
|
+
@script = @script_klass.new(self)
|
128
|
+
|
129
|
+
if @view.setup()
|
130
|
+
@view.start(@script)
|
131
|
+
@view.prepare_render_loop()
|
132
|
+
|
133
|
+
@physics.setup()
|
134
|
+
@script.setup()
|
135
|
+
|
136
|
+
return true
|
137
|
+
else
|
138
|
+
return false
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
#
|
143
|
+
# mainloop
|
144
|
+
#
|
145
|
+
def run()
|
146
|
+
return false unless setup()
|
147
|
+
|
148
|
+
last_time = Time.now.to_f
|
149
|
+
while !@quit
|
150
|
+
now_time = Time.now.to_f
|
151
|
+
delta = now_time - last_time
|
152
|
+
last_time = now_time
|
153
|
+
break unless update(delta)
|
154
|
+
end
|
155
|
+
|
156
|
+
finalize()
|
157
|
+
|
158
|
+
return true
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
def update(delta)
|
163
|
+
@physics.dynamics_world.debugDrawWorld() if @debug_draw
|
164
|
+
|
165
|
+
return @view.update(delta)
|
166
|
+
end
|
167
|
+
|
168
|
+
# called from view.update()
|
169
|
+
# This function is divided from update() by the purpose of an optimization,
|
170
|
+
# which archives to run in parallel with GPU process.
|
171
|
+
def updateInFrameRenderingQueued(delta)
|
172
|
+
return false unless @physics.update(delta)
|
173
|
+
|
174
|
+
@objects.each_value {|obj|
|
175
|
+
obj.update(delta)
|
176
|
+
}
|
177
|
+
|
178
|
+
return @script.update(delta)
|
179
|
+
end
|
180
|
+
|
181
|
+
def clean_up()
|
182
|
+
@physics.finalize()
|
183
|
+
@objects = {}
|
184
|
+
@view.stop()
|
185
|
+
end
|
186
|
+
|
187
|
+
# called by Garden class.
|
188
|
+
# clear all managers.
|
189
|
+
def finalize()
|
190
|
+
@physics.finalize()
|
191
|
+
@view.root.saveConfig()
|
192
|
+
@view.finalize()
|
193
|
+
@objects = {}
|
194
|
+
end
|
195
|
+
|
196
|
+
|
197
|
+
# quit the current running garden.
|
198
|
+
def quit()
|
199
|
+
@quit = true
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
end # module
|