raylib-go/examples/models/mesh_picking/main.go
2025-06-22 17:48:18 -04:00

261 lines
7.2 KiB
Go

/*******************************************************************************************
*
* raylib [models] example - Mesh picking in 3d mode, ground plane, triangle, mesh
*
* Example originally created with raylib 1.7, last time updated with raylib 4.0
*
* Example contributed by Joel Davis (@joeld42) and reviewed by Ramon Santamaria (@raysan5)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2017-2024 Joel Davis (@joeld42) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/
package main
import (
"fmt"
"math"
"unsafe"
rl "git.terah.dev/UnrealXR/raylib-go/raylib"
)
const (
screenWidth = 800
screenHeight = 450
)
// Program main entry point
func main() {
rl.InitWindow(screenWidth, screenHeight, "raylib [models] example - mesh picking")
// Define the camera to look into our 3d world
camera := rl.Camera{
Position: rl.Vector3{
X: 20.0,
Y: 20.0,
Z: 20.0,
}, // Camera position
Target: rl.Vector3{Y: 8.0}, // Camera looking at point
Up: rl.Vector3{Y: 1.6}, // Camera up vector (rotation towards target)
Fovy: 45.0, // Camera field-of-view Y
Projection: rl.CameraPerspective, // Camera projection type
}
var ray rl.Ray // Picking ray
tower := rl.LoadModel("turret.obj") // Load OBJ model
texture := rl.LoadTexture("turret_diffuse.png") // Load model texture
materials := unsafe.Slice(tower.Materials, tower.MaterialCount)
materials[0].GetMap(rl.MapDiffuse).Texture = texture // Set model diffuse texture
towerPos := rl.Vector3{} // Set model position
meshes := unsafe.Slice(tower.Meshes, tower.MeshCount)
towerBBox := rl.GetMeshBoundingBox(meshes[0]) // Get mesh bounding box
// Ground quad
g0 := rl.Vector3{
X: -50.0,
Z: -50.0,
}
g1 := rl.Vector3{
X: -50.0,
Z: 50.0,
}
g2 := rl.Vector3{
X: 50.0,
Z: 50.0,
}
g3 := rl.Vector3{
X: 50.0,
Z: -50.0,
}
// Test triangle
ta := rl.Vector3{
X: -25.0,
Y: 0.5,
}
tb := rl.Vector3{
X: -4.0,
Y: 2.5,
Z: 1.0,
}
tc := rl.Vector3{
X: -8.0,
Y: 6.5,
}
bary := rl.Vector3{}
// Test sphere
sp := rl.Vector3{
X: -30.0,
Y: 5.0,
Z: 5.0,
}
sr := float32(4.0)
rl.SetTargetFPS(60) // Set our game to run at 60 frames-per-second
// Main game loop
for !rl.WindowShouldClose() { // Detect window close button or ESC key
// Update
if rl.IsCursorHidden() {
rl.UpdateCamera(&camera, rl.CameraFirstPerson) // Update camera
}
// Toggle camera controls
if rl.IsMouseButtonPressed(rl.MouseButtonRight) {
if rl.IsCursorHidden() {
rl.EnableCursor()
} else {
rl.DisableCursor()
}
}
// Display information about the closest hit
collision := rl.RayCollision{
Distance: math.MaxFloat32,
Hit: false,
}
hitObjectName := "None"
cursorColor := rl.White
// Get ray and test against objects
// See issue : https://git.terah.dev/UnrealXR/raylib-go/issues/457
//ray = rl.GetScreenToWorldRay(rl.GetMousePosition(), camera)
ray = rl.GetMouseRay(rl.GetMousePosition(), camera)
// Check ray collision against ground quad
groundHitInfo := rl.GetRayCollisionQuad(ray, g0, g1, g2, g3)
if (groundHitInfo.Hit) && (groundHitInfo.Distance < collision.Distance) {
collision = groundHitInfo
cursorColor = rl.Green
hitObjectName = "Ground"
}
// Check ray collision against test triangle
triHitInfo := rl.GetRayCollisionTriangle(ray, ta, tb, tc)
if (triHitInfo.Hit) && (triHitInfo.Distance < collision.Distance) {
collision = triHitInfo
cursorColor = rl.Purple
hitObjectName = "Triangle"
bary = rl.Vector3Barycenter(collision.Point, ta, tb, tc)
}
// Check ray collision against test sphere
sphereHitInfo := rl.GetRayCollisionSphere(ray, sp, sr)
if (sphereHitInfo.Hit) && (sphereHitInfo.Distance < collision.Distance) {
collision = sphereHitInfo
cursorColor = rl.Orange
hitObjectName = "Sphere"
}
// Check ray collision against bounding box first, before trying the full ray-mesh test
boxHitInfo := rl.GetRayCollisionBox(ray, towerBBox)
if (boxHitInfo.Hit) && (boxHitInfo.Distance < collision.Distance) {
collision = boxHitInfo
cursorColor = rl.Orange
hitObjectName = "Box"
// Check ray collision against model meshes
meshHitInfo := rl.RayCollision{}
for m := int32(0); m < tower.MeshCount; m++ {
// NOTE: We consider the model.transform for the collision check, but
// it can be checked against any transform Matrix, used when checking against same
// model drawn multiple times with multiple transforms
meshHitInfo = rl.GetRayCollisionMesh(ray, meshes[m], tower.Transform)
if meshHitInfo.Hit {
// Save the closest hit mesh
if (!collision.Hit) || (collision.Distance > meshHitInfo.Distance) {
collision = meshHitInfo
}
break // Stop once one mesh collision is detected, the colliding mesh is m
}
}
if meshHitInfo.Hit {
collision = meshHitInfo
cursorColor = rl.Orange
hitObjectName = "Mesh"
}
}
// Draw
rl.BeginDrawing()
rl.ClearBackground(rl.RayWhite)
rl.BeginMode3D(camera)
// Draw the tower
// WARNING: If scale is different from 1.0f,
// not considered by GetRayCollisionModel()
rl.DrawModel(tower, towerPos, 1.0, rl.White)
// Draw the test triangle
rl.DrawLine3D(ta, tb, rl.Purple)
rl.DrawLine3D(tb, tc, rl.Purple)
rl.DrawLine3D(tc, ta, rl.Purple)
// Draw the test sphere
rl.DrawSphereWires(sp, sr, 8, 8, rl.Purple)
// Draw the mesh bbox if we hit it
if boxHitInfo.Hit {
rl.DrawBoundingBox(towerBBox, rl.Lime)
}
// If we hit something, draw the cursor at the hit point
if collision.Hit {
rl.DrawCube(collision.Point, 0.3, 0.3, 0.3, cursorColor)
rl.DrawCubeWires(collision.Point, 0.3, 0.3, 0.3, rl.Red)
normalEnd := rl.Vector3{}
normalEnd.X = collision.Point.X + collision.Normal.X
normalEnd.Y = collision.Point.Y + collision.Normal.Y
normalEnd.Z = collision.Point.Z + collision.Normal.Z
rl.DrawLine3D(collision.Point, normalEnd, rl.Red)
}
rl.DrawRay(ray, rl.Maroon)
rl.DrawGrid(10, 10.0)
rl.EndMode3D()
// Draw some debug GUI text
rl.DrawText(fmt.Sprintf("Hit Object: %s", hitObjectName), 10, 50, 10, rl.Black)
if collision.Hit {
ypos := int32(70)
rl.DrawText(fmt.Sprintf("Distance: %3.2f", collision.Distance), 10, ypos, 10, rl.Black)
rl.DrawText(Vec2Str("Hit Pos : %3.2f %3.2f %3.2f", collision.Point), 10, ypos+15, 10, rl.Black)
rl.DrawText(Vec2Str("Hit Norm: %3.2f %3.2f %3.2f", collision.Normal), 10, ypos+30, 10, rl.Black)
if triHitInfo.Hit && hitObjectName == "Triangle" {
rl.DrawText(Vec2Str("Barycenter: %3.2f %3.2f %3.2f", bary), 10, ypos+45, 10, rl.Black)
}
}
rl.DrawText("Right click mouse to toggle camera controls", 10, 430, 10, rl.Gray)
rl.DrawText("(c) Turret 3D model by Alberto Cano", screenWidth-200, screenHeight-20, 10, rl.Gray)
rl.DrawFPS(10, 10)
rl.EndDrawing()
}
// De-Initialization
rl.UnloadModel(tower) // Unload model
rl.UnloadTexture(texture) // Unload texture
rl.CloseWindow() // Close window and OpenGL context
}
func Vec2Str(format string, vec rl.Vector3) string {
return fmt.Sprintf(format, vec.X, vec.Y, vec.Z)
}