Sharedwww / talks / 2006-05-09-sage-digipen / tutorial / raypicking-2.pyOpen in CoCalc
Author: William A. Stein
1
# Soya 3D tutorial
2
# Copyright (C) 2004 Jean-Baptiste LAMY
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18
19
# raypicking-2: Drag-dropable 3D objects
20
21
# In this lesson, you'll learn how to use raypicking to grab the object under the mouse.
22
23
# Raypicking consists in casting a ray in a 3D World, and it returns information abour
24
# the object the ray hit.
25
26
# Soya provides 2 raypicking functions: raypick and raypick_b ("b" stands for boolean).
27
# The first version returns a (IMPACT, NORMAL) tuple. IMPACT is the impact Point, and
28
# IMPACT.parent is the object hit. NORMAL is the normal Vector of the object at the
29
# impact (usefull e.g. for reflection).
30
# The boolean version simply returns true if something is hit.
31
32
# Both take the same arguments:
33
# - ORIGIN: the origin of the ray (a Position)
34
# - DIRECTION: the direction of the ray (a Vector)
35
# - DISTANCE: the maximum distance of the ray; -1.0 (default) for no distance limit
36
# - HALF_LINE: if true (default), the ray goes only in the direction of DIRECTION.
37
# if false, the ray goes both in DIRECTION and -DIRECTION, and so can hit
38
# objects backward.
39
# - CULL_FACE if true (default), does not take into account invisible sides of non-double
40
# sided faces (Face.double_sided = 0).
41
42
# For speeding up, raypick has 2 optional arguments, a Point and a Vector. If given,
43
# these Point and Vector will be returned in the tuple, instead of creating new objects.
44
45
46
import sys, os, os.path, soya, soya.cube, soya.sphere, soya.sdlconst
47
48
soya.init()
49
soya.path.append(os.path.join(os.path.dirname(sys.argv[0]), "data"))
50
51
# Creates the scene.
52
53
scene = soya.World()
54
55
56
# DragDropWorld is a world that allows to dragdrop its content with the mouse.
57
58
class DragDropWorld(soya.World):
59
def __init__(self, parent):
60
soya.World.__init__(self, parent)
61
62
# The object we are currently dragdroping (None => no dragdrop).
63
64
self.dragdroping = None
65
66
# The impact point
67
68
self.impact = None
69
70
71
def begin_round(self):
72
soya.World.begin_round(self)
73
74
# Processes the events
75
76
for event in soya.process_event():
77
78
# Mouse down initiates the dragdrop.
79
80
if event[0] == soya.sdlconst.MOUSEBUTTONDOWN:
81
82
# The event give us the 2D mouse coordinates in pixel. The camera.coord2d_to_3d
83
# convert these 2D pixel coordinates into a soy.Point object.
84
85
mouse = camera.coord2d_to_3d(event[2], event[3])
86
87
# Performs a raypicking, starting at the camera and going toward the mouse.
88
# The vector_to method returns the vector between 2 positions.
89
# This raypicking grabs anything that is under the mouse. Raypicking returns
90
# None if nothing is encountered, or a (impact, normal) tuple, where impact is the
91
# position of the impact and normal is the normal vector at this position.
92
# The object encountered is impact.parent ; here, we don't need the normal.
93
94
result = self.raypick(camera, camera.vector_to(mouse))
95
if result:
96
self.impact, normal = result
97
self.dragdroping = self.impact.parent
98
99
# Converts impact into the camera coordinate system, in order to get its Z value.
100
# camera.coord2d_to_3d cannot choose a Z value for you, so you need to pass it
101
# as a third argument (it defaults to -1.0). Then, we computes the old mouse
102
# position, which has the same Z value than impact.
103
104
self.impact.convert_to(camera)
105
self.old_mouse = camera.coord2d_to_3d(event[2], event[3], self.impact.z)
106
107
108
# Mouse up ends the dragdrop.
109
110
elif event[0] == soya.sdlconst.MOUSEBUTTONUP:
111
self.dragdroping = None
112
113
114
# Mouse motion moves the dragdroping object, if there is one.
115
116
elif event[0] == soya.sdlconst.MOUSEMOTION:
117
if self.dragdroping:
118
119
# Computes the new mouse position, at the same Z value than impact.
120
121
new_mouse = camera.coord2d_to_3d(event[1], event[2], self.impact.z)
122
123
# Translates dragdroping by a vector starting at old_mouse and ending at
124
# new_mouse.
125
126
self.dragdroping.add_vector(self.old_mouse.vector_to(new_mouse))
127
128
# Store the current mouse position.
129
130
self.old_mouse = new_mouse
131
132
133
# Creates a dragdrop world.
134
135
world = DragDropWorld(scene)
136
137
# Adds some volumes with different shapes, at different positions.
138
139
red = soya.Material(); red .diffuse = (1.0, 0.0, 0.0, 1.0)
140
green = soya.Material(); green.diffuse = (0.0, 1.0, 0.0, 1.0)
141
blue = soya.Material(); blue .diffuse = (0.0, 0.0, 1.0, 1.0)
142
143
soya.Volume(world, soya.cube.Cube(None, red ).shapify()).set_xyz(-1.0, -1.0, 1.0)
144
soya.Volume(world, soya.cube.Cube(None, green).shapify()).set_xyz( 0.0, -1.0, 0.0)
145
soya.Volume(world, soya.cube.Cube(None, blue ).shapify()).set_xyz( 1.0, -1.0, -1.0)
146
147
soya.Volume(world, soya.sphere.Sphere().shapify()).set_xyz(1.0, 1.0, 0.0)
148
149
# Adds a light.
150
151
light = soya.Light(scene)
152
light.set_xyz(0.0, 0.2, 1.0)
153
154
# Creates a camera.
155
156
camera = soya.Camera(scene)
157
camera.set_xyz(0.0, 0.0, 4.0)
158
camera.fov = 100.0
159
soya.set_root_widget(camera)
160
161
162
# Main loop
163
164
soya.Idler(scene).idle()
165
166
167
# TODO / exercice : turn this demo into a puzzle game !
168