kinematics.odin 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. package kinematics
  2. import rl "vendor:raylib"
  3. import "core:math"
  4. import r3d "../r3d"
  5. GRAVITY :: -15.0
  6. MOVE_SPEED :: 5.0
  7. JUMP_FORCE :: 8.0
  8. capsule_center :: proc(caps: r3d.Capsule) -> rl.Vector3 {
  9. return (caps.start + caps.end) * 0.5
  10. }
  11. box_center :: proc(box: rl.BoundingBox) -> rl.Vector3 {
  12. return (box.min + box.max) * 0.5
  13. }
  14. main :: proc() {
  15. rl.InitWindow(800, 450, "[r3d] - Kinematics Example")
  16. defer rl.CloseWindow()
  17. rl.SetTargetFPS(60)
  18. r3d.Init(rl.GetScreenWidth(), rl.GetScreenHeight())
  19. defer r3d.Close()
  20. r3d.SetTextureFilter(.ANISOTROPIC_8X)
  21. sky := r3d.GenCubemapSky(4096, r3d.CUBEMAP_SKY_BASE)
  22. ambient := r3d.GenAmbientMap(sky, {.ILLUMINATION, .REFLECTION})
  23. env := r3d.GetEnvironment()
  24. env.background.sky = sky
  25. env.ambient._map = ambient
  26. light := r3d.CreateLight(.DIR)
  27. r3d.SetLightDirection(light, {-1, -1, -1})
  28. r3d.SetLightRange(light, 16.0)
  29. r3d.SetLightActive(light, true)
  30. r3d.EnableShadow(light)
  31. r3d.SetShadowDepthBias(light, 0.005)
  32. // Load materials
  33. baseAlbedo := r3d.LoadAlbedoMap("./resources/images/placeholder.png", rl.WHITE)
  34. groundMat := r3d.GetDefaultMaterial()
  35. groundMat.uvScale = {250.0, 250.0}
  36. groundMat.albedo = baseAlbedo
  37. slopeMat := r3d.GetDefaultMaterial()
  38. slopeMat.albedo.color = {255, 255, 0, 255}
  39. slopeMat.albedo.texture = baseAlbedo.texture
  40. // Ground
  41. groundMesh := r3d.GenMeshPlane(1000, 1000, 1, 1)
  42. defer r3d.UnloadMesh(groundMesh)
  43. groundBox: rl.BoundingBox = {min = {-500, -1, -500}, max = {500, 0, 500}}
  44. // Slope obstacle
  45. slopeMeshData := r3d.GenMeshDataSlope(2, 2, 2, {0, 1, -1})
  46. defer r3d.UnloadMeshData(slopeMeshData)
  47. slopeMesh := r3d.LoadMesh(.TRIANGLES, slopeMeshData, nil, .STATIC_MESH)
  48. defer r3d.UnloadMesh(slopeMesh)
  49. slopeTransform := rl.MatrixTranslate(0, 1, 5)
  50. // Player capsule
  51. capsule: r3d.Capsule = {start = {0, 0.5, 0}, end = {0, 1.5, 0}, radius = 0.5}
  52. capsMesh := r3d.GenMeshCapsule(0.5, 1.0, 64, 32)
  53. defer r3d.UnloadMesh(capsMesh)
  54. velocity: rl.Vector3 = {0, 0, 0}
  55. // Camera
  56. cameraAngle: f32 = 0.0
  57. cameraPitch: f32 = 30.0
  58. camera: rl.Camera3D = {
  59. position = {0, 5, 5},
  60. target = capsule_center(capsule),
  61. up = {0, 1, 0},
  62. fovy = 60,
  63. }
  64. rl.DisableCursor()
  65. for !rl.WindowShouldClose()
  66. {
  67. dt := rl.GetFrameTime()
  68. // Camera rotation
  69. mouseDelta := rl.GetMouseDelta()
  70. cameraAngle -= mouseDelta.x * 0.15
  71. cameraPitch = clamp(cameraPitch + mouseDelta.y * 0.15, -7.5, 80.0)
  72. // Movement input relative to camera
  73. dx := i32(rl.IsKeyDown(.A)) - i32(rl.IsKeyDown(.D))
  74. dz := i32(rl.IsKeyDown(.W)) - i32(rl.IsKeyDown(.S))
  75. moveInput: rl.Vector3 = {0, 0, 0}
  76. if dx != 0 || dz != 0 {
  77. angleRad := cameraAngle * rl.DEG2RAD
  78. right := rl.Vector3{math.cos_f32(angleRad), 0, -math.sin_f32(angleRad)}
  79. forward := rl.Vector3{math.sin_f32(angleRad), 0, math.cos_f32(angleRad)}
  80. moveInput = rl.Vector3Normalize(right * f32(dx) + forward * f32(dz))
  81. }
  82. // Check grounded
  83. isGrounded := r3d.IsCapsuleGroundedBox(capsule, 0.01, groundBox, nil) ||
  84. r3d.IsCapsuleGroundedMesh(capsule, 0.3, slopeMeshData, slopeTransform, nil)
  85. // Jump and apply gravity
  86. if isGrounded && rl.IsKeyPressed(.SPACE) do velocity.y = JUMP_FORCE
  87. if !isGrounded do velocity.y += GRAVITY * dt
  88. else if velocity.y < 0 do velocity.y = 0
  89. // Calculate total movement
  90. movement := moveInput * MOVE_SPEED * dt
  91. movement.y = velocity.y * dt
  92. // Apply movement with collision
  93. movement = r3d.SlideCapsuleMesh(capsule, movement, slopeMeshData, slopeTransform, nil)
  94. capsule.start = capsule.start + movement
  95. capsule.end = capsule.end + movement
  96. // Ground clamp
  97. if capsule.start.y < 0.5 {
  98. correction := 0.5 - capsule.start.y
  99. capsule.start.y += correction
  100. capsule.end.y += correction
  101. velocity.y = 0
  102. }
  103. // Update camera position
  104. target := capsule_center(capsule)
  105. pitchRad := cameraPitch * rl.DEG2RAD
  106. angleRad := cameraAngle * rl.DEG2RAD
  107. camera.position = {
  108. target.x - math.sin_f32(angleRad) * math.cos_f32(pitchRad) * 5.0,
  109. target.y + math.sin_f32(pitchRad) * 5.0,
  110. target.z - math.cos_f32(angleRad) * math.cos_f32(pitchRad) * 5.0,
  111. }
  112. camera.target = target
  113. rl.BeginDrawing()
  114. rl.ClearBackground(rl.BLACK)
  115. r3d.Begin(camera)
  116. r3d.DrawMeshPro(slopeMesh, slopeMat, slopeTransform)
  117. r3d.DrawMesh(groundMesh, groundMat, {0, 0, 0}, 1.0)
  118. r3d.DrawMesh(capsMesh, r3d.GetDefaultMaterial(), capsule_center(capsule), 1.0)
  119. r3d.End()
  120. rl.DrawFPS(10, 10)
  121. rl.DrawText(
  122. isGrounded ? "GROUNDED" : "AIRBORNE",
  123. 10, rl.GetScreenHeight() - 30, 20,
  124. isGrounded ? rl.LIME : rl.YELLOW,
  125. )
  126. rl.EndDrawing()
  127. }
  128. }