File: raypicking-2.py

package info (click to toggle)
soya-doc 0.14-2
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd, squeeze, wheezy
  • size: 15,264 kB
  • ctags: 1,039
  • sloc: python: 4,334; makefile: 9; sh: 5
file content (169 lines) | stat: -rw-r--r-- 5,823 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# -*- indent-tabs-mode: t -*-

# Soya 3D tutorial
# Copyright (C) 2004 Jean-Baptiste LAMY
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA


# raypicking-2: Drag-droppable 3D objects

# In this lesson, you'll learn how to use raypicking to grab the object under the mouse.

# Raypicking consists in casting a ray in a 3D World, and it returns information abour
# the object the ray hit.

# Soya provides 2 raypicking functions: raypick and raypick_b ("b" stands for boolean).
# The first version returns a (IMPACT, NORMAL) tuple. IMPACT is the impact Point, and
# IMPACT.parent is the object hit. NORMAL is the normal Vector of the object at the
# impact (usefull e.g. for reflection).
# The boolean version simply returns true if something is hit.

# Both take the same arguments:
# - ORIGIN:    the origin of the ray (a Position)
# - DIRECTION: the direction of the ray (a Vector)
# - DISTANCE:  the maximum distance of the ray; -1.0 (default) for no distance limit
# - HALF_LINE: if true (default), the ray goes only in the direction of DIRECTION.
#              if false, the ray goes both in DIRECTION and -DIRECTION, and so can hit
#              objects backward.
# - CULL_FACE  if true (default), does not take into account invisible sides of non-double
#              sided faces (Face.double_sided = 0).

# For speeding up, raypick has 2 optional arguments, a Point and a Vector. If given,
# these Point and Vector will be returned in the tuple, instead of creating new objects.


import sys, os, os.path, soya, soya.cube, soya.sphere, soya.sdlconst

soya.init()
soya.path.append(os.path.join(os.path.dirname(sys.argv[0]), "data"))

# Creates the scene.

scene = soya.World()


# DragDropWorld is a world that allows to dragdrop its content with the mouse.

class DragDropWorld(soya.World):
	def __init__(self, parent):
		soya.World.__init__(self, parent)
		
		# The object we are currently dragdroping (None => no dragdrop).
		
		self.dragdroping = None
		
		# The impact point
		
		self.impact = None
		
		
	def begin_round(self):
		soya.World.begin_round(self)
		
		# Processes the events
		
		for event in soya.process_event():
			
			# Mouse down initiates the dragdrop.
			
			if   event[0] == soya.sdlconst.MOUSEBUTTONDOWN:
				
				# The event give us the 2D mouse coordinates in pixel. The camera.coord2d_to_3d
				# convert these 2D pixel coordinates into a soy.Point object.
				
				mouse = camera.coord2d_to_3d(event[2], event[3])
				
				# Performs a raypicking, starting at the camera and going toward the mouse.
				# The vector_to method returns the vector between 2 positions.
				# This raypicking grabs anything that is under the mouse. Raypicking returns
				# None if nothing is encountered, or a (impact, normal) tuple, where impact is the
				# position of the impact and normal is the normal vector at this position.
				# The object encountered is impact.parent ; here, we don't need the normal.
				
				result = self.raypick(camera, camera.vector_to(mouse))
				if result:
					self.impact, normal = result
					self.dragdroping = self.impact.parent
					
					# Converts impact into the camera coordinate system, in order to get its Z value.
					# camera.coord2d_to_3d cannot choose a Z value for you, so you need to pass it
					# as a third argument (it defaults to -1.0). Then, we computes the old mouse
					# position, which has the same Z value than impact.
					
					self.impact.convert_to(camera)
					self.old_mouse = camera.coord2d_to_3d(event[2], event[3], self.impact.z)
					
					
			# Mouse up ends the dragdrop.
			
			elif event[0] == soya.sdlconst.MOUSEBUTTONUP:
				self.dragdroping = None
				
				
			# Mouse motion moves the dragdroping object, if there is one.
			
			elif event[0] == soya.sdlconst.MOUSEMOTION:
				if self.dragdroping:
					
					# Computes the new mouse position, at the same Z value than impact.
					
					new_mouse = camera.coord2d_to_3d(event[1], event[2], self.impact.z)
					
					# Translates dragdroping by a vector starting at old_mouse and ending at
					# new_mouse.
					
					self.dragdroping.add_vector(self.old_mouse.vector_to(new_mouse))
					
					# Store the current mouse position.
					
					self.old_mouse = new_mouse
					

# Creates a dragdrop world.

world = DragDropWorld(scene)

# Adds some bodys with different models, at different positions.

red   = soya.Material(); red  .diffuse = (1.0, 0.0, 0.0, 1.0)
green = soya.Material(); green.diffuse = (0.0, 1.0, 0.0, 1.0)
blue  = soya.Material(); blue .diffuse = (0.0, 0.0, 1.0, 1.0)

soya.Body(world, soya.cube.Cube(None, red  ).to_model()).set_xyz(-1.0, -1.0, 1.0)
soya.Body(world, soya.cube.Cube(None, green).to_model()).set_xyz( 0.0, -1.0, 0.0)
soya.Body(world, soya.cube.Cube(None, blue ).to_model()).set_xyz( 1.0, -1.0, -1.0)

soya.Body(world, soya.sphere.Sphere().to_model()).set_xyz(1.0, 1.0, 0.0)

# Adds a light.

light = soya.Light(scene)
light.set_xyz(0.0, 0.2, 1.0)

# Creates a camera.

camera = soya.Camera(scene)
camera.set_xyz(0.0, 0.0, 4.0)
camera.fov = 100.0
soya.set_root_widget(camera)


# Main loop

soya.MainLoop(scene).main_loop()


# TODO / exercice : turn this demo into a puzzle game !