diff --git a/examples/models/models_first_person_maze/cubicmap.png b/examples/models/models_first_person_maze/cubicmap.png new file mode 100644 index 0000000..392dbf2 Binary files /dev/null and b/examples/models/models_first_person_maze/cubicmap.png differ diff --git a/examples/models/models_first_person_maze/cubicmap_atlas.png b/examples/models/models_first_person_maze/cubicmap_atlas.png new file mode 100644 index 0000000..9fc404a Binary files /dev/null and b/examples/models/models_first_person_maze/cubicmap_atlas.png differ diff --git a/examples/models/models_first_person_maze/main.go b/examples/models/models_first_person_maze/main.go new file mode 100644 index 0000000..dafad58 --- /dev/null +++ b/examples/models/models_first_person_maze/main.go @@ -0,0 +1,119 @@ +/******************************************************************************************* +* +* raylib [models] example - first person maze +* +* This example has been created using raylib-go v0.0.0-20220104071325-2f072dc2d259 (https://github.com/gen2brain/raylib-go) +* raylib-go is licensed under an unmodified zlib/libpng license (https://github.com/gen2brain/raylib-go/blob/master/LICENSE) +* +* Original C version for Raylib 2.5 Copyright (c) 2019 Ramon Santamaria (@raysan5) +* Converted to Go by Michael Redman January 4, 2022 +* +********************************************************************************************/ + +package main + +import ( + rl "github.com/gen2brain/raylib-go/raylib" +) + +func main() { + // Initialization + //-------------------------------------------------------------------------------------- + var screenWidth int32 = 800 + var screenHeight int32 = 450 + + rl.InitWindow(screenWidth, screenHeight, "raylib [models] example - first person maze") + + // Define the camera to look into our 3d world + camera := rl.Camera{} + camera.Position = rl.NewVector3(0.2, 0.4, 0.2) + camera.Target = rl.NewVector3(0.0, 0.0, 0.0) + camera.Up = rl.NewVector3(0.0, 1.0, 0.0) + camera.Fovy = 45.0 + camera.Projection = rl.CameraPerspective + + imMap := rl.LoadImage("cubicmap.png") // Load cubicmap image (RAM) + cubicmap := rl.LoadTextureFromImage(imMap) // Convert image to texture to display (VRAM) + mesh := rl.GenMeshCubicmap(*imMap, rl.NewVector3(1.0, 1.0, 1.0)) + model := rl.LoadModelFromMesh(mesh) + + // NOTE: By default each cube is mapped to one part of texture atlas + texture := rl.LoadTexture("cubicmap_atlas.png") // load map texture + model.Materials.GetMap(rl.MapDiffuse).Texture = texture // Set map diffuse texture + // Get map image data to be used for collision detectio + mapPixels := rl.LoadImageColors(imMap) + rl.UnloadImage(imMap) // Unload image from RAM + + mapPosition := rl.NewVector3(-16.0, 0.0, -8.0) // Set model position + + rl.SetCameraMode(camera, rl.CameraFirstPerson) // Set camera mode + + 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 + //---------------------------------------------------------------------------------- + oldCamPos := camera.Position // Store old camera position + + rl.UpdateCamera(&camera) // Update camera + + // Check player collision (we simplify to 2D collision detection) + playerPos := rl.NewVector2(camera.Position.X, camera.Position.Z) + playerRadius := 0.1 // Collision radius (player is modelled as a cylinder for collision) + + playerCellX := (int)(playerPos.X - mapPosition.X + 0.5) + playerCellY := (int)(playerPos.Y - mapPosition.Z + 0.5) + + // Out-of-limits security check + if playerCellX < 0 { + playerCellX = 0 + } else if playerCellX >= int(cubicmap.Width) { + playerCellX = int(cubicmap.Width) - 1 + } + + if playerCellY < 0 { + playerCellY = 0 + } else if playerCellY >= int(cubicmap.Height) { + playerCellY = int(cubicmap.Height) - 1 + } + + // Check map collisions using image data and player position + // TODO: Improvement: Just check player surrounding cells for collision + for y := 0; y < int(cubicmap.Height); y++ { + for x := 0; x < int(cubicmap.Width); x++ { + // Collision: white pixel, only check R channel + if mapPixels[y*int(cubicmap.Width)+x].R == 255 && (rl.CheckCollisionCircleRec(playerPos, float32(playerRadius), rl.NewRectangle(float32(mapPosition.X-0.5+float32(x)), float32(mapPosition.Z-0.5+float32(y)), 1.0, 1.0))) { + // Collision detected, reset camera position + camera.Position = oldCamPos + } + } + } + //---------------------------------------------------------------------------------- + // Draw + //---------------------------------------------------------------------------------- + rl.BeginDrawing() + rl.ClearBackground(rl.RayWhite) + rl.BeginMode3D(camera) + rl.DrawModel(model, mapPosition, 1.0, rl.White) // Draw maze map + rl.EndMode3D() + rl.DrawTextureEx(cubicmap, rl.NewVector2(float32(rl.GetScreenWidth())-float32(cubicmap.Width)*4.0-20, 20.0), 0.0, 4.0, rl.White) + rl.DrawRectangleLines(int32(rl.GetScreenWidth())-cubicmap.Width*4-20, 20, cubicmap.Width*4, cubicmap.Height*4, rl.Green) + // Draw player position radar + rl.DrawRectangle(int32(rl.GetScreenWidth()-int(cubicmap.Width*4)-20+(playerCellX*4)), int32(20+playerCellY*4), 4, 4, rl.Red) + + rl.DrawFPS(10, 10) + + rl.EndDrawing() + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + rl.UnloadTexture(cubicmap) // Unload cubicmap texture + rl.UnloadTexture(texture) // Unload map texture + rl.UnloadModel(model) // Unload map model + rl.CloseWindow() // Close window and OpenGL context + //-------------------------------------------------------------------------------------- +}