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
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
46import sys, os, os.path, soya, soya.cube, soya.sphere, soya.sdlconst
47
48soya.init()
49soya.path.append(os.path.join(os.path.dirname(sys.argv[0]), "data"))
50
51# Creates the scene.
52
53scene = soya.World()
54
55
56# DragDropWorld is a world that allows to dragdrop its content with the mouse.
57
58class 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
127
128          # Store the current mouse position.
129
130          self.old_mouse = new_mouse
131
132
133# Creates a dragdrop world.
134
135world = DragDropWorld(scene)
136
137# Adds some volumes with different shapes, at different positions.
138
139red   = soya.Material(); red  .diffuse = (1.0, 0.0, 0.0, 1.0)
140green = soya.Material(); green.diffuse = (0.0, 1.0, 0.0, 1.0)
141blue  = soya.Material(); blue .diffuse = (0.0, 0.0, 1.0, 1.0)
142
143soya.Volume(world, soya.cube.Cube(None, red  ).shapify()).set_xyz(-1.0, -1.0, 1.0)
144soya.Volume(world, soya.cube.Cube(None, green).shapify()).set_xyz( 0.0, -1.0, 0.0)
145soya.Volume(world, soya.cube.Cube(None, blue ).shapify()).set_xyz( 1.0, -1.0, -1.0)
146
147soya.Volume(world, soya.sphere.Sphere().shapify()).set_xyz(1.0, 1.0, 0.0)
148
150
151light = soya.Light(scene)
152light.set_xyz(0.0, 0.2, 1.0)
153
154# Creates a camera.
155
156camera = soya.Camera(scene)
157camera.set_xyz(0.0, 0.0, 4.0)
158camera.fov = 100.0
159soya.set_root_widget(camera)
160
161
162# Main loop
163
164soya.Idler(scene).idle()
165
166
167# TODO / exercice : turn this demo into a puzzle game !
168