# -*- indent-tabs-mode: t -*-

# Soya 3D tutorial 
# Copyright (C) 2004      Jean-Baptiste 'Jiba'  LAMY
# Copyright (C) 2001-2003 Bertrand      'blam!' 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


# portal-1: Portal

# A portal is a 2D rectangle used to link 2 worlds just as if the world
# beyond was seen through an open door.
# Portals are usefull to compute visibility and avoid renderering the
# world beyond when it is not visible.


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

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

m1 = soya.Material(soya.Image.get("block2.png"))
m2 = soya.Material(soya.Image.get("metal1.png"))

scene = soya.World()

# Create world 1

w1 = soya.World(scene)
w1.set_xyz(0.0, 0.0, 0.0)
w1.atmosphere = soya.Atmosphere()
w1.atmosphere.bg_color = (0.0, 0.0, 1.0, 1.0)
w1.atmosphere.ambient  = (0.0, 0.5, 0.0, 1.0)
w1.atmosphere.fog_color = (0.0, 0.0, 1.0, 1.0)
w1.atmosphere.fog_start = 10.0
w1.atmosphere.fog_end = 50.0
w1.atmosphere.fog = 1
w1.atmosphere.fog_type = 0
w1.set_model(soya.cube.Cube(None, m1).to_model())

# Create world 2
# Notice that w2 doesn't have the same atmosphere than w1

w2 = soya.World(scene)
w2.set_xyz(0.0, 0.0, -10.0)
w2.atmosphere = soya.SkyAtmosphere()
w2.atmosphere.bg_color  = (1.0, 0.0, 0.0, 1.0)
w2.atmosphere.ambient   = (0.5, 0.5, 0.0, 1.0)
w2.atmosphere.skyplane  = 1
w2.atmosphere.sky_color = (1.0, 1.0, 0.0, 1.0)
w2.atmosphere.cloud = soya.Material(soya.Image.get("cloud.png"))
w2.set_model(soya.cube.Cube(None, m2).to_model())


# Add a light in world 2.
# The light will not light the objects in world 1 unless light.top_level is true.

light = soya.Light(w2)
light.top_level = 0
light.ambient = (1.0, 1.0, 0.0, 1.0)
light.set_xyz(0.0, 2.0, 0.0)


# Portal creation
# Create a portal that link to world 2. The attribute "beyond" is the world beyond
# the portal

portal1 = soya.Portal(w1)
portal1.beyond = w2
portal1.set_xyz(0.0, 0.0, -5.0)

# To change the size of the portal, we scale it (the z scale factor has no meaning)

portal1.scale(4.0, 4.0, 1.0)
#   Attr bound_atm must be set to 1 if the 2 worlds linked doesn't have the
#   same atmosphere (else set the value to 0).
#portal1.bound_atmosphere = 1
#   Setting use_clip_plane to 1 will affect object rendered in world beyond,
#   this means only the part of the objects that are visible through the portal
#   2D rectangle will be rendered.
#portal1.nb_clip_planes = 4

# Create a portal that link world 2 to world 1

portal2 = soya.Portal(w2)
portal2.rotate_y(180.0)
portal2.beyond = w1
portal2.scale(4.0, 4.0, 1.0)
portal2.set_xyz(0.0, 0.0, 5.0)

#portal1.nb_clip_planes = 0
#portal2.nb_clip_planes = 0

# ASCII art representation of the two world :
#                           \
#               \            |
#    +-+         |           |
#    | |\        |           |
#    +-+ +        > world2   |
#     \ \|       |           |
#      +-+       |           |
#                |           |
# <-portal2->   /            |
#--------------+             |
# <-portal1->   \            |
#                |            > scene
#    +-+         |           |
#    | |\        |           |
#    +-+ +       |           |
#     \ \|       |           |
#      +-+        > world1   |
#                |           |
#     ___        |           |
#     \^/        |           |
#      V         |           |
#    camera     /            |
#                           /

class MovableCamera(soya.Camera):
	def __init__(self, parent):
		soya.Camera.__init__(self, parent)
		
		self.speed = soya.Vector(self)
		self.rotation_y_speed = 0.0
		self.rotation_x_speed = 0.0
		
	def begin_round(self):
		soya.Camera.begin_round(self)
		
		for event in soya.process_event():
			if event[0] == soya.sdlconst.KEYDOWN:
				if   event[1] == soya.sdlconst.K_UP:     self.speed.z = -1.0
				elif event[1] == soya.sdlconst.K_DOWN:   self.speed.z =  1.0
				elif event[1] == soya.sdlconst.K_LEFT:   self.rotation_y_speed =  10.0
				elif event[1] == soya.sdlconst.K_RIGHT:  self.rotation_y_speed = -10.0
				elif event[1] == soya.sdlconst.K_q:      soya.MAIN_LOOP.stop()
				elif event[1] == soya.sdlconst.K_ESCAPE: soya.MAIN_LOOP.stop()
			if event[0] == soya.sdlconst.KEYUP:
				if   event[1] == soya.sdlconst.K_UP:     self.speed.z = 0.0
				elif event[1] == soya.sdlconst.K_DOWN:   self.speed.z = 0.0
				elif event[1] == soya.sdlconst.K_LEFT:   self.rotation_y_speed = 0.0
				elif event[1] == soya.sdlconst.K_RIGHT:  self.rotation_y_speed = 0.0
		
		# Checks if the camera has passed through a portal.
		# First, collects all portals in the camera's root world.
		# the World.search_all method take a predicat (a one argument callable), and
		# returns a list of all items (recursively) in the world that satisfy the predicat.
		
		portals = camera.to_render.search_all(lambda item: isinstance(item, soya.Portal))
		
		# Then for each portal, checks if the camera has pass through it, and if so,
		# transfers the camera in the world beyond the portal.
		# The has_passed_through method takes two argument : the old position of the object
		# and the new one or (as here) the speed vector.
		
		for portal in portals:
			if portal.has_passed_through(self, self.speed):
				print "pass !", self.position(), self.speed
				portal.pass_through(camera)
				
	def advance_time(self, proportion):
		self.add_mul_vector(proportion, self.speed)
		self.turn_y(self.rotation_y_speed * proportion)
		self.turn_x(self.rotation_x_speed * proportion)


# Creates a movable camera, that render the world 1 only (to_render attribute)

camera = MovableCamera(scene)
camera.to_render = w1
camera.set_xyz(0.0, 0.0, 8.0)

root = soya.widget.Group()

root.add(camera)

label = soya.widget.Label(root, " - Portal demo - use cursor keys to move.")
label.resize_style = soya.widget.WIDGET_RESIZE_MAXIMIZE

soya.set_root_widget(root)

soya.MainLoop(scene).main_loop()
