;
; ------------------------------------------------------------
;
;   PureBasic - CreateVehicle
;
;    (c) Fantaisie Software
;
; ------------------------------------------------------------
;
IncludeFile #PB_Compiler_Home + "examples/3d/Screen3DRequester.pb"

#CameraSpeed = 2

Global.f KeyX, KeyY, MouseX, MouseY, ElapsedTime

Structure Vector3
  x.f
  y.f
  z.f
EndStructure

Macro VECTOR3(V, a, b, c)
  V\x = a
  V\y = b
  V\z = c
EndMacro  

Structure s_Vehicle
  Chassis.i 
  Wheels.i[4]	
  EngineBrake.f
  EngineForce.f
  Steering.f
  
  SteeringLeft.i
  SteeringRight.i
EndStructure

Global Recul = #False 

Global MaxEngineForce.f = 2000.0
Global MaxEngineBrake.f = 150.0

Global SteeringIncrement.f = 0.5
Global SteeringClamp.f = 27     

Global WheelRadius.f = 0.5;
Global WheelWidth.f = 0.4 ;

Global SuspensionStiffness.f = 20.0
Global SuspensionDamping.f = 3.3
Global SuspensionCompression.f = 4.4
Global MaxSuspensionTravelCm.f = 500.0;
Global FrictionSlip.f = 20		

Global RollInfluence.f = 0.3
Global SuspensionRestLength.f = 0.6;

Global Vehicle.s_Vehicle

#CUBE_HALF_EXTENTS = 1

Declare BuildVehicle(*Vehicle.s_Vehicle)
Declare HandleVehicle()
Declare ControlVehicle(elapsedTime.f)
Declare.f  Interpolation(x1.f, x2.f, percent.f)

If InitEngine3D()
  
  InitSprite()
  InitKeyboard()
  InitMouse()
  
  If Screen3DRequester()
    Add3DArchive(#PB_Compiler_Home + "examples/3d/Data/Textures/", #PB_3DArchive_FileSystem)
    Add3DArchive(#PB_Compiler_Home + "examples/3d/Data/Packs/desert.zip", #PB_3DArchive_Zip)
    Add3DArchive(#PB_Compiler_Home + "examples/3d/Data/Scripts" , #PB_3DArchive_FileSystem)
    Parse3DScripts()
    
    WorldShadows(#PB_Shadow_Modulative, -1, RGB(105, 105, 105))
    
    ;- Material
    ;
    CreateMaterial(0, LoadTexture(0, "Wood.jpg"))
    GetScriptMaterial(1, "SphereMap/SphereMappedRustySteel")
    CreateMaterial(2, LoadTexture(2, "Dirt.jpg"))
    ScaleMaterial(2,0.05,0.05)
    GetScriptMaterial(3, "Scene/GroundBlend")
    
    ;-Ground
    ;
    CreatePlane(0, 500, 500, 5, 5, 5, 5)
    CreateEntity(0,MeshID(0),MaterialID(2))
    EntityRenderMode(0, 0) 
    CreateEntityBody(0, #PB_Entity_PlaneBody, 0, 0, 1)
    
    ;-Walls
    ;
    CreateCube(1, 1)
    CreateEntity(1,MeshID(1),MaterialID(2),0,1, 250)
    ScaleEntity(1,500,2,0.5) 
    CreateEntityBody(1, #PB_Entity_PlaneBody, 0, 0, 1, #PB_Vector_NegativeZ, -1,-1,-1)
    
    CreateEntity(2,MeshID(1),MaterialID(2),0,1, -250)
    ScaleEntity(2,500,2,0.5) 
    CreateEntityBody(2, #PB_Entity_PlaneBody, 0, 0, 1, #PB_Vector_Z, -1,-1,-1)
    
    CreateEntity(3,MeshID(1),MaterialID(2),250,1, 0)
    ScaleEntity(3,0.5,2,500) 
    CreateEntityBody(3, #PB_Entity_PlaneBody, 0, 0, 1, #PB_Vector_NegativeX, -1,-1,-1)
    
    CreateEntity(4,MeshID(1),MaterialID(2),-250,1, 0)
    ScaleEntity(4,0.5,2,500) 
    CreateEntityBody(4, #PB_Entity_PlaneBody, 0, 0, 1, #PB_Vector_X, -1,-1,-1)
    
    
    CylinderMEsh = CreateCylinder(#PB_Any, 0.5, 2)
    
    For i=-250 To 250 Step 30
      Cylinder = CreateEntity(#PB_Any,MeshID(CylinderMEsh),MaterialID(1), 0, 1, i)
      CreateEntityBody(Cylinder, #PB_Entity_CylinderBody, 0, 0, 1)
    Next
    
    ;- Light 
    ;
    CreateLight(0 ,RGB(190, 190, 190), 400, 120, 100,#PB_Light_Directional)
    SetLightColor(0, #PB_Light_SpecularColor, RGB(255*0.4, 255*0.4,255*0.4)) 
    LightDirection(0 ,0.55, -0.3, -0.75) 
    AmbientColor(RGB(255*0.2, 255*0.2,255*0.2))
    
    ;- Camera 
    ;
    CreateCamera(0, 0, 0, 100, 100)
    MoveCamera(0,  800, 400, 80, #PB_Absolute)
    
    ; SkyBox
    ;
    SkyBox("desert07.jpg")
    
    ;-
    BuildVehicle(@Vehicle)
    
    ;-Main 
    ;
    Repeat
      Screen3DEvents()
      
      ExamineMouse()
      ExamineKeyboard()
      
      HandleVehicle()
      
      ControlVehicle(ElapsedTime/20)
      
      CameraFollow(0, EntityID(Vehicle\Chassis),180, 3.5, 10, 0.1, 0.1)
      
      ElapsedTime = RenderWorld()
      
      FlipBuffers()
      
    Until KeyboardPushed(#PB_Key_Escape)   
    
    End 
    
  EndIf 
  
Else
  MessageRequester("Error","Can't initialize engine3D")
EndIf 

Procedure Clamp(*var.float, min.f, max.f)
  If *var\f < min
    *var\f = min
  ElseIf *var\f > max
    *var\f = max
  EndIf
EndProcedure  

Procedure BuildVehicle(*Vehicle.s_Vehicle)
  Protected.VECTOR3 connectionPointCS0; wheelDirectionCS0,wheelAxleCS,
  
  With *Vehicle  
    
    \SteeringLeft = #False
    \SteeringRight = #False
    
    \EngineForce = 0
    \Steering = 0
    
    
    ;- >>> create vehicle  <<<<<
    
    connectionHeight.f = 0.6
    
    ChassisMesh = CreateCube(#PB_Any, 2)
    
    ChassisEntity = CreateEntity(#PB_Any, MeshID(chassisMesh), MaterialID(3), 0, 1, 0)
    ScaleEntity(ChassisEntity, 0.8, 0.7, 2)
    \Chassis = CreateVehicle(#PB_Any)
    AddSubEntity(\Chassis, ChassisEntity, #PB_Entity_BoxBody)
    
    EntityRenderMode(\Chassis, #PB_Entity_CastShadow)
    CreateVehicleBody(\Chassis, 700, 0.3, 0.8,suspensionStiffness, suspensionCompression, suspensionDamping, maxSuspensionTravelCm, frictionSlip)
    
    MoveEntity(\Chassis, 0, 3, 0,#PB_Absolute)
    
    SetEntityAttribute(\Chassis, #PB_Entity_LinearDamping, 0.2)
    SetEntityAttribute(\Chassis, #PB_Entity_AngularDamping, 0.2)
        
    Wheel = CreateSphere(#PB_Any, WheelRadius)
    For i = 0 To 3
      \Wheels[i] = CreateEntity(#PB_Any, MeshID(Wheel), #PB_Material_None)
      ScaleEntity(\Wheels[i], WheelWidth,1,1)
    Next
    
    ;-WheelSteerable and WheelsEngine
    VECTOR3(connectionPointCS0, #CUBE_HALF_EXTENTS-(0.2*WheelWidth), connectionHeight,2*#CUBE_HALF_EXTENTS-WheelRadius)
    AddVehicleWheel(\Chassis, \Wheels[0], 
                    connectionPointCS0\x, connectionPointCS0\y,connectionPointCS0\z,
                    -1, 0,0, SuspensionRestLength, WheelRadius, #True, RollInfluence)
    
    
    VECTOR3(connectionPointCS0, -#CUBE_HALF_EXTENTS+(0.2*WheelWidth), connectionHeight, 2*#CUBE_HALF_EXTENTS-WheelRadius)
    AddVehicleWheel(\Chassis, \Wheels[1], 
                    connectionPointCS0\x, connectionPointCS0\y,connectionPointCS0\z,
                    -1, 0,0, SuspensionRestLength, WheelRadius, #True, RollInfluence)
    
    
    VECTOR3(connectionPointCS0, -#CUBE_HALF_EXTENTS+(0.2*WheelWidth), connectionHeight, -2*#CUBE_HALF_EXTENTS+WheelRadius);
    AddVehicleWheel(\Chassis, \Wheels[2], 
                    connectionPointCS0\x, connectionPointCS0\y,connectionPointCS0\z,
                    -1, 0,0, SuspensionRestLength, WheelRadius, #False, RollInfluence)
    
    
    VECTOR3(connectionPointCS0, #CUBE_HALF_EXTENTS-(0.2*WheelWidth), connectionHeight, -2*#CUBE_HALF_EXTENTS+WheelRadius);
    AddVehicleWheel(\Chassis, \Wheels[3], 
                    connectionPointCS0\x, connectionPointCS0\y,connectionPointCS0\z,
                    -1, 0,0, SuspensionRestLength, WheelRadius, #False, RollInfluence)
    
    
  EndWith
EndProcedure

Procedure HandleVehicle()
  
  If KeyboardPushed(#PB_Key_Left)  
    Vehicle\SteeringLeft = #True
    Vehicle\SteeringRight = #False
    
  ElseIf KeyboardPushed(#PB_Key_Right)  
    Vehicle\SteeringRight = #True
    Vehicle\SteeringLeft = #False
  Else 
    Vehicle\SteeringRight = #False
    Vehicle\SteeringLeft = #False          
  EndIf
  
  If KeyboardPushed(#PB_Key_Down) 
    If GetEntityAttribute(Vehicle\Chassis, #PB_Entity_LinearVelocity)< 0.4
      Recul = #True
    EndIf  
    If Recul 
      Vehicle\EngineForce = -MaxEngineForce
      Vehicle\EngineBrake = 0  
    Else
      Vehicle\EngineForce = 0
      Vehicle\EngineBrake = MaxEngineBrake
    EndIf 
  ElseIf KeyboardPushed(#PB_Key_Up) 
    Vehicle\EngineForce = MaxEngineForce
    Vehicle\EngineBrake = 0
  Else
    Vehicle\EngineBrake = MaxEngineForce/ 100
    Vehicle\EngineForce = 0
    Recul = #False
  EndIf
  
  
EndProcedure

Procedure ControlVehicle(elapsedTime.f)

  ; apply engine Force on relevant wheels
  For i = 0 To 1
    ApplyVehicleBrake(Vehicle\Chassis, i, Vehicle\EngineBrake)  
    ApplyVehicleForce(Vehicle\Chassis, i, Vehicle\EngineForce)
  Next
  
  
  If (Vehicle\SteeringLeft)
    
    Vehicle\Steering + SteeringIncrement*elapsedTime
    If (Vehicle\Steering > SteeringClamp)
      Vehicle\Steering = SteeringClamp
    EndIf  
    
  ElseIf (Vehicle\SteeringRight)
    
    Vehicle\Steering - SteeringIncrement*elapsedTime
    If (Vehicle\Steering < -SteeringClamp)
      Vehicle\Steering = -SteeringClamp
    EndIf  
    
  Else
     Vehicle\Steering = Interpolation(Vehicle\Steering, 0, 0.05)
    
  EndIf
  
  ; apply Steering on relevant wheels
  
  For i = 0 To 1	
    ApplyVehicleSteering(Vehicle\Chassis, i, Vehicle\Steering)
  Next
  
EndProcedure

Procedure.f Interpolation(x1.f, x2.f, percent.f)
  If percent<0
    percent=0
  EndIf
  If percent>1
    percent=1
  EndIf
  ProcedureReturn x1 + percent * (x2 - x1)
  
EndProcedure