Smooth align Player to Object/Terrain he's standing

Everything related to 3D programming
Fred
Administrator
Administrator
Posts: 18384
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

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

Post by Fred »

Please post a snippet showing the issue if you want help.
User avatar
Comtois
Addict
Addict
Posts: 1432
Joined: Tue Aug 19, 2003 11:36 am
Location: Doubs - France

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

Post 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
Please correct my english
http://purebasic.developpez.com/
User avatar
Comtois
Addict
Addict
Posts: 1432
Joined: Tue Aug 19, 2003 11:36 am
Location: Doubs - France

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

Post 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
Last edited by Comtois on Sun Sep 08, 2013 3:36 pm, edited 1 time in total.
Please correct my english
http://purebasic.developpez.com/
Num3
PureBasic Expert
PureBasic Expert
Posts: 2812
Joined: Fri Apr 25, 2003 4:51 pm
Location: Portugal, Lisbon
Contact:

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

Post by Num3 »

Looks good!
User avatar
Comtois
Addict
Addict
Posts: 1432
Joined: Tue Aug 19, 2003 11:36 am
Location: Doubs - France

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

Post 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)
Please correct my english
http://purebasic.developpez.com/
User avatar
Comtois
Addict
Addict
Posts: 1432
Joined: Tue Aug 19, 2003 11:36 am
Location: Doubs - France

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

Post 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
Please correct my english
http://purebasic.developpez.com/
User avatar
DK_PETER
Addict
Addict
Posts: 904
Joined: Sat Feb 19, 2011 10:06 am
Location: Denmark
Contact:

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

Post 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. :-)
Current configurations:
Ubuntu 20.04/64 bit - Window 10 64 bit
Intel 6800K, GeForce Gtx 1060, 32 gb ram.
Amd Ryzen 9 5950X, GeForce 3070, 128 gb ram.
Post Reply