#!/usr/bin/env python
# Soya 3D tutorial
# Copyright (C) 2004      Jean-Baptiste 'Jiba'  LAMY
# Copyright (C) 2001-2002 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


# buggy: ODE


# Imports and inits Soya (see lesson basic-1.py).

import sys, os, os.path, soya, soya.sdlconst as sdl
from soya import ode

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

# Creates the scene.
scene = ode.World()
scene.gravity = (0.0, -9.8, 0.0)

# Create a collision space
space = ode.HashSpace(world=scene)

# Creates a new landscape in the scene.
land = ode.Land(scene, space=space)

# Gets the image "map1.png" from the tutorial data dir, and create the landscape
# from this image. The image dimension must be power of 2 plus 1 : (2 ** n) + 1.

land.from_image(soya.Image.get("map1.png"))

# By default, the landscape height ranges from 0.0 (black pixels) to 1.0 (white pixels).
# Here, we multiply the height by 8.0 so it ranges from 0.0 to 8.0.

land.multiply_height(4.0)

# Now that we have the landscape, we are going to texture it
# (see lesson modeling-material-2 about texturing). First, we creates two textured
# materials.

material1 = soya.Material(soya.Image.get("block2.png"))
material2 = soya.Material(soya.Image.get("metal1.png"))

# asigns MATERIAL1 to any point whose height is in the range 0.0-6.0, and material2 to
# any point whose height is in the range 6.0-8.0 (remember, height ranges from 0.0 to 8.0).

land.set_material_layer(material1, 0.0, 3.0)
land.set_material_layer(material2, 3.0, 4.0)

# Assigns material1 to any point whose height is in the range 0.0-8.0 and if the angle
# between the surface normal and the verticalvector is in the range 0.0-20.0.

#land.set_material_layer_angle(material1, 0.0, 8.0, 0.0, 20.0)

# Now we set some Land attributes:
#  - texture_factor specifies how much the textures are zoomed (higher values mean
#    smaller texture)

#  - scale_factor specifies how the landscape is scaled in the 2 horizontal dimensions.

#  - the 2 last attributes influence the behaviour of the level of detail (LOD) algorithm
#    (LOD means that parts of the landscape are rendered with more detail / more triangle
#    if they are close to the camera). They are a trading between speed and quality.
#    
#    The higher split_factor is, the better precision you have (it means more triangles
#    to draw the Land even far from Camera).
#    
#    map_size represents the size of a map. A map is a square part of the Land that
#    computes its visibility and precision.

# the values below are the default ones.
 
land.texture_factor = 1.0
land.scale_factor   = 1.0
land.map_size       = 8
land.split_factor   = 2.0

# Moves the landscape.

land.y = -2.5

# Adds a light.

light = soya.Light(scene)
light.set_xyz(0.0, 15.0, 0.0)

class Car(ode.Body):
    speed = 3.0

    def __init__(self, scene):
        self.space = ode.SimpleSpace(space)
        car_shape = soya.Shape.load("buggy_chassis")
        ode.Body.__init__(self, scene, shape=car_shape)
        #self.set_xyz(16.0, 15.0, 10.0)
        
        car_mass = ode.Mass()
        car_mass.setBox(1.0, 5.0, 2.0, 4.0)
        car_mass.adjust(7.0)
        
        self.mass = car_mass

        # Create the wheels
        wheel_shape = soya.Shape.load("wheel4")
        wheel_mass = ode.Mass()
        wheel_mass.setSphere(1.0, 1.0)
        wheel_mass.adjust(1.0)
        
        self.wheels = []
        # Make sure the wheel geoms don't get garbage collected
        # XXX this shouldn't be necessary
        self.wheel_geoms = []
        for i in range(4):
            wheel = ode.Body(self, shape=wheel_shape)
            wheel.mass = wheel_mass
            wheel_geom = ode.GeomSphere(self.space, 1.0)
            wheel_geom.setBody(wheel)
            self.wheel_geoms.append(wheel_geom)
            self.wheels.append(wheel)
        
        self.wheels[0].set_xyz(2.5, 0.0, -2.0)
        self.wheels[1].set_xyz(2.5, 0.0, 2.0)
        self.wheels[2].set_xyz(-2.5, 0.0, -2.0)
        self.wheels[3].set_xyz(-2.5, 0.0, 2.0)

        self.joints = []
        for i in range(4):
            joint = ode.Hinge2Joint(scene)
            joint.attach(self, self.wheels[i])
            joint.anchor = (self.wheels[i].x, self.wheels[i].y, self.wheels[i].z)
            joint.axis1 = (0.0, 1.0, 0.0)
            joint.axis2 = (0.0, 0.0, 1.0)
            joint.suspension_erp = 0.25
            joint.suspension_cfm = 0.004
        
            joint.velocity2 = 0.0
            joint.fmax2 = 80.0
        
            joint.lo_stop = 0.0
            joint.hi_stop = 0.0
            joint.fmax = 50

            self.joints.append(joint)

    def begin_round(self):
        ode.Body.begin_round(self)

        for event in soya.process_event():
          if event[0] == sdl.KEYDOWN:
            if   event[1] == sdl.K_UP:
                for joint in self.joints:
                    joint.velocity2 = self.speed
            elif event[1] == sdl.K_DOWN:
                for joint in self.joints:
                    joint.velocity2 = -self.speed
            elif event[1] == sdl.K_LEFT:   pass
            elif event[1] == sdl.K_RIGHT:  pass
            elif event[1] == sdl.K_q:      soya.IDLER.stop()
            elif event[1] == sdl.K_r:
                self.wheels[0].set_xyz(2.5, 0.0, -2.0)
                self.wheels[1].set_xyz(2.5, 0.0, 2.0)
                self.wheels[2].set_xyz(-2.5, 0.0, -2.0)
                self.wheels[3].set_xyz(-2.5, 0.0, 2.0)
        
            elif event[1] == sdl.K_ESCAPE: soya.IDLER.stop()
    
          if event[0] == sdl.KEYUP:
            if   event[1] == sdl.K_UP:
                for joint in self.joints:
                    joint.velocity2 = 0.0
            elif event[1] == sdl.K_DOWN:
                for joint in self.joints:
                    joint.velocity2 = 0.0
            elif event[1] == sdl.K_LEFT:   pass
            elif event[1] == sdl.K_RIGHT:  pass
    

car = Car(scene)
car.set_xyz(16.0, 15.0, 10.0)

camera = soya.TravelingCamera(scene)

traveling = soya.ThirdPersonTraveling(car)
#traveling = soya.ThirdPersonTraveling(soya.Point(car, 0.0, 1.0, 0.0))
traveling.distance = 15.0
#traveling.smooth_move     = 1
traveling.smooth_rotation = 0
#traveling.direction = soya.Vector(camera, 1.0, 2.0, 0.0)
#traveling.incline_as = None

camera.add_traveling(traveling)
camera.speed = 0.3
camera.set_xyz(16.0, 15.0, 0.0)
camera.look_at(car)

#camera = MovableCamera(scene)
#camera.set_xyz(16.0, 6.0, 0.0)
#camera.look_at(soya.Point(scene, 16.0, 6.0, 10.0))
soya.set_root_widget(camera)

print "idling"
soya.Idler(scene).idle()
