main.odin 4.6 KB

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