Sharedwww / talks / 2006-05-09-sage-digipen / tutorial / buggy.pyOpen in CoCalc
Author: William A. Stein
1
#!/usr/bin/env python
2
# Soya 3D tutorial
3
# Copyright (C) 2004 Jean-Baptiste 'Jiba' LAMY
4
# Copyright (C) 2001-2002 Bertrand 'blam!' LAMY
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20
# buggy: ODE
21
22
# Need pi and stuff
23
from math import *
24
25
# Imports and inits Soya (see lesson basic-1.py).
26
27
import sys, os, os.path, soya, soya.sdlconst as sdl
28
from soya import ode
29
30
soya.init()
31
soya.path.append(os.path.join(os.path.dirname(sys.argv[0]), "data"))
32
33
# Creates the scene.
34
scene = ode.World()
35
scene.gravity = (0.0, -9.8, 0.0)
36
37
# Create a collision space
38
print "creating space"
39
space = ode.HashSpace(scene)
40
41
# Creates a new landscape in the scene.
42
print "creating land"
43
land = soya.Land(scene)
44
45
# Gets the image "map1.png" from the tutorial data dir, and create the landscape
46
# from this image. The image dimension must be power of 2 plus 1 : (2 ** n) + 1.
47
48
print "setting land's image"
49
land.from_image(soya.Image.get("map1.png"))
50
51
# By default, the landscape height ranges from 0.0 (black pixels) to 1.0 (white pixels).
52
# Here, we multiply the height by 4.0 so it ranges from 0.0 to 4.0.
53
54
print "multiplying height"
55
land.multiply_height(1.0)
56
57
# Now that we have the landscape, we are going to texture it
58
# (see lesson modeling-material-2 about texturing). First, we creates two textured
59
# materials.
60
61
print "making materials"
62
material1 = soya.Material(soya.Image.get("block2.png"))
63
material2 = soya.Material(soya.Image.get("metal1.png"))
64
65
# asigns MATERIAL1 to any point whose height is in the range 0.0-6.0, and material2 to
66
# any point whose height is in the range 6.0-8.0 (remember, height ranges from 0.0 to 8.0).
67
68
print "setting land's materials"
69
land.set_material_layer(material1, 0.0, 3.0)
70
land.set_material_layer(material2, 3.0, 4.0)
71
72
# Assigns material1 to any point whose height is in the range 0.0-8.0 and if the angle
73
# between the surface normal and the verticalvector is in the range 0.0-20.0.
74
75
#land.set_material_layer_angle(material1, 0.0, 8.0, 0.0, 20.0)
76
77
# Now we set some Land attributes:
78
# - texture_factor specifies how much the textures are zoomed (higher values mean
79
# smaller texture)
80
81
# - scale_factor specifies how the landscape is scaled in the 2 horizontal dimensions.
82
83
# - the 2 last attributes influence the behaviour of the level of detail (LOD) algorithm
84
# (LOD means that parts of the landscape are rendered with more detail / more triangle
85
# if they are close to the camera). They are a trading between speed and quality.
86
#
87
# The higher split_factor is, the better precision you have (it means more triangles
88
# to draw the Land even far from Camera).
89
90
# the values below are the default ones.
91
92
land.texture_factor = 1.0
93
94
# XXX for some reason collisions don't work if this is set to anything other
95
# than 1.0
96
land.scale_factor = 1.0
97
98
land.split_factor = 2.0
99
100
# Moves the landscape.
101
land.y = -2.5
102
#land.scale(8.0, 1.0, 8.0)
103
# Make sure not to modify the land once the simulation starts, because
104
# the AABB is not recalculated
105
land_geom = ode.GeomLand(land, space)
106
land_geom.set_xyz(0.0, -2.5, 0.0)
107
#land_geom = ode.Land(land, space)
108
#print land_geom.getAABB()
109
110
# Adds a light.
111
112
light = soya.Light(scene)
113
light.set_xyz(0.0, 30.0, 0.0)
114
115
class Car(ode.Body):
116
speed = 3.0
117
118
def __init__(self, scene):
119
120
# Set initial turn angle to 0
121
# XXX should do Ackerman steering and probably differential
122
# drive on the rear wheels
123
self.turn_angle = 0.0
124
125
print "making SimpleSpace"
126
self.space = ode.SimpleSpace(None, space)
127
print "setting car's shape"
128
car_shape = soya.Shape.load("buggy_chassis")
129
print "Initializing body"
130
ode.Body.__init__(self, scene, shape=car_shape)
131
132
print "Creating chassis geom"
133
#self.chassis_geom = ode.GeomShape(self, self.space)
134
135
print "setting car's mass"
136
car_mass = ode.Mass()
137
car_mass.setBox(1.0, 5.0, 2.0, 4.0)
138
car_mass.adjust(7.0)
139
140
self.mass = car_mass
141
142
print "making wheel mass object"
143
# Create the wheels
144
wheel_shape = soya.Shape.load("wheel4")
145
wheel_mass = ode.Mass()
146
wheel_mass.setSphere(1.0, 1.0)
147
wheel_mass.adjust(1.0)
148
149
self.wheels = []
150
# Make sure the wheel geoms don't get garbage collected
151
# XXX this shouldn't be necessary
152
self.wheel_geoms = []
153
print "making wheels"
154
for i in range(4):
155
wheel = ode.Body(scene, shape=wheel_shape)
156
wheel.mass = wheel_mass
157
wheel_geom = ode.GeomSphere(wheel, self.space, 1.0)
158
#wheel_geom = ode.GeomShape(wheel, space)
159
self.wheel_geoms.append(wheel_geom)
160
self.wheels.append(wheel)
161
162
print "setting wheels' positions"
163
self.wheels[0].set_xyz(2.5, 0.0, -2.0)
164
self.wheels[1].set_xyz(2.5, 0.0, 2.0)
165
self.wheels[2].set_xyz(-2.5, 0.0, -2.0)
166
self.wheels[3].set_xyz(-2.5, 0.0, 2.0)
167
168
self.wheel_joints = []
169
for i in range(4):
170
joint = ode.Hinge2Joint(scene)
171
joint.attach(self, self.wheels[i])
172
joint.anchor = (self.wheels[i].x, self.wheels[i].y, self.wheels[i].z)
173
joint.axis1 = (0.0, 1.0, 0.0)
174
joint.axis2 = (0.0, 0.0, 1.0)
175
joint.suspension_erp = 0.25
176
joint.suspension_cfm = 0.004
177
178
joint.velocity2 = 0.0
179
joint.fmax2 = 120.0
180
181
# Only set stops on the back wheels. The controller for the
182
# front wheels will handle them.
183
if i > 2:
184
joint.lo_stop = 0.0
185
joint.hi_stop = 0.0
186
187
joint.fmax = 120.0
188
189
self.wheel_joints.append(joint)
190
191
def begin_round(self):
192
ode.Body.begin_round(self)
193
194
for event in soya.process_event():
195
if event[0] == sdl.KEYDOWN:
196
if event[1] == sdl.K_UP:
197
for joint in self.wheel_joints:
198
joint.velocity2 = self.speed
199
elif event[1] == sdl.K_DOWN:
200
for joint in self.wheel_joints:
201
joint.velocity2 = -self.speed
202
elif event[1] == sdl.K_LEFT:
203
self.turn_angle = -0.25 * pi
204
elif event[1] == sdl.K_RIGHT:
205
self.turn_angle = 0.25 * pi
206
elif event[1] == sdl.K_q:
207
soya.IDLER.stop()
208
elif event[1] == sdl.K_r:
209
self.wheels[0].set_xyz(2.5, 0.0, -2.0)
210
self.wheels[1].set_xyz(2.5, 0.0, 2.0)
211
self.wheels[2].set_xyz(-2.5, 0.0, -2.0)
212
self.wheels[3].set_xyz(-2.5, 0.0, 2.0)
213
elif event[1] == sdl.K_w:
214
soya.toggle_wireframe()
215
216
elif event[1] == sdl.K_ESCAPE: soya.IDLER.stop()
217
218
if event[0] == sdl.KEYUP:
219
if event[1] == sdl.K_UP:
220
for joint in self.wheel_joints:
221
joint.velocity2 = 0.0
222
elif event[1] == sdl.K_DOWN:
223
for joint in self.wheel_joints:
224
joint.velocity2 = 0.0
225
elif event[1] in (sdl.K_LEFT, sdl.K_RIGHT):
226
self.turn_angle = 0.0
227
228
for i in (0, 1):
229
# Steer the wheels to the desired position
230
# we should do ackerman steering here
231
joint = self.wheel_joints[i]
232
233
v = (self.turn_angle - joint.angle1) * 10.0
234
joint.velocity = v
235
236
237
print "making car"
238
car = Car(scene)
239
print "setting car's position"
240
car.set_xyz(32.0, 30.0, 20.0)
241
print "done"
242
243
camera = soya.TravelingCamera(scene)
244
245
traveling = soya.ThirdPersonTraveling(car)
246
#traveling = soya.ThirdPersonTraveling(soya.Point(car, 0.0, 1.0, 0.0))
247
traveling.distance = 15.0
248
#traveling.smooth_move = 1
249
traveling.smooth_rotation = 0
250
#traveling.direction = soya.Vector(camera, 1.0, 2.0, 0.0)
251
#traveling.incline_as = None
252
253
camera.add_traveling(traveling)
254
camera.speed = 0.3
255
camera.set_xyz(16.0, 15.0, 0.0)
256
camera.look_at(car)
257
258
#camera = MovableCamera(scene)
259
#camera.set_xyz(16.0, 6.0, 0.0)
260
#camera.look_at(soya.Point(scene, 16.0, 6.0, 10.0))
261
soya.set_root_widget(camera)
262
263
contactgroup = ode.JointGroup()
264
265
def near_callback(g1, g2):
266
"""Called for each potentially intersecting geom. Not called (right now)
267
for spaces because they're handled automatically."""
268
269
#print g1, g2
270
271
contacts = ode.collide(g1, g2, 20)
272
273
for contact in contacts:
274
#print contact
275
# Set surface parameters here
276
#contact.setMu(5.0)
277
joint = ode.ContactJoint(scene, contactgroup, contact)
278
joint.attach(g1.body, g2.body)
279
280
281
class BuggyIdler(soya.Idler):
282
"""Idle with collision testing"""
283
284
def begin_round(self):
285
286
# Eliminate all contact joints
287
contactgroup.empty()
288
289
# First, do collisions
290
space.collide(near_callback)
291
292
# Do everything else
293
soya.Idler.begin_round(self)
294
295
296
print "idling"
297
BuggyIdler(scene).idle()
298
299