Add resourcePath. Update examples

This commit is contained in:
Kenta 2023-09-16 21:44:48 +01:00
parent 284e28ace9
commit 9a0ab8e7b6
13 changed files with 477 additions and 332 deletions

View file

@ -16,6 +16,7 @@
- [Dependencies](#dependencies)
- [Pre-compiled klibs](#pre-compiled-klibs)
- [Build It Yourself](#build-it-yourself)
- [What is resourcePath?](#what-is-resourcePath?)
## Compatibility
@ -57,3 +58,16 @@ implementation("com.prism-architect:KaylibKit-macosarm64:1.0.4")
```
You're now done and ready to play with KaylibKit!
## What is resourcePath?
`resourcePath` was added because, well, Kotlin/Native doesn't play as nice as Java when it comes to handling resources in your Raylib application/game. So, what's the deal? It's a necessary feature that simply hands you the path to a folder called 'resources' that hangs out in the same spot as your app's executable.
Let's say you're loading a texture; all you have to do is ensure that a folder named resources is in the same folder as your executable, alongside your asset added to the said folder 'resources'.
Now simply call
```kotlin
loadTexture($resourcePath/myTexture.png)
```
That is all. `resourcePath` simplifies it all for you.
In the future we will plan to add ability to change the folder name as currently it's hardcoded to be named 'resources'

View file

@ -1,21 +1,20 @@
import kaylib.kCamera.setCameraMode
import kaylib.kCamera.updateCamera
import kaylib.kCore.*
import kaylib.kCore.endDrawing
import kaylib.kEnums.CameraMode
import kaylib.kEnums.CameraProjection
import kaylib.kEnums.ConfigFlag
import kaylib.kEnums.MouseButton
import kaylib.kMath.kVector3
import kaylib.kModels.*
import kaylib.kText.drawFPS
import kaylib.kText.drawText
import kaylib.kText.measureText
import kaylib.kTypes.kBoundingBox
import kaylib.kTypes.kCamera3D
import kaylib.kTypes.kRay
import kaylib.kTypes.kRayCollision
import kaylibkit.kCamera.updateCamera
import kaylibkit.kCore.*
import kaylibkit.kEnums.CameraMode
import kaylibkit.kEnums.CameraProjection
import kaylibkit.kEnums.ConfigFlag
import kaylibkit.kEnums.MouseButton
import kaylibkit.kMath.kVector3
import kaylibkit.kModels.*
import kaylibkit.kText.drawFPS
import kaylibkit.kText.drawText
import kaylibkit.kText.measureText
import kaylibkit.kTypes.kBoundingBox
import kaylibkit.kTypes.kCamera3D
import kaylibkit.kTypes.kRay
import kaylibkit.kTypes.kRayCollision
import kaylibc.*
import kotlinx.cinterop.memScoped
const val SCREEN_WIDTH = 800
const val SCREEN_HEIGHT = 450
@ -24,35 +23,51 @@ const val SCREEN_HEIGHT = 450
// Program main entry point
//------------------------------------------------------------------------------------
fun main() {
memScoped {
setConfigFlags(ConfigFlag.VSYNC_HINT) // Turn VSYNC On
initWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [core] example - 3d picking")
val camera = kCamera3D(kVector3(10F, 10F, 10F), kVector3(), kVector3(0F, 1F, 0F), 45F, CameraProjection.PERSPECTIVE)
val camera = kCamera3D(position = kVector3(10F, 10F, 10F, allocator = this), target = kVector3(0F, 0F, 0F, allocator = this), up = kVector3(0F, 1F, 0F, allocator = this), fovy = 45F, projection = CameraProjection.PERSPECTIVE, allocator = this)
val cubePosition = kVector3(0F, 1F, 0F)
val cubeSize = kVector3(2F, 2F, 2F)
val cubePosition = kVector3(0F, 1F, 0F, allocator = this)
val cubeSize = kVector3(2F, 2F, 2F, allocator = this)
var ray = kRay() // Picking line ray
var collision = kRayCollision()
var ray = kRay(allocator = this, position = kVector3(allocator = this), direction = kVector3(allocator = this)) // Picking line ray
var collision = kRayCollision(allocator = this, point = kVector3(allocator = this), normal = kVector3(allocator = this))
setCameraMode(camera, CameraMode.FREE) // Set a free camera mode
//--------------------------------------------------------------------------------------
// Main game loop
while (!windowShouldClose) { // Detect window close button or ESC key
// Update
//----------------------------------------------------------------------------------
updateCamera(camera)
if (isCursorHidden()) {
updateCamera(camera, CameraMode.FIRST_PERSON)
}
if (isMouseButtonPressed(MouseButton.LEFT)) {
if (!collision.hit) {
ray = getMouseRay(getMousePosition(), camera)
ray = getMouseRay(getMousePosition(allocator = this), camera = camera, allocator = this)
// Check collision between ray and box
collision = getRayCollisionBox(ray,
collision = getRayCollisionBox(
ray,
kBoundingBox(
kVector3(cubePosition.x - cubeSize.x/2, cubePosition.y - cubeSize.y/2, cubePosition.z - cubeSize.z/2),
kVector3(cubePosition.x + cubeSize.x/2, cubePosition.y + cubeSize.y/2, cubePosition.z + cubeSize.z/2)))
kVector3(
cubePosition.x - cubeSize.x / 2,
cubePosition.y - cubeSize.y / 2,
cubePosition.z - cubeSize.z / 2,
allocator = this
),
kVector3(
cubePosition.x + cubeSize.x / 2,
cubePosition.y + cubeSize.y / 2,
cubePosition.z + cubeSize.z / 2,
allocator = this
),
allocator = this
), allocator = this
)
} else {
collision.hit = false
}
@ -79,11 +94,17 @@ fun main() {
drawGrid(10, 1F)
}
drawText("Try selecting the box with mouse!", 240, 10, 20, darkGray);
drawText("Try selecting the box with mouse!", 240, 10, 20, darkGray)
if (collision.hit) drawText("BOX SELECTED", (SCREEN_WIDTH - measureText("BOX SELECTED", 30)) / 2, (SCREEN_HEIGHT * .1F).toInt(), 30, green)
if (collision.hit) drawText(
"BOX SELECTED",
(SCREEN_WIDTH - measureText("BOX SELECTED", 30)) / 2,
(SCREEN_HEIGHT * .1F).toInt(),
30,
green
)
drawFPS(10, 10);
drawFPS(10, 10)
//----------------------------------------------------------------------------------
}
}
@ -91,6 +112,5 @@ fun main() {
//--------------------------------------------------------------------------------------
closeWindow() // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}
}

View file

@ -5,6 +5,7 @@ import kaylibkit.kMath.kVector2
import kaylibkit.kShapes.drawCircle
import kaylibkit.kText.drawText
import kaylibc.*
import kotlinx.cinterop.memScoped
const val SCREEN_WIDTH = 800
const val SCREEN_HEIGHT = 450
@ -13,20 +14,29 @@ const val SCREEN_HEIGHT = 450
// Program main entry point
//------------------------------------------------------------------------------------
fun main() {
memScoped {
setConfigFlags(ConfigFlag.VSYNC_HINT) // Turn VSYNC On
initWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [core] example - keyboard input")
val ballPosition = kVector2(SCREEN_WIDTH/2F, SCREEN_HEIGHT/2F)
val ballPosition = kVector2(SCREEN_WIDTH / 2F, SCREEN_HEIGHT / 2F, allocator = this)
//--------------------------------------------------------------------------------------
// Main game loop
while (!windowShouldClose) { // Detect window close button or ESC key
// Update
//----------------------------------------------------------------------------------
if (isKeyDown(KeyboardKey.RIGHT)) { ballPosition.x += 2 }
if (isKeyDown(KeyboardKey.LEFT)) { ballPosition.x -= 2 }
if (isKeyDown(KeyboardKey.UP)) { ballPosition.y -= 2 }
if (isKeyDown(KeyboardKey.DOWN)) { ballPosition.y += 2 }
if (isKeyDown(KeyboardKey.RIGHT)) {
ballPosition.x += 2
}
if (isKeyDown(KeyboardKey.LEFT)) {
ballPosition.x -= 2
}
if (isKeyDown(KeyboardKey.UP)) {
ballPosition.y -= 2
}
if (isKeyDown(KeyboardKey.DOWN)) {
ballPosition.y += 2
}
//----------------------------------------------------------------------------------
drawing {
@ -45,5 +55,7 @@ fun main() {
//--------------------------------------------------------------------------------------
closeWindow() // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}
}

View file

@ -4,6 +4,7 @@ import kaylibkit.kEnums.MouseButton
import kaylibkit.kShapes.drawCircle
import kaylibkit.kText.drawText
import kaylibc.*
import kotlinx.cinterop.memScoped
const val SCREEN_WIDTH = 800
const val SCREEN_HEIGHT = 450
@ -12,6 +13,7 @@ const val SCREEN_HEIGHT = 450
// Program main entry point
//------------------------------------------------------------------------------------
fun main() {
memScoped {
setConfigFlags(ConfigFlag.VSYNC_HINT) // Turn VSYNC On
initWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [core] example - mouse input")
@ -23,15 +25,15 @@ fun main() {
while (!windowShouldClose) { // Detect window close button or ESC key
// Update
//----------------------------------------------------------------------------------
ballPosition = getMousePosition()
ballPosition = getMousePosition(allocator = this)
if (isMouseButtonPressed(MouseButton.LEFT)) ballColor = maroon;
else if (isMouseButtonPressed(MouseButton.MIDDLE)) ballColor = lime;
else if (isMouseButtonPressed(MouseButton.RIGHT)) ballColor = darkBlue;
else if (isMouseButtonPressed(MouseButton.SIDE)) ballColor = purple;
else if (isMouseButtonPressed(MouseButton.EXTRA)) ballColor = yellow;
else if (isMouseButtonPressed(MouseButton.FORWARD)) ballColor = orange;
else if (isMouseButtonPressed(MouseButton.BACK)) ballColor = beige;
if (isMouseButtonPressed(MouseButton.LEFT)) ballColor = maroon
else if (isMouseButtonPressed(MouseButton.MIDDLE)) ballColor = lime
else if (isMouseButtonPressed(MouseButton.RIGHT)) ballColor = darkBlue
else if (isMouseButtonPressed(MouseButton.SIDE)) ballColor = purple
else if (isMouseButtonPressed(MouseButton.EXTRA)) ballColor = yellow
else if (isMouseButtonPressed(MouseButton.FORWARD)) ballColor = orange
else if (isMouseButtonPressed(MouseButton.BACK)) ballColor = beige
//----------------------------------------------------------------------------------
drawing {
@ -48,6 +50,7 @@ fun main() {
// De-Initialization
//--------------------------------------------------------------------------------------
closeWindow() // Close window and OpenGL context
//--------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------- }
}
}

View file

@ -1,4 +1,3 @@
import kaylibkit.kCamera.setCameraMode
import kaylibkit.kCamera.updateCamera
import kaylibkit.kCore.*
import kaylibkit.kCore.closeWindow
@ -15,6 +14,7 @@ import kaylibkit.kTextures.*
import kaylibkit.kTypes.kCamera3D
import kaylibc.*
import kotlinx.cinterop.get
import kotlinx.cinterop.memScoped
const val SCREEN_WIDTH = 800
const val SCREEN_HEIGHT = 450
@ -23,32 +23,38 @@ const val SCREEN_HEIGHT = 450
// Program main entry point
//------------------------------------------------------------------------------------
fun main() {
memScoped {
setConfigFlags(ConfigFlag.VSYNC_HINT) // Turn VSYNC On
initWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [models] example - heightmap loading and drawing")
//--------------------------------------------------------------------------------------
val camera = kCamera3D(kVector3(18F, 18F, 18F), kVector3(0F, 0F, 0F), kVector3(0F, 1F, 0F), 45F, CameraProjection.PERSPECTIVE)
val camera = kCamera3D(
kVector3(18F, 18F, 18F, allocator = this),
kVector3(0F, 0F, 0F, allocator = this),
kVector3(0F, 1F, 0F, allocator = this),
45F,
CameraProjection.PERSPECTIVE,
allocator = this
)
val image = loadImage("resources/heightmap.png") // Load heightmap image (RAM)
val texture = loadTextureFromImage(image) // Convert image to texture (VRAM)
val image = loadImage("$resourcePath/heightmap.png", allocator = this) // Load heightmap image (RAM)
val texture = loadTextureFromImage(image, allocator = this) // Convert image to texture (VRAM)
val mesh = genMeshHeightmap(image, kVector3(16F, 8F, 16F)) // Generate heightmap mesh (RAM and VRAM)
val model = loadModelFromMesh(mesh) // Load model from generated mesh
val mesh = genMeshHeightmap(image, kVector3(16F, 8F, 16F, allocator = this), allocator = this) // Generate heightmap mesh (RAM and VRAM)
val model = loadModelFromMesh(mesh, allocator = this) // Load model from generated mesh
// Set map diffuse texture. NOTE that we have to use .get and check for nullability as its possible materials is null
model.materials?.get(0)?.maps?.get(MATERIAL_MAP_DIFFUSE)?.texture?.set(texture) // Also note that we have to use K/N function of .get to retrieve the data from the material
val mapPosition = kVector3(-8F, 0F, -8F) // Define model position
val mapPosition = kVector3(-8F, 0F, -8F, allocator = this) // Define model position
unloadImage(image) // Unload heightmap image from RAM, already uploaded to VRAM
setCameraMode(camera, CameraMode.ORBITAL) // Set an orbital camera mode
// Main game loop
while (!windowShouldClose) { // Detect window close button or ESC key
// Update
//----------------------------------------------------------------------------------
updateCamera(camera)
updateCamera(camera, CameraMode.ORBITAL)
//----------------------------------------------------------------------------------
drawing {
@ -75,4 +81,6 @@ fun main() {
closeWindow() // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}
}

View file

@ -8,6 +8,7 @@ import kaylibkit.kShapes.kRectangle
import kaylibkit.kText.drawText
import kaylibkit.kUtils.fade
import kaylibc.*
import kotlinx.cinterop.memScoped
const val SCREEN_WIDTH = 800
const val SCREEN_HEIGHT = 450
@ -16,10 +17,11 @@ const val SCREEN_HEIGHT = 450
// Program main entry point
//------------------------------------------------------------------------------------
fun main() {
memScoped {
setConfigFlags(ConfigFlag.VSYNC_HINT) // Turn VSYNC On
initWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [shapes] example - easings box anim")
var rec: Rectangle = kRectangle(kVector2(getScreenWidth()/2F, -100F), 100F, 100F)
var rec: Rectangle = kRectangle(allocator = this, kVector2(getScreenWidth() / 2F, -100F, allocator = this), 100F, 100F)
var rotation: Float = .0F
var alpha: Float = 1.0F
@ -85,7 +87,7 @@ fun main() {
}
if (isKeyPressed(KeyboardKey.SPACE)) {
rec = kRectangle(kVector2(getScreenWidth()/2F, -100F), 100F, 100F)
rec = kRectangle(allocator = this, kVector2(getScreenWidth() / 2F, -100F, allocator = this), 100F, 100F)
rotation = .0F
alpha = 1.0F
state = 0
@ -98,7 +100,7 @@ fun main() {
//----------------------------------------------------------------------------------
clearBackground(rayWhite)
drawRectangle(rec, kVector2(rec.width/2, rec.height/2), rotation, fade(black, alpha))
drawRectangle(rec, kVector2(rec.width / 2, rec.height / 2, allocator = this), rotation, fade(black, alpha, allocator = this))
drawText("PRESS [SPACE] TO RESET BOX ANIMATION!", 10, getScreenHeight() - 25, 20, lightGray)
//----------------------------------------------------------------------------------
@ -109,4 +111,5 @@ fun main() {
closeWindow() // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}
}

View file

@ -9,6 +9,7 @@ import kaylibkit.kTextures.loadTexture
import kaylibkit.kTextures.unloadTexture
import kaylibkit.kTypes.kColor
import kaylibc.*
import kotlinx.cinterop.memScoped
import kotlin.random.Random
const val SCREEN_WIDTH: Int = 800
@ -20,14 +21,14 @@ var MAX_BUNNIES = 500000
var MAX_BATCH_ELEMENTS = 8192
fun main() {
// Initialization
memScoped { // Initialization
//--------------------------------------------------------------------------------------
initWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [textures] example - bunnymark")
setTargetFPS(60)
// Load bunny texture
val texBunny = loadTexture("resources/wabbit_alpha.png")
val bunnies = Array(MAX_BUNNIES) { Bunny(kVector2(), kVector2(), kColor()) }
val texBunny = loadTexture("$resourcePath/wabbit_alpha.png", allocator = this)
val bunnies = Array(MAX_BUNNIES) { Bunny(kVector2(allocator = this), kVector2(allocator = this), kColor(allocator = this)) }
var bunniesCount = 0 // Bunnies counter
//--------------------------------------------------------------------------------------
@ -39,7 +40,7 @@ fun main() {
// Create more bunnies
repeat(100) {
if (bunniesCount < MAX_BUNNIES) {
bunnies[bunniesCount].position = getMousePosition()
bunnies[bunniesCount].position = getMousePosition(allocator = this)
bunnies[bunniesCount].speed.x = (Random.nextInt(-250, 250) / 60.0f)
bunnies[bunniesCount].speed.y = (Random.nextInt(-250, 250) / 60.0f)
bunnies[bunniesCount].color.apply {
@ -100,5 +101,6 @@ fun main() {
//--------------------------------------------------------------------------------------
unloadTexture(texBunny) // Unload bunny texture
closeWindow() // Close window and OpenGL context
//--------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------- }
}
}

View file

@ -20,7 +20,7 @@ fun main() {
//--------------------------------------------------------------------------------------
// NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
val texture = loadTexture("resources/raylib_logo.png", allocator = this) // Texture loading
val texture = loadTexture("$resourcePath/raylib_logo.png", allocator = this) // Texture loading
// Main game loop
while (!windowShouldClose) { // Detect window close button or ESC key

View file

@ -847,6 +847,15 @@ inline fun setTraceLogLevel(logLevel: TraceLogLevel) {
@OptIn(ExperimentalForeignApi::class)
typealias TraceLogCallback = CPointer<CFunction<(Int, CPointer<ByteVar>?, platform.posix.va_list?) -> Unit>>
/**
* Returns the absolute path to the 'resources' folder located at the same location as your executable.
*
* This property provides the path to the 'resources' folder associated with your executable.
* Ensure that a folder named 'resources' is present in the same directory as your executable.
*
* @return The absolute path to the 'resources' folder, or null if it doesn't exist or cannot be determined.
*/
internal expect val resourcePath: String?
/**
* Set custom trace log

View file

@ -245,7 +245,7 @@ inline fun Vector2.lerp(v2: Vector2, amount: Float) : Vector2 {
// * @return [Vector2]
// */
//@OptIn(ExperimentalForeignApi::class)
//inline fun Vector2.invert(): Vector2 { return kVector2(1.0f / this.x, 1.0f / this.y) }
//inline fun Vector2.invert(): Vector2 { return memScoped { kVector2(1.0f / this@invert.x, 1.0f / this@invert.y, this) } }
///**
// * Clamp the components of the [Vector2] between

View file

@ -2,8 +2,42 @@ package kaylibkit.kCore
import kaylibc.TraceLogCallback
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.refTo
import kotlinx.cinterop.toKString
import platform.posix.PATH_MAX
import platform.posix.getpid
import platform.posix.realpath
@OptIn(ExperimentalForeignApi::class)
actual fun setTraceLogCallbackInternal(callback: TraceLogCallback) {
kaylibc.SetTraceLogCallback(callback)
}
@OptIn(ExperimentalForeignApi::class)
actual inline val resourcePath: String?
get() {
// Get the process ID (PID) of the current process
val pid = getpid()
// Create a buffer to store the path
val buffer = ByteArray(PATH_MAX)
// Build the path to the '/proc' directory for the process
val procPath = "/proc/$pid/exe"
// Use the realpath function to get the canonicalised absolute pathname of the executable
if (realpath(procPath, buffer.refTo(0)) == null) {
// If realpath fails, return null to indicate that the resource path could not be determined
return null
}
// Convert the buffer, which contains the absolute path, to a Kotlin string
val executablePath = buffer.toKString()
// Find the last '/' character to extract the directory path
val lastSlashIndex = executablePath.lastIndexOf('/')
val executableDir = executablePath.substring(0, lastSlashIndex + 1)
// Construct the resource path by appending "/resources" to the executable directory
return executableDir + "resources"
}

View file

@ -1,8 +1,45 @@
package kaylibkit.kCore
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.convert
import kotlinx.cinterop.refTo
import kotlinx.cinterop.toKString
import platform.osx.proc_pidpath
import platform.posix.PATH_MAX
import platform.posix.getpid
@OptIn(ExperimentalForeignApi::class)
actual fun setTraceLogCallbackInternal(callback: TraceLogCallback) {
kaylibc.SetTraceLogCallback(callback)
}
@OptIn(ExperimentalForeignApi::class)
actual inline val resourcePath: String?
get() {
// Get the process ID of the current running process.
val pid = getpid()
// Create a buffer to store the path to the executable file.
val buffer = ByteArray(PATH_MAX)
// Use the proc_pidpath function to obtain the path to the executable file.
// The result is the length of the path.
val pathLength = proc_pidpath(pid, buffer.refTo(0), buffer.size.convert())
// Check if the path length is less than or equal to zero, indicating an error.
if (pathLength <= 0) {
return null
}
// Convert the buffer to a Kotlin string to get the full path to the executable.
val executablePath = buffer.toKString()
// Find the index of the last slash character ('/') in the executable path,
// which separates the directory from the executable file name.
val lastSlashIndex = executablePath.lastIndexOf('/')
// Extract the directory portion of the executable path, including the trailing slash.
val executableDir = executablePath.substring(0, lastSlashIndex + 1)
// Return the directory path of the executable file along with the "resources" subdirectory.
return executableDir + "resources"
}

View file

@ -6,3 +6,6 @@ import kotlinx.cinterop.ExperimentalForeignApi
actual fun setTraceLogCallbackInternal(callback: TraceLogCallback) {
kaylibc.SetTraceLogCallback(callback)
}
actual inline val resourcePath: String?
get() { return "Not yet implemented" }