Sharedwww / talks / 2006-05-09-sage-digipen / tutorial / buggy_trimesh.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(4.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.Land(land, space)
106
land_geom = ode.GeomLand(land, space)
107
land_geom.set_xyz(0.0, -2.5, 0.0)
108
#land_geom = ode.GeomBox(scene, space, (100.0, 1.0, 100.0))
109
#land_geom.y = -0.5
110
#print land_geom.getAABB()
111
112
# Adds a light.
113
114
light = soya.Light(scene)
115
light.set_xyz(0.0, 30.0, 0.0)
116
117
class Car(ode.Body):
118
speed = 3.0
119
120
def __init__(self, scene):
121
122
# Set initial turn angle to 0
123
# XXX should do Ackerman steering and probably differential
124
# drive on the rear wheels
125
self.turn_angle = 0.0
126
127
print "making SimpleSpace"
128
self.space = ode.SimpleSpace(None, space)
129
print "setting car's shape"
130
car_shape = soya.Shape.load("buggy_chassis")
131
print "Initializing body"
132
ode.Body.__init__(self, scene, shape=car_shape)
133
#self.set_xyz(32.0, 15.0, 20.0)
134
135
self.chassis_geom = ode.GeomShape(self, self.space)
136
137
print "setting car's mass"
138
car_mass = ode.Mass()
139
car_mass.setBox(1.0, 5.0, 2.0, 4.0)
140
car_mass.adjust(7.0)
141
142
self.mass = car_mass
143
144
print "making wheel mass object"
145
# Create the wheels
146
wheel_shape = soya.Shape.load("wheel4")
147
wheel_mass = ode.Mass()
148
wheel_mass.setSphere(1.0, 1.0)
149
wheel_mass.adjust(1.0)
150
151
self.wheels = []
152
# Make sure the wheel geoms don't get garbage collected
153
# XXX this shouldn't be necessary
154
self.wheel_geoms = []
155
print "making wheels"
156
for i in range(4):
157
wheel = ode.Body(scene, shape=wheel_shape)
158
wheel.mass = wheel_mass
159
#wheel_geom = ode.GeomSphere(wheel, self.space, 1.0)
160
wheel_geom = ode.GeomShape(wheel, self.space)
161
self.wheel_geoms.append(wheel_geom)
162
self.wheels.append(wheel)
163
164
print "setting wheels' positions"
165
self.wheels[0].set_xyz(2.5, 0.0, -2.0)
166
self.wheels[1].set_xyz(2.5, 0.0, 2.0)
167
self.wheels[2].set_xyz(-2.5, 0.0, -2.0)
168
self.wheels[3].set_xyz(-2.5, 0.0, 2.0)
169
170
self.wheel_joints = []
171
for i in range(4):
172
joint = ode.Hinge2Joint(scene)
173
joint.attach(self, self.wheels[i])
174
joint.anchor = (self.wheels[i].x, self.wheels[i].y, self.wheels[i].z)
175
joint.axis1 = (0.0, 1.0, 0.0)
176
joint.axis2 = (0.0, 0.0, 1.0)
177
joint.suspension_erp = 0.25
178
joint.suspension_cfm = 0.004
179
180
joint.velocity2 = 0.0
181
joint.fmax2 = 120.0
182
183
# Only set stops on the back wheels. The controller for the
184
# front wheels will handle them.
185
if i > 2:
186
joint.lo_stop = 0.0
187
joint.hi_stop = 0.0
188
189
joint.fmax = 120.0
190
191
self.wheel_joints.append(joint)
192
193
def begin_round(self):
194
ode.Body.begin_round(self)
195
196
for event in soya.process_event():
197
if event[0] == sdl.KEYDOWN:
198
if event[1] == sdl.K_UP:
199
for joint in self.wheel_joints:
200
joint.velocity2 = self.speed
201
elif event[1] == sdl.K_DOWN:
202
for joint in self.wheel_joints:
203
joint.velocity2 = -self.speed
204
elif event[1] == sdl.K_LEFT:
205
self.turn_angle = -0.25 * pi
206
elif event[1] == sdl.K_RIGHT:
207
self.turn_angle = 0.25 * pi
208
elif event[1] == sdl.K_q:
209
soya.IDLER.stop()
210
elif event[1] == sdl.K_r:
211
self.wheels[0].set_xyz(2.5, 0.0, -2.0)
212
self.wheels[1].set_xyz(2.5, 0.0, 2.0)
213
self.wheels[2].set_xyz(-2.5, 0.0, -2.0)
214
self.wheels[3].set_xyz(-2.5, 0.0, 2.0)
215
elif event[1] == sdl.K_w:
216
soya.toggle_wireframe()
217
218
elif event[1] == sdl.K_ESCAPE: soya.IDLER.stop()
219
220
if event[0] == sdl.KEYUP:
221
if event[1] == sdl.K_UP:
222
for joint in self.wheel_joints:
223
joint.velocity2 = 0.0
224
elif event[1] == sdl.K_DOWN:
225
for joint in self.wheel_joints:
226
joint.velocity2 = 0.0
227
elif event[1] in (sdl.K_LEFT, sdl.K_RIGHT):
228
self.turn_angle = 0.0
229
230
for i in (0, 1):
231
# Steer the wheels to the desired position
232
# we should do ackerman steering here
233
joint = self.wheel_joints[i]
234
235
v = (self.turn_angle - joint.angle1) * 10.0
236
joint.velocity = v
237
238
239
print "making car"
240
car = Car(scene)
241
print "setting car's position"
242
car.set_xyz(32.0, 30.0, 20.0)
243
print "done"
244
245
camera = soya.TravelingCamera(scene)
246
247
traveling = soya.ThirdPersonTraveling(car)
248
#traveling = soya.ThirdPersonTraveling(soya.Point(car, 0.0, 1.0, 0.0))
249
traveling.distance = 15.0
250
#traveling.smooth_move = 1
251
traveling.smooth_rotation = 0
252
#traveling.direction = soya.Vector(camera, 1.0, 2.0, 0.0)
253
#traveling.incline_as = None
254
255
camera.add_traveling(traveling)
256
camera.speed = 0.3
257
camera.set_xyz(16.0, 15.0, 0.0)
258
camera.look_at(car)
259
260
#camera = MovableCamera(scene)
261
#camera.set_xyz(16.0, 6.0, 0.0)
262
#camera.look_at(soya.Point(scene, 16.0, 6.0, 10.0))
263
soya.set_root_widget(camera)
264
265
contactgroup = ode.JointGroup()
266
267
def near_callback(g1, g2):
268
"""Called for each potentially intersecting geom. Not called (right now)
269
for spaces because they're handled automatically."""
270
271
#print g1, g2
272
273
contacts = ode.collide(g1, g2)
274
275
for contact in contacts:
276
#print contact
277
# Set surface parameters here
278
#contact.setMu(5.0)
279
joint = ode.ContactJoint(scene, contactgroup, contact)
280
joint.attach(g1.body, g2.body)
281
282
283
class BuggyIdler(soya.Idler):
284
"""Idle with collision testing"""
285
286
def begin_round(self):
287
288
# Eliminate all contact joints
289
contactgroup.empty()
290
291
# First, do collisions
292
space.collide(near_callback)
293
294
# Do everything else
295
soya.Idler.begin_round(self)
296
297
#print car.x, car.y, car.z, land_geom.params
298
299
print "idling"
300
BuggyIdler(scene).idle()
301
302