Page 1 of 1

Re: Smooth align Player to Object/Terrain he's standing

Posted: Tue Sep 03, 2013 6:17 pm
by Fred
Please post a snippet showing the issue if you want help.

Re: Smooth align Player to Object/Terrain he's standing

Posted: Sat Sep 07, 2013 7:13 pm
by Comtois
how to use normalX/Y/Z() ? here an example (smooth's stuff is not done)

Code: Select all

IncludeFile "Screen3DRequester.pb"

#TerrainMiniX = 0
#TerrainMiniY = 0
#TerrainMaxiX = 0
#TerrainMaxiY = 0
#PlayerSpeed = 6
#CameraSpeed = 1

Define.f KeyX, KeyY, MouseX, MouseY, TimeSinceLastFrame
Declare InitBlendMaps()

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

Structure s_Key
  Up.i
  Down.i
  Left.i
  Right.i
  StrafeLeft.i
  StrafeRight.i
  Jump.i
EndStructure

Structure s_Entity
  Entity.i
  EntityBody.i
  BodyOffsetY.f 
  elapsedTime.f
  Key.s_Key
  MainNode.i  
  SightNode.i
  CameraNode.i  
  ForwardNode.i
  StrafeNode.i
EndStructure

Structure s_Camera
  Camera.i
  Tightness.f
  CameraNode.i 
  TargetNode.i
EndStructure    

Macro GetNodePosition(Position, Node)
  Position\x = NodeX(Node)  
  Position\y = NodeY(Node)  
  Position\z = NodeZ(Node)  
EndMacro

Macro SubVector3(V, V1, V2)
  V\x = V1\x - V2\x
  V\y = V1\y - V2\y
  V\z = V1\z - V2\z
EndMacro

;-Declare
Declare HandleEntity(*Entity.s_Entity)
Declare CameraTrack(*Camera.s_Camera, *Entity.s_Entity)
Declare OnGround(*Entity.s_Entity)


Define Robot.s_Entity
Define Camera.s_Camera

; OpenGL needs to have CG enabled to work (Linux and OS X have OpenGL by default)
;
CompilerIf #PB_Compiler_OS <> #PB_OS_Windows Or Subsystem("OpenGL")
  Flags = #PB_Engine3D_EnableCG
CompilerEndIf

If InitEngine3D(Flags)
  
  InitSprite()
  InitKeyboard()
  InitMouse()
  
  If Screen3DRequester()
    Add3DArchive("Data/Textures/"       , #PB_3DArchive_FileSystem)
    Add3DArchive("Data/Models"          , #PB_3DArchive_FileSystem)
    Add3DArchive("Data/Scripts"         , #PB_3DArchive_FileSystem)
    Add3DArchive("Data/Textures/nvidia" , #PB_3DArchive_FileSystem)
    Add3DArchive("Data/Packs/desert.zip", #PB_3DArchive_Zip)
    Parse3DScripts()
    
    WorldShadows(#PB_Shadow_Modulative, 200, RGB(120, 120, 120))
    
    MaterialFilteringMode(#PB_Default, #PB_Material_Anisotropic, 8)
    
    ;- Light 
    ;
    light = CreateLight(#PB_Any ,RGB(185, 185, 185), 4000, 1200, 1000, #PB_Light_Directional)
    SetLightColor(light, #PB_Light_SpecularColor, RGB(255*0.4, 255*0.4,255*0.4)) 
    LightDirection(light ,0.55, -0.3, -0.75) 
    AmbientColor(RGB(5, 5,5))
    
    ;- Camera 
    ;
    CreateCamera(0, 0, 0, 100, 100)
    CameraBackColor(0, RGB(5, 5, 10))
    
    ;----------------------------------
    ;-terrain definition
    SetupTerrains(LightID(Light), 300, #PB_Terrain_NormalMapping)
    ;-initialize terrain 
    CreateTerrain(0, 513, 1200, 60, 3, "TerrainPhysic", "dat")
    ;-set all texture will be use when terrrain will be constructed 
    AddTerrainTexture(0,  0, 10, "dirt_grayrocky_diffusespecular.jpg",  "dirt_grayrocky_normalheight.jpg")
    AddTerrainTexture(0,  1,  3, "grass_green-01_diffusespecular.jpg", "grass_green-01_normalheight.jpg")
    AddTerrainTexture(0,  2, 20, "growth_weirdfungus-03_diffusespecular.jpg", "growth_weirdfungus-03_normalheight.jpg")
    
    ;-Construct terrains
    For ty = #TerrainMiniY To #TerrainMaxiY
      For tx = #TerrainMiniX To #TerrainMaxiX
        Imported = DefineTerrainTile(0, tx, ty, "terrain513.png", ty % 2, tx % 2)    
      Next
    Next  
    BuildTerrain(0)  
    
    If Imported = #True
      InitBlendMaps()
      UpdateTerrain(0)
      
      ; If enabled, it will save the terrain as a (big) cache for a faster load next time the program is executed
      ; SaveTerrain(0, #False)
    EndIf 
    
    ; enable shadow terrain
    TerrainRenderMode(0, 0)
    
    ;Add Physic
    TerrainPhysicBody(0, 0.1, 1)
    ;Texture
    CreateTexture(1, 256, 256)
    StartDrawing(TextureOutput(1))
    Box(0, 0, 256, 256, $002255)
    DrawingMode(#PB_2DDrawing_Outlined)
    Box(0, 0, 256, 256, $FFFFFF)
    Box(10, 10, 236, 236, $FFFF)
    StopDrawing()
    
    ;Material
    CreateMaterial(0, LoadTexture(0, "r2skin.jpg"))
    CreateMaterial(1, TextureID(1))
    CreateMaterial(2, LoadTexture(2, "Dirt.jpg"))
    CreateMaterial(3, LoadTexture(3, "Wood.jpg"))
    GetScriptMaterial(4, "Scene/GroundBlend")
    
    ;Robot
    CreateSphere(0, 1)
    Rob = LoadMesh(#PB_Any, "Robot.mesh")
    CreateEntity (0, MeshID(Rob), #PB_Material_None)
    ScaleEntity(0, 0.1, 0.1, 0.1)
    StartEntityAnimation(0, "Walk")
    
    ;Robot Body
    CreateEntity(1, MeshID(0), #PB_Material_None, 0, 426, 0)
    HideEntity(1, 1)
    
    ;Body
    EntityPhysicBody(1, #PB_Entity_SphereBody, 1, 0, 0) 
    
    ; Skybox
    SkyBox("desert07.jpg")
    
    ;
    With Robot
      \Entity = 0
      \EntityBody = 1
      \BodyOffsetY = 1 
      
      \Key\Down        = #PB_Key_Down
      \Key\Left        = #PB_Key_Left
      \Key\Right       = #PB_Key_Right
      \Key\Up          = #PB_Key_Up
      \Key\StrafeLeft  = #PB_Key_X
      \Key\StrafeRight = #PB_Key_C
      \Key\Jump        = #PB_Key_Space
      
      \MainNode    = CreateNode(#PB_Any) ; Entity position
      \SightNode   = CreateNode(#PB_Any,  12,  2,  0) ; For cameraLookAt 
      \CameraNode  = CreateNode(#PB_Any, -14, 10,  0) ; Camera position
      \ForwardNode = CreateNode(#PB_Any,   1,  0,  0) ; Direction normalized 
      \StrafeNode  = CreateNode(#PB_Any,   0,  0, -1) ; Direction normalized 
      
      AttachNodeObject(\MainNode, NodeID(\SightNode))
      AttachNodeObject(\MainNode, NodeID(\CameraNode))   
      AttachNodeObject(\MainNode, NodeID(\ForwardNode))     
      AttachNodeObject(\MainNode, NodeID(\StrafeNode))    
      AttachNodeObject(\MainNode, EntityID(\Entity))
    EndWith
    
    ;-Camera
    With Camera  
      \Camera = 0
      \Tightness = 0.035
      ; Camera use 2 nodes
      \CameraNode = CreateNode(#PB_Any, -3000, 700, 0) ; Camera position
      \TargetNode = CreateNode(#PB_Any) ; For cameraLookAt 
      AttachNodeObject(\CameraNode, CameraID(\Camera))
    EndWith  
    
    ;==================================
    ; create material
    Red = GetScriptMaterial(#PB_Any, "Color/Red")
    Blue = GetScriptMaterial(#PB_Any, "Color/Blue")
    Yellow = GetScriptMaterial(#PB_Any, "Color/Yellow")
    Green = GetScriptMaterial(#PB_Any, "Color/Green")
    
    ;==================================
    ; create Sphere
    MeshSphere = CreateSphere(#PB_Any, 1.0)
    For i = 0 To 5
      Entity=CreateEntity(#PB_Any, MeshID(MeshSphere), MaterialID(Green))
      MoveEntity(Entity, Random(200)-100, 80, Random(400)-200, #PB_Absolute)
      ; create bodies
      EntityPhysicBody(Entity, #PB_Entity_SphereBody, 1.0)
    Next
    
    ;==================================
    ; create Cylinder
    MeshCylinder = CreateCylinder(#PB_Any, 1.0, 6)
    For i = 0 To 5
      Entity=CreateEntity(#PB_Any, MeshID(MeshCylinder), MaterialID(Red))
      MoveEntity(Entity, Random(200)-100, 80, Random(400)-200, #PB_Absolute)
      ; create bodies
      EntityPhysicBody(Entity, #PB_Entity_CylinderBody, 1.0, 0, 1)
    Next
    
    ;==================================
    ; create Cube
    MeshCube = CreateCube(#PB_Any, 0.25)
    For i = 0 To 5
      Entity=CreateEntity(#PB_Any, MeshID(MeshCube), MaterialID(Yellow))
      MoveEntity(Entity, Random(200)-100, 80, Random(400)-200, #PB_Absolute)
      ; create bodies
      EntityPhysicBody(Entity, #PB_Entity_BoxBody, 1.0)
    Next
    
    Repeat
      
      Screen3DEvents()   
      
      Robot\elapsedTime = TimeSinceLastFrame 
      
      HandleEntity(@Robot)
      
      CameraTrack(@Camera, @Robot)
      
      TimeSinceLastFrame = RenderWorld(60) * 40 / 1000
      FlipBuffers()
      
    Until KeyboardPushed(#PB_Key_Escape)   
    
    End 
    
  EndIf 
Else
  CompilerIf #PB_Compiler_OS <> #PB_OS_Windows Or Subsystem("OpenGL")
    ;
    ; Terrain on Linux/OSX and Windows with OpenGL needs CG toolkit from nvidia
    ; It can be freely downloaded and installed from this site: https://developer.nvidia.com/cg-toolkit-download
    ;
    MessageRequester("Error","Can't initialize engine3D (Please ensures than CG Toolkit from nvidia is correcly installed)")
  CompilerElse
    MessageRequester("Error","Can't initialize engine3D")
  CompilerEndIf
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 InitBlendMaps()
  minHeight1.f = 7
  fadeDist1.f = 40
  minHeight2.f = 7
  fadeDist2.f = 15    
  For ty = #TerrainMiniY To #TerrainMaxiY
    For tx = #TerrainMiniX To #TerrainMaxiX
      Size = TerrainTileLayerMapSize(0, tx, ty)
      For y = 0 To Size-1
        For x = 0 To Size-1
          Height.f = TerrainTileHeightAtPosition(0, tx, ty, 1, x, y)
          
          val.f = (Height - minHeight1) / fadeDist1
          Clamp(@val, 0, 1)
          SetTerrainTileLayerBlend(0, tx, ty, 1, x, y, val)
          
          val.f = (Height - minHeight2) / fadeDist2
          Clamp(@val, 0, 1)
          SetTerrainTileLayerBlend(0, tx, ty, 2, x, y, val)
        Next
      Next
      UpdateTerrainTileLayerBlend(0, tx, ty, 1)
      UpdateTerrainTileLayerBlend(0, tx, ty, 2)
    Next
  Next  
EndProcedure  

Procedure OnGround(*Entity.s_Entity)
  With *Entity
    Result = RayCollide(EntityX(\EntityBody), EntityY(\EntityBody), EntityZ(\EntityBody), EntityX(\EntityBody), EntityY(\EntityBody)-7,  EntityZ(\EntityBody)) 
    ;CreateLine3D(20,EntityX(\EntityBody), EntityY(\EntityBody), EntityZ(\EntityBody), $FFFF, EntityX(\EntityBody), EntityY(\EntityBody)-7,  EntityZ(\EntityBody), $FFFF)  
    Delta.f = EntityY(\EntityBody) - PickY() - \BodyOffsetY

    If Result=-1 Or (Result>-1 And (delta >= 1))
      ProcedureReturn 0
    Else
      CreateLine3D(20,PickX(), PickY(), PickZ(), $FFFF, PickX()+NormalX()*6, PickY()+NormalY()*6, PickZ()+NormalZ()*6, $FFFF)  
      RotateEntity(\Entity, Degree(ASin(NormalZ())), 0, Degree(ASin(-NormalX()))) 
      ProcedureReturn 1
    EndIf  
  EndWith  
EndProcedure

Procedure HandleEntity(*Entity.s_Entity)
  Protected.Vector3 Forward, Strafe, PosMain, PosDir, PosStrafe
  Protected.f Speed, Speed2, x, y, MouseX, MouseY
  Static Jump.f, MemJump.i, Rot.Vector3, Trans.Vector3, Clic
  
  With *Entity
    GetNodePosition(PosMain, \MainNode)
    GetNodePosition(PosDir, \ForwardNode)
    GetNodePosition(PosStrafe, \StrafeNode)
    SubVector3(Forward, PosDir, PosMain)
    SubVector3(Strafe, PosStrafe, PosMain)
    
    Speed = #PlayerSpeed * \elapsedTime 
    Speed2 = Speed / 2
    
    If ExamineKeyboard()
      
      If KeyboardReleased(#PB_Key_F5)
        WorldDebug(#PB_World_DebugBody)
      ElseIf KeyboardReleased(#PB_Key_F6)
        WorldDebug(#PB_World_DebugEntity)
      ElseIf KeyboardReleased(#PB_Key_F7)
        WorldDebug(#PB_World_DebugNone)
      EndIf
      
      If KeyboardPushed(\Key\Jump) And OnGround(*Entity) 
        Jump = 2
        MemJump = 1
      EndIf  
      
      Rot\x * 0.30
      Rot\y * 0.30
      Rot\z * 0.30
      Trans\x * 0.20
      Trans\y = Jump
      Trans\z * 0.20
      
      If KeyboardPushed(\Key\Up)
        Trans\x + Forward\x * Speed 
        Trans\z + Forward\z * Speed
      ElseIf KeyboardPushed(\Key\Down)
        Trans\x + Forward\x * -Speed2
        Trans\z + Forward\z * -Speed2
      EndIf
      
      If KeyboardPushed(\Key\Left)
        Rot\y + 2 * \elapsedTime
      ElseIf KeyboardPushed(\Key\Right)
        Rot\y - 2 * \elapsedTime
      EndIf 
      
      If KeyboardPushed(\Key\StrafeLeft)
        Trans\x + Strafe\x * Speed2
        Trans\z + Strafe\z * Speed2
      ElseIf KeyboardPushed(\Key\StrafeRight)
        Trans\x + Strafe\x * -Speed2
        Trans\z + Strafe\z * -Speed2
      EndIf 
      
      If OnGround(*Entity) 
        Jump = 0 
      ElseIf MemJump  
        Jump + 2 * \elapsedTime 
        If Jump > 15 
          MemJump = 0
        EndIf  
      Else  
        Jump - 9 
      EndIf  
      
    EndIf
    
    MoveEntity  (\EntityBody, Trans\x, Trans\y, Trans\z)
    RotateEntity(\EntityBody, 0, Rot\y, 0, #PB_Relative)   
    
    MoveNode(\MainNode, EntityX(\EntityBody), EntityY(\EntityBody)-\BodyOffsetY, EntityZ(\EntityBody), #PB_Absolute) 
    RotateNode(\MainNode, 0, EntityYaw(\EntityBody), 0) 
 
  EndWith   
EndProcedure

Procedure CameraTrack(*Camera.s_Camera, *Entity.s_Entity)
  Protected.Vector3 CameraPosition, TargetPosition 
  Protected.f x, y, z
  
  GetNodePosition(CameraPosition, *Entity\CameraNode)
  GetNodePosition(TargetPosition, *Entity\SightNode)
  x = NodeX(*Camera\CameraNode)
  y = NodeY(*Camera\CameraNode)
  z = NodeZ(*Camera\CameraNode)
  x = (CameraPosition\x - x) *  *Camera\Tightness
  y = (CameraPosition\y - y) *  *Camera\Tightness
  z = (CameraPosition\z - z) *  *Camera\Tightness
  MoveNode(*Camera\CameraNode, x, y, z)
  
  x = NodeX(*Camera\TargetNode)
  y = NodeY(*Camera\TargetNode)
  z = NodeZ(*Camera\TargetNode)
  x = (TargetPosition\x - x) *  *Camera\Tightness
  y = (TargetPosition\y - y) *  *Camera\Tightness
  z = (TargetPosition\z - z) *  *Camera\Tightness
  MoveNode(*Camera\TargetNode, x, y, z)
  
  CameraLookAt(*Camera\Camera, NodeX(*Camera\TargetNode), NodeY(*Camera\TargetNode), NodeZ(*Camera\TargetNode))
  
EndProcedure

Re: Smooth align Player to Object/Terrain he's standing

Posted: Sun Sep 08, 2013 11:12 am
by Comtois
added smooth orientation

Code: Select all

;
; ------------------------------------------------------------
;
;   PureBasic - Terrain : Physic
;
;    (c) 2012 - Fantaisie Software
;
; ------------------------------------------------------------
;

IncludeFile "Screen3DRequester.pb"

#TerrainMiniX = 0
#TerrainMiniY = 0
#TerrainMaxiX = 0
#TerrainMaxiY = 0
#PlayerSpeed = 6
#CameraSpeed = 1

Define.f KeyX, KeyY, MouseX, MouseY, TimeSinceLastFrame
Declare InitBlendMaps()

Structure Vector3
  x.f
  y.f
  z.f
EndStructure
Structure Quaternion
  w.f
  x.f
  y.f
  z.f
EndStructure
Structure Colonne3
  n.f[3]
EndStructure
Structure Matrix3
  m.Colonne3[3]
EndStructure
Structure s_Key
  Up.i
  Down.i
  Left.i
  Right.i
  StrafeLeft.i
  StrafeRight.i
  Jump.i
EndStructure

Structure s_Entity
  Entity.i
  EntityBody.i
  BodyOffsetY.f 
  elapsedTime.f
  Key.s_Key
  MainNode.i  
  SightNode.i
  Direction.i
EndStructure

Structure s_Camera
  Camera.i
  Tightness.f
  CameraNode.i 
  TargetNode.i
EndStructure    

Macro GetNodePosition(Position, Node)
  Position\x = NodeX(Node)  
  Position\y = NodeY(Node)  
  Position\z = NodeZ(Node)  
EndMacro

Macro SubVector3(V, V1, V2)
  V\x = V1\x - V2\x
  V\y = V1\y - V2\y
  V\z = V1\z - V2\z
EndMacro
Macro AddVector3(V, V1, V2)
  V\x = V1\x + V2\x
  V\y = V1\y + V2\y
  V\z = V1\z + V2\z
EndMacro
Macro PRODUIT_VECTORIEL(N, V1, V2)
  N\x = ((V1\y * V2\z) - (V1\z * V2\y))
  N\y = ((V1\z * V2\x) - (V1\x * V2\z))
  N\z = ((V1\x * V2\y) - (V1\y * V2\x))
EndMacro
;-Declare
Declare HandleEntity(*Entity.s_Entity)
Declare CameraTrack(*Camera.s_Camera, *Entity.s_Entity)
Declare OnGround(*Entity.s_Entity)


Define Robot.s_Entity
Define Camera.s_Camera
Global R.Quaternion

; OpenGL needs to have CG enabled to work (Linux and OS X have OpenGL by default)
;
CompilerIf #PB_Compiler_OS <> #PB_OS_Windows Or Subsystem("OpenGL")
  Flags = #PB_Engine3D_EnableCG
CompilerEndIf

If InitEngine3D(Flags)
  
  InitSprite()
  InitKeyboard()
  InitMouse()
  
  If Screen3DRequester()
    Add3DArchive("Data/Textures/"       , #PB_3DArchive_FileSystem)
    Add3DArchive("Data/Models"          , #PB_3DArchive_FileSystem)
    Add3DArchive("Data/Scripts"         , #PB_3DArchive_FileSystem)
    Add3DArchive("Data/Textures/nvidia" , #PB_3DArchive_FileSystem)
    Add3DArchive("Data/Packs/desert.zip", #PB_3DArchive_Zip)
    Parse3DScripts()
    
    WorldShadows(#PB_Shadow_Modulative, 200, RGB(120, 120, 120))
    
    MaterialFilteringMode(#PB_Default, #PB_Material_Anisotropic, 8)
    
    ;- Light 
    ;
    light = CreateLight(#PB_Any ,RGB(185, 185, 185), 4000, 1200, 1000, #PB_Light_Directional)
    SetLightColor(light, #PB_Light_SpecularColor, RGB(255*0.4, 255*0.4,255*0.4)) 
    LightDirection(light ,0.55, -0.3, -0.75) 
    AmbientColor(RGB(5, 5,5))
    
    ;----------------------------------
    ;-terrain definition
    SetupTerrains(LightID(Light), 300, #PB_Terrain_NormalMapping)
    ;-initialize terrain 
    CreateTerrain(0, 513, 1200, 60, 3, "TerrainPhysic", "dat")
    ;-set all texture will be use when terrrain will be constructed 
    AddTerrainTexture(0,  0, 10, "dirt_grayrocky_diffusespecular.jpg",  "dirt_grayrocky_normalheight.jpg")
    AddTerrainTexture(0,  1,  3, "grass_green-01_diffusespecular.jpg", "grass_green-01_normalheight.jpg")
    AddTerrainTexture(0,  2, 20, "growth_weirdfungus-03_diffusespecular.jpg", "growth_weirdfungus-03_normalheight.jpg")
    
    ;-Construct terrains
    For ty = #TerrainMiniY To #TerrainMaxiY
      For tx = #TerrainMiniX To #TerrainMaxiX
        Imported = DefineTerrainTile(0, tx, ty, "terrain513.png", ty % 2, tx % 2)    
      Next
    Next  
    BuildTerrain(0)  
    
    If Imported = #True
      InitBlendMaps()
      UpdateTerrain(0)
      
      ; If enabled, it will save the terrain as a (big) cache for a faster load next time the program is executed
      ; SaveTerrain(0, #False)
    EndIf 
    
    ; enable shadow terrain
    TerrainRenderMode(0, 0)
    
    ;Add Physic
    TerrainPhysicBody(0, 0.1, 1)
    ;Texture
    CreateTexture(1, 256, 256)
    StartDrawing(TextureOutput(1))
    Box(0, 0, 256, 256, $002255)
    DrawingMode(#PB_2DDrawing_Outlined)
    Box(0, 0, 256, 256, $FFFFFF)
    Box(10, 10, 236, 236, $FFFF)
    StopDrawing()
    
    ;Material
    CreateMaterial(0, LoadTexture(0, "r2skin.jpg"))
    CreateMaterial(1, TextureID(1))
    CreateMaterial(2, LoadTexture(2, "Dirt.jpg"))
    CreateMaterial(3, LoadTexture(3, "Wood.jpg"))
    GetScriptMaterial(4, "Scene/GroundBlend")
    
    ;Robot
    CreateSphere(0, 1)
    Rob = LoadMesh(#PB_Any, "Robot.mesh")
    CreateEntity (0, MeshID(Rob), #PB_Material_None)
    ScaleEntity(0, 0.1, 0.1, 0.1)
    StartEntityAnimation(0, "Walk")
    
    ;Robot Body
    CreateEntity(1, MeshID(0), #PB_Material_None, 0, 426, 0)
    HideEntity(1, 1)
    
    ;Body
    EntityPhysicBody(1, #PB_Entity_SphereBody, 1, 0, 0) 
    
    ; Skybox
    SkyBox("desert07.jpg")
    
    ;
    With Robot
      \Entity = 0
      \EntityBody = 1
      \BodyOffsetY = 1 
      
      \Key\Down        = #PB_Key_Down
      \Key\Left        = #PB_Key_Left
      \Key\Right       = #PB_Key_Right
      \Key\Up          = #PB_Key_Up
      \Key\StrafeLeft  = #PB_Key_X
      \Key\StrafeRight = #PB_Key_C
      \Key\Jump        = #PB_Key_Space
      
      ;\MainNode    = CreateNode(#PB_Any) ; Entity position
      \Direction   = CreateNode(#PB_Any,   1,  0,  0) ; For cameraLookAt 
      \SightNode   = CreateNode(#PB_Any,  12,  2,  0) ; For cameraLookAt 
      
      AttachEntityObject(\EntityBody, "", NodeID(\Direction))
      AttachEntityObject(\EntityBody, "", NodeID(\SightNode))
      ;AttachNodeObject(\MainNode, EntityID(\Entity))
    EndWith
    
    ;- Camera 
    ;
    CreateCamera(0, 0, 0, 100, 100)
    CameraBackColor(0, RGB(5, 5, 10))
    
    ;==================================
    ; create material
    Red = GetScriptMaterial(#PB_Any, "Color/Red")
    Blue = GetScriptMaterial(#PB_Any, "Color/Blue")
    Yellow = GetScriptMaterial(#PB_Any, "Color/Yellow")
    Green = GetScriptMaterial(#PB_Any, "Color/Green")
    
    ;==================================
    ; create Sphere
    MeshSphere = CreateSphere(#PB_Any, 1.0)
    For i = 0 To 5
      Entity=CreateEntity(#PB_Any, MeshID(MeshSphere), MaterialID(Green))
      MoveEntity(Entity, Random(200)-100, 80, Random(400)-200, #PB_Absolute)
      ; create bodies
      EntityPhysicBody(Entity, #PB_Entity_SphereBody, 1.0)
    Next
    
    ;==================================
    ; create Cylinder
    MeshCylinder = CreateCylinder(#PB_Any, 1.0, 6)
    For i = 0 To 5
      Entity=CreateEntity(#PB_Any, MeshID(MeshCylinder), MaterialID(Red))
      MoveEntity(Entity, Random(200)-100, 80, Random(400)-200, #PB_Absolute)
      ; create bodies
      EntityPhysicBody(Entity, #PB_Entity_CylinderBody, 1.0, 0, 1)
    Next
    
    ;==================================
    ; create Cube
    MeshCube = CreateCube(#PB_Any, 0.25)
    For i = 0 To 5
      Entity=CreateEntity(#PB_Any, MeshID(MeshCube), MaterialID(Yellow))
      MoveEntity(Entity, Random(200)-100, 80, Random(400)-200, #PB_Absolute)
      ; create bodies
      EntityPhysicBody(Entity, #PB_Entity_BoxBody, 1.0)
    Next
    
    Repeat
      
      Screen3DEvents()   
      
      Robot\elapsedTime = TimeSinceLastFrame 
      
      HandleEntity(@Robot)
      
      CameraFollow(0, EntityID(Robot\Entity), -90, EntityY(Robot\Entity) + 10, 25, 0.1, 0.1, 0)
      CameraLookAt(0, NodeX(Robot\SightNode), NodeY(Robot\SightNode), NodeZ(Robot\SightNode))
      TimeSinceLastFrame = RenderWorld(60) * 40 / 1000
      FlipBuffers()
      
    Until KeyboardPushed(#PB_Key_Escape)   
    
    End 
    
  EndIf 
Else
  CompilerIf #PB_Compiler_OS <> #PB_OS_Windows Or Subsystem("OpenGL")
    ;
    ; Terrain on Linux/OSX and Windows with OpenGL needs CG toolkit from nvidia
    ; It can be freely downloaded and installed from this site: https://developer.nvidia.com/cg-toolkit-download
    ;
    MessageRequester("Error","Can't initialize engine3D (Please ensures than CG Toolkit from nvidia is correcly installed)")
  CompilerElse
    MessageRequester("Error","Can't initialize engine3D")
  CompilerEndIf
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 InitBlendMaps()
  minHeight1.f = 7
  fadeDist1.f = 40
  minHeight2.f = 7
  fadeDist2.f = 15    
  For ty = #TerrainMiniY To #TerrainMaxiY
    For tx = #TerrainMiniX To #TerrainMaxiX
      Size = TerrainTileLayerMapSize(0, tx, ty)
      For y = 0 To Size-1
        For x = 0 To Size-1
          Height.f = TerrainTileHeightAtPosition(0, tx, ty, 1, x, y)
          
          val.f = (Height - minHeight1) / fadeDist1
          Clamp(@val, 0, 1)
          SetTerrainTileLayerBlend(0, tx, ty, 1, x, y, val)
          
          val.f = (Height - minHeight2) / fadeDist2
          Clamp(@val, 0, 1)
          SetTerrainTileLayerBlend(0, tx, ty, 2, x, y, val)
        Next
      Next
      UpdateTerrainTileLayerBlend(0, tx, ty, 1)
      UpdateTerrainTileLayerBlend(0, tx, ty, 2)
    Next
  Next  
EndProcedure  

Procedure Normalize(*V.Vector3)
  Protected.f magSq, oneOverMag 
  
  magSq = *V\x * *V\x + *V\y * *V\y + *V\z * *V\z 
  If magsq > 0
    oneOverMag = 1.0 / Sqr(magSq)
    *V\x * oneOverMag
    *V\y * oneOverMag
    *V\z * oneOverMag
  EndIf  
  
EndProcedure

Procedure.f QuaternionNormalise(*Q.Quaternion)
  Protected.f magSq, oneOverMag 
  
  magSq = *Q\w**Q\w+*Q\x**Q\x+*Q\y**Q\y+*Q\z**Q\z
  oneOverMag = 1.0 / Sqr(magSq)
  *Q\w * oneOverMag
  *Q\x * oneOverMag
  *Q\y * oneOverMag
  *Q\z * oneOverMag
EndProcedure


Procedure.f DotProduct(*V1.Vector3, *V2.Vector3)
  ProcedureReturn *V1\x * *V2\x + *V1\y * *V2\y + *V1\z * *V2\z   
EndProcedure  

Procedure.f  QuaternionDot(*Q.Quaternion, *rkQ.Quaternion) 
  
  ProcedureReturn *Q\w**rkQ\w+*Q\x**rkQ\x+*Q\y**rkQ\y+*Q\z**rkQ\z
EndProcedure

Procedure FromRotationMatrix(*Q.Quaternion, *kRot.Matrix3)
  
  ; Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes
  ; article "Quaternion Calculus and Fast Animation".
  
  fTrace.f = *kRot\m[0]\n[0]+*kRot\m[1]\n[1]+*kRot\m[2]\n[2]
  fRoot.f
  
  If fTrace > 0.0
    
    ; |w| > 1/2, may As well choose w > 1/2
    fRoot =Sqr(fTrace + 1.0);  2w
    *Q\w = 0.5*fRoot
    fRoot = 0.5/fRoot;   1/(4w)
    *Q\x = (*kRot\m[2]\n[1]-*kRot\m[1]\n[2])*fRoot;
    *Q\y = (*kRot\m[0]\n[2]-*kRot\m[2]\n[0])*fRoot;
    *Q\z = (*kRot\m[1]\n[0]-*kRot\m[0]\n[1])*fRoot;
    
  Else
    
    ; |w| <= 1/2
    Dim s_iNext(2)
    s_iNext(0) =  1
    s_iNext(1) =  2
    s_iNext(2) =  0
    
    i = 0
    If *kRot\m[1]\n[1] > *kRot\m[0]\n[0]
      i = 1
    EndIf    
    If *kRot\m[2]\n[2] > *kRot\m[i]\n[i]
      i = 2
    EndIf    
    j = s_iNext(i)
    k = s_iNext(j)
    
    fRoot =Sqr(*kRot\m[i]\n[i]-*kRot\m[j]\n[j]-*kRot\m[k]\n[k] + 1.0)
    Dim *apkQuat.Float(2)
    *apkQuat(0) = @*Q\x
    *apkQuat(1) = @*Q\y
    *apkQuat(2) = @*Q\z
    
    *apkQuat(i)\f = 0.5*fRoot
    fRoot = 0.5/fRoot
    *Q\w = (*kRot\m[k]\n[j]-*kRot\m[j]\n[k])*fRoot
    *apkQuat(j)\f = (*kRot\m[j]\n[i]+*kRot\m[i]\n[j])*fRoot
    *apkQuat(k)\f = (*kRot\m[k]\n[i]+*kRot\m[i]\n[k])*fRoot
  EndIf
EndProcedure

Procedure QuaternionFromAxes(*Q.Quaternion, *xaxis.Vector3, *yaxis.Vector3, *zaxis.Vector3)
  
  Protected kRot.Matrix3
  
  kRot\m[0]\n[0] = *xaxis\x
  kRot\m[1]\n[0] = *xaxis\y
  kRot\m[2]\n[0] = *xaxis\z
  
  kRot\m[0]\n[1] = *yaxis\x
  kRot\m[1]\n[1] = *yaxis\y
  kRot\m[2]\n[1] = *yaxis\z
  
  kRot\m[0]\n[2] = *zaxis\x
  kRot\m[1]\n[2] = *zaxis\y
  kRot\m[2]\n[2] = *zaxis\z
  
  FromRotationMatrix(*Q, @kRot)
  
EndProcedure


Procedure Slerp(*t.Quaternion, fT.f, *rkP.Quaternion, *rkQ.Quaternion, shortestPath)
  msEpsilon = 1e-03
  fCos.f = QuaternionDot(*rkP, *rkQ)
  rkT.Quaternion
  
  ; Do we need To invert rotation?
  If Bool((fCos < 0.0) And shortestPath)
    
    fCos = -fCos
    rkT\w = -*rkQ\w
    rkT\x = -*rkQ\x
    rkT\y = -*rkQ\y
    rkT\z = -*rkQ\z
  Else
    
    rkT\w = *rkQ\w
    rkT\x = *rkQ\x
    rkT\y = *rkQ\y
    rkT\z = *rkQ\z
  EndIf
  
  If (Abs(fCos) < 1 - msEpsilon)
    
    ; Standard Case (slerp)
    fSin.f = Sqr(1 - (fCos*fCos))
    fAngle.f =ATan2(fSin, fCos)
    fInvSin.f = 1.0 / fSin;
    fCoeff0.f = Sin((1.0 - fT) * fAngle) * fInvSin
    fCoeff1.f = Sin(fT * fAngle) * fInvSin
    *t\w = fCoeff0 * *rkP\w + fCoeff1 * rkT\w
    *t\x = fCoeff0 * *rkP\x + fCoeff1 * rkT\x
    *t\y = fCoeff0 * *rkP\y + fCoeff1 * rkT\y
    *t\z = fCoeff0 * *rkP\z + fCoeff1 * rkT\z
  Else
    
    ; There are two situations:
    ; 1. "rkP" And "rkQ" are very close (fCos ~= +1), so we can do a linear
    ;    interpolation safely.
    ; 2. "rkP" And "rkQ" are almost inverse of each other (fCos ~= -1), there
    ;    are an infinite number of possibilities interpolation. but we haven't
    ;    have method To fix this Case, so just use linear interpolation here.
    *t\w = (1.0 - fT) * *rkP\w + fT * rkT\w
    *t\x = (1.0 - fT) * *rkP\x + fT * rkT\x
    *t\y = (1.0 - fT) * *rkP\y + fT * rkT\y
    *t\z = (1.0 - fT) * *rkP\z + fT * rkT\z
    ; taking the complement requires renormalisation
    QuaternionNormalise(*t)
  EndIf
EndProcedure

Procedure OnGround(*Entity.s_Entity)
  Protected.Quaternion S, Q
  Protected.Vector3 xaxis, yaxis, zaxis
  
  With *Entity
    Result = RayCollide(EntityX(\EntityBody), EntityY(\EntityBody), EntityZ(\EntityBody), EntityX(\EntityBody), EntityY(\EntityBody)-7,  EntityZ(\EntityBody)) 
    ;CreateLine3D(20,EntityX(\EntityBody), EntityY(\EntityBody), EntityZ(\EntityBody), $FFFF, EntityX(\EntityBody), EntityY(\EntityBody)-7,  EntityZ(\EntityBody), $FFFF)  
    Delta.f = EntityY(\EntityBody) - PickY() - \BodyOffsetY
    
    If Result=-1 Or (Result>-1 And (delta >= 1))
      ProcedureReturn 0
    Else
      ;CreateLine3D(20,PickX(), PickY(), PickZ(), $FFFF, PickX()+NormalX()*6, PickY()+NormalY()*6, PickZ()+NormalZ()*6, $FFFF)  
      
      xaxis\x = NodeX(\Direction) - EntityX(\EntityBody)
      xaxis\y = NodeY(\Direction) - EntityY(\EntityBody)
      xaxis\z = NodeZ(\Direction) - EntityZ(\EntityBody)
      
      yaxis\x = NormalX()
      yaxis\y = NormalY()
      yaxis\z = NormalZ()
      
      PRODUIT_VECTORIEL(zaxis, xaxis, yaxis)
      PRODUIT_VECTORIEL(xaxis, yaxis, zaxis)
      
      QuaternionFromAxes(@Q, @xaxis, @yaxis, @zaxis)
      
      FetchOrientation(EntityID(\Entity))
      S\w = GetW()
      S\x = GetX()
      S\y = GetY()
      S\z = GetZ()
      Slerp(@R, 0.05, @S, @Q, 0) 
      SetOrientation(EntityID(\Entity),r\x, r\y, r\z, r\w)
      ProcedureReturn 1
    EndIf  
  EndWith  
EndProcedure

Procedure HandleEntity(*Entity.s_Entity)
  Protected.f Speed, Speed2, x, y, MouseX, MouseY
  Static Jump.f, MemJump.i, Rot.Vector3, Trans.Vector3, Clic
  
  With *Entity
    
    Speed = #PlayerSpeed * \elapsedTime 
    Speed2 = Speed / 2
    
    If ExamineKeyboard()
      
      If KeyboardReleased(#PB_Key_F5)
        WorldDebug(#PB_World_DebugBody)
      ElseIf KeyboardReleased(#PB_Key_F6)
        WorldDebug(#PB_World_DebugEntity)
      ElseIf KeyboardReleased(#PB_Key_F7)
        WorldDebug(#PB_World_DebugNone)
      EndIf
      
      If KeyboardPushed(\Key\Jump) And OnGround(*Entity) 
        Jump = 2
        MemJump = 1
      EndIf  
      
      Rot\x * 0.30
      Rot\y * 0.30
      Rot\z * 0.30
      Trans\x * 0.20
      Trans\y = Jump
      Trans\z * 0.20
      
      If KeyboardPushed(\Key\Up)
        Trans\x = Speed
      ElseIf KeyboardPushed(\Key\Down)
        Trans\x = -Speed2
      EndIf
      
      If KeyboardPushed(\Key\Left)
        Rot\y + 2 * \elapsedTime
      ElseIf KeyboardPushed(\Key\Right)
        Rot\y - 2 * \elapsedTime
      EndIf 
      
      If KeyboardPushed(\Key\StrafeLeft)
        Trans\z = -Speed2
      ElseIf KeyboardPushed(\Key\StrafeRight)
        Trans\z = Speed2
      EndIf 
      
      
      If OnGround(*Entity) 
        Jump = 0 
        
      ElseIf MemJump  
        Jump + 2 * \elapsedTime 
        If Jump > 15 
          MemJump = 0
        EndIf  
      Else  
        Jump - 9 
      EndIf  
      
    EndIf
    
    MoveEntity  (\EntityBody, Trans\x, Trans\y, Trans\z, #PB_Local)
    RotateEntity(\EntityBody, 0, Rot\y, 0, #PB_Relative)   
    MoveEntity(\Entity, EntityX(\EntityBody), EntityY(\EntityBody)-\BodyOffsetY, EntityZ(\EntityBody), #PB_Absolute) 
    
  EndWith   
EndProcedure

Re: Smooth align Player to Object/Terrain he's standing

Posted: Sun Sep 08, 2013 1:47 pm
by Num3
Looks good!

Re: Smooth align Player to Object/Terrain he's standing

Posted: Sun Sep 08, 2013 3:34 pm
by Comtois
there is a bug (code edited)

replace this

Code: Select all

      xaxis\x = NodeX(\Direction) - EntityX(\Entity)
      xaxis\y = NodeY(\Direction) - EntityY(\Entity)
      xaxis\z = NodeZ(\Direction) - EntityZ(\Entity)
by this

Code: Select all

      xaxis\x = NodeX(\Direction) - EntityX(\EntityBody)
      xaxis\y = NodeY(\Direction) - EntityY(\EntityBody)
      xaxis\z = NodeZ(\Direction) - EntityZ(\EntityBody)
Edit: oh, and by pressing Space it freezes
yes this is an example, there are still things to do.

How it work :
- 1 - Create quaternion using normal and Entity's direction (the axes are assumed to be orthonormal.)

Code: Select all

      xaxis\x = NodeX(\Direction) - EntityX(\EntityBody)
      xaxis\y = NodeY(\Direction) - EntityY(\EntityBody)
      xaxis\z = NodeZ(\Direction) - EntityZ(\EntityBody)

      yaxis\x = NormalX()
      yaxis\y = NormalY()
      yaxis\z = NormalZ()
     
      PRODUIT_VECTORIEL(zaxis, xaxis, yaxis) 
      PRODUIT_VECTORIEL(xaxis, yaxis, zaxis)
     
      QuaternionFromAxes(@Q, @xaxis, @yaxis, @zaxis)
- 2 - Get entity's orientation (quaternion)

Code: Select all

      FetchOrientation(EntityID(\Entity))
      S\w = GetW()
      S\x = GetX()
      S\y = GetY()
      S\z = GetZ()
- 3 - interpolate orientation

Code: Select all

Slerp(@R, 0.05, @S, @Q, 0)

Re: Smooth align Player to Object/Terrain he's standing

Posted: Thu Sep 12, 2013 6:09 pm
by Comtois
small change to show you how to keep the robot on the ground. Another solution would be to calculate slope of the land and move the robot along the slope. I added the calculation of the slope and a 3D line to materialize it.

Code: Select all

;
; ------------------------------------------------------------
;
;   PureBasic - Terrain : Physic
;
;    (c) 2012 - Fantaisie Software
;
; ------------------------------------------------------------
;

IncludeFile "Screen3DRequester.pb"

#TerrainMiniX = 0
#TerrainMiniY = 0
#TerrainMaxiX = 0
#TerrainMaxiY = 0
#PlayerSpeed = 6
#CameraSpeed = 1

Define.f KeyX, KeyY, MouseX, MouseY, TimeSinceLastFrame
Declare InitBlendMaps()

Structure Vector3
  x.f
  y.f
  z.f
EndStructure
Structure Quaternion
  w.f
  x.f
  y.f
  z.f
EndStructure
Structure Colonne3
  n.f[3]
EndStructure
Structure Matrix3
  m.Colonne3[3]
EndStructure
Structure s_Key
  Up.i
  Down.i
  Left.i
  Right.i
  StrafeLeft.i
  StrafeRight.i
  Jump.i
EndStructure

Structure s_Entity
  Entity.i
  EntityBody.i
  BodyOffsetY.f
  elapsedTime.f
  Key.s_Key
  MainNode.i 
  SightNode.i
  Direction.i
EndStructure

Structure s_Camera
  Camera.i
  Tightness.f
  CameraNode.i
  TargetNode.i
EndStructure   

Macro GetNodePosition(Position, Node)
  Position\x = NodeX(Node) 
  Position\y = NodeY(Node) 
  Position\z = NodeZ(Node) 
EndMacro

Macro SubVector3(V, V1, V2)
  V\x = V1\x - V2\x
  V\y = V1\y - V2\y
  V\z = V1\z - V2\z
EndMacro
Macro AddVector3(V, V1, V2)
  V\x = V1\x + V2\x
  V\y = V1\y + V2\y
  V\z = V1\z + V2\z
EndMacro
Macro PRODUIT_VECTORIEL(N, V1, V2)
  N\x = ((V1\y * V2\z) - (V1\z * V2\y))
  N\y = ((V1\z * V2\x) - (V1\x * V2\z))
  N\z = ((V1\x * V2\y) - (V1\y * V2\x))
EndMacro
;-Declare
Declare HandleEntity(*Entity.s_Entity)
Declare CameraTrack(*Camera.s_Camera, *Entity.s_Entity)
Declare OnGround(*Entity.s_Entity)


Define Robot.s_Entity
Define Camera.s_Camera
Global R.Quaternion

; OpenGL needs to have CG enabled to work (Linux and OS X have OpenGL by default)
;
CompilerIf #PB_Compiler_OS <> #PB_OS_Windows Or Subsystem("OpenGL")
  Flags = #PB_Engine3D_EnableCG
CompilerEndIf

If InitEngine3D(Flags)
  
  InitSprite()
  InitKeyboard()
  InitMouse()
  
  If Screen3DRequester()
    Add3DArchive("Data/Textures/"       , #PB_3DArchive_FileSystem)
    Add3DArchive("Data/Models"          , #PB_3DArchive_FileSystem)
    Add3DArchive("Data/Scripts"         , #PB_3DArchive_FileSystem)
    Add3DArchive("Data/Textures/nvidia" , #PB_3DArchive_FileSystem)
    Add3DArchive("Data/Packs/desert.zip", #PB_3DArchive_Zip)
    Parse3DScripts()
    
    WorldShadows(#PB_Shadow_Modulative, 200, RGB(120, 120, 120))
    
    MaterialFilteringMode(#PB_Default, #PB_Material_Anisotropic, 8)
    
    ;- Light
    ;
    light = CreateLight(#PB_Any ,RGB(185, 185, 185), 4000, 1200, 1000, #PB_Light_Directional)
    SetLightColor(light, #PB_Light_SpecularColor, RGB(255*0.4, 255*0.4,255*0.4))
    LightDirection(light ,0.55, -0.3, -0.75)
    AmbientColor(RGB(5, 5,5))
    
    ;----------------------------------
    ;-terrain definition
    SetupTerrains(LightID(Light), 300, #PB_Terrain_NormalMapping)
    ;-initialize terrain
    CreateTerrain(0, 513, 1200, 60, 3, "TerrainPhysic", "dat")
    ;-set all texture will be use when terrrain will be constructed
    AddTerrainTexture(0,  0, 10, "dirt_grayrocky_diffusespecular.jpg",  "dirt_grayrocky_normalheight.jpg")
    AddTerrainTexture(0,  1,  3, "grass_green-01_diffusespecular.jpg", "grass_green-01_normalheight.jpg")
    AddTerrainTexture(0,  2, 20, "growth_weirdfungus-03_diffusespecular.jpg", "growth_weirdfungus-03_normalheight.jpg")
    
    ;-Construct terrains
    For ty = #TerrainMiniY To #TerrainMaxiY
      For tx = #TerrainMiniX To #TerrainMaxiX
        Imported = DefineTerrainTile(0, tx, ty, "terrain513.png", ty % 2, tx % 2)   
      Next
    Next 
    BuildTerrain(0) 
    
    If Imported = #True
      InitBlendMaps()
      UpdateTerrain(0)
      
      ; If enabled, it will save the terrain as a (big) cache for a faster load next time the program is executed
      ; SaveTerrain(0, #False)
    EndIf
    
    ; enable shadow terrain
    TerrainRenderMode(0, 0)
    
    ;Add Physic
    TerrainPhysicBody(0, 0, 0.5)
    ;Texture
    CreateTexture(1, 256, 256)
    StartDrawing(TextureOutput(1))
    Box(0, 0, 256, 256, $002255)
    DrawingMode(#PB_2DDrawing_Outlined)
    Box(0, 0, 256, 256, $FFFFFF)
    Box(10, 10, 236, 236, $FFFF)
    StopDrawing()
    
    ;Material
    CreateMaterial(0, LoadTexture(0, "r2skin.jpg"))
    CreateMaterial(1, TextureID(1))
    CreateMaterial(2, LoadTexture(2, "Dirt.jpg"))
    CreateMaterial(3, LoadTexture(3, "Wood.jpg"))
    GetScriptMaterial(4, "Scene/GroundBlend")
    
    ;Robot
    CreateSphere(0, 1)
    Rob = LoadMesh(#PB_Any, "Robot.mesh")
    CreateEntity (0, MeshID(Rob), #PB_Material_None)
    ScaleEntity(0, 0.1, 0.1, 0.1)
    StartEntityAnimation(0, "Walk")
    
    ;Robot Body
    CreateEntity(1, MeshID(0), #PB_Material_None, 0, 426, 0)
    HideEntity(1, 1)
    
    ;Body
    EntityPhysicBody(1, #PB_Entity_SphereBody, 1, 0, 0.5)
    
    ; Skybox
    SkyBox("desert07.jpg")
    
    ;
    With Robot
      \Entity = 0
      \EntityBody = 1
      \BodyOffsetY = 1
      
      \Key\Down        = #PB_Key_Down
      \Key\Left        = #PB_Key_Left
      \Key\Right       = #PB_Key_Right
      \Key\Up          = #PB_Key_Up
      \Key\StrafeLeft  = #PB_Key_X
      \Key\StrafeRight = #PB_Key_C
      \Key\Jump        = #PB_Key_Space
      
      ;\MainNode    = CreateNode(#PB_Any) ; Entity position
      \Direction   = CreateNode(#PB_Any,   1,  0,  0) ; For cameraLookAt
      \SightNode   = CreateNode(#PB_Any,  12,  2,  0) ; For cameraLookAt
      
      AttachEntityObject(\EntityBody, "", NodeID(\Direction))
      AttachEntityObject(\EntityBody, "", NodeID(\SightNode))
      ;AttachNodeObject(\MainNode, EntityID(\Entity))
    EndWith
    
    ;- Camera
    ;
    CreateCamera(0, 0, 0, 100, 100)
    CameraBackColor(0, RGB(5, 5, 10))
    
    ;==================================
    ; create material
    Red = GetScriptMaterial(#PB_Any, "Color/Red")
    Blue = GetScriptMaterial(#PB_Any, "Color/Blue")
    Yellow = GetScriptMaterial(#PB_Any, "Color/Yellow")
    Green = GetScriptMaterial(#PB_Any, "Color/Green")
    
    ;==================================
    ; create Sphere
    MeshSphere = CreateSphere(#PB_Any, 1.0)
    For i = 0 To 5
      Entity=CreateEntity(#PB_Any, MeshID(MeshSphere), MaterialID(Green))
      MoveEntity(Entity, Random(200)-100, 80, Random(400)-200, #PB_Absolute)
      ; create bodies
      EntityPhysicBody(Entity, #PB_Entity_SphereBody, 1.0)
    Next
    
    ;==================================
    ; create Cylinder
    MeshCylinder = CreateCylinder(#PB_Any, 1.0, 6)
    For i = 0 To 5
      Entity=CreateEntity(#PB_Any, MeshID(MeshCylinder), MaterialID(Red))
      MoveEntity(Entity, Random(200)-100, 80, Random(400)-200, #PB_Absolute)
      ; create bodies
      EntityPhysicBody(Entity, #PB_Entity_CylinderBody, 1.0, 0, 1)
    Next
    
    ;==================================
    ; create Cube
    MeshCube = CreateCube(#PB_Any, 3)
    For i = 0 To 5
      Entity=CreateEntity(#PB_Any, MeshID(MeshCube), MaterialID(Yellow))
      MoveEntity(Entity, Random(200)-100, 80, Random(400)-200, #PB_Absolute)
      ; create bodies
      EntityPhysicBody(Entity, #PB_Entity_BoxBody, 1.0)
    Next
    
    Repeat
      
      Screen3DEvents()   
      
      Robot\elapsedTime = TimeSinceLastFrame
      
      HandleEntity(@Robot)
      
      CameraFollow(0, EntityID(Robot\Entity), -90, EntityY(Robot\Entity) + 10, 25, 0.1, 0.1, 0)
      CameraLookAt(0, NodeX(Robot\SightNode), NodeY(Robot\SightNode), NodeZ(Robot\SightNode))
      TimeSinceLastFrame = RenderWorld(60) * 40 / 1000
      FlipBuffers()
      
    Until KeyboardPushed(#PB_Key_Escape)   
    
    End
    
  EndIf
Else
  CompilerIf #PB_Compiler_OS <> #PB_OS_Windows Or Subsystem("OpenGL")
    ;
    ; Terrain on Linux/OSX and Windows with OpenGL needs CG toolkit from nvidia
    ; It can be freely downloaded and installed from this site: https://developer.nvidia.com/cg-toolkit-download
    ;
    MessageRequester("Error","Can't initialize engine3D (Please ensures than CG Toolkit from nvidia is correcly installed)")
  CompilerElse
    MessageRequester("Error","Can't initialize engine3D")
  CompilerEndIf
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 InitBlendMaps()
  minHeight1.f = 7
  fadeDist1.f = 40
  minHeight2.f = 7
  fadeDist2.f = 15   
  For ty = #TerrainMiniY To #TerrainMaxiY
    For tx = #TerrainMiniX To #TerrainMaxiX
      Size = TerrainTileLayerMapSize(0, tx, ty)
      For y = 0 To Size-1
        For x = 0 To Size-1
          Height.f = TerrainTileHeightAtPosition(0, tx, ty, 1, x, y)
          
          val.f = (Height - minHeight1) / fadeDist1
          Clamp(@val, 0, 1)
          SetTerrainTileLayerBlend(0, tx, ty, 1, x, y, val)
          
          val.f = (Height - minHeight2) / fadeDist2
          Clamp(@val, 0, 1)
          SetTerrainTileLayerBlend(0, tx, ty, 2, x, y, val)
        Next
      Next
      UpdateTerrainTileLayerBlend(0, tx, ty, 1)
      UpdateTerrainTileLayerBlend(0, tx, ty, 2)
    Next
  Next 
EndProcedure 

Procedure Normalize(*V.Vector3)
  Protected.f magSq, oneOverMag
  
  magSq = *V\x * *V\x + *V\y * *V\y + *V\z * *V\z
  If magsq > 0
    oneOverMag = 1.0 / Sqr(magSq)
    *V\x * oneOverMag
    *V\y * oneOverMag
    *V\z * oneOverMag
  EndIf 
  
EndProcedure

Procedure.f QuaternionNormalise(*Q.Quaternion)
  Protected.f magSq, oneOverMag
  
  magSq = *Q\w**Q\w+*Q\x**Q\x+*Q\y**Q\y+*Q\z**Q\z
  oneOverMag = 1.0 / Sqr(magSq)
  *Q\w * oneOverMag
  *Q\x * oneOverMag
  *Q\y * oneOverMag
  *Q\z * oneOverMag
EndProcedure


Procedure.f DotProduct(*V1.Vector3, *V2.Vector3)
  ProcedureReturn *V1\x * *V2\x + *V1\y * *V2\y + *V1\z * *V2\z   
EndProcedure 

Procedure.f  QuaternionDot(*Q.Quaternion, *rkQ.Quaternion)
  
  ProcedureReturn *Q\w**rkQ\w+*Q\x**rkQ\x+*Q\y**rkQ\y+*Q\z**rkQ\z
EndProcedure

Procedure FromRotationMatrix(*Q.Quaternion, *kRot.Matrix3)
  
  ; Algorithm in Ken Shoemake's article in 1987 SIGGRAPH course notes
  ; article "Quaternion Calculus and Fast Animation".
  
  fTrace.f = *kRot\m[0]\n[0]+*kRot\m[1]\n[1]+*kRot\m[2]\n[2]
  fRoot.f
  
  If fTrace > 0.0
    
    ; |w| > 1/2, may As well choose w > 1/2
    fRoot =Sqr(fTrace + 1.0);  2w
    *Q\w = 0.5*fRoot
    fRoot = 0.5/fRoot;   1/(4w)
    *Q\x = (*kRot\m[2]\n[1]-*kRot\m[1]\n[2])*fRoot;
    *Q\y = (*kRot\m[0]\n[2]-*kRot\m[2]\n[0])*fRoot;
    *Q\z = (*kRot\m[1]\n[0]-*kRot\m[0]\n[1])*fRoot;
    
  Else
    
    ; |w| <= 1/2
    Dim s_iNext(2)
    s_iNext(0) =  1
    s_iNext(1) =  2
    s_iNext(2) =  0
    
    i = 0
    If *kRot\m[1]\n[1] > *kRot\m[0]\n[0]
      i = 1
    EndIf   
    If *kRot\m[2]\n[2] > *kRot\m[i]\n[i]
      i = 2
    EndIf   
    j = s_iNext(i)
    k = s_iNext(j)
    
    fRoot =Sqr(*kRot\m[i]\n[i]-*kRot\m[j]\n[j]-*kRot\m[k]\n[k] + 1.0)
    Dim *apkQuat.Float(2)
    *apkQuat(0) = @*Q\x
    *apkQuat(1) = @*Q\y
    *apkQuat(2) = @*Q\z
    
    *apkQuat(i)\f = 0.5*fRoot
    fRoot = 0.5/fRoot
    *Q\w = (*kRot\m[k]\n[j]-*kRot\m[j]\n[k])*fRoot
    *apkQuat(j)\f = (*kRot\m[j]\n[i]+*kRot\m[i]\n[j])*fRoot
    *apkQuat(k)\f = (*kRot\m[k]\n[i]+*kRot\m[i]\n[k])*fRoot
  EndIf
EndProcedure

Procedure QuaternionFromAxes(*Q.Quaternion, *xaxis.Vector3, *yaxis.Vector3, *zaxis.Vector3)
  
  Protected kRot.Matrix3
  
  kRot\m[0]\n[0] = *xaxis\x
  kRot\m[1]\n[0] = *xaxis\y
  kRot\m[2]\n[0] = *xaxis\z
  
  kRot\m[0]\n[1] = *yaxis\x
  kRot\m[1]\n[1] = *yaxis\y
  kRot\m[2]\n[1] = *yaxis\z
  
  kRot\m[0]\n[2] = *zaxis\x
  kRot\m[1]\n[2] = *zaxis\y
  kRot\m[2]\n[2] = *zaxis\z
  
  FromRotationMatrix(*Q, @kRot)
  
EndProcedure


Procedure Slerp(*t.Quaternion, fT.f, *rkP.Quaternion, *rkQ.Quaternion, shortestPath)
  msEpsilon = 1e-03
  fCos.f = QuaternionDot(*rkP, *rkQ)
  rkT.Quaternion
  
  ; Do we need To invert rotation?
  If Bool((fCos < 0.0) And shortestPath)
    
    fCos = -fCos
    rkT\w = -*rkQ\w
    rkT\x = -*rkQ\x
    rkT\y = -*rkQ\y
    rkT\z = -*rkQ\z
  Else
    
    rkT\w = *rkQ\w
    rkT\x = *rkQ\x
    rkT\y = *rkQ\y
    rkT\z = *rkQ\z
  EndIf
  
  If (Abs(fCos) < 1 - msEpsilon)
    
    ; Standard Case (slerp)
    fSin.f = Sqr(1 - (fCos*fCos))
    fAngle.f =ATan2(fSin, fCos)
    fInvSin.f = 1.0 / fSin;
    fCoeff0.f = Sin((1.0 - fT) * fAngle) * fInvSin
    fCoeff1.f = Sin(fT * fAngle) * fInvSin
    *t\w = fCoeff0 * *rkP\w + fCoeff1 * rkT\w
    *t\x = fCoeff0 * *rkP\x + fCoeff1 * rkT\x
    *t\y = fCoeff0 * *rkP\y + fCoeff1 * rkT\y
    *t\z = fCoeff0 * *rkP\z + fCoeff1 * rkT\z
  Else
    
    ; There are two situations:
    ; 1. "rkP" And "rkQ" are very close (fCos ~= +1), so we can do a linear
    ;    interpolation safely.
    ; 2. "rkP" And "rkQ" are almost inverse of each other (fCos ~= -1), there
    ;    are an infinite number of possibilities interpolation. but we haven't
    ;    have method To fix this Case, so just use linear interpolation here.
    *t\w = (1.0 - fT) * *rkP\w + fT * rkT\w
    *t\x = (1.0 - fT) * *rkP\x + fT * rkT\x
    *t\y = (1.0 - fT) * *rkP\y + fT * rkT\y
    *t\z = (1.0 - fT) * *rkP\z + fT * rkT\z
    ; taking the complement requires renormalisation
    QuaternionNormalise(*t)
  EndIf
EndProcedure

Procedure OnGround(*Entity.s_Entity)
  Protected.Quaternion S, Q
  Protected.Vector3 xaxis, yaxis, zaxis
  
  With *Entity
    Result = RayCollide(EntityX(\EntityBody), EntityY(\EntityBody), EntityZ(\EntityBody), EntityX(\EntityBody), EntityY(\EntityBody)-7,  EntityZ(\EntityBody))
    ;CreateLine3D(20,EntityX(\EntityBody), EntityY(\EntityBody), EntityZ(\EntityBody), $FFFF, EntityX(\EntityBody), EntityY(\EntityBody)-7,  EntityZ(\EntityBody), $FFFF) 
    Delta.f = EntityY(\EntityBody) - PickY() - \BodyOffsetY
    
    If Result=-1 Or (Result>-1 And (delta >= 1))
      ProcedureReturn 0
    Else
      xaxis\x = NodeX(\Direction) - EntityX(\EntityBody)
      xaxis\y = NodeY(\Direction) - EntityY(\EntityBody)
      xaxis\z = NodeZ(\Direction) - EntityZ(\EntityBody)
      
      yaxis\x = NormalX()
      yaxis\y = NormalY()
      yaxis\z = NormalZ()
      
      PRODUIT_VECTORIEL(zaxis, xaxis, yaxis)
      PRODUIT_VECTORIEL(xaxis, yaxis, zaxis)
      
      CreateLine3D(20,EntityX(\EntityBody), EntityY(\EntityBody), EntityZ(\EntityBody), $FFFF, EntityX(\EntityBody)+xaxis\x*3, EntityY(\EntityBody)+xaxis\y*3,  EntityZ(\EntityBody)+xaxis\z*3, $FFFF) 
      
      QuaternionFromAxes(@Q, @xaxis, @yaxis, @zaxis)
      
      FetchOrientation(EntityID(\Entity))
      S\w = GetW()
      S\x = GetX()
      S\y = GetY()
      S\z = GetZ()
      Slerp(@R, 0.05, @S, @Q, 0)
      SetOrientation(EntityID(\Entity),r\x, r\y, r\z, r\w)
      ProcedureReturn 1
    EndIf 
  EndWith 
EndProcedure

Procedure HandleEntity(*Entity.s_Entity)
  Protected.f Speed, Speed2, x, y, MouseX, MouseY
  Static Jump.f, MemJump.i, Rot.Vector3, Trans.Vector3, Clic
  
  With *Entity
    
    Speed = #PlayerSpeed * \elapsedTime
    Speed2 = Speed / 2
    
    If ExamineKeyboard()
      
      If KeyboardReleased(#PB_Key_F5)
        WorldDebug(#PB_World_DebugBody)
      ElseIf KeyboardReleased(#PB_Key_F6)
        WorldDebug(#PB_World_DebugEntity)
      ElseIf KeyboardReleased(#PB_Key_F7)
        WorldDebug(#PB_World_DebugNone)
      EndIf
      
      If KeyboardPushed(\Key\Jump) And OnGround(*Entity)
        Jump = 90
        MemJump = 1
      EndIf 
      
      Rot\x * 0.30
      Rot\y * 0.30
      Rot\z * 0.30
      Trans\x * 0.20
      Trans\y = Sin(Radian(Jump)) * 5
      Trans\z * 0.20
      
      If KeyboardPushed(\Key\Up)
        Trans\x = Speed
      ElseIf KeyboardPushed(\Key\Down)
        Trans\x = -Speed2
      EndIf
      
      If KeyboardPushed(\Key\Left)
        Rot\y + 2 * \elapsedTime
      ElseIf KeyboardPushed(\Key\Right)
        Rot\y - 2 * \elapsedTime
      EndIf
      
      If KeyboardPushed(\Key\StrafeLeft)
        Trans\z = -Speed2
      ElseIf KeyboardPushed(\Key\StrafeRight)
        Trans\z = Speed2
      EndIf
      
      OnGround = OnGround(*Entity)
      
      If OnGround And MemJump = 0
        MoveEntity  (\EntityBody, EntityX(\EntityBody), TerrainHeight(0, EntityX(\EntityBody), EntityZ(\EntityBody))+1,EntityZ(\EntityBody),#PB_Absolute)      
      ElseIf MemJump 
        Jump - 9 * \elapsedTime
        If Jump < -90
          Jump = -90
        EndIf 
        If OnGround And Jump < 0
          MemJump = 0
          Jump = 0
        EndIf  
      Else 
        MoveEntity  (\EntityBody, EntityX(\EntityBody), TerrainHeight(0, EntityX(\EntityBody), EntityZ(\EntityBody))+1,EntityZ(\EntityBody),#PB_Absolute)     
      EndIf 
      
    EndIf
    
    MoveEntity  (\EntityBody, Trans\x,Trans\y, Trans\z, #PB_Local)
    
    RotateEntity(\EntityBody, 0, Rot\y, 0, #PB_Relative)   
    MoveEntity(\Entity, EntityX(\EntityBody), EntityY(\EntityBody)-\BodyOffsetY, EntityZ(\EntityBody), #PB_Absolute)
    
  EndWith   
EndProcedure

Re: Smooth align Player to Object/Terrain he's standing

Posted: Thu Sep 12, 2013 6:47 pm
by DK_PETER
@Comtois.

Nice example, Comtois :-)
But it's a little bit buggy. Move around and suddently the robot pops below the terrain.
Keep walking and our 'Cylon from Battlestar Galactica' 'pops' above ground again and starts a nice jitterbug.
Keep walking and now he begins to walk again until it all starts over again. :-)