Initial commit

This commit is contained in:
Kenta 2023-09-11 21:52:22 +01:00
parent 55c6ad0813
commit 5542fbf84f
82 changed files with 11113 additions and 8 deletions

42
.gitignore vendored Normal file
View file

@ -0,0 +1,42 @@
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

3
.idea/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml

16
.idea/gradle.xml Normal file
View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

View file

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="ReplaceUntilWithRangeUntil" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
</profile>
</component>

7
.idea/misc.xml Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

25
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,25 @@
<div align="center">
<p>
[Back to Main Page](./README.md)
</p>
# Contributing
</div>
Contributions to KaylibKit are more than welcome! I would highly suggest opening up an issue first to discuss the upcoming change.
## Small guideline
- Follow the same code structure.
- Comment your code.
- Ensure that your code is in the correct package.
## How to
1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create a new Pull Request

50
DEVELOPMENT.md Normal file
View file

@ -0,0 +1,50 @@
<div align="center">
<p>
[Back to Main Page](./README.md)
</p>
# Development
</div>
## Table of Contents
- [Development](#development)
- [Table of Contents](#table-of-contents)
- [Summary](#summary)
- [Roadmap](#roadmap)
- [Covered API](#covered-api)
- [Known Issues](#known-issues)
## Summary
**KaylibKit** is still in early stages of development.
Kotlin/Native is known for its terrible performance on the desktop as it's currently not the focus for JetBrains so please don't expect amazing performance coming out of this just yet!
We can only hope and dream that JetBrains will one day decide to prioritise performance over portability at some point, and so this is why this library exists, for that one day to come.
For most cases, while KaylibKit is still in early stages of development, all parts of Raylib have been wrapped apart from raygui which is planned for future.
## Roadmap
The current roadmap is:
- [x] Add support for Window MinGW.
- [x] Add support for MacOS.
- [x] Better approach towards CStruct constructors.
- [ ] WASM Export. *(Needs new WASI/WASM backend from Jetbrains)*
- [ ] Cleanup the code and remove unnecessary mess.
- [ ] Optimise example codes.
## Covered API
- [x] raylib.h
- [x] raymath.h
- [x] easings.h
- [ ] raygui.h
## Known Issues
1. Performance on Windows is abysmal due to the `-femulated-tls` flag passed by Kotlin/Native that can't be removed. Linux and macOS is unaffected.
2. You're unable to pass path to resources module when using IntelliJ IDEA, all resources have to be located in the directory as your main entry code. Hopefully to be fixed by Jetbrains at some point.

54
INSTALL.md Normal file
View file

@ -0,0 +1,54 @@
<div align="center">
<p>
[Back to Main Page](./README.md)
</p>
# Installation Guide
</div>
## Table of Contents
- [Installation Guide](#installation-guide)
- [Table of Contents](#table-of-contents)
- [Compatibility](#compatibility)
- [Dependencies](#dependencies)
- [Pre-compiled klibs](#pre-compiled-klibs)
- [Build It Yourself](#build-it-yourself)
## Compatibility
**Supported Platforms:**
- [x] Linux
- [X] Windows
- [X] MacOS
- [ ] WASM *(Awaiting few WASM backend from Jetbrains)*
## Dependencies
You don't need to have Raylib installed on your system to use KaylibKit.
## Gradle Package
KaylibKit has its own Gradle package hosted on Codeberg as the previous usage of `klibs` is now broken.
To add KaylibKit as a dependency to your project, firstly you have to add Codeberg URL to Maven Repository:
```kotlin
repositories {
mavenCentral()
maven { url = uri("https://codeberg.org/api/packages/Kenta/maven") }
}
```
Now that Maven is aware of KaylibKit, we can simply add it as a dependency:
```kotlin
implementation("com.prism-architect:kaylibkit-macos64-native:1.0.3")
// OR if you're on Linux:
implementation("com.prism-architect-kaylibkit-linux64-native:1.0.3")
```
You're now done and ready to play with KaylibKit!

20
LICENSE
View file

@ -1,11 +1,19 @@
zlib License
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
Copyright (c) 2023 Krystian Alabrudzinski (@Kenta)
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

277
README.md
View file

@ -1,3 +1,276 @@
# KaylibKit
<div align="center">
<p>
Raylib for Kotlin/Native
# Kaylib - Raylib on Kotlin/Native
</p>
[Documentation](https://Soutaisei.codeberg.page/kaylib/@pages/) • [Installation](./INSTALL.md) • [Development](./DEVELOPMENT.md) • [Contributing](./CONTRIBUTING.md) • [License](./LICENSE)
</div>
**KaylibKit** is a [Kotlin/Native](https://kotlinlang.org/docs/native-overview.html) binding for [Raylib 4.5](http://www.raylib.com/) a simple and easy-to-use library to learn videogames programming.
_A successor to Kaylib (Now more user friendly!)_
Its purpose is to hide away the pain and horrid syntax of K/N cinterop and joy in using Kotlin with an amazing game development framework.
This library is heavily influenced by [Raylib on Swift by STREGAsGate](https://github.com/STREGAsGate/Raylib).
### Code Example
<details>
<summary><b>Basic Window</b></summary>
```kotlin
import kaylibkit.kCore.*
import kaylibkit.kEnums.ConfigFlag
import kaylibkit.kText.drawText
import kaylibc.*
const val SCREEN_WIDTH = 800
const val SCREEN_HEIGHT = 450
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
fun main() {
setConfigFlags(ConfigFlag.VSYNC_HINT) // Turn VSYNC On
initWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [core] example - basic window")
//--------------------------------------------------------------------------------------
// Main game loop
while (!windowShouldClose) { // Detect window close button or ESC key
// Update
//----------------------------------------------------------------------------------
// TODO: Update your variables here
//----------------------------------------------------------------------------------
drawing {
// Draw
//----------------------------------------------------------------------------------
clearBackground(rayWhite)
drawText("Hello from Kotlin/Native", 190, 200, 20, lightGray)
//----------------------------------------------------------------------------------
}
}
// De-Initialization
//--------------------------------------------------------------------------------------
closeWindow() // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}
```
</details>
<details>
<summary><b>Basic Keyboard Input</b></summary>
```kotlin
import kaylibkit.kCore.*
import kaylibkit.kEnums.ConfigFlag
import kaylibkit.kEnums.KeyboardKey
import kaylibkit.kMath.kVector2
import kaylibkit.kShapes.drawCircle
import kaylibkit.kText.drawText
import kaylibc.*
const val SCREEN_WIDTH = 800
const val SCREEN_HEIGHT = 450
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
fun main() {
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)
//--------------------------------------------------------------------------------------
// 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 }
//----------------------------------------------------------------------------------
drawing {
// Draw
//----------------------------------------------------------------------------------
clearBackground(rayWhite)
drawText("move the ball with arrow keys", 10, 10, 20, darkGray)
drawCircle(ballPosition, 50F, maroon)
//----------------------------------------------------------------------------------
}
}
// De-Initialization
//--------------------------------------------------------------------------------------
closeWindow() // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}
```
</details>
<details>
<summary><b>Texture Loading and Drawing</b></summary>
```kotlin
import kaylibkit.kCore.*
import kaylibkit.kEnums.ConfigFlag
import kaylibkit.kText.drawText
import kaylibkit.kTextures.drawTexture
import kaylibkit.kTextures.loadTexture
import kaylibkit.kTextures.unloadTexture
import kaylibc.*
const val SCREEN_WIDTH = 800
const val SCREEN_HEIGHT = 450
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
fun main() {
setConfigFlags(ConfigFlag.VSYNC_HINT) // Turn VSYNC On
initWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [textures] example - texture loading and drawing")
//--------------------------------------------------------------------------------------
// NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
val texture = loadTexture("resources/raylib_logo.png") // Texture loading
// Main game loop
while (!windowShouldClose) { // Detect window close button or ESC key
// Update
//----------------------------------------------------------------------------------
// TODO: Update your variables here
//----------------------------------------------------------------------------------
drawing {
// Draw
//----------------------------------------------------------------------------------
clearBackground(rayWhite)
drawTexture(texture, SCREEN_WIDTH/2 - texture.width/2, SCREEN_HEIGHT/2 - texture.height/2, white)
drawText("this IS a texture!", 360, 370, 10, gray)
//----------------------------------------------------------------------------------
}
}
// De-Initialization
//--------------------------------------------------------------------------------------
unloadTexture(texture) // Texture unloading
closeWindow() // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}
```
</details>
<details>
<summary><b>3D Free Camera</b></summary>
```kotlin
iimport kaylibkit.kCamera.setCameraMode
import kaylibkit.kCamera.updateCamera
import kaylibkit.kCore.*
import kaylibkit.kEnums.CameraMode
import kaylibkit.kEnums.CameraProjection
import kaylibkit.kEnums.ConfigFlag
import kaylibkit.kEnums.KeyboardKey
import kaylibkit.kMath.kVector3
import kaylibkit.kMath.setZero
import kaylibkit.kModels.drawCube
import kaylibkit.kModels.drawCubeWires
import kaylibkit.kModels.drawGrid
import kaylibkit.kShapes.drawRectangle
import kaylibkit.kShapes.drawRectangleLines
import kaylibkit.kText.drawText
import kaylibkit.kTypes.kCamera3D
import kaylibkit.kUtils.fade
import kaylibc.*
const val SCREEN_WIDTH = 800
const val SCREEN_HEIGHT = 450
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
fun main() {
setConfigFlags(ConfigFlag.VSYNC_HINT) // Turn VSYNC On
initWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [core] example - 3d camera free")
val camera = kCamera3D(kVector3(10F, 10F, 10F), kVector3(), kVector3(0F, 1F, 0F), 45F, CameraProjection.PERSPECTIVE)
val cubePosition = kVector3()
setCameraMode(camera, CameraMode.FREE)
//--------------------------------------------------------------------------------------
// Main game loop
while (!windowShouldClose) { // Detect window close button or ESC key
// Update
//----------------------------------------------------------------------------------
updateCamera(camera)
if (isKeyDown(KeyboardKey.Z)) { camera.target.setZero() }
//----------------------------------------------------------------------------------
drawing {
// Draw
//----------------------------------------------------------------------------------
clearBackground(rayWhite)
mode3D(camera) {
drawCube(cubePosition, 2F, 2F, 2F, red)
drawCubeWires(cubePosition, 2F, 2F, 2F, maroon)
drawGrid(10, 1F)
}
drawRectangle(10, 10, 320, 133, fade(skyBlue, .5F))
drawRectangleLines(10, 10, 320, 133, blue)
drawText("Free camera default controls:", 20, 20, 10, black)
drawText("- Mouse Wheel to Zoom in-out", 40, 40, 10, darkGray)
drawText("- Mouse Wheel Pressed to Pan", 40, 60, 10, darkGray)
drawText("- Alt + Mouse Wheel Pressed to Rotate", 40, 80, 10, darkGray)
drawText("- Alt + Ctrl + Mouse Wheel Pressed for Smooth Zoom", 40, 100, 10, darkGray)
drawText("- Z to zoom to (0, 0, 0)", 40, 120, 10, darkGray)
//----------------------------------------------------------------------------------
}
}
// De-Initialization
//--------------------------------------------------------------------------------------
closeWindow() // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}
```
</details>
More examples can be found [here.](https://codeberg.org/Kenta/KaylibKit/src/branch/main/examples)
## Contributors
- [Soutaisei](https://codeberg.org/Soutaisei) - Creator and maintainer
- [fredthedeadhead](https://codeberg.org/fredthedeadhead) - Contributor
- [dallas-hyde](https://codeberg.org/dallas-hyde) - Contributor
## Credits & Mentions
- [STREGAsGate](https://github.com/STREGAsGate/Raylib) - An amazing person who drove me into learning how to do bindings, and now I can't stop. Creator of Raylib on Swift that drove this project to exist.

86
build.gradle.kts Normal file
View file

@ -0,0 +1,86 @@
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
plugins {
kotlin("multiplatform") version "1.9.10"
id("maven-publish")
}
group = "com.prism-architect"
version = "1.0.3"
// Codeberg repository properties providers
val tokenProvider: Provider<String> = providers.gradleProperty("KaylibKitToken")
val mavenUrlProvider: Provider<String> = providers.gradleProperty("KaylibKitMavenUrl")
val raylibVersion = "4.5.0"
val raylibMakeVersion = "VERSION=$raylibVersion"
repositories {
mavenCentral()
mavenLocal()
extensions.configure<PublishingExtension> {
repositories {
if (tokenProvider.isPresent && mavenUrlProvider.isPresent) {
maven(mavenUrlProvider) {
name = "CodebergPackages"
credentials(HttpHeaderCredentials::class) {
name = "Authorization"
value = "token ${tokenProvider.get()}"
}
authentication.create<HttpHeaderAuthentication>("header")
}
}
}
}
}
@Suppress("UNUSED_VARIABLE")
kotlin {
linuxX64()
mingwX64()
macosX64()
macosArm64()
targets.withType<KotlinNativeTarget> {
compilations.getByName("main") {
cinterops {
val kaylibc by creating
}
}
binaries {
binaries.staticLib()
binaries.RELEASE
}
}
sourceSets {
val commonMain by getting
val macosMain by creating { dependsOn(commonMain) }
val macosX64Main by getting { dependsOn(macosMain) }
val macosArm64Main by getting { dependsOn(macosMain) }
val linuxMain by creating { dependsOn(commonMain) }
val linuxX64Main by getting { dependsOn(linuxMain) }
val mingwMain by creating { dependsOn(commonMain) }
val mingwX64Main by getting { dependsOn(mingwMain) }
}
}
val sourceRaylib by tasks.registering(Exec::class) {
group = project.name
description = "Run Make to download static libraries and headers"
workingDir = file("src/nativeInterop/cinterop")
commandLine("make", raylibMakeVersion)
}
val cleanRaylib by tasks.registering(Exec::class) {
group = project.name
description = "Run Make to cleanup static libraries and headers (Useful for version update)"
workingDir = file("src/nativeInterop/cinterop")
commandLine("make", "clean")
}

View file

@ -0,0 +1,122 @@
import kaylibkit.kCore.*
import kaylibkit.kEnums.ConfigFlag
import kaylibkit.kEnums.KeyboardKey
import kaylibkit.kMath.kVector2
import kaylibkit.kMath.set
import kaylibkit.kShapes.drawLine
import kaylibkit.kShapes.drawRectangle
import kaylibkit.kShapes.drawRectangleLines
import kaylibkit.kShapes.kRectangle
import kaylibkit.kText.drawText
import kaylibkit.kTypes.kCamera2D
import kaylibkit.kTypes.kColor
import kaylibkit.kUtils.fade
import kaylibc.*
import kotlin.random.Random
import kotlin.random.nextInt
const val SCREEN_WIDTH = 800
const val SCREEN_HEIGHT = 450
const val MAX_BUILDINGS = 100
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
fun main() {
setConfigFlags(ConfigFlag.VSYNC_HINT) // Turn VSYNC On
initWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [core] example - 2d camera")
val player = kRectangle(400F, 280F, 40F, 40F)
val buildings = Array(MAX_BUILDINGS) { kRectangle() }
val buildColor = Array(MAX_BUILDINGS) {
kColor(Random.nextInt(200..240).toUByte(), Random.nextInt(200..240).toUByte(), Random.nextInt(200..250).toUByte(), 255U)
}
var spacing = 0
buildings.forEach { building ->
building.width = Random.nextInt(50..200).toFloat()
building.height = Random.nextInt(100..800).toFloat()
building.y = SCREEN_HEIGHT - 130F - building.height
building.x = -6000F + spacing
spacing += building.width.toInt()
}
val camera = kCamera2D(kVector2(SCREEN_WIDTH/2F, SCREEN_HEIGHT/2F), kVector2(player.x + 20F, player.y + 20F), 0F, 1F)
//--------------------------------------------------------------------------------------
// Main game loop
while (!windowShouldClose) { // Detect window close button or ESC key
// Update
//----------------------------------------------------------------------------------
// Player movement
if (isKeyDown(KeyboardKey.RIGHT)) { player.x += 2 }
else if (isKeyDown(KeyboardKey.LEFT)) { player.x -= 2 }
// Camera target follows player
// We're unable to modify .target directly due to how cinterop works with K/N as cinterop will translate any nested CStruct as a immutable (val)
// if they're not pointing to anything (pointer to), but we can easily modify it by passing immutable CStruct value:
camera.target.set(kVector2(player.x + 20F, player.y + 20F))
// We can also do it a different way by simply modifying the CStruct members
// Camera rotation controls
if (isKeyDown(KeyboardKey.A)) { camera.rotation-- }
else if (isKeyDown(KeyboardKey.S)) { camera.rotation++ }
// Limit camera rotation to 80 degrees (-40 to 40)
if (camera.rotation > 40) { camera.rotation = 40F }
else if (camera.rotation < -40) { camera.rotation = -40F }
// Camera zoom controls
camera.zoom += getMouseWheelMove() * .05F
if (camera.zoom > 3F) { camera.zoom = 3F }
else if (camera.zoom < .1F) { camera.zoom = .1F }
// Camera reset (zoom and rotation)
if (isKeyPressed(KeyboardKey.R)) {
camera.zoom = 1F
camera.rotation = 0F
}
//----------------------------------------------------------------------------------
drawing {
// Draw
//----------------------------------------------------------------------------------
mode2D(camera) {
clearBackground(rayWhite)
drawRectangle(-6000, 320, 13000, 8000, darkGray)
buildings.forEachIndexed { index, rect -> drawRectangle(rect, buildColor[index]) }
drawRectangle(player, red)
drawLine(camera.target.x.toInt(), -SCREEN_HEIGHT*10, camera.target.x.toInt(), SCREEN_HEIGHT*10, green)
drawLine(-SCREEN_WIDTH*10, camera.target.y.toInt(), SCREEN_WIDTH*10, camera.target.y.toInt(), green)
}
drawText("SCREEN AREA", 640, 10, 20, red)
drawRectangle(0, 0, SCREEN_WIDTH, 5, red)
drawRectangle(0, 5, 5, SCREEN_HEIGHT - 10, red)
drawRectangle(SCREEN_WIDTH - 5, 5, 5, SCREEN_HEIGHT - 10, red)
drawRectangle(0, SCREEN_HEIGHT - 5, SCREEN_WIDTH, 5, red)
drawRectangle(10, 10, 250, 113, fade(skyBlue, 0.5F))
drawRectangleLines(10, 10, 250, 113, blue)
drawText("Free 2D camera controls:", 20, 20, 10, black)
drawText("- Right/Left to move Offset", 40, 40, 10, darkGreen)
drawText("- Mouse Wheel to Zoom in-out", 40, 60, 10, darkGreen)
drawText("- A / S to Rotate", 40, 80, 10, darkGreen)
drawText("- R to reset Zoom and Rotation", 40, 100, 10, darkGreen)
//----------------------------------------------------------------------------------
}
}
// De-Initialization
//--------------------------------------------------------------------------------------
closeWindow() // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}

View file

@ -0,0 +1,55 @@
import kaylibkit.kCore.*
import kaylibkit.kEnums.CameraProjection
import kaylibkit.kEnums.ConfigFlag
import kaylibkit.kMath.kVector3
import kaylibkit.kModels.drawCube
import kaylibkit.kModels.drawCubeWires
import kaylibkit.kModels.drawGrid
import kaylibkit.kText.drawFPS
import kaylibkit.kText.drawText
import kaylibkit.kTypes.kCamera3D
import kaylibc.*
const val SCREEN_WIDTH = 800
const val SCREEN_HEIGHT = 450
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
fun main() {
setConfigFlags(ConfigFlag.VSYNC_HINT) // Turn VSYNC On
initWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [core] example - 3d camera mode")
val camera = kCamera3D(kVector3(0F, 10F, 10F), kVector3(0F, 0F, 0F), kVector3(0F, 1F, 0F), 45F, CameraProjection.PERSPECTIVE)
val cubePosition = kVector3(0F, 0F, 0F)
//--------------------------------------------------------------------------------------
// Main game loop
while (!windowShouldClose) { // Detect window close button or ESC key
// Update
//----------------------------------------------------------------------------------
// TODO: Update your variables here
//----------------------------------------------------------------------------------
drawing {
// Draw
//----------------------------------------------------------------------------------
clearBackground(rayWhite)
mode3D(camera) {
drawCube(cubePosition, 2F, 2F, 2F, red)
drawCubeWires(cubePosition, 2F, 2F, 2F, maroon)
drawGrid(10, 1F)
}
drawText("Welcome to the third dimension!", 10, 40, 20, darkGray)
drawFPS(10, 10)
//----------------------------------------------------------------------------------
}
}
// De-Initialization
//--------------------------------------------------------------------------------------
closeWindow() // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}

View file

@ -0,0 +1,88 @@
import kaylibkit.kCamera.setCameraMode
import kaylibkit.kCamera.updateCamera
import kaylibkit.kCore.*
import kaylibkit.kEnums.CameraMode
import kaylibkit.kEnums.CameraProjection
import kaylibkit.kEnums.ConfigFlag
import kaylibkit.kMath.kVector2
import kaylibkit.kMath.kVector3
import kaylibkit.kModels.drawCube
import kaylibkit.kModels.drawCubeWires
import kaylibkit.kModels.drawPlane
import kaylibkit.kShapes.drawRectangle
import kaylibkit.kShapes.drawRectangleLines
import kaylibkit.kText.drawText
import kaylibkit.kTypes.kCamera3D
import kaylibkit.kTypes.kColor
import kaylibkit.kUtils.fade
import kaylibc.*
import kotlin.random.Random
import kotlin.random.nextInt
const val SCREEN_WIDTH = 800
const val SCREEN_HEIGHT = 450
const val MAX_COLUMNS = 20
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
fun main() {
setConfigFlags(ConfigFlag.VSYNC_HINT) // Turn VSYNC On
initWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [core] example - 3d camera first person")
val camera = kCamera3D(kVector3(4F, 2F, 4F), kVector3(0F, 1.8F, 0F), kVector3(0F, 1F, 0F), 60F, CameraProjection.PERSPECTIVE)
val heights = Array(MAX_COLUMNS) {
Random.nextInt(1..12).toFloat()
}
val position = Array(MAX_COLUMNS) { i ->
kVector3(Random.nextInt(-15..15).toFloat(), heights[i]/2F, Random.nextInt(-15..15).toFloat())
}
val colors = Array(MAX_COLUMNS) {
kColor(Random.nextInt(20..255).toUByte(), Random.nextInt(10..55).toUByte(), 30U, 255U)
}
setCameraMode(camera, CameraMode.FIRST_PERSON)
//--------------------------------------------------------------------------------------
// Main game loop
while (!windowShouldClose) { // Detect window close button or ESC key
// Update
//----------------------------------------------------------------------------------
updateCamera(camera)
//----------------------------------------------------------------------------------
drawing {
// Draw
//----------------------------------------------------------------------------------
clearBackground(rayWhite)
mode3D(camera) {
drawPlane(kVector3(0F, 0F, 0F), kVector2(32F, 32F), lightGray) // Draw ground
drawCube(kVector3(-16F, 2.5F, 0F), 1F, 5F, 32F, blue) // Draw a blue wall
drawCube(kVector3(16F, 2.5F, 0F), 1F, 5F, 32F, blue) // Draw a green wall
drawCube(kVector3(0F, 2.5F, 16F), 32F, 5F, 1F, blue) // Draw a yellow wall
// Draw some cubes around
heights.indices.forEach { i ->
drawCube(position[i], 2F, heights[i], 2F, colors[i])
drawCubeWires(position[i], 2F, heights[i], 2F, maroon)
}
}
drawRectangle(10, 10, 220, 70, fade(skyBlue, .5F))
drawRectangleLines(10, 10, 220, 70, blue)
drawText("First person camera default controls:", 20, 20, 10, black);
drawText("- Move with keys: W, A, S, D", 40, 40, 10, darkGray);
drawText("- Mouse move to look around", 40, 60, 10, darkGray);
//----------------------------------------------------------------------------------
}
}
// De-Initialization
//--------------------------------------------------------------------------------------
closeWindow() // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}

View file

@ -0,0 +1,73 @@
import kaylibkit.kCamera.setCameraMode
import kaylibkit.kCamera.updateCamera
import kaylibkit.kCore.*
import kaylibkit.kEnums.CameraMode
import kaylibkit.kEnums.CameraProjection
import kaylibkit.kEnums.ConfigFlag
import kaylibkit.kEnums.KeyboardKey
import kaylibkit.kMath.kVector3
import kaylibkit.kMath.setZero
import kaylibkit.kModels.drawCube
import kaylibkit.kModels.drawCubeWires
import kaylibkit.kModels.drawGrid
import kaylibkit.kShapes.drawRectangle
import kaylibkit.kShapes.drawRectangleLines
import kaylibkit.kText.drawText
import kaylibkit.kTypes.kCamera3D
import kaylibkit.kUtils.fade
import kaylibc.*
const val SCREEN_WIDTH = 800
const val SCREEN_HEIGHT = 450
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
fun main() {
setConfigFlags(ConfigFlag.VSYNC_HINT) // Turn VSYNC On
initWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [core] example - 3d camera free")
val camera = kCamera3D(kVector3(10F, 10F, 10F), kVector3(), kVector3(0F, 1F, 0F), 45F, CameraProjection.PERSPECTIVE)
val cubePosition = kVector3()
setCameraMode(camera, CameraMode.FREE)
//--------------------------------------------------------------------------------------
// Main game loop
while (!windowShouldClose) { // Detect window close button or ESC key
// Update
//----------------------------------------------------------------------------------
updateCamera(camera)
if (isKeyDown(KeyboardKey.Z)) { camera.target.setZero() }
//----------------------------------------------------------------------------------
drawing {
// Draw
//----------------------------------------------------------------------------------
clearBackground(rayWhite)
mode3D(camera) {
drawCube(cubePosition, 2F, 2F, 2F, red)
drawCubeWires(cubePosition, 2F, 2F, 2F, maroon)
drawGrid(10, 1F)
}
drawRectangle(10, 10, 320, 133, fade(skyBlue, .5F))
drawRectangleLines(10, 10, 320, 133, blue)
drawText("Free camera default controls:", 20, 20, 10, black)
drawText("- Mouse Wheel to Zoom in-out", 40, 40, 10, darkGray)
drawText("- Mouse Wheel Pressed to Pan", 40, 60, 10, darkGray)
drawText("- Alt + Mouse Wheel Pressed to Rotate", 40, 80, 10, darkGray)
drawText("- Alt + Ctrl + Mouse Wheel Pressed for Smooth Zoom", 40, 100, 10, darkGray)
drawText("- Z to zoom to (0, 0, 0)", 40, 120, 10, darkGray)
//----------------------------------------------------------------------------------
}
}
// De-Initialization
//--------------------------------------------------------------------------------------
closeWindow() // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}

View file

@ -0,0 +1,96 @@
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 kaylibc.*
const val SCREEN_WIDTH = 800
const val SCREEN_HEIGHT = 450
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
fun main() {
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 cubePosition = kVector3(0F, 1F, 0F)
val cubeSize = kVector3(2F, 2F, 2F)
var ray = kRay() // Picking line ray
var collision = kRayCollision()
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 (isMouseButtonPressed(MouseButton.LEFT)) {
if (!collision.hit) {
ray = getMouseRay(getMousePosition(), camera)
// Check collision between ray and box
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)))
} else {
collision.hit = false
}
}
//----------------------------------------------------------------------------------
drawing {
// Draw
//----------------------------------------------------------------------------------
clearBackground(rayWhite)
mode3D(camera) {
if (collision.hit) {
drawCube(cubePosition, cubeSize.x, cubeSize.y, cubeSize.z, red)
drawCubeWires(cubePosition, cubeSize.x, cubeSize.y, cubeSize.z, maroon)
drawCubeWires(cubePosition, cubeSize.x + 0.2f, cubeSize.y + 0.2f, cubeSize.z + 0.2f, green)
} else {
drawCube(cubePosition, cubeSize.x, cubeSize.y, cubeSize.z, gray)
drawCubeWires(cubePosition, cubeSize.x, cubeSize.y, cubeSize.z, darkGray)
}
drawRay(ray, maroon)
drawGrid(10, 1F)
}
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)
drawFPS(10, 10);
//----------------------------------------------------------------------------------
}
}
// De-Initialization
//--------------------------------------------------------------------------------------
closeWindow() // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}

View file

@ -0,0 +1,40 @@
import kaylibkit.kCore.*
import kaylibkit.kEnums.ConfigFlag
import kaylibkit.kText.drawText
import kaylibc.*
const val SCREEN_WIDTH = 800
const val SCREEN_HEIGHT = 450
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
fun main() {
setConfigFlags(ConfigFlag.VSYNC_HINT) // Turn VSYNC On
initWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [core] example - basic window")
//--------------------------------------------------------------------------------------
// Main game loop
while (!windowShouldClose) { // Detect window close button or ESC key
// Update
//----------------------------------------------------------------------------------
// TODO: Update your variables here
//----------------------------------------------------------------------------------
drawing {
// Draw
//----------------------------------------------------------------------------------
clearBackground(rayWhite)
drawText("Hello from Kotlin/Native", 190, 200, 20, lightGray)
//----------------------------------------------------------------------------------
}
}
// De-Initialization
//--------------------------------------------------------------------------------------
closeWindow() // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}

View file

@ -0,0 +1,49 @@
import kaylibkit.kCore.*
import kaylibkit.kEnums.ConfigFlag
import kaylibkit.kEnums.KeyboardKey
import kaylibkit.kMath.kVector2
import kaylibkit.kShapes.drawCircle
import kaylibkit.kText.drawText
import kaylibc.*
const val SCREEN_WIDTH = 800
const val SCREEN_HEIGHT = 450
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
fun main() {
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)
//--------------------------------------------------------------------------------------
// 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 }
//----------------------------------------------------------------------------------
drawing {
// Draw
//----------------------------------------------------------------------------------
clearBackground(rayWhite)
drawText("move the ball with arrow keys", 10, 10, 20, darkGray)
drawCircle(ballPosition, 50F, maroon)
//----------------------------------------------------------------------------------
}
}
// De-Initialization
//--------------------------------------------------------------------------------------
closeWindow() // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}

View file

@ -0,0 +1,53 @@
import kaylibkit.kCore.*
import kaylibkit.kEnums.ConfigFlag
import kaylibkit.kEnums.MouseButton
import kaylibkit.kShapes.drawCircle
import kaylibkit.kText.drawText
import kaylibc.*
const val SCREEN_WIDTH = 800
const val SCREEN_HEIGHT = 450
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
fun main() {
setConfigFlags(ConfigFlag.VSYNC_HINT) // Turn VSYNC On
initWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [core] example - mouse input")
var ballPosition: Vector2
var ballColor = darkBlue
//--------------------------------------------------------------------------------------
// Main game loop
while (!windowShouldClose) { // Detect window close button or ESC key
// Update
//----------------------------------------------------------------------------------
ballPosition = getMousePosition()
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 {
// Draw
//----------------------------------------------------------------------------------
clearBackground(rayWhite)
drawText("move ball with mouse and click mouse button to change color", 10, 10, 20, darkGray)
drawCircle(ballPosition, 50F, ballColor)
//----------------------------------------------------------------------------------
}
}
// De-Initialization
//--------------------------------------------------------------------------------------
closeWindow() // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}

View file

@ -0,0 +1,45 @@
import kaylibkit.kCore.*
import kaylibkit.kEnums.ConfigFlag
import kaylibkit.kShapes.drawRectangle
import kaylibkit.kText.drawText
import kaylibc.*
const val SCREEN_WIDTH = 800
const val SCREEN_HEIGHT = 450
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
fun main() {
setConfigFlags(ConfigFlag.VSYNC_HINT) // Turn VSYNC On
initWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [core] example - mouse wheel input")
var boxPositionY = SCREEN_HEIGHT/2 - 40
val scrollSpeed = 4
//--------------------------------------------------------------------------------------
// Main game loop
while (!windowShouldClose) { // Detect window close button or ESC key
// Update
//----------------------------------------------------------------------------------
boxPositionY -= (getMouseWheelMove() * scrollSpeed).toInt()
//----------------------------------------------------------------------------------
drawing {
// Draw
//----------------------------------------------------------------------------------
clearBackground(rayWhite)
drawRectangle(SCREEN_WIDTH/2 - 40, boxPositionY, 80, 80, maroon)
drawText("Use mouse wheel to move the cube up and down!", 10, 10, 20, gray)
drawText("Box Position Y: $boxPositionY", 10, 40, 20, lightGray)
//----------------------------------------------------------------------------------
}
}
// De-Initialization
//--------------------------------------------------------------------------------------
closeWindow() // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}

View file

@ -0,0 +1,78 @@
import kaylibkit.kCamera.setCameraMode
import kaylibkit.kCamera.updateCamera
import kaylibkit.kCore.*
import kaylibkit.kCore.closeWindow
import kaylibkit.kEnums.CameraMode
import kaylibkit.kEnums.CameraProjection
import kaylibkit.kEnums.ConfigFlag
import kaylibkit.kImage.loadImage
import kaylibkit.kImage.unloadImage
import kaylibkit.kMath.kVector3
import kaylibkit.kModels.*
import kaylibkit.kShapes.drawRectangleLines
import kaylibkit.kText.drawFPS
import kaylibkit.kTextures.*
import kaylibkit.kTypes.kCamera3D
import kaylibc.*
import kotlinx.cinterop.get
const val SCREEN_WIDTH = 800
const val SCREEN_HEIGHT = 450
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
fun main() {
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 image = loadImage("resources/heightmap.png") // Load heightmap image (RAM)
val texture = loadTextureFromImage(image) // 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
// 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
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)
//----------------------------------------------------------------------------------
drawing {
// Draw
//----------------------------------------------------------------------------------
clearBackground(rayWhite)
mode3D(camera) {
drawModel(model, mapPosition, 1F, red)
drawGrid(20, 1F)
}
drawTexture(texture, SCREEN_WIDTH - texture.width - 20, 20, white)
drawRectangleLines(SCREEN_WIDTH - texture.width - 20, 20, texture.width, texture.height, green)
drawFPS(10, 10)
//----------------------------------------------------------------------------------
}
}
// De-Initialization
//--------------------------------------------------------------------------------------
unloadTexture(texture) // Texture unloading
unloadModel(model) // Unload model
closeWindow() // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -0,0 +1,112 @@
import kaylibkit.kCore.*
import kaylibkit.kEasing.*
import kaylibkit.kEnums.ConfigFlag
import kaylibkit.kEnums.KeyboardKey
import kaylibkit.kMath.kVector2
import kaylibkit.kShapes.drawRectangle
import kaylibkit.kShapes.kRectangle
import kaylibkit.kText.drawText
import kaylibkit.kUtils.fade
import kaylibc.*
const val SCREEN_WIDTH = 800
const val SCREEN_HEIGHT = 450
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
fun main() {
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 rotation: Float = .0F
var alpha: Float = 1.0F
var state: Int = 0
var framesCounter: Int = 0
//--------------------------------------------------------------------------------------
// Main game loop
while (!windowShouldClose) { // Detect window close button or ESC key
// Update
//----------------------------------------------------------------------------------
when (state) {
0 -> {
framesCounter++
// NOTE: Remember that 3rd parameter of easing function refers to
// desired value variation, do not confuse it with expected final value!
rec.y = elasticOut(framesCounter.toFloat(), -100F, getScreenHeight() / 2.0F + 100, 120F)
if (framesCounter >= 120) {
framesCounter = 0
state = 1
}
}
1 -> {
framesCounter++
rec.height = bounceOut(framesCounter.toFloat(), 100F, -90F, 120F)
rec.width = bounceOut(framesCounter.toFloat(), 100F, getScreenWidth().toFloat(), 120F)
if (framesCounter >= 120) {
framesCounter = 0
state = 2
}
}
2 -> {
framesCounter++
rotation = quadOut(framesCounter.toFloat(), .0F, 270.0F, 240F)
if (framesCounter >= 240) {
framesCounter = 0
state = 3
}
}
3 -> {
framesCounter++
rec.height = circOut(framesCounter.toFloat(), 10F, GetScreenWidth().toFloat(), 120F)
if (framesCounter >= 120) {
framesCounter = 0
state = 4
}
}
4 -> {
framesCounter++
alpha = sineOut(framesCounter.toFloat(), 1.0F, -1.0F, 160F)
if (framesCounter >= 160) {
framesCounter = 0
state = 5
}
}
else -> {}
}
if (isKeyPressed(KeyboardKey.SPACE)) {
rec = kRectangle(kVector2(getScreenWidth()/2F, -100F), 100F, 100F)
rotation = .0F
alpha = 1.0F
state = 0
framesCounter = 0
}
//----------------------------------------------------------------------------------
drawing {
// Draw
//----------------------------------------------------------------------------------
clearBackground(rayWhite)
drawRectangle(rec, kVector2(rec.width/2, rec.height/2), rotation, fade(black, alpha))
drawText("PRESS [SPACE] TO RESET BOX ANIMATION!", 10, getScreenHeight() - 25, 20, lightGray)
//----------------------------------------------------------------------------------
}
}
// De-Initialization
//--------------------------------------------------------------------------------------
closeWindow() // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 449 B

View file

@ -0,0 +1,104 @@
import kaylibkit.kCore.*
import kaylibkit.kEnums.MouseButton
import kaylibkit.kMath.kVector2
import kaylibkit.kShapes.drawRectangle
import kaylibkit.kText.drawFPS
import kaylibkit.kText.drawText
import kaylibkit.kTextures.drawTexture
import kaylibkit.kTextures.loadTexture
import kaylibkit.kTextures.unloadTexture
import kaylibkit.kTypes.kColor
import kaylibc.*
import kotlin.random.Random
const val SCREEN_WIDTH: Int = 800
const val SCREEN_HEIGHT: Int = 450
data class Bunny(var position: Vector2, var speed: Vector2, var color: Color)
var MAX_BUNNIES = 500000
var MAX_BATCH_ELEMENTS = 8192
fun main() {
// Initialization
//--------------------------------------------------------------------------------------
initWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [textures] example - bunnymark")
setTargetFPS(60)
// Load bunny texture
val texBunny = loadTexture("/Users/kenta/Documents/Repositories/untitled/src/nativeMain/kotlin/resources/wabbit_alpha.png")
val bunnies = Array(MAX_BUNNIES) { Bunny(kVector2(), kVector2(), kColor()) }
var bunniesCount = 0 // Bunnies counter
//--------------------------------------------------------------------------------------
// Main game loop
while (!windowShouldClose) { // Detect window close button or ESC key
// Update
//----------------------------------------------------------------------------------
if (isMouseButtonDown(MouseButton.LEFT)) {
// Create more bunnies
repeat(100) {
if (bunniesCount < MAX_BUNNIES) {
bunnies[bunniesCount].position = getMousePosition()
bunnies[bunniesCount].speed.x = (Random.nextInt(-250, 250) / 60.0f)
bunnies[bunniesCount].speed.y = (Random.nextInt(-250, 250) / 60.0f)
bunnies[bunniesCount].color.apply {
this.r = getRandomValue(50, 240).toUByte()
this.g = getRandomValue(80, 240).toUByte()
this.b = getRandomValue(100, 200).toUByte()
this.a = 255U
}
bunniesCount++
}
}
}
// Update bunnies
for (i in 0..<bunniesCount) {
bunnies[i].position.x += bunnies[i].speed.x
bunnies[i].position.y += bunnies[i].speed.y
if (bunnies[i].position.x + texBunny.width / 2.0 > SCREEN_WIDTH ||
bunnies[i].position.x + texBunny.width / 2.0 < 0
) {
bunnies[i].speed.x *= -1f
}
if (bunnies[i].position.y + texBunny.height / 2.0 > SCREEN_HEIGHT ||
bunnies[i].position.y + texBunny.height / 2.0 - 40 < 0
) {
bunnies[i].speed.y *= -1f
}
}
//----------------------------------------------------------------------------------
drawing {
// Draw
//----------------------------------------------------------------------------------
clearBackground(rayWhite)
for (i in 0..<bunniesCount) {
// NOTE: When internal batch buffer limit is reached (MAX_BATCH_ELEMENTS),
// a draw call is launched and buffer starts being filled again;
// before issuing a draw call, updated vertex data from internal CPU buffer is send to GPU...
// Process of sending data is costly, and it could happen that GPU data has not been completely
// processed for drawing while new data is tried to be sent (updating current in-use buffers)
// it could generate a stall and consequently a frame drop, limiting the number of drawn bunnies
drawTexture(
texBunny,
bunnies[i].position.x.toInt(),
bunnies[i].position.y.toInt(),
bunnies[i].color
)
}
drawRectangle(0, 0, SCREEN_WIDTH, 40, black)
drawText("bunnies: $bunniesCount", 120, 10, 20, green)
drawText("batched draw calls: " + bunniesCount / MAX_BATCH_ELEMENTS, 320, 10, 20, maroon)
drawFPS(10, 10)
//----------------------------------------------------------------------------------
}
}
// De-Initialization
//--------------------------------------------------------------------------------------
unloadTexture(texBunny) // Unload bunny texture
closeWindow() // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}

View file

@ -0,0 +1,48 @@
import kaylibkit.kCore.*
import kaylibkit.kEnums.ConfigFlag
import kaylibkit.kText.drawText
import kaylibkit.kTextures.drawTexture
import kaylibkit.kTextures.loadTexture
import kaylibkit.kTextures.unloadTexture
import kaylibc.*
const val SCREEN_WIDTH = 800
const val SCREEN_HEIGHT = 450
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
fun main() {
setConfigFlags(ConfigFlag.VSYNC_HINT) // Turn VSYNC On
initWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [textures] example - texture loading and drawing")
//--------------------------------------------------------------------------------------
// NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
val texture = loadTexture("resources/raylib_logo.png") // Texture loading
// Main game loop
while (!windowShouldClose) { // Detect window close button or ESC key
// Update
//----------------------------------------------------------------------------------
// TODO: Update your variables here
//----------------------------------------------------------------------------------
drawing {
// Draw
//----------------------------------------------------------------------------------
clearBackground(rayWhite)
drawTexture(texture, SCREEN_WIDTH/2 - texture.width/2, SCREEN_HEIGHT/2 - texture.height/2, white)
drawText("this IS a texture!", 360, 370, 10, gray)
//----------------------------------------------------------------------------------
}
}
// De-Initialization
//--------------------------------------------------------------------------------------
unloadTexture(texture) // Texture unloading
closeWindow() // Close window and OpenGL context
//--------------------------------------------------------------------------------------
}

6
gradle.properties Normal file
View file

@ -0,0 +1,6 @@
kotlin.code.style=official
org.gradle.jvmargs=-Xmx6g -XX:MaxMetaspaceSize=1g -Dfile.encoding=UTF-8
org.gradle.parallel=true
org.gradle.welcome=never
kotlin.incremental.useClasspathSnapshot=true
kotlin.mpp.enableCInteropCommonization=true

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View file

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

234
gradlew vendored Executable file
View file

@ -0,0 +1,234 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

89
gradlew.bat vendored Normal file
View file

@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

8
settings.gradle.kts Normal file
View file

@ -0,0 +1,8 @@
pluginManagement {
repositories {
mavenCentral()
gradlePluginPortal()
}
}
rootProject.name = "KaylibKit"

View file

@ -0,0 +1,476 @@
package kaylibkit.kAudio
import kaylibc.*
import kotlinx.cinterop.*
// -- Module: kAudio
//=======================================================//
// AUDIO DEVICE MANAGEMENT
//=======================================================//
/**
* Initialize audio device and context
*/
inline fun initAudioDevice() {
InitAudioDevice()
}
/**
* Close the audio device and context
*/
inline fun closeAudioDevice() {
CloseAudioDevice()
}
/**
* Check if audio device has been initialized successfully
* @return [Boolean]
*/
inline val isAudioDeviceReady: Boolean
get() { return IsAudioDeviceReady() }
/**
* Set master volume (listener)
*/
inline fun setMasterVolume(volume: Float) {
SetMasterVolume(volume)
}
//=======================================================//
// WAVE/SOUND LOADING & UNLOADING FUNCTIONS
//=======================================================//
/**
* Load wave data from file
* @return [Wave]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadWave(fileName: String) : Wave {
return LoadWave(fileName).getPointer(MemScope()).pointed
}
/**
* Load wave from memory buffer, [fileType] refers to extension: i.e. ".wav"
* @return [Wave]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadWaveFromMemory(fileType: String, fileData: UByteVar, dataSize: Int) : Wave {
return LoadWaveFromMemory(fileType, fileData.ptr, dataSize).getPointer(MemScope()).pointed
}
/**
* Load [Sound] from file
* @return [Sound]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadSound(fileName: String) : Sound {
return LoadSound(fileName).getPointer(MemScope()).pointed
}
/**
* Load [Sound] from [wave] data
* @return [Sound]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadSoundFromWave(wave: Wave) : Sound {
return LoadSoundFromWave(wave.readValue()).getPointer(MemScope()).pointed
}
/**
* Update [sound] buffer with new [data]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun updateSound(sound: Sound, data: COpaquePointer, samplesCount: Int) {
return UpdateSound(sound.readValue(), data, samplesCount)
}
/**
* Unload [wave] data
*/
@OptIn(ExperimentalForeignApi::class)
inline fun unloadWave(wave: Wave) {
UnloadWave(wave.readValue())
}
/**
* Unload [sound]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun unloadSound(sound: Sound) {
UnloadSound(sound.readValue())
}
/**
* Export [wave] data to file, returns true on success
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun exportWave(wave: Wave, fileName: String) : Boolean {
return ExportWave(wave.readValue(), fileName)
}
/**
* Export wave sample data to code (.h), returns true on success
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun exportWaveAsCode(wave: Wave, fileName: String) : Boolean {
return ExportWaveAsCode(wave.readValue(), fileName)
}
//=======================================================//
// WAVE/SOUND MANAGEMENT FUNCTIONS
//=======================================================//
/**
* Play a [sound]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun playSound(sound: Sound) {
PlaySound(sound.readValue())
}
/**
* Stop playing a [sound]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun stopSound(sound: Sound) {
StopSound(sound.readValue())
}
/**
* Pause a [sound]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun pauseSound(sound: Sound) {
PauseSound(sound.readValue())
}
/**
* Resume a paused [sound]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun resumeSound(sound: Sound) {
ResumeSound(sound.readValue())
}
/**
* Check if a [sound] is currently playing
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun isSoundPlaying(sound: Sound) : Boolean {
return IsSoundPlaying(sound.readValue())
}
/**
* Check if a [sound] is currently playing
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun isSoundReady(sound: Sound) : Boolean {
return IsSoundReady(sound.readValue())
}
/**
* Set [volume] for a [sound] (1.0 is max level)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun setSoundVolume(sound: Sound, volume: Float) {
SetSoundVolume(sound.readValue(), volume)
}
/**
* Set [pitch] for a [sound] (1.0 is base level)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun setSoundPitch(sound: Sound, pitch: Float) {
SetSoundPitch(sound.readValue(), pitch)
}
/**
* Convert [wave] data to desired format
*/
@OptIn(ExperimentalForeignApi::class)
inline fun waveFormat(wave: Wave, sampleRate: Int, sampleSize: Int, channels: Int) {
WaveFormat(wave.readValue(), sampleRate, sampleSize, channels)
}
/**
* Copy a [wave] to a new [wave]
* @return [Wave]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun waveCopy(wave: Wave) : Wave {
return WaveCopy(wave.readValue()).getPointer(MemScope()).pointed
}
/**
* Crop a [wave] to defined samples range
*/
@OptIn(ExperimentalForeignApi::class)
inline fun waveCrop(wave: Wave, initSample: Int, finalSample: Int) {
WaveCrop(wave.readValue(), initSample, finalSample)
}
/**
* Load samples data from wave as a floats array
* @return [FloatVar]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadWaveSamples(wave: Wave) : FloatVar? {
return LoadWaveSamples(wave.readValue())?.getPointer(MemScope())?.pointed
}
/**
* Unload samples data loaded with LoadWaveSamples()
*/
@OptIn(ExperimentalForeignApi::class)
inline fun unloadWaveSamples(samples: FloatVar) {
UnloadWaveSamples(samples.ptr)
}
/**
* Checks if wave data is ready
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun isWaveReady(wave: Wave) : Boolean {
return IsWaveReady(wave.readValue())
}
//=======================================================//
// MUSIC MANAGEMENT FUNCTIONS
//=======================================================//
/**
* Load music stream from file
* @return [Music]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadMusicStream(fileName: String) : Music {
return LoadMusicStream(fileName).getPointer(MemScope()).pointed
}
/**
*Load music stream from data
* @return [Music]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadMusicStreamFromMemory(fileType: String, data: CPointer<UByteVar>, dataSize: Int) : Music {
return LoadMusicStreamFromMemory(fileType, data, dataSize).getPointer(MemScope()).pointed
}
/**
* Unload [music] stream
*/
@OptIn(ExperimentalForeignApi::class)
inline fun unloadMusicStream(music: Music) {
UnloadMusicStream(music.readValue())
}
/**
* Start [music] playing
*/
@OptIn(ExperimentalForeignApi::class)
inline fun playMusicStream(music: Music) {
PlayMusicStream(music.readValue())
}
/**
* Check if [music] is playing
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun isMusicStreamPlaying(music: Music) : Boolean {
return IsMusicStreamPlaying(music.readValue())
}
/**
* Check if [music] is playing
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun isMusicReady(music: Music) : Boolean {
return IsMusicReady(music.readValue())
}
/**
* Updates buffers for [music] streaming
*/
@OptIn(ExperimentalForeignApi::class)
inline fun updateMusicStream(music: Music) {
UpdateMusicStream(music.readValue())
}
/**
* Stop [music] playing
*/
@OptIn(ExperimentalForeignApi::class)
inline fun stopMusicStream(music: Music) {
StopMusicStream(music.readValue())
}
/**
* Pause [music] playing
*/
@OptIn(ExperimentalForeignApi::class)
inline fun pauseMusicStream(music: Music) {
PauseMusicStream(music.readValue())
}
/**
* Resume playing paused [music]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun resumeMusicStream(music: Music) {
ResumeMusicStream(music.readValue())
}
/**
* Seek [music] to a [position] (in seconds)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun seekMusicStream(music: Music, position: Float) {
SeekMusicStream(music.readValue(), position)
}
/**
* Set [volume] for [music] (1.0 is max level)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun setMusicVolume(music: Music, volume: Float) {
SetMusicVolume(music.readValue(), volume)
}
/**
* Set [pitch] for a [music] (1.0 is base level)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun setMusicPitch(music: Music, pitch: Float) {
SetMusicPitch(music.readValue(), pitch)
}
/**
* Get [music] time length (in seconds)
* @return [Float]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getMusicTimeLength(music: Music) : Float {
return GetMusicTimeLength(music.readValue())
}
/**
* Get current [music] time played (in seconds)
* @return [Float]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getMusicTimePlayed(music: Music) : Float {
return GetMusicTimeLength(music.readValue())
}
//=======================================================//
// AUDIOSTREAM MANAGEMENT FUNCTIONS
//=======================================================//
/**
* Init audio stream (to stream raw audio pcm data)
* @return [AudioStream]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadAudioStream(sampleRate: UInt, sampleSize: UInt, channels: UInt) : AudioStream {
return LoadAudioStream(sampleRate, sampleSize, channels).getPointer(MemScope()).pointed
}
/**
* Unload audio [stream] and free memory
*/
@OptIn(ExperimentalForeignApi::class)
inline fun unloadAudioStream(stream: AudioStream) {
UnloadAudioStream(stream.readValue())
}
/**
* Update audio [stream] buffers with data
*/
@OptIn(ExperimentalForeignApi::class)
inline fun updateAudioStream(stream: AudioStream, data: COpaquePointer, samplesCount: Int) {
UpdateAudioStream(stream.readValue(), data, samplesCount)
}
/**
* Check if any audio [stream] buffers requires refill
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun isAudioStreamProcessed(stream: AudioStream) : Boolean {
return IsAudioStreamProcessed(stream.readValue())
}
/**
* Play audio [stream]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun playAudioStream(stream: AudioStream) {
PlayAudioStream(stream.readValue())
}
/**
* Pause audio [stream]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun pauseAudioStream(stream: AudioStream) {
PauseAudioStream(stream.readValue())
}
/**
* Resume audio [stream]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun resumeAudioStream(stream: AudioStream) {
ResumeAudioStream(stream.readValue())
}
/**
* Check if audio [stream] is playing
*/
@OptIn(ExperimentalForeignApi::class)
inline fun isAudioStreamPlaying(stream: AudioStream) : Boolean {
return IsAudioStreamPlaying(stream.readValue())
}
/**
* Stop audio [stream]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun stopAudioStream(stream: AudioStream) {
StopAudioStream(stream.readValue())
}
/**
* Set [volume] for audio [stream] (1.0 is max level)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun setAudioStreamVolume(stream: AudioStream, volume: Float) {
SetAudioStreamVolume(stream.readValue(), volume)
}
/**
* Set [pitch] for [audio] stream (1.0 is base level)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun setAudioStreamPitch(stream: AudioStream, pitch: Float) {
SetAudioStreamPitch(stream.readValue(), pitch)
}
/**
* Default [size] for new audio streams
*/
@OptIn(ExperimentalForeignApi::class)
inline fun setAudioStreamBufferSizeDefault(size: Int) {
SetAudioStreamBufferSizeDefault(size)
}

View file

@ -0,0 +1,137 @@
package kaylibkit.kCamera
import kaylibc.*
import kotlinx.cinterop.*
// -- Module: kCamera
//=======================================================//
// CAMERA SYSTEM FUNCTIONS
//=======================================================//
/**
* Returns the [camera] forward [Vector3] (normalized)
* @return [Vector3]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getCameraForward(camera: Camera) : Vector3 {
return GetCameraForward(camera.ptr).getPointer(MemScope()).pointed
}
/**
* Returns the [camera] up [Vector3] (normalized)
* Note: The up vector might not be perpendicular to the forward vector
* @return [Vector3]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getCameraUp(camera: Camera) : Vector3 {
return GetCameraUp(camera.ptr).getPointer(MemScope()).pointed
}
/**
* Returns the [camera] right [Vector3] (normalized)
* @return [Vector3]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getCameraRight(camera: Camera) : Vector3 {
return GetCameraRight(camera.ptr).getPointer(MemScope()).pointed
}
/**
* Moves the camera in its forward direction
*/
@OptIn(ExperimentalForeignApi::class)
inline fun cameraMoveForward(camera: Camera, distance: Float, moveInWorldPlane: Boolean) {
CameraMoveForward(camera.ptr, distance, moveInWorldPlane)
}
/**
* Moves the camera in its up direction
*/
@OptIn(ExperimentalForeignApi::class)
inline fun cameraMoveUp(camera: Camera, distance: Float) {
CameraMoveUp(camera.ptr, distance)
}
/**
* Moves the camera target in its current right direction
*/
@OptIn(ExperimentalForeignApi::class)
inline fun cameraMoveRight(camera: Camera, distance: Float, moveInWorldPlane: Boolean) {
CameraMoveRight(camera.ptr, distance, moveInWorldPlane)
}
/**
* Moves the camera position closer/farther to/from the camera target
*/
@OptIn(ExperimentalForeignApi::class)
inline fun cameraMoveToTarget(camera: Camera, delta: Float) {
CameraMoveToTarget(camera.ptr, delta)
}
/**
* Rotates the [camera] around its up vector
* Yaw is "looking left and right"
* If [rotateAroundTarget] is false, the [camera] rotates around its position
* Note: [angle] must be provided in radians
*/
@OptIn(ExperimentalForeignApi::class)
inline fun cameraYaw(camera: Camera, angle: Float, rotateAroundTarget: Boolean) {
CameraYaw(camera.ptr, angle, rotateAroundTarget)
}
/**
* Rotates the [camera] around its right vector, pitch is "looking up and down"
* lockView prevents [camera] overrotation (aka "somersaults")
* [rotateAroundTarget] defines if rotation is around target or around its position
* [rotateUp] rotates the up direction as well (typically only usefull in CAMERA_FREE)
* NOTE: [angle] must be provided in radians
*/
@OptIn(ExperimentalForeignApi::class)
inline fun cameraPitch(camera: Camera, angle: Float, lockView: Boolean, rotateAroundTarget: Boolean, rotateUp: Boolean) {
CameraPitch(camera.ptr, angle, lockView, rotateAroundTarget, rotateUp)
}
/**
* Rotates the [camera] around its forward vector
* Roll is "turning your head sideways to the left or right"
* Note: [angle] must be provided in radians
*/
@OptIn(ExperimentalForeignApi::class)
inline fun cameraRoll(camera: Camera, angle: Float) {
CameraRoll(camera.ptr, angle)
}
/**
* Returns the [camera] view [Matrix]
* @return [Matrix]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getCameraViewMatrix(camera: Camera) : Matrix {
return GetCameraViewMatrix(camera.ptr).getPointer(MemScope()).pointed
}
/**
* Returns the [camera] projection [Matrix]
* @return [Matrix]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getCameraProjectionMatrix(camera: Camera, aspect: Float) : Matrix {
return GetCameraProjectionMatrix(camera.ptr, aspect).getPointer(MemScope()).pointed
}
/**
* Update [camera] position for selected [mode]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun updateCamera(camera: Camera, mode: kaylibkit.kEnums.CameraMode) {
UpdateCamera(camera.ptr, mode.value)
}
/**
* Update [camera] movement, [movement]/[rotation] values should be provided by user
*/
@OptIn(ExperimentalForeignApi::class)
inline fun updateCameraPro(camera: Camera, movement: Vector3, rotation: Vector3, zoom: Float) {
UpdateCameraPro(camera.ptr, movement.readValue(), rotation.readValue(), zoom)
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,289 @@
package kaylibkit.kEasing
import platform.posix.pow
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.sqrt
// -- Module: kEasing
//=======================================================//
// EASINGS FUNCTIONS
//=======================================================//
/**
* * A port of Robert Penner's easing equations to Kotlin from C (Raylib) (http://robertpenner.com/easing/)
*
* Robert Penner License
* ---------------------------------------------------------------------------------
* Open source under the BSD License.
*
* Copyright (c) 2001 Robert Penner. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the author nor the names of contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
* ---------------------------------------------------------------------------------
*
* Copyright (c) 2015-2023 Ramon Santamaria (@raysan5)
*
* This software is provided "as-is", without any express or implied warranty. In no event
* will the authors be held liable for any damages arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose, including commercial
* applications, and to alter it and redistribute it freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not claim that you
* wrote the original software. If you use this software in a product, an acknowledgment
* in the product documentation would be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
* as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*
**********************************************************************************************/
/*
How to use:
* The four inputs t,b,c,d are defined as follows:
* t = current time (in any unit measure, but same unit as duration)
* b = starting value to interpolate
* c = the total change in value of b that needs to occur
* d = total time it should take to complete (duration)
*/
// Linear Easing Functions
inline fun linearNone(t: Float, b: Float, c: Float, d: Float): Float {
return (c*t/d + b)
}
inline fun linearIn(t: Float, b: Float, c: Float, d: Float): Float {
return (c*t/d + b)
}
inline fun linearOut(t: Float, b: Float, c: Float, d: Float): Float {
return (c*t/d + b)
}
inline fun linearInOut(t: Float, b: Float, c: Float, d: Float): Float {
return (c*t/d + b)
}
// Sine Easing Functions
inline fun sineIn(t: Float, b: Float, c: Float, d: Float): Float {
return (-c*cos(t/d*(PI/2.0f)) + c + b).toFloat()
}
inline fun sineOut(t: Float, b: Float, c: Float, d: Float): Float {
return c * sin(t / d * (PI / 2.0f)).toFloat() + b
}
inline fun sineInOut(t: Float, b: Float, c: Float, d: Float): Float {
return -c / 2.0f * (cos(PI * t / d) - 1.0f).toFloat() + b
}
// Circular Easing Functions
inline fun circIn(t: Float, b: Float, c: Float, d: Float): Float {
var t2 = t
t2 /= d
return -c * (sqrt(1.0f - t2 * t2) - 1.0f) + b
}
inline fun circOut(t: Float, b: Float, c: Float, d: Float): Float {
var t2 = t
t2 = t2 / d - 1.0f
return c * sqrt(1.0f - t2 * t2) + b
}
inline fun circInOut(t: Float, b: Float, c: Float, d: Float): Float
{
var t2 = t
if (d / 2.0f.let { t2 /= it; t2 } < 1.0f) return -c / 2.0f * (sqrt(1.0f - t2 * t2) - 1.0f) + b
t2 -= 2.0f
return c / 2.0f * (sqrt(1.0f - t2 * t2) + 1.0f) + b
}
// Cubic Easing Functions
inline fun cubicIn(t: Float, b: Float, c: Float, d: Float): Float {
var t2 = t
t2 /= d
return c * t2 * t2 * t2 + b
}
inline fun cubicOut(t: Float, b: Float, c: Float, d: Float): Float {
var t2 = t
t2 = t2 / d - 1.0f
return c * (t2 * t2 * t2 + 1.0f) + b
}
inline fun cubicInOut(t: Float, b: Float, c: Float, d: Float): Float
{
var t2 = t
if (d / 2.0f.let { t2 /= it; t2 } < 1.0f) return c / 2.0f * t2 * t2 * t2 + b
t2 -= 2.0f
return c / 2.0f * (t2 * t2 * t2 + 2.0f) + b
}
// Quadratic Easing Functions
inline fun quadIn(t: Float, b: Float, c: Float, d: Float): Float {
var t2 = t
t2 /= d
return c * t2 * t2 + b
}
inline fun quadOut(t: Float, b: Float, c: Float, d: Float): Float {
var t2 = t
t2 /= d
return -c * t2 * (t2 - 2.0f) + b
}
inline fun quadInOut(t: Float, b: Float, c: Float, d: Float): Float
{
var t2 = t
return if (d / 2.let { t2 /= it; t2 } < 1) c / 2 * (t2 * t2) + b else -c / 2.0f * ((t2 - 1.0f) * (t2 - 3.0f) - 1.0f) + b
}
// Exponential Easing Functions
inline fun expoIn(t: Float, b: Float, c: Float, d: Float): Float {
return if (t == 0.0f) b else c * pow(2.0, 10.0 * (t / d - 1.0f)).toFloat() + b
}
inline fun expoOut(t: Float, b: Float, c: Float, d: Float): Float {
return if (t == d) b + c else c * (-pow(2.0, -10.0 * t / d) + 1.0f).toFloat() + b
}
inline fun expoInOut(t: Float, b: Float, c: Float, d: Float): Float
{
var t2 = t
if (t2 == 0.0f) return b
if (t2 == d) return b + c
return if (d / 2.0f.let { t2 /= it; t2 } < 1.0f) (c / 2.0f * pow(2.0, 10.0 * (t2 - 1.0f)) + b).toFloat() else c / 2.0f * (-pow(
2.0,
-10.0 * (t2 - 1.0f)
) + 2.0f).toFloat() + b
}
// Back Easing Functions
inline fun backIn(t: Float, b: Float, c: Float, d: Float): Float
{
var t2 = t
val s = 1.70158f
t2 /= d
val postFix = t2
return c * postFix * t2 * ((s + 1.0f) * t2 - s) + b
}
fun backOut(t: Float, b: Float, c: Float, d: Float): Float
{
var t2 = t
val s = 1.70158f
t2 = t2 / d - 1.0f
return c * (t2 * t2 * ((s + 1.0f) * t2 + s) + 1.0f) + b
}
fun backInOut(t: Float, b: Float, c: Float, d: Float): Float
{
var t2 = t
var s = 1.70158f
if (d / 2.0f.let { t2 /= it; t2 } < 1.0f) {
s *= 1.525f
return c / 2.0f * (t2 * t2 * ((s + 1.0f) * t2 - s)) + b
}
t2 -= 2.0f
val postFix = t2
s *= 1.525f
return c / 2.0f * (postFix * t2 * ((s + 1.0f) * t2 + s) + 2.0f) + b
}
// Bounce Easing Function
inline fun bounceOut(t: Float, b: Float, c: Float, d: Float): Float
{
var t2 = t
return if (d.let { t2 /= it; t2 } < 1.0f / 2.75f) {
c * (7.5625f * t2 * t2) + b
} else if (t2 < 2.0f / 2.75f) {
t2 -= 1.5f / 2.75f
val postFix = t2
c * (7.5625f * postFix * t2 + 0.75f) + b
} else if (t2 < 2.5 / 2.75) {
t2 -= 2.25f / 2.75f
val postFix = t2
c * (7.5625f * postFix * t2 + 0.9375f) + b
} else {
t2 -= 2.625f / 2.75f
val postFix = t2
c * (7.5625f * postFix * t2 + 0.984375f) + b
}
}
inline fun bounceIn(t: Float, b: Float, c: Float, d: Float): Float {
return c - bounceOut(d - t, 0.0f, c, d) + b
}
inline fun bounceInOut(t: Float, b: Float, c: Float, d: Float): Float // Ease: Bounce In Out
{
return if (t < d / 2.0f) bounceIn(t * 2.0f, 0.0f, c, d) * 0.5f + b else bounceOut(
t * 2.0f - d,
0.0f,
c,
d
) * 0.5f + c * 0.5f + b
}
// Elastic Easing Function
inline fun elasticIn(t: Float, b: Float, c: Float, d: Float): Float // Ease: Elastic In
{
var t2 = t
if (t2 == 0.0f) return b
if (d.let { t2 /= it; t2 } == 1.0f) return b + c
val p = d * 0.3f
val s = p / 4.0f
val postFix: Float = c * pow(2.0, 10.0 * 1.0f.let { t2 -= it; t2 }).toFloat()
return -(postFix * sin((t2 * d - s) * (2.0f * PI) / p)).toFloat() + b
}
inline fun elasticOut(t: Float, b: Float, c: Float, d: Float): Float // Ease: Elastic Out
{
var t2 = t
if (t2 == 0.0f) return b
if (d.let { t2 /= it; t2 } == 1.0f) return b + c
val p = d * 0.3f
val s = p / 4.0f
return c * pow(2.0, -10.0 * t2).toFloat() * sin((t2 * d - s) * (2.0f * PI).toFloat() / p) + c + b
}
fun elasticInOut(t: Float, b: Float, c: Float, d: Float): Float {
var t2 = t
if (t2 == 0.0f) return b
if (d / 2.0f.let { t2 /= it; t2 } == 2.0f) return b + c
val p = d * (0.3f * 1.5f)
val s = p / 4.0f
if (t2 < 1.0f) {
val postFix: Float = c * pow(2.0, 10.0 * 1.0f.let { t2 -= it; t2 }).toFloat()
return -0.5f * (postFix * sin((t2 * d - s) * (2.0f * PI) / p)).toFloat() + b
}
val postFix: Float = c * pow(2.0, -10.0 * 1.0f.let { t2 -= it; t2 }).toFloat()
return postFix * sin((t2 * d - s) * (2.0f * PI) / p).toFloat() * 0.5f + c + b
}

View file

@ -0,0 +1,41 @@
package kaylibkit.kEnums
/**
* Color blending modes (pre-defined)
*/
enum class BlendMode(val value: Int) {
/**
* Blend textures considering alpha (default)
*/
ALPHA(0),
/**
* Blend textures adding colors
*/
ADDITIVE(1),
/**
* Blend textures multiplying colors
*/
MULTIPLIED(2),
/**
* Blend textures adding colors (alternative)
*/
ADDCOLORS(3),
/**
* Blend textures subtracting colors (alternative)
*/
SUBTRACT_COLORS(4),
/**
* Blend premultiplied textures considering alpha
*/
ALPHA_PRE_MULTIPLIED(5),
/**
* Blend textures using custom src/dst factors (use rlSetBlendMode())
*/
CUSTOM(6,)
}

View file

@ -0,0 +1,31 @@
package kaylibkit.kEnums
/**
* Camera system modes
*/
enum class CameraMode(val value: Int) {
/**
* Custom camera
*/
CUSTOM(0),
/**
* Free camera
*/
FREE(1),
/**
* Orbital camera
*/
ORBITAL(2),
/**
* First person camera
*/
FIRST_PERSON(3),
/**
* Third person camera
*/
THIRD_PERSON(4),
}

View file

@ -0,0 +1,16 @@
package kaylibkit.kEnums
/**
* Camera projection
*/
enum class CameraProjection(val value: Int) {
/**
* Perspective Projection
*/
PERSPECTIVE(0),
/**
* Orthographic projection
*/
ORTHOGRAPHIC(1),
}

View file

@ -0,0 +1,83 @@
package kaylibkit.kEnums
/**
* System/Window config flags
* NOTE: Every bit registers one state (use it with bit masks)
* By default all flags are set to 0
*/
enum class ConfigFlag(val value: UInt) {
/**
* Set to try enabling V-Sync on GPU
*/
VSYNC_HINT(0x00000040u),
/**
* Set to run program in fullscreen
*/
FULLSCREEN_MODE(0x00000002u),
/**
* Set to allow resizable window
*/
WINDOW_RESIZABLE(0x00000004u),
/**
* Set to disable window decoration (frame and buttons)
*/
WINDOW_UNDECORATED(0x00000008u),
/**
* Set to hide window
*/
WINDOW_HIDDEN(0x00000080u),
/**
* Set to minimize window (iconify)
*/
WINDOW_MINIMIZED(0x00000200u),
/**
* Set to maximize window (expanded to monitor)
*/
WINDOW_MAXIMIZED(0x00000400u),
/**
* Set to window non focused
*/
WINDOW_UNFOCUSED(0x00000800u),
/**
* Set to window always on top
*/
WINDOW_TOPMOST(0x00001000u),
/**
* Set to allow windows running while minimized
*/
WINDOW_ALWAYS_RUN(0x00000100u),
/**
* Set to allow transparent framebuffer
*/
WINDOW_TRANSPARENT(0x00000010u),
/**
* Set to support HighDPI
*/
WINDOW_HIGHDPI(0x00002000u),
/**
* Set to support mouse passthrough, only supported when FLAG_WINDOW_UNDECORATED
*/
WINDOW_MOUSE_PASSTHROUGH(0x00004000u),
/**
* Set to try enabling MSAA 4X
*/
MSAA_4X_HINT(0x00000020u),
/**
* Set to try enabling interlaced video format (for V3D)
*/
INTERLACED_HINT(0x00010000u)
}

View file

@ -0,0 +1,36 @@
package kaylibkit.kEnums
/**
* Cubemap layouts
*/
enum class CubemapLayout(val value: Int) {
/**
* Automatically detect layout type
*/
AUTO_DETECT(0),
/**
* Layout is defined by a vertical line with faces
*/
LINE_VERTICAL(1),
/**
* Layout is defined by a horizontal line with faces
*/
LINE_HORIZONTAL(2),
/**
* Layout is defined by a 3x4 cross with cubemap face
*/
CROSS_THREE_BY_FOUR(3),
/**
* Layout is defined by a 4x3 cross with cubemap face
*/
CROSS_FOUR_BY_THREE(4),
/**
* Layout is defined by a panorama image (equirectangular map)
*/
PANORAMA(5),
}

View file

@ -0,0 +1,21 @@
package kaylibkit.kEnums
/**
* Font type defines generation method
*/
enum class FontType(val value: Int) {
/**
* Default font generation, anti-aliased
*/
DEFAULT(0),
/**
* Bitmap font generation, no anti-aliasing
*/
BITMAP(1),
/**
* SDF Font generation, requires external shader
*/
SDF(2),
}

View file

@ -0,0 +1,33 @@
package kaylibkit.kEnums
enum class GamepadAxis(val value: Int) {
/**
* Gamepad left stick X axis
*/
LEFT_X(0),
/**
* Gamepad left stick Y axis
*/
LEFT_Y(1),
/**
* Gamepad right stick X axis
*/
RIGHT_X(2),
/**
* Gamepad right stick Y axis
*/
RIGHT_Y(3),
/**
* Gamepad back trigger left, pressure level: [1..-1]
*/
LEFT_TRIGGER(4),
/**
* Gamepad back trigger right, pressure level: [1..-1]
*/
RIGHT_TRIGGER(5),
}

View file

@ -0,0 +1,93 @@
package kaylibkit.kEnums
enum class GamepadButton(val value: Int) {
/**
* Unknown button, just for error checking
*/
UNKNOWN(0),
/**
* Gamepad left DPAD up button
*/
LEFT_FACE_UP(1),
/**
* Gamepad left DPAD right button
*/
LEFT_FACE_RIGHT(2),
/**
* Gamepad left DPAD down button
*/
LEFT_FACE_DOWN(3),
/**
* Gamepad left DPAD left button
*/
LEFT_FACE_LEFT(4),
/**
* Gamepad right button up (i.e. PS3: Triangle, Xbox: Y)
*/
RIGHT_FACE_UP(5),
/**
* Gamepad right button right (i.e. PS3: Square, Xbox: X)
*/
RIGHT_FACE_RIGHT(6),
/**
* Gamepad right button down (i.e. PS3: Cross, Xbox: A)
*/
RIGHT_FACE_DOWN(7),
/**
* Gamepad right button left (i.e. PS3: Circle, Xbox: B)
*/
RIGHT_FACE_LEFT(8),
/**
* Gamepad top/back trigger left (first), it could be a trailing button
*/
LEFT_TRIGGER1(9),
/**
* Gamepad top/back trigger left (second), it could be a trailing button
*/
LEFT_TRIGGER2(10),
/**
* Gamepad top/back trigger right (one), it could be a trailing button
*/
RIGHT_TRIGGER1(11),
/**
* Gamepad top/back trigger right (second), it could be a trailing button
*/
RIGHT_TRIGGER2(12),
/**
* Gamepad center buttons, left one (i.e. PS3: Select)
*/
MIDDLE_LEFT(13),
/**
* Gamepad center buttons, middle one (i.e. PS3: PS, Xbox: XBOX)
*/
MIDDLE(14),
/**
* Gamepad center buttons, right one (i.e. PS3: Start)
*/
MIDDLE_RIGHT(15),
/**
* Gamepad joystick pressed button left
*/
LEFT_THUMB(16),
/**
* Gamepad joystick pressed button right
*/
RIGHT_THUMB(17),
}

View file

@ -0,0 +1,19 @@
package kaylibkit.kEnums
/**
* Gestures
* It could be used as flags to enable only some gestures
*/
enum class Gesture(val value: Int) {
NONE(0),
TAP(1,),
DOUBLE_TAP(2),
HOLD(4),
DRAG(8),
SWIPE_RIGHT(16),
SWIPE_LEFT(32),
SWIPE_UP(64),
SWIPE_DOWN(128),
PINCH_IN(256),
PINCH_OUT(512),
}

View file

@ -0,0 +1,130 @@
package kaylibkit.kEnums
/**
* Keyboard keys (US keyboard layout)
*
* Use GetKeyPressed() to allow redefining
*
* required keys for alternative layouts
*/
enum class KeyboardKey(val key: Int) {
// Alphanumeric keys
APOSTROPHE(39),
COMMA(44),
MINUS(45),
PERIOD(46),
SLASH(47),
NUMBER0(48),
NUMBER1(49),
NUMBER2(50),
NUMBER3(51),
NUMBER4(52),
NUMBER5(53),
NUMBER6(54),
NUMBER7(55),
NUMBER8(56),
NUMBER(57),
SEMICOLON(59),
EQUAL(61),
A(65),
B(66),
C(67),
D(68),
E(69),
F(70),
G(71),
H(72),
I(73),
J(74),
K(75),
L(76),
M(77),
N(78),
O(79),
P(80),
Q(81),
R(82),
S(83),
T(84),
U(85),
V(86),
W(87),
X(88),
Y(89),
Z(90),
// Function keys
SPACE(32),
ESCAPE(256),
ENTER(257),
TAB(258),
BACKSPACE(259),
INSERT(260),
DELETE(261),
RIGHT(262),
LEFT(263),
DOWN(264),
UP(265),
PAGEUP(266),
PAGEDOWN(267),
HOME(268),
END(269),
CAPSLOCK(280),
SCROLLLOCK(281),
NUMLOCK(282),
PRINTSCREEM(283),
PAUSE(284),
FUNCTION1(290),
FUNCTION2(291),
FUNCTION3(292),
FUNCTION4(293),
FUNCTION5(294),
FUNCTION6(295),
FUNCTION7(296),
FUNCTION8(297),
FUNCTION9(298),
FUNCTION10(299),
FUNCTION11(300),
FUNCTION12(301),
LEFTSHIFT(340),
LEFTCONTROL(341),
LEFTALT(342),
LEFTSUPER(343),
RIGHTSHIFT(344),
RIGHTCONTROL(345),
RIGHTALT(346),
RIGHTSUPER(347),
KBMENU(348),
LEFTBRACKET(91),
BACKSLASH(92),
RIGHTBRACKET(93),
GRAVE(96),
// Keypad keys
KEYPAD0(320),
KEYPAD1(321),
KEYPAD2(322),
KEYPAD3(323),
KEYPAD4(324),
KEYPAD5(325),
KEYPAD6(326),
KEYPAD7(327),
KEYPAD8(328),
KEYPAD9(329),
KEYPADDECIMAL(330),
KEYPADDIVIDE(331),
KEYPADMULTIPLY(332),
KEYPADSUBTRACT(333),
KEYPADADD(334),
KEYPADENTER(335),
KEYPADEQUAL(336),
UNKNOWN(-1);
// Android key buttons
val ANDROIDBACK:Int = 4
val ANDROIDMENU:Int = 82
val ANDROIDVOLUMEUP:Int = 24
val ANDROIDVOLUMEDOWN:Int = 25
}

View file

@ -0,0 +1,63 @@
package kaylibkit.kEnums
enum class MaterialMapIndex(val value: Int) {
/**
* Albedo material (same as: MATERIAL_MAP_DIFFUSE)
*/
ALBEDO(0),
/**
* Metalness material (same as: MATERIAL_MAP_SPECULAR)
*/
METALNESS(1),
/**
* Normal material
*/
NORMAL(2),
/**
* Roughness material
*/
ROUGHNESS(3),
/**
* Ambient occlusion material
*/
OCCLUSION(4),
/**
* Emission material
*/
EMISSION(5),
/**
* Heightmap material
*/
HEIGHT(6),
/**
* Cubemap material (NOTE: Uses GL_TEXTURE_CUBE_MAP)
*/
CUBEMAP(7),
/**
* Irradiance material (NOTE: Uses GL_TEXTURE_CUBE_MAP)
*/
IRRADIANCE(8),
/**
* Prefilter material (NOTE: Uses GL_TEXTURE_CUBE_MAP)
*/
PREFILTER(9),
/**
* Brdf material
*/
BRDF(10),
DIFFUSE(0),
SPECULAR(1),
}

View file

@ -0,0 +1,41 @@
package kaylibkit.kEnums
/**
* Mouse Buttons
*/
enum class MouseButton(val value: Int) {
/**
* Mouse button left
*/
LEFT(0),
/**
* Mouse button right
*/
RIGHT(1),
/**
* Mouse button middle (pressed wheel)
*/
MIDDLE(2),
/**
* Mouse button side (advanced mouse device)
*/
SIDE(3),
/**
* Mouse button extra (advanced mouse device)
*/
EXTRA(4),
/**
* Mouse button fordward (advanced mouse device)
*/
FORWARD(5),
/**
* Mouse button back (advanced mouse device)
*/
BACK(6),
}

View file

@ -0,0 +1,61 @@
package kaylibkit.kEnums
/**
* Mouse Cursors
*/
enum class MouseCursor(val value: Int) {
/**
* Default pointer shape
*/
DEFAULT(0),
/**
* Arrow shape
*/
ARROW(1),
/**
* Text writing cursor shape
*/
IBEAM(2),
/**
* Cross shape
*/
CROSSHAIR(3),
/**
* Pointing hand cursor
*/
POINTINGHAND(4),
/**
* The horizontal resize/move arrow shape
*/
RESIZE_EW(5),
/**
* The vertical resize/move arrow shape
*/
RESIZE_NS(6),
/**
* The top-left to bottom-right diagonal resize/move arrow shape
*/
RESIZE_NWSE(7),
/**
* The top-right to bottom-left diagonal resize/move arrow shape
*/
RESIZENESW(8),
/**
* The omni-directional resize/move cursor shape
*/
RESIZE_ALL(9),
/**
* The operation-not-allowed shape
*/
NOT_ALLOWED(10),
}

View file

@ -0,0 +1,21 @@
package kaylibkit.kEnums
/**
* N-patch layout
*/
enum class NPatchLayout(val value: Int) {
/**
* Npatch layout: 3x3 tiles
*/
NINE_PATCH(0),
/**
* Npatch layout: 1x3 tiles
*/
THREE_PATCH_VERTICAL(1),
/**
* Npatch layout: 3x1 tiles
*/
THREE_PATCH_HORIZONTAL(2),
}

View file

@ -0,0 +1,107 @@
package kaylibkit.kEnums
/**
* Pixel formats
* Support depends on OpenGL version and platform
*/
enum class PixelFormat(val value: Int) {
/**
* 8 bit per pixel (no alpha)
*/
UNCOMPRESSED_GRAYSCALE(1),
/**
* 8*2 bpp (2 channels)
*/
UNCOMPRESSED_GRAYALPHA(2),
/**
* 16 bpp
*/
UNCOMPRESSED_R5G6B5(3),
/**
* 24 bpp
*/
UNCOMPRESSED_R8G8B8(4),
/**
* 16 bpp (1 bit alpha)
*/
UNCOMPRESSED_R5G5B5A1(5),
/**
* 16 bpp (4 bit alpha)
*/
UNCOMPRESSED_R4G4B4A4(6),
/**
*32 bpp
*/
UNCOMPRESSED_R8G8B8A8(7),
/**
* 32 bpp (1 channel - float)
*/
UNCOMPRESSED_R32(8),
/**
* 32*3 bpp (3 channels - float)
*/
UNCOMPRESSED_R32G32B32(9),
/**
* 32*4 bpp (4 channels - float)
*/
UNCOMPRESSED_R32G32B32A32(10),
/**
* 4 bpp (no alpha)
*/
COMPRESSED_DXT_1RGB(11),
/**
* 4 bpp (1 bit alpha)
*/
COMPRESSED_DXT1_RGBA(12),
/**
* 8 bpp
*/
COMPRESSED_DXT5_RGBA(13),
/**
* 4 bpp
*/
COMPRESSED_ETC1_RGB(14),
/**
* 4 bpp
*/
COMPRESSED_ETC2_RGB(15),
/**
* 8 bpp
*/
COMPRESSED_ETC2EAC_RGBA(16),
/**
* 4 bpp
*/
COMPRESSED_PVRT_RGB(17),
/**
* 4 bpp
*/
COMPRESSED_PVRT_RGBA(18),
/**
* 8 bpp
*/
COMPRESSED_ASTC4X4_RGBA(19),
/**
* 2 bpp
*/
COMPRESSED_ASTC8X8_RGBA(20),
}

View file

@ -0,0 +1,137 @@
package kaylibkit.kEnums
/**
* Shader location index
*/
enum class ShaderLocationIndex(val value: Int) {
/**
* Shader location: vertex attribute: position
*/
VERTEX_POSITION_0(0),
/**
* Shader location: vertex attribute: texcoord01
*/
VERTEX_TEXCOORD01_1(1),
/**
* Shader location: vertex attribute: texcoord02
*/
VERTEX_TEXCOORD02_2(2),
/**
* Shader location: vertex attribute: normal
*/
VERTEX_NORMAL(3),
/**
* Shader location: vertex attribute: tangent
*/
VERTEX_TANGENT(4),
/**
* Shader location: vertex attribute: tangent
*/
VERTEX_COLOR(5),
/**
* Shader location: matrix uniform: model-view-projection
*/
MATRIX_MVP(6),
/**
* Shader location: matrix uniform: view (camera transform)
*/
MATRIX_VIEW(7),
/**
* Shader location: matrix uniform: projection
*/
MATRIX_PROJECTION(8),
/**
* Shader location: matrix uniform: model (transform)
*/
MATRIX_MODEL(9),
/**
* Shader location: matrix uniform: normal
*/
MATRIX_NORMAL(10),
/**
* Shader location: vector uniform: view
*/
VECTOR_VIEW(11),
/**
* Shader location: vector uniform: diffuse color
*/
COLOR_DIFFUSE(12),
/**
* Shader location: vector uniform: specular color
*/
COLOR_SPECULAR(13),
/**
* Shader location: vector uniform: ambient color
*/
COLOR_AMBIENT(14),
/**
* Shader location: sampler2d texture: albedo (same as: SHADER_LOC_MAP_DIFFUSE)
*/
MAP_ALBEDO(15),
/**
* Shader location: sampler2d texture: metalness (same as: SHADER_LOC_MAP_SPECULAR)
*/
MAP_METALNESS(16),
/**
* Shader location: sampler2d texture: normal
*/
MAP_NORMAL(17),
/**
* Shader location: sampler2d texture: roughness
*/
MAP_ROUGHNESS(18),
/**
* Shader location: sampler2d texture: occlusion
*/
MAP_OCCLUSION(19),
/**
* Shader location: sampler2d texture: emission
*/
MAP_EMISSION(20),
/**
* Shader location: sampler2d texture: height
*/
MAP_HEIGHT(21),
/**
* Shader location: samplerCube texture: cubemap
*/
MAP_CUBEMAP(22),
/**
* Shader location: samplerCube texture: irradiance
*/
MAP_IRRADIANCE(23),
/**
* Shader location: samplerCube texture: prefilter
*/
MAP_PREFILTER(24),
/**
* Shader location: sampler2d texture: brdf
*/
MAP_BRDF(25),
}

View file

@ -0,0 +1,48 @@
package kaylibkit.kEnums
enum class ShaderUniformDataType(val value: Int) {
/**
* Shader uniform type: float
*/
FLOAT(0),
/**
* Shader uniform type: vec2 (2 float)
*/
VEC2(1),
/**
* Shader uniform type: vec3 (3 float)
*/
VEC3(2),
/**
* Shader uniform type: vec4 (4 float)
*/
VEC4(3),
/**
* Shader uniform type: int
*/
INT(4),
/**
* Shader uniform type: ivec2 (2 int)
*/
IVEC2(5),
/**
* Shader uniform type: ivec3 (3 int)
*/
IVEC3(6),
/**
* Shader uniform type: ivec4 (4 int)
*/
IVEC4(7),
/**
* Shader uniform type: sampler2d
*/
SAMPLER2D(8),
}

View file

@ -0,0 +1,33 @@
package kaylibkit.kEnums
enum class TextureFilter(val value: Int) {
/**
* No filter, just pixel aproximation
*/
POINT(0),
/**
* Linear filtering
*/
BILINEAR(1),
/**
* Trilinear filtering (linear with mipmaps)
*/
TRILINEAR(2),
/**
* Anisotropic filtering 4x
*/
ANISOTROPIC_4X(3),
/**
* Anisotropic filtering 8x
*/
ANISOTROPIC_8X(4),
/**
* Anisotropic filtering 16x
*/
ANISOTROPIC_16X(5),
}

View file

@ -0,0 +1,23 @@
package kaylibkit.kEnums
enum class TextureWrap(val value: Int) {
/**
* Repeats texture in tiled mode
*/
REPEAT(0),
/**
* Clamps texture to edge pixel in tiled mode
*/
CLAMP(1),
/**
* Mirrors and repeats the texture in tiled mode
*/
MIRROR_REPEAT(2),
/**
* Mirrors and clamps to border the texture in tiled mode
*/
MIRROR_CLAMP(3),
}

View file

@ -0,0 +1,43 @@
package kaylibkit.kEnums
enum class TraceLogLevel(val level: Int) {
/**
* Display all logs
*/
ALL(0),
/**
* Trace logging, intended for internal use only
*/
TRACE(1),
/**
* Debug logging, used for internal debugging, it should be disabled on release builds
*/
DEBUG(2),
/**
* Info logging, used for program execution info
*/
INFO(3),
/**
* Warning logging, used on recoverable failures
*/
WARNING(4),
/**
* Error logging, used on unrecoverable failures
*/
ERROR(5),
/**
* Fatal logging, used to abort program: exit(EXIT_FAILURE)
*/
FATAL(6),
/**
* Disable logging
*/
NONE(7)
}

View file

@ -0,0 +1,77 @@
package kaylibkit.kGestures
import kaylibc.*
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.MemScope
import kotlinx.cinterop.pointed
// -- Module: kGestures
//=======================================================//
// GESTURES AND TOUCH HANDLING FUNCTIONS
//=======================================================//
/**
* Enable a set of gestures using [flags]
*/
inline fun setGesturesEnabled(flags: Gesture) {
SetGesturesEnabled(flags)
}
/**
* Check if a gesture have been detected
* @return [Boolean]
*/
inline fun isGestureDetected(gesture: kaylibkit.kEnums.Gesture) : Boolean {
return IsGestureDetected(gesture.value)
}
/**
* Get latest detected gesture
* @return [Int]
*/
inline fun getGestureDetected() : Int {
return GetGestureDetected()
}
/**
* Get gesture hold time in milliseconds
* @return [Float]
*/
inline fun getGestureHoldDuration() : Float {
return GetGestureHoldDuration()
}
/**
* Get gesture drag [Vector2]
* @return [Vector2]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getGestureDragVector() : Vector2 {
return GetGestureDragVector().getPointer(MemScope()).pointed
}
/**
* Get gesture drag angle
* @return [Float]
*/
inline fun getGestureDragAngle() : Float {
return GetGestureDragAngle()
}
/**
* Get gesture pinch delta
* @return [Vector2]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getGesturePinchVector() : Vector2 {
return GetGesturePinchVector().getPointer(MemScope()).pointed
}
/**
* Get gesture pinch angle
* @return [Float]
*/
inline fun getGesturePinchAngle() : Float {
return GetGesturePinchAngle()
}

View file

@ -0,0 +1,615 @@
package kaylibkit.kImage
import kaylibc.*
import kotlinx.cinterop.*
// -- Module: kImage
//=======================================================//
// IMAGE CONSTRUCTOR
//=======================================================//
/**
* Constructor function for [Image].
* Important to note that this uses MemScope() by default.
* @return [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kImage(data: COpaquePointer?, width: Int, height: Int, mipmaps: Int, format: Int, allocator: AutofreeScope = MemScope()) : Image {
return allocator.alloc<Image> {
this.data = data
this.width = width
this.height = height
this.mipmaps = mipmaps
this.format = format
}
}
//=======================================================//
// IMAGE LOADING FUNCTIONS
//=======================================================//
/**
* Load [Image] from file into CPU memory (RAM)
* @return [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadImage(fileName: String) : Image {
return LoadImage(fileName).getPointer(MemScope()).pointed
}
/**
* Load [Image] from RAW file data
* @return [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadImageRaw(fileName: String, width: Int, height: Int, format: PixelFormat, headerSize: Int) : Image {
return LoadImageRaw(fileName, width, height, format.toInt(), headerSize).getPointer(MemScope()).pointed
}
/**
* Load [Image] sequence from file (frames appended to image.data)
* @return [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadImageAnim(fileName: String, frames: IntVar) : Image {
return LoadImageAnim(fileName, frames.ptr).getPointer(MemScope()).pointed
}
/**
* Load [Image] from memory buffer, fileType refers to extension: i.e. `.png`
* @return [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadImageFromMemory(fileType: String, fileData: UByteVar, dataSize: Int) : Image {
return LoadImageFromMemory(fileType, fileData.ptr, dataSize).getPointer(MemScope()).pointed
}
/**
* Load [Image] from GPU texture data
* @return [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadImageFromTexture(texture: Texture2D) : Image {
return LoadImageFromTexture(texture.readValue()).getPointer(MemScope()).pointed
}
/**
* Load image from screen buffer and (screenshot)
* @return [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadImageFromScreen() : Image {
return LoadImageFromScreen().getPointer(MemScope()).pointed
}
/**
* Unload [Image] from CPU memory (RAM)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun unloadImage(image: Image) {
UnloadImage(image.readValue())
}
/**
* Export [Image] data to file, returns true on success
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun exportImage(image: Image, fileName: String) : Boolean {
return ExportImage(image.readValue(), fileName)
}
/**
* Export [Image] as code file defining an array of bytes, returns true on success
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun exportImageAsCode(image: Image, fileName: String) : Boolean {
return ExportImageAsCode(image.readValue(), fileName)
}
//=======================================================//
// IMAGE GENERATION FUNCTIONS
//=======================================================//
/**
* Generate [Image]: plain [color]
* @return [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun genImageColor(width: Int, height: Int, color: Color) : Image {
return GenImageColor(width, height, color.readValue()).getPointer(MemScope()).pointed
}
/**
* Generate [Image]: vertical gradient
* @return [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun genImageGradientV(width: Int, height: Int, top: Color, bottom: Color) : Image {
return GenImageGradientV(width, height, top.readValue(), bottom.readValue()).getPointer(MemScope()).pointed
}
/**
* Generate [Image]: horizontal gradient
* @return [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun genImageGradientH(width: Int, height: Int, left: Color, right: Color) : Image {
return GenImageGradientH(width, height, left.readValue(), right.readValue()).getPointer(MemScope()).pointed
}
/**
* Generate [Image]: radial gradient
* @return [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun genImageGradientRadial(width: Int, height: Int, density: Float, inner: Color, outer: Color) : Image {
return GenImageGradientRadial(width, height, density, inner.readValue(), outer.readValue()).getPointer(MemScope()).pointed
}
/**
* Generate [Image]: checked
* @return [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun genImageChecked(width: Int, height: Int, checksX: Int, checksY: Int, col1: Color, col2: Color) : Image {
return GenImageChecked(width, height, checksX, checksY, col1.readValue(), col2.readValue()).getPointer(MemScope()).pointed
}
/**
* Generate [Image]: white noise
* @return [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun genImageWhiteNoise(width: Int, height: Int, factor: Float) : Image {
return GenImageWhiteNoise(width, height, factor).getPointer(MemScope()).pointed
}
/**
* Generate [Image]: cellular algorithm, bigger tileSize means bigger cells
* @return [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun genImageCellular(width: Int, height: Int, tileSize: Int) : Image {
return GenImageCellular(width, height, tileSize).getPointer(MemScope()).pointed
}
//=======================================================//
// IMAGE MANIPULATION FUNCTIONS
//=======================================================//
/**
* Create an [Image] duplicate (useful for transformations)
* @return [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageCopy(image: Image) : Image {
return ImageCopy(image.readValue()).getPointer(MemScope()).pointed
}
/**
* Create an [Image] from another [Image] piece
* @return [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageFromImage(image: Image, rec: Rectangle) : Image {
return ImageFromImage(image.readValue(), rec.readValue()).getPointer(MemScope()).pointed
}
/**
* Create an [Image] from text (default font)
* @return [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageText(text: String, fontSize: Int, color: Color) : Image {
return ImageText(text, fontSize, color.readValue()).getPointer(MemScope()).pointed
}
/**
* Create an [Image] from text (custom sprite font)
* @return [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageTextEx(font: Font, text: String, fontSize: Float, spacing: Float, tint: Color) : Image {
return ImageTextEx(font.readValue(), text, fontSize, spacing, tint.readValue()).getPointer(MemScope()).pointed
}
/**
* Convert [Image] data to desired format
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageFormat(image: Image, newFormat: kaylibkit.kEnums.PixelFormat) {
ImageFormat(image.ptr, newFormat.value)
}
/**
* Convert [Image] to POT (power-of-two)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageToPOT(image: Image, fill: Color) {
ImageToPOT(image.ptr, fill.readValue())
}
/**
* Crop an [Image] to a defined [Rectangle]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageCrop(image: Image, crop: Rectangle) {
ImageCrop(image.ptr, crop.readValue())
}
/**
* Crop [Image] depending on alpha value
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageAlphaCrop(image: Image, threshold: Float) {
ImageAlphaCrop(image.ptr, threshold)
}
/**
* Clear alpha channel to desired [color]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageAlphaClear(image: Image, color: Color, threshold: Float) {
ImageAlphaClear(image.ptr, color.readValue(), threshold)
}
/**
* Apply alpha mask to [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageAlphaMask(image: Image, alphaMask: Image) {
ImageAlphaMask(image.ptr, alphaMask.readValue())
}
/**
* Premultiply alpha channel
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageAlphaPremultiply(image: Image) {
ImageAlphaPremultiply(image.ptr)
}
/**
* Apply Gaussian blur using a box blur approximation
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageBlurGaussian(image: Image, blurSize: Int) {
ImageBlurGaussian(image.ptr, blurSize)
}
/**
* Resize [Image] (Bicubic scaling algorithm)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageResize(image: Image, newWidth: Int, newHeight: Int) {
ImageResize(image.ptr, newWidth, newHeight)
}
/**
* Resize [Image] (Nearest-Neighbor scaling algorithm)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageResizeNN(image: Image, newWidth: Int, newHeight: Int) {
ImageResizeNN(image.ptr, newWidth, newHeight)
}
/**
* Resize canvas and fill with [color]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageResizeCanvas(image: Image, newWidth: Int, newHeight: Int, offsetX: Int, offsetY: Int, fill: Color) {
ImageResizeCanvas(image.ptr, newWidth, newHeight, offsetX, offsetY, fill.readValue())
}
/**
* Compute all mipmap levels for a provided [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageMipmaps(image: Image) {
ImageMipmaps(image.ptr)
}
/**
* Dither [Image] data to 16bpp or lower (Floyd-Steinberg dithering)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageDither(image: Image, rBpp: Int, gBpp: Int, bBpp: Int, aBpp: Int) {
ImageDither(image.ptr, rBpp, gBpp, bBpp, aBpp)
}
/**
* Flip [Image] vertically
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageFlipVertical(image: Image) {
ImageFlipVertical(image.ptr)
}
/**
* Flip [Image] horizontally
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageFlipHorizontal(image: Image) {
ImageFlipHorizontal(image.ptr)
}
/**
* Rotate [Image] clockwise 90deg
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageRotateCW(image: Image) {
ImageRotateCW(image.ptr)
}
/**
* Rotate [Image] counter-clockwise 90deg
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageRotateCCW(image: Image) {
ImageRotateCCW(image.ptr)
}
/**
* Modify [Image] [color]: tint
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageColorTint(image: Image, color: Color) {
ImageColorTint(image.ptr, color.readValue())
}
/**
* Modify [Image] [color]: invert
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageColorInvert(image: Image) {
ImageColorInvert(image.ptr)
}
/**
* Modify [Image] [color]: grayscale
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageColorGrayscale(image: Image) {
ImageColorGrayscale(image.ptr)
}
/**
* Modify [Image] [color]: contrast (-100 to 100)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageColorContrast(image: Image, contrast: Float) {
ImageColorContrast(image.ptr, contrast)
}
/**
* Modify [Image] [color]: brightness (-255 to 255)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageColorBrightness(image: Image, brightness: Int) {
ImageColorBrightness(image.ptr, brightness)
}
/**
* Modify [Image] [color]: replace [color]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageColorReplace(image: Image, color: Color, replace: Color) {
ImageColorReplace(image.ptr, color.readValue(), replace.readValue())
}
/**
* Load color data from [Image] as a [Color] [Array] (RGBA - 32bit) Memory allocated should be freed
* @return [Array] of [Color]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadImageColors(image: Image): Array<Color> {
val colorPointer = LoadImageColors(image.readValue())
val colorList = mutableListOf<Color>()
colorPointer?.pointed?.let { colorList.add(it) }
return colorList.toTypedArray()
}
/**
* Load colors palette from [Image] as a [Color] [Array] (RGBA - 32bit)
* @return [Array] of [Color]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadImagePalette(image: Image, maxPaletteSize: Int, colorCount: IntVar) : Array<Color> {
val result = LoadImagePalette(image.readValue(), maxPaletteSize, colorCount.ptr)
val colorList = mutableListOf<Color>()
result?.pointed?.let { colorList.add(it) }
return colorList.toTypedArray()
}
/**
* Unload [Color] data loaded with LoadImageColors() Read Deprecated note
*/
@OptIn(ExperimentalForeignApi::class)
inline fun unloadImageColors(colors: Color) {
UnloadImageColors(colors.ptr)
}
/**
* Unload [colors] palette loaded with LoadImagePalette() Read Deprecated note
*/
@OptIn(ExperimentalForeignApi::class)
inline fun unloadImagePalette(colors: Color) {
UnloadImagePalette(colors.ptr)
}
/**
* Get [Image] alpha border [Rectangle]
* @return [Rectangle]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getImageAlphaBorder(image: Image, threshold: Float) : Rectangle {
return GetImageAlphaBorder(image.readValue(), threshold).getPointer(MemScope()).pointed
}
/**
* Get [Image] pixel [Color] at (x, y) position
* @return [Color]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getImageColor(image: Image, x: Int, y: Int) : Color {
return GetImageColor(image.readValue(), x, y).getPointer(MemScope()).pointed
}
//=======================================================//
// IMAGE DRAWING FUNCTIONS
//=======================================================//
/**
* Clear [Image] background with given [color]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageClearBackground(dst: Image, color: Color) {
ImageClearBackground(dst.ptr, color.readValue())
}
/**
* Draw pixel within an [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageDrawPixel(dst: Image, posX: Int, posY: Int, color: Color) {
ImageDrawPixel(dst.ptr, posX, posY, color.readValue())
}
/**
* Draw pixel within an [Image] ([Vector2] version)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageDrawPixelV(dst: Image, position: Vector2, color: Color) {
ImageDrawPixelV(dst.ptr, position.readValue(), color.readValue())
}
/**
* Draw line within an [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageDrawLine(dst: Image, startPosX: Int, startPosY: Int, endPosX: Int, endPosY: Int, color: Color) {
ImageDrawLine(dst.ptr, startPosX, startPosY, endPosX, endPosY, color.readValue())
}
/**
* Draw line within an [Image] ([Vector2] version)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageDrawLineV(dst: Image, start: Vector2, end: Vector2, color: Color) {
ImageDrawLineV(dst.ptr, start.readValue(), end.readValue(), color.readValue())
}
/**
* Draw circle within an [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageDrawCircle(dst: Image, centerX: Int, centerY: Int, radius: Int, color: Color) {
ImageDrawCircle(dst.ptr, centerX, centerY, radius, color.readValue())
}
/**
* Draw circle within an [Image] ([Vector2] version)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageDrawCircleV(dst: Image, center: Vector2, radius: Int, color: Color) {
ImageDrawCircleV(dst.ptr, center.readValue(), radius, color.readValue())
}
/**
* Draw [Rectangle] within an [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageDrawRectangle(dst: Image, posX: Int, posY: Int, width: Int, height: Int, color: Color) {
ImageDrawRectangle(dst.ptr, posX, posY, width, height, color.readValue())
}
/**
* Draw [Rectangle] within an [Image] ([Vector2] version)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageDrawRectangleV(dst: Image, position: Vector2, size: Vector2, color: Color) {
ImageDrawRectangleV(dst.ptr, position.readValue(), size.readValue(), color.readValue())
}
/**
* Draw [Rectangle] within an [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageDrawRectangleRec(dst: Image, rec: Rectangle, color: Color) {
ImageDrawRectangleRec(dst.ptr, rec.readValue(), color.readValue())
}
/**
* Draw [Rectangle] lines within an [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageDrawRectangleLines(dst: Image, rec: Rectangle, thick: Int, color: Color) {
ImageDrawRectangleLines(dst.ptr, rec.readValue(), thick, color.readValue())
}
/**
* Draw a source [Image] within a destination [Image] (tint applied to source)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageDraw(dst: Image, src: Image, srcRec: Rectangle, dstRec: Rectangle, tint: Color) {
ImageDraw(dst.ptr, src.readValue(), srcRec.readValue(), dstRec.readValue(), tint.readValue())
}
/**
* Draw text (using default font) within an [Image] (destination)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageDrawText(dst: Image, text: String, posX: Int, posY: Int, fontSize: Int, color: Color) {
return ImageDrawText(dst.ptr, text, posX, posY, fontSize, color.readValue())
}
/**
* Draw text (custom sprite font) within an [Image] (destination)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageDrawText(dst: Image, font: Font, text: String, position: Vector2, fontSize: Float, spacing: Float, tint: Color) {
return ImageDrawTextEx(dst.ptr, font.readValue(), text, position.readValue(), fontSize, spacing, tint.readValue())
}
/**
* Draw circle outline within an [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageDrawCircleLines(image: Image, centerX: Int, centerY: Int, radius: Int, color: Color) {
ImageDrawCircleLines(image.ptr, centerX, centerY, radius, color.readValue())
}
/**
* Draw circle outline within an [Image] using [Vector2]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun imageDrawCircleLines(image: Image, center: Vector2, radius: Int, color: Color) {
ImageDrawCircleLines(image.ptr, center.x.toInt(), center.y.toInt(), radius, color.readValue())
}
/**
* Check if an [Image] is ready
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun isImageReady(image: Image) : Boolean {
return IsImageReady(image.readValue())
}
/**
* Set value of an [Image] with another provided value of same type.
* This is useful when dealing with cinterop CStruct that holds nested CStructs which are marked as immutable (val).
* NOTE: While the CStruct is immutable itself, the inner members of that CStruct are mutable.
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Image.set(other: Image) {
this.format = other.format
this.height = other.height
this.data = other.data
this.mipmaps = other.mipmaps
this.width = other.width
}

View file

@ -0,0 +1,267 @@
package kaylibkit.kMath
import kaylibc.*
import kotlinx.cinterop.ExperimentalForeignApi
import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.sqrt
// -- Module: kMath
//=======================================================//
// MATH FUNCTIONS
//=======================================================//
/**
* Clamp float value
* @return [Float]
*/
inline fun clamp(value: Float, min: Float, max: Float) : Float = Clamp(value, min, max)
/**
* Calculate linear interpolation between two floats
* @return [Float]
*/
inline fun lerp(value: Float, start: Float, end: Float) : Float = Lerp(value, start, end)
/**
* Normalize input value within input range
* @return [Float]
*/
inline fun normalize(value: Float, start: Float, end: Float) : Float = Normalize(value, start, end)
/**
* Remap input value within input range to output range
* @return [Float]
*/
inline fun remap(value: Float, inputStart: Float, inputEnd: Float, outputStart: Float, outputEnd: Float): Float {
return (value - inputStart) / (inputEnd - inputStart) * (outputEnd - outputStart) + outputStart
}
/**
* Calculate [Quaternion] based on the rotation from one vector to another
* @return [Quaternion]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun quaternionFromVector3toVector3(from: Vector3, to: Vector3): Quaternion {
// val result: Quaternion = kQuaternion()
//
// val cos2Theta = from.dot(to)
//
// val cross: Vector3 = from.crossProduct(to)
//
//
// result.x = cross.x
// result.y = cross.y
// result.z = cross.z
// result.w = 1.0f + cos2Theta
//
// var length: Quaternion = result.normalize()
//
// return result
val result: Quaternion = kQuaternion()
val cos2Theta = from.x * to.x + from.y * to.y + from.z * to.z // Vector3DotProduct(from, to)
val cross: Vector3 = kVector3(
from.y * to.z - from.z * to.y,
from.z * to.x - from.x * to.z,
from.x * to.y - from.y * to.x
) // Vector3CrossProduct(from, to)
result.x = cross.x
result.y = cross.y
result.z = cross.z
result.w = 1.0f + cos2Theta
// QuaternionNormalize(q);
// NOTE: Normalize to essentially nlerp the original and identity to 0.5
var length: Float = sqrt(result.x * result.x + result.y * result.y + result.z * result.z + result.w * result.w)
if (length == 0.0f) length = 1.0f
val ilength = 1.0f / length
result.x = result.x * ilength
result.y = result.y * ilength
result.z = result.z * ilength
result.w = result.w * ilength
return result
}
/**
* Get a [Quaternion] for a given rotation matrix
* @return [Quaternion]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun quaternionFromMatrix(mat: Matrix): Quaternion {
val result: Quaternion = kQuaternion()
val fourWSquaredMinus1 = mat.m0 + mat.m5 + mat.m10
val fourXSquaredMinus1 = mat.m0 - mat.m5 - mat.m10
val fourYSquaredMinus1 = mat.m5 - mat.m0 - mat.m10
val fourZSquaredMinus1 = mat.m10 - mat.m0 - mat.m5
var biggestIndex = 0
var fourBiggestSquaredMinus1 = fourWSquaredMinus1
if (fourXSquaredMinus1 > fourBiggestSquaredMinus1) {
fourBiggestSquaredMinus1 = fourXSquaredMinus1
biggestIndex = 1
}
if (fourYSquaredMinus1 > fourBiggestSquaredMinus1) {
fourBiggestSquaredMinus1 = fourYSquaredMinus1
biggestIndex = 2
}
if (fourZSquaredMinus1 > fourBiggestSquaredMinus1) {
fourBiggestSquaredMinus1 = fourZSquaredMinus1
biggestIndex = 3
}
val biggestVal: Float = sqrt(fourBiggestSquaredMinus1 + 1.0f) * 0.5f
val mult = 0.25f / biggestVal
when (biggestIndex) {
0 -> {
result.w = biggestVal
result.x = (mat.m6 - mat.m9) * mult
result.y = (mat.m8 - mat.m2) * mult
result.z = (mat.m1 - mat.m4) * mult
}
1 -> {
result.x = biggestVal
result.w = (mat.m6 - mat.m9) * mult
result.y = (mat.m1 + mat.m4) * mult
result.z = (mat.m8 + mat.m2) * mult
}
2 -> {
result.y = biggestVal
result.w = (mat.m8 - mat.m2) * mult
result.x = (mat.m1 + mat.m4) * mult
result.z = (mat.m6 + mat.m9) * mult
}
3 -> {
result.z = biggestVal
result.w = (mat.m1 - mat.m4) * mult
result.x = (mat.m8 + mat.m2) * mult
result.y = (mat.m6 + mat.m9) * mult
}
}
return result
}
/**
* Get a [Matrix] for a given [Quaternion]
* @return [Matrix]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun quaternionToMatrix(q: Quaternion): Matrix {
val result: Matrix = kMatrix(
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
) // MatrixIdentity()
val a2: Float = q.x * q.x
val b2: Float = q.y * q.y
val c2: Float = q.z * q.z
val ac: Float = q.x * q.z
val ab: Float = q.x * q.y
val bc: Float = q.y * q.z
val ad: Float = q.w * q.x
val bd: Float = q.w * q.y
val cd: Float = q.w * q.z
result.m0 = 1 - 2 * (b2 + c2)
result.m1 = 2 * (ab + cd)
result.m2 = 2 * (ac - bd)
result.m4 = 2 * (ab - cd)
result.m5 = 1 - 2 * (a2 + c2)
result.m6 = 2 * (bc + ad)
result.m8 = 2 * (ac + bd)
result.m9 = 2 * (bc - ad)
result.m10 = 1 - 2 * (a2 + b2)
return result
}
/**
* Get rotation [Quaternion] for an [angle] and [axis]
*
* NOTE: [angle] must be provided in radians
* @return [Quaternion]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun quaternionFromAxisAngle(axis: Vector3, angle: Float): Quaternion {
var ang = angle
val result: Quaternion = kQuaternion(0.0f, 0.0f, 0.0f, 1.0f)
val axisLength: Float = sqrt(axis.x * axis.x + axis.y * axis.y + axis.z * axis.z)
if (axisLength != 0.0f) {
ang *= 0.5f
var length: Float
var ilength: Float
// Vector3Normalize(axis)
length = sqrt(axis.x * axis.x + axis.y * axis.y + axis.z * axis.z)
if (length == 0.0f) length = 1.0f
ilength = 1.0f / length
axis.x *= ilength
axis.y *= ilength
axis.z *= ilength
val sinres: Float = sin(angle)
val cosres: Float = cos(angle)
result.x = axis.x * sinres
result.y = axis.y * sinres
result.z = axis.z * sinres
result.w = cosres
// QuaternionNormalize(q);
length = sqrt(result.x * result.x + result.y * result.y + result.z * result.z + result.w * result.w)
if (length == 0.0f) length = 1.0f
ilength = 1.0f / length
result.x = result.x * ilength
result.y = result.y * ilength
result.z = result.z * ilength
result.w = result.w * ilength
}
return result
}
/**
* Get the [Quaternion] equivalent to Euler angles
*
* NOTE: Rotation order is ZYX
* @return [Quaternion]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun quaternionFromEuler(pitch: Float, yaw: Float, roll: Float): Quaternion {
val result: Quaternion = kQuaternion()
val x0: Float = cos(pitch * 0.5f)
val x1: Float = sin(pitch * 0.5f)
val y0: Float = cos(yaw * 0.5f)
val y1: Float = sin(yaw * 0.5f)
val z0: Float = cos(roll * 0.5f)
val z1: Float = sin(roll * 0.5f)
result.x = x1 * y0 * z0 - x0 * y1 * z1
result.y = x0 * y1 * z0 + x1 * y0 * z1
result.z = x0 * y0 * z1 - x1 * y1 * z0
result.w = x0 * y0 * z0 + x1 * y1 * z1
return result
}

View file

@ -0,0 +1,711 @@
package kaylibkit.kMath
import kaylibc.*
import kotlinx.cinterop.*
import platform.posix.tan
import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.sqrt
// -- Module: kMath
//=======================================================//
// Matrix DATA TYPE
//=======================================================//
/**
* Constructor function for [Matrix]
* @param [allocator] Uses `MemScope()` by default.
* @return [Matrix]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kMatrix(
m0: Float = 0F, m4: Float = 0F, m8: Float = 0F, m12: Float = 0F, m1: Float = 0F, m5: Float = 0F, m9: Float = 0F, m13: Float = 0F, m2: Float = 0F, m6: Float = 0F, m10: Float = 0F, m14: Float = 0F, m3: Float = 0F, m7: Float = 0F, m11: Float = 0F, m15: Float = 0F, allocator: AutofreeScope = MemScope()): Matrix {
return allocator.alloc<Matrix> {
this.m0 = m0
this.m4 = m4
this.m8 = m8
this.m12 = m12
this.m1 = m1
this.m5 = m5
this.m9 = m9
this.m13 = m13
this.m2 = m2
this.m6 = m6
this.m10 = m10
this.m14 = m14
this.m3 = m3
this.m7 = m7
this.m11 = m11
this.m15 = m15
}
}
/**
* Compute [Matrix] determinant
* @return [Float]
*/
inline fun Matrix.determinant(): Float {
var result: Float
// Cache the matrix values (speed optimization)
val a00: Float = this.m0
val a01: Float = this.m1
val a02: Float = this.m2
val a03: Float = this.m3
val a10: Float = this.m4
val a11: Float = this.m5
val a12: Float = this.m6
val a13: Float = this.m7
val a20: Float = this.m8
val a21: Float = this.m9
val a22: Float = this.m10
val a23: Float = this.m11
val a30: Float = this.m12
val a31: Float = this.m13
val a32: Float = this.m14
val a33: Float = this.m15
result = a30 * a21 * a12 * a03 - a20 *
a31 * a12 * a03 - a30 * a11 *
a22 * a03 + a10 * a31 * a22 *
a03 + a20 * a11 * a32 *
a03 - a10 * a21 * a32 *
a03 - a30 * a21 * a02 *
a13 + a20 * a31 * a02 *
a13 + a30 * a01 * a22 *
a13 - a00 * a31 * a22 *
a13 - a20 * a01 * a32 *
a13 + a00 * a21 * a32 *
a13 + a30 * a11 * a02 *
a23 - a10 * a31 * a02 *
a23 - a30 * a01 * a12 *
a23 + a00 * a31 * a12 *
a23 + a10 * a01 * a32 *
a23 - a00 * a11 * a32 *
a23 - a20 * a11 * a02 *
a33 + a10 * a21 * a02 *
a33 + a20 * a01 * a12 *
a33 - a00 * a21 * a12 *
a33 - a10 * a01 * a22 *
a33 + a00 * a11 * a22 * a33
return result
}
/**
* Get the trace of the [Matrix] (sum of the values along the diagonal)
* @return [Float]
*/
inline fun Matrix.trace(): Float {
return this.m0 + this.m5 + this.m10 + this.m15
}
/**
* Get the trace of the [Matrix] (sum of the values along the diagonal)
* @return [Matrix]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Matrix.transpose(): Matrix {
val result: Matrix = kMatrix()
result.m0 = this.m0
result.m1 = this.m4
result.m2 = this.m8
result.m3 = this.m12
result.m4 = this.m1
result.m5 = this.m5
result.m6 = this.m9
result.m7 = this.m13
result.m8 = this.m2
result.m9 = this.m6
result.m10 = this.m10
result.m11 = this.m14
result.m12 = this.m3
result.m13 = this.m7
result.m14 = this.m11
result.m15 = this.m15
return result
}
/**
* Invert provided [Matrix]
* @return [Matrix]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Matrix.invert(): Matrix {
val result: Matrix = kMatrix()
// Cache the matrix values (speed optimization)
val a00: Float = this.m0
val a01: Float = this.m1
val a02: Float = this.m2
val a03: Float = this.m3
val a10: Float = this.m4
val a11: Float = this.m5
val a12: Float = this.m6
val a13: Float = this.m7
val a20: Float = this.m8
val a21: Float = this.m9
val a22: Float = this.m10
val a23: Float = this.m11
val a30: Float = this.m12
val a31: Float = this.m13
val a32: Float = this.m14
val a33: Float = this.m15
val b00 = a00 * a11 - a01 * a10
val b01 = a00 * a12 - a02 * a10
val b02 = a00 * a13 - a03 * a10
val b03 = a01 * a12 - a02 * a11
val b04 = a01 * a13 - a03 * a11
val b05 = a02 * a13 - a03 * a12
val b06 = a20 * a31 - a21 * a30
val b07 = a20 * a32 - a22 * a30
val b08 = a20 * a33 - a23 * a30
val b09 = a21 * a32 - a22 * a31
val b10 = a21 * a33 - a23 * a31
val b11 = a22 * a33 - a23 * a32
// Calculate the invert determinant (inlined to avoid double-caching)
// Calculate the invert determinant (inlined to avoid double-caching)
val invDet = 1.0f / (b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06)
result.m0 = (a11 * b11 - a12 * b10 + a13 * b09) * invDet
result.m1 = (-a01 * b11 + a02 * b10 - a03 * b09) * invDet
result.m2 = (a31 * b05 - a32 * b04 + a33 * b03) * invDet
result.m3 = (-a21 * b05 + a22 * b04 - a23 * b03) * invDet
result.m4 = (-a10 * b11 + a12 * b08 - a13 * b07) * invDet
result.m5 = (a00 * b11 - a02 * b08 + a03 * b07) * invDet
result.m6 = (-a30 * b05 + a32 * b02 - a33 * b01) * invDet
result.m7 = (a20 * b05 - a22 * b02 + a23 * b01) * invDet
result.m8 = (a10 * b10 - a11 * b08 + a13 * b06) * invDet
result.m9 = (-a00 * b10 + a01 * b08 - a03 * b06) * invDet
result.m10 = (a30 * b04 - a31 * b02 + a33 * b00) * invDet
result.m11 = (-a20 * b04 + a21 * b02 - a23 * b00) * invDet
result.m12 = (-a10 * b09 + a11 * b07 - a12 * b06) * invDet
result.m13 = (a00 * b09 - a01 * b07 + a02 * b06) * invDet
result.m14 = (-a30 * b03 + a31 * b01 - a32 * b00) * invDet
result.m15 = (a20 * b03 - a21 * b01 + a22 * b00) * invDet
return result
}
/**
* Get identity [Matrix]
* @return [Matrix]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Matrix.identity(): Matrix {
return kMatrix(
1.0F, 0.0F, 0.0F, 0.0F,
0.0F, 1.0F, 0.0F, 0.0F,
0.0F, 0.0F, 1.0F, 0.0F,
0.0F, 0.0F, 0.0F, 1.0F
)
}
/**
* Add two matrices
*/
inline operator fun Matrix.plus(m: Matrix) {
this.m0 + m.m0
this.m1 + m.m1
this.m2 + m.m2
this.m3 + m.m3
this.m4 + m.m4
this.m5 + m.m5
this.m6 + m.m6
this.m7 + m.m7
this.m8 + m.m8
this.m9 + m.m9
this.m10 + m.m10
this.m11 + m.m11
this.m12 + m.m12
this.m13 + m.m13
this.m14 + m.m14
this.m15 + m.m15
}
/**
* Subtract two matrices
*/
inline operator fun Matrix.minus(m: Matrix) {
this.m0 - m.m0
this.m1 - m.m1
this.m2 - m.m2
this.m3 - m.m3
this.m4 - m.m4
this.m5 - m.m5
this.m6 - m.m6
this.m7 - m.m7
this.m8 - m.m8
this.m9 - m.m9
this.m10 - m.m10
this.m11 - m.m11
this.m12 - m.m12
this.m13 - m.m13
this.m14 - m.m14
this.m15 - m.m15
}
/**
* Multiply two matrices
*/
inline operator fun Matrix.times(m: Matrix) {
this.m0 * m.m0
this.m1 * m.m1
this.m2 * m.m2
this.m3 * m.m3
this.m4 * m.m4
this.m5 * m.m5
this.m6 * m.m6
this.m7 * m.m7
this.m8 * m.m8
this.m9 * m.m9
this.m10 * m.m10
this.m11 * m.m11
this.m12 * m.m12
this.m13 * m.m13
this.m14 * m.m14
this.m15 * m.m15
}
/**
* Get translation [Matrix]
* @return [Matrix]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Matrix.translate(x: Float, y: Float, z: Float): Matrix {
return kMatrix(
1.0f, 0.0f, 0.0f, x,
0.0f, 1.0f, 0.0f, y,
0.0f, 0.0f, 1.0f, z,
0.0f, 0.0f, 0.0f, 1.0f
)
}
/**
* Create rotation [Matrix] from [axis] and [angle]
* NOTE: [angle] should be provided in radians
* @return [Matrix]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Matrix.rotate(axis: Vector3, angle: Float): Matrix {
val result: Matrix = kMatrix()
var x = axis.x
var y = axis.y
var z = axis.z
val lengthSquared = x * x + y * y + z * z
if (lengthSquared != 1.0f && lengthSquared != 0.0f) {
val ilength: Float = 1.0f / sqrt(lengthSquared)
x *= ilength
y *= ilength
z *= ilength
}
val sinres: Float = sin(angle)
val cosres: Float = cos(angle)
val t = 1.0f - cosres
result.m0 = x * x * t + cosres
result.m1 = y * x * t + z * sinres
result.m2 = z * x * t - y * sinres
result.m3 = 0.0f
result.m4 = x * y * t - z * sinres
result.m5 = y * y * t + cosres
result.m6 = z * y * t + x * sinres
result.m7 = 0.0f
result.m8 = x * z * t + y * sinres
result.m9 = y * z * t - x * sinres
result.m10 = z * z * t + cosres
result.m11 = 0.0f
result.m12 = 0.0f
result.m13 = 0.0f
result.m14 = 0.0f
result.m15 = 1.0f
return result
}
/**
* Get x-rotation [Matrix]
* NOTE: [angle] must be provided in radians
* @return [Matrix]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Matrix.rotateX(angle: Float): Matrix {
val result: Matrix = kMatrix(
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
) // MatrixIdentity()
val cosres: Float = cos(angle)
val sinres: Float = sin(angle)
result.m5 = cosres
result.m6 = sinres
result.m9 = -sinres
result.m10 = cosres
return result
}
/**
* Get y-rotation [Matrix]
* NOTE: [angle] must be provided in radians
* @return [Matrix]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Matrix.rotateY(angle: Float): Matrix {
val result: Matrix = kMatrix(
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f
) // MatrixIdentity()
val cosres: Float = cos(angle)
val sinres: Float = sin(angle)
result.m0 = cosres
result.m2 = -sinres
result.m8 = sinres
result.m10 = cosres
return result
}
/**
* Get z-rotation [Matrix]
* NOTE: [angle] must be provided in radians
* @return [Matrix]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Matrix.rotateZ(angle: Float): Matrix {
val result: Matrix = kMatrix(
1.0F, 0.0F, 0.0F, 0.0F,
0.0F, 1.0F, 0.0F, 0.0F,
0.0F, 0.0F, 1.0F, 0.0F,
0.0F, 0.0F, 0.0F, 1.0F
) // MatrixIdentity()
val cosres: Float = cos(angle)
val sinres: Float = sin(angle)
result.m0 = cosres
result.m1 = sinres
result.m4 = -sinres
result.m5 = cosres
return result
}
/**
* Get XYZ-rotation [Matrix]
* NOTE: [angle] must be provided in radians
* @return [Matrix]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Matrix.rotateXYZ(angle: Vector3): Matrix {
val result: Matrix = kMatrix(
1.0F, 0.0F, 0.0F, 0.0F,
0.0F, 1.0F, 0.0F, 0.0F,
0.0F, 0.0F, 1.0F, 0.0F,
0.0F, 0.0F, 0.0F, 1.0F
) // MatrixIdentity()
val cosz: Float = cos(-angle.z)
val sinz: Float = sin(-angle.z)
val cosy: Float = cos(-angle.y)
val siny: Float = sin(-angle.y)
val cosx: Float = cos(-angle.x)
val sinx: Float = sin(-angle.x)
result.m0 = cosz * cosy
result.m1 = cosz * siny * sinx - sinz * cosx
result.m2 = cosz * siny * cosx + sinz * sinx
result.m4 = sinz * cosy
result.m5 = sinz * siny * sinx + cosz * cosx
result.m6 = sinz * siny * cosx - cosz * sinx
result.m8 = -siny
result.m9 = cosy * sinx
result.m10 = cosy * cosx
return result
}
/**
* Get ZYX-rotation [Matrix]
* NOTE: [angle] must be provided in radians
* @return [Matrix]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Matrix.rotateZYX(angle: Vector3): Matrix {
val result: Matrix = kMatrix()
val cz: Float = cos(angle.z)
val sz: Float = sin(angle.z)
val cy: Float = cos(angle.y)
val sy: Float = sin(angle.y)
val cx: Float = cos(angle.x)
val sx: Float = sin(angle.x)
result.m0 = cz * cy
result.m4 = cz * sy * sx - cx * sz
result.m8 = sz * sx + cz * cx * sy
result.m12 = 0F
result.m1 = cy * sz
result.m5 = cz * cx + sz * sy * sx
result.m9 = cx * sz * sy - cz * sx
result.m13 = 0F
result.m2 = -sy
result.m6 = cy * sx
result.m10 = cy * cx
result.m14 = 0F
result.m3 = 0F
result.m7 = 0F
result.m11 = 0F
result.m15 = 1F
return result
}
/**
* Get scaling [Matrix]
* @return [Matrix]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Matrix.scale(x: Float, y: Float, z: Float): Matrix {
return kMatrix(
x, 0.0F, 0.0F, 0.0F,
0.0F, y, 0.0F, 0.0F,
0.0F, 0.0F, z, 0.0F,
0.0F, 0.0F, 0.0F, 1.0F
)
}
/**
* Get perspective projection [Matrix]
* @return [Matrix]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Matrix.frustum(left: Double, right: Double,bottom: Double, top: Double, near: Double, far: Double): Matrix {
val result: Matrix = kMatrix()
val rl = (right - left).toFloat()
val tb = (top - bottom).toFloat()
val fn = (far - near).toFloat()
result.m0 = near.toFloat() * 2.0f / rl
result.m1 = 0.0f
result.m2 = 0.0f
result.m3 = 0.0f
result.m4 = 0.0f
result.m5 = near.toFloat() * 2.0f / tb
result.m6 = 0.0f
result.m7 = 0.0f
result.m8 = (right.toFloat() + left.toFloat()) / rl
result.m9 = (top.toFloat() + bottom.toFloat()) / tb
result.m10 = -(far.toFloat() + near.toFloat()) / fn
result.m11 = -1.0f
result.m12 = 0.0f
result.m13 = 0.0f
result.m14 = -(far.toFloat() * near.toFloat() * 2.0f) / fn
result.m15 = 0.0f
return result
}
/**
* Get perspective projection [Matrix]
* NOTE: [fovy] angle must be provided in radians
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Matrix.perspective(fovy: Double, aspect: Double, near: Double, far: Double): Matrix {
val result: Matrix = kMatrix()
val top: Double = near * tan(fovy * 0.5)
val bottom = -top
val right = top * aspect
val left = -right
// MatrixFrustum(-right, right, -top, top, near, far);
// MatrixFrustum(-right, right, -top, top, near, far);
val rl = (right - left).toFloat()
val tb = (top - bottom).toFloat()
val fn = (far - near).toFloat()
result.m0 = near.toFloat() * 2.0f / rl
result.m5 = near.toFloat() * 2.0f / tb
result.m8 = (right.toFloat() + left.toFloat()) / rl
result.m9 = (top.toFloat() + bottom.toFloat()) / tb
result.m10 = -(far.toFloat() + near.toFloat()) / fn
result.m11 = -1.0f
result.m14 = -(far.toFloat() * near.toFloat() * 2.0f) / fn
return result
}
/**
* Get orthographic projection [Matrix]
* @return [Matrix]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Matrix.ortho(left: Double, right: Double,bottom: Double, top: Double, near: Double, far: Double): Matrix {
val result: Matrix = kMatrix()
val rl = (right - left).toFloat()
val tb = (top - bottom).toFloat()
val fn = (far - near).toFloat()
result.m0 = 2.0f / rl
result.m1 = 0.0f
result.m2 = 0.0f
result.m3 = 0.0f
result.m4 = 0.0f
result.m5 = 2.0f / tb
result.m6 = 0.0f
result.m7 = 0.0f
result.m8 = 0.0f
result.m9 = 0.0f
result.m10 = -2.0f / fn
result.m11 = 0.0f
result.m12 = -(left.toFloat() + right.toFloat()) / rl
result.m13 = -(top.toFloat() + bottom.toFloat()) / tb
result.m14 = -(far.toFloat() + near.toFloat()) / fn
result.m15 = 1.0f
return result
}
/**
* Get camera look-at [Matrix] (view matrix)
* @return [Matrix]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Matrix.lookAt(eye: Vector3, target: Vector3, up: Vector3): Matrix {
val result: Matrix = kMatrix()
var length: Float
var ilength: Float
// Vector3Subtract(eye, target)
// Vector3Subtract(eye, target)
val vz: Vector3 = kVector3(eye.x - target.x, eye.y - target.y, eye.z - target.z)
// Vector3Normalize(vz)
// Vector3Normalize(vz)
var v = vz
length = sqrt(v.x * v.x + v.y * v.y + v.z * v.z)
if (length == 0.0f) length = 1.0f
ilength = 1.0f / length
vz.x *= ilength
vz.y *= ilength
vz.z *= ilength
// Vector3CrossProduct(up, vz)
// Vector3CrossProduct(up, vz)
val vx: Vector3 = kVector3(up.y * vz.z - up.z * vz.y, up.z * vz.x - up.x * vz.z, up.x * vz.y - up.y * vz.x)
// Vector3Normalize(x)
// Vector3Normalize(x)
v = vx
length = sqrt(v.x * v.x + v.y * v.y + v.z * v.z)
if (length == 0.0f) length = 1.0f
ilength = 1.0f / length
vx.x *= ilength
vx.y *= ilength
vx.z *= ilength
// Vector3CrossProduct(vz, vx)
// Vector3CrossProduct(vz, vx)
val vy: Vector3 = kVector3(vz.y * vx.z - vz.z * vx.y, vz.z * vx.x - vz.x * vx.z, vz.x * vx.y - vz.y * vx.x)
result.m0 = vx.x
result.m1 = vy.x
result.m2 = vz.x
result.m3 = 0.0f
result.m4 = vx.y
result.m5 = vy.y
result.m6 = vz.y
result.m7 = 0.0f
result.m8 = vx.z
result.m9 = vy.z
result.m10 = vz.z
result.m11 = 0.0f
result.m12 = -(vx.x * eye.x + vx.y * eye.y + vx.z * eye.z) // Vector3DotProduct(vx, eye)
result.m13 = -(vy.x * eye.x + vy.y * eye.y + vy.z * eye.z) // Vector3DotProduct(vy, eye)
result.m14 = -(vz.x * eye.x + vz.y * eye.y + vz.z * eye.z) // Vector3DotProduct(vz, eye)
result.m15 = 1.0f
return result
}
/**
* Get float array of [Matrix] data
* @return [float16]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Matrix.toFloatV(): float16 {
return MatrixToFloatV(this.readValue()).getPointer(MemScope()).pointed
}
/**
* Set value of a Matrix with another provided value.
* This is useful when dealing with cinterop CStruct that holds nested CStructs which are marked as immutable (val).
* NOTE: While the CStruct is immutable itself, the inner members of that CStruct are mutable.
*/
inline fun Matrix.set(other: Matrix) {
this.m0 - other.m0
this.m1 - other.m1
this.m2 - other.m2
this.m3 - other.m3
this.m4 - other.m4
this.m5 - other.m5
this.m6 - other.m6
this.m7 - other.m7
this.m8 - other.m8
this.m9 - other.m9
this.m10 - other.m10
this.m11 - other.m11
this.m12 - other.m12
this.m13 - other.m13
this.m14 - other.m14
this.m15 - other.m15
}

View file

@ -0,0 +1,157 @@
package kaylibkit.kMath
import kaylibc.*
import kotlinx.cinterop.*
import kotlin.math.*
// -- Module: kMath
//=======================================================//
// Quaternion DATA TYPE - Vector4 Alias
// NOTE: All functions are located in KVector4.kt
//=======================================================//
/**
* Constructor function for [Quaternion]
* @param [allocator] Uses `MemScope()` by default.
* @return [Quaternion]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kQuaternion(x: Float = 0F, y: Float = 0F, z: Float = 0F, w: Float = 0F, allocator: AutofreeScope = MemScope()): Quaternion {
return allocator.alloc<Quaternion> {
this.x = x
this.y = y
this.z = z
this.w = w
}
}
/**
* Calculate slerp-optimized interpolation between two [Quaternion]
* @return [Quaternion]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Quaternion.nLerp(q2: Vector4, amount: Float): Quaternion {
val result: Quaternion = kQuaternion()
// QuaternionLerp(q1, q2, amount)
// QuaternionLerp(q1, q2, amount)
result.x = this.x + amount * (q2.x - this.x)
result.y = this.y + amount * (q2.y - this.y)
result.z = this.z + amount * (q2.z - this.z)
result.w = this.w + amount * (q2.w - this.w)
// QuaternionNormalize(q);
// QuaternionNormalize(q);
var length: Float = sqrt(result.x * result.x + result.y * result.y + result.z * result.z + result.w * result.w)
if (length == 0.0f) length = 1.0f
val ilength = 1.0f / length
result.x *= ilength
result.y *= ilength
result.z *= ilength
result.w *= ilength
return result
}
/**
* Calculates spherical linear interpolation between two [Quaternion]
* @return [Quaternion]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Quaternion.sLerp(q2: Vector4, amount: Float): Quaternion {
var result: Quaternion = kQuaternion()
var cosHalfTheta: Float = this.x * q2.x + this.y * q2.y + this.z * q2.z + this.w * q2.w
if (cosHalfTheta < 0) {
q2.x = -q2.x
q2.y = -q2.y
q2.z = -q2.z
q2.w = -q2.w
cosHalfTheta = -cosHalfTheta
}
if (abs(cosHalfTheta) >= 1.0f) result = this else if (cosHalfTheta > 0.95f) result =
this.nLerp(q2, amount) else {
val halfTheta: Float = acos(cosHalfTheta)
val sinHalfTheta: Float = sqrt(1.0f - cosHalfTheta * cosHalfTheta)
if (abs(sinHalfTheta) < 0.001f) {
result.x = this.x * 0.5f + q2.x * 0.5f
result.y = this.y * 0.5f + q2.y * 0.5f
result.z = this.z * 0.5f + q2.z * 0.5f
result.w = this.w * 0.5f + q2.w * 0.5f
} else {
val ratioA: Float = sin((1 - amount) * halfTheta) / sinHalfTheta
val ratioB: Float = sin(amount * halfTheta) / sinHalfTheta
result.x = this.x * ratioA + q2.x * ratioB
result.y = this.y * ratioA + q2.y * ratioB
result.z = this.z * ratioA + q2.z * ratioB
result.w = this.w * ratioA + q2.w * ratioB
}
}
return result
}
/**
* Get the rotation angle and axis for a given [Quaternion]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Quaternion.toAxisAngle(outAxis: CPointer<Vector3>, outAngle: CPointer<FloatVar>) {
QuaternionToAxisAngle(this.readValue(), outAxis, outAngle)
}
/**
* Get the Euler angles equivalent to [Quaternion] (roll, pitch, yaw)
* NOTE: Angles are returned in a Vector3 struct in radians
* @return [Quaternion]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Quaternion.toEuler(): Vector3 {
val result: Vector3 = kVector3()
// Roll (x-axis rotation)
// Roll (x-axis rotation)
val x0: Float = 2.0f * (this.w * this.x + this.y * this.z)
val x1: Float = 1.0f - 2.0f * (this.x * this.x + this.y * this.y)
result.x = atan2(x0, x1)
// Pitch (y-axis rotation)
// Pitch (y-axis rotation)
var y0: Float = 2.0f * (this.w * this.y - this.z * this.x)
y0 = if (y0 > 1.0f) 1.0f else y0
y0 = if (y0 < -1.0f) -1.0f else y0
result.y = asin(y0)
// Yaw (z-axis rotation)
// Yaw (z-axis rotation)
val z0: Float = 2.0f * (this.w * this.z + this.x * this.y)
val z1: Float = 1.0f - 2.0f * (this.y * this.y + this.z * this.z)
result.z = atan2(z0, z1)
return result
}
/**
* Get the Euler angles equivalent to [Quaternion] (roll, pitch, yaw)
* NOTE: Angles are returned in a [Vector3] struct in radians
* @return [Quaternion]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Quaternion.transform(mat: Matrix): Quaternion {
val result: Quaternion = kQuaternion()
result.x = mat.m0 * this.x + mat.m4 * this.y + mat.m8 * this.z + mat.m12 * this.w
result.y = mat.m1 * this.x + mat.m5 * this.y + mat.m9 * this.z + mat.m13 * this.w
result.z = mat.m2 * this.x + mat.m6 * this.y + mat.m10 * this.z + mat.m14 * this.w
result.w = mat.m3 * this.x + mat.m7 * this.y + mat.m11 * this.z + mat.m15 * this.w
return result
}

View file

@ -0,0 +1,455 @@
package kaylibkit.kMath
import kaylibc.*
import kotlinx.cinterop.*
import kotlin.math.*
// -- Module: kMath
//=======================================================//
// VECTOR2 DATA TYPE
//=======================================================//
/**
* Constructor function for [Vector2]
* @param [allocator] Uses `MemScope()` by default.
* @return [Vector2]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kVector2(x: Float = 0F, y: Float = 0F, allocator: AutofreeScope = MemScope()): Vector2 {
return allocator.alloc<Vector2> {
this.x = x
this.y = y
}
}
/**
* [Vector2] with components value 0
* @return zero values Y and X of passed [Vector2]
*/
inline fun Vector2.setZero() : Vector2 = this.apply { this.x = 0F; this.y = 0F }
/**
* [Vector2] with components value 1
* @return values of 1 in Y and X of passed [Vector2]
*/
inline fun Vector2.setOne() : Vector2 = this.apply { this.x = 1F; this.y = 1F }
/**
* Calculate [Vector2] length
* @return [Float]
*/
inline fun Vector2.length() : Float = sqrt((this.x*this.x) + (this.y*this.y))
/**
* Calculate [Vector2] square length
* @return [Float]
*/
inline fun Vector2.lengthSqr() = (this.x*this.x) + (this.y*this.y)
/**
* Calculate two [Vector2] dot product
* @return [Float]
*/
inline fun Vector2.dot(v2: Vector2) : Float = (this.x*v2.x + this.y*v2.y)
/**
* Calculate distance between two [Vector2]
* @return [Float]
*/
inline fun Vector2.distance(v2: Vector2) : Float = sqrt((this.x - v2.x)*(this.x - v2.x) + (this.y - v2.y)*(this.y - v2.y))
/**
* Calculate square distance between two [Vector2]
* @return [Float]
*/
inline fun Vector2.distanceSqr(v2: Vector2) : Float = ((this.x - v2.x)*(this.x - v2.x) + (this.y - v2.y)*(this.y - v2.y))
/**
* Calculate angle from two [Vector2]
* @return [Float]
*/
inline fun Vector2.angle(v2: Vector2) : Float = atan2(v2.y - this.y, v2.x - this.x)
/**
* Scale [Vector2] (multiply by value)
*/
inline fun Vector2.times(scale: Float) {
this.x *= scale
this.y *= scale
}
/**
* Add two [Vector2]
*/
inline fun Vector2.add(v2: Vector2) {
this.x += v2.x
this.y += v2.y
}
/**
* Add two [Vector2]
*/
inline operator fun Vector2.plus(v2: Vector2) {
this.x += v2.x
this.y += v2.y
}
/**
* Multiply [Vector2] by [Vector2]
*/
inline fun Vector2.multiply(v2: Vector2) {
this.x *= v2.x
this.y *= v2.y
}
/**
* Add [Vector2] and float value
*/
inline fun Vector2.addValue(add: Float) {
this.x += add
this.y += add
}
/**
* Subtract two [Vector2]
*/
inline fun Vector2.subtract(v2: Vector2) {
this.x -= v2.x
this.y -= v2.y
}
/**
* Subtract [Vector2] by float value
*/
inline fun Vector2.subtractValue(sub: Float) {
this.x -= sub
this.y -= sub
}
/**
* Negate [Vector2]
*/
inline fun Vector2.negate() {
this.x = this.x.unaryMinus()
this.y = this.y.unaryMinus()
}
/**
* Normalize provided [Vector2]
* @return [Vector2]
*/
inline fun Vector2.normalize() : Vector2 {
val result = this
val length = sqrt((this.x*this.x) + (this.y*this.y))
if (length > 0) {
val iLength = 1F/length
result.x = this.x*iLength
result.y = this.y*iLength
}
return result
}
/**
* Transforms a [Vector2] by a given [Matrix]
* @return [Vector2]
*/
inline fun Vector2.transform(mat: Matrix) : Vector2 {
val result = this
val x = this.x
val y = this.y
val z = 0
result.x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12
result.y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13
return result
}
/**
* Calculate linear interpolation between two [Vector2]
* @return [Vector2]
*/
inline fun Vector2.lerp(v2: Vector2, amount: Float) : Vector2 {
val result = this
result.x = this.x + amount*(v2.x - this.x)
result.y = this.y + amount*(v2.y - this.y)
return result
}
/**
* Calculate reflected [Vector2] to normal
* * @return [Vector2]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Vector2.reflect(normal: Vector2) : Vector2 {
val result: Vector2 = kVector2()
val dotProduct: Float = this.x * normal.x + this.y * normal.y // Dot product
result.x = this.x - 2.0f * normal.x * dotProduct
result.y = this.y - 2.0f * normal.y * dotProduct
return result
}
/**
* Rotate [Vector2] by [angle]
* @return [Vector2]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Vector2.rotate(angle: Float) : Vector2 {
val result: Vector2 = kVector2()
val cosres: Float = cos(angle)
val sinres: Float = sin(angle)
result.x = this.x * cosres - this.y * sinres
result.y = this.x * sinres + this.y * cosres
return result
}
/**
* Move [Vector2] towards target
* @return [Vector2]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Vector2.moveTowards(target: Vector2, maxDistance: Float) : Vector2 {
val result: Vector2 = kVector2()
val dx: Float = target.x - this.x
val dy: Float = target.y - this.y
val value = dx * dx + dy * dy
if (value == 0f || maxDistance >= 0 && value <= maxDistance * maxDistance) return target
val dist: Float = sqrt(value)
result.x = this.x + dx / dist * maxDistance
result.y = this.y + dy / dist * maxDistance
return result
}
/**
* Invert the given [Vector2]
* @return [Vector2]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Vector2.invert(): Vector2 { return kVector2(1.0f / this.x, 1.0f / this.y) }
/**
* Clamp the components of the [Vector2] between
* @return [Vector2]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Vector2.clamp(min: Vector2, max: Vector2) : Vector2 {
val result: Vector2 = kVector2()
result.x = min(max.x, max(min.x, this.x))
result.y = min(max.y, max(min.y, this.y))
return result
}
@Deprecated("Use Kotlin's internal coerceIn function", ReplaceWith("coerceIn()"))
/**
* Clamp the magnitude of the vector between two values
* @return [Unit]
*/
inline fun Vector2.clampValue() {}
/**
* Calculate angle defined by a two vectors line
* NOTE: Parameters need to be normalized
* Current implementation should be aligned with glm::angle
* @return [Vector2]
*/
inline fun Vector2.lineAngle(start: Vector2, end: Vector2) : Float {
val result: Float
val dot: Float = start.x * end.x + start.y * end.y // Dot product
var dotClamp = if (dot < -1.0f) -1.0f else dot // Clamp
if (dotClamp > 1.0f) dotClamp = 1.0f
result = acos(dotClamp)
return result
}
/**
* Deconstruct this [Vector2].
*/
inline operator fun Vector2.component1(): Float = x
/**
* Deconstruct this [Vector2].
*/
inline operator fun Vector2.component2(): Float = y
/**
* Divide vector by [Vector2]
*/
inline operator fun Vector2.divAssign(v2: Vector2) {
this.x /= v2.x
this.y /= v2.y
}
/**
* Divide vector by [Vector2]
*/
inline operator fun Vector2.div(v2: Vector2) {
this.x /= v2.x
this.y /= v2.y
}
/**
* Divide both [Vector2] values by scalar.
*/
inline operator fun Vector2.divAssign(scalar: Float) {
this.x /= scalar
this.y /= scalar
}
/**
* Divide both [Vector2] values by scalar.
*/
inline operator fun Vector2.divAssign(scalar: Int) {
this.x /= scalar
this.y /= scalar
}
/**
* Multiply both [Vector2] values
*/
inline operator fun Vector2.timesAssign(scalar: Int) {
this.x *= scalar.toFloat()
this.y *= scalar.toFloat()
}
/**
* Multiply both [Vector2] values
*/
inline operator fun Vector2.timesAssign(scalar: Float) {
this.x *= scalar
this.y *= scalar
}
/**
* Multiply both [Vector2] values
*/
inline operator fun Vector2.timesAssign(v2: Vector2) {
this.x *= v2.x
this.y *= v2.y
}
/**
* Add two [Vector2]
*/
inline operator fun Vector2.plusAssign(v2: Vector2) {
this.add(v2)
}
/**
* Add two [Vector2] to both x and y of the [Vector2].
*/
inline operator fun Vector2.plusAssign(add: Float) {
this.x += add
this.y += add
}
/**
* Add two [Vector2] to both x and y of the [Vector2].
*/
inline operator fun Vector2.plusAssign(addend: Int) {
plusAssign(addend.toFloat())
}
/**
* Subtract a [Vector2] by another [Vector2]
*/
inline operator fun Vector2.minusAssign(v2: Vector2) {
this.x -= v2.x
this.y -= v2.y
}
/**
* Value will be subtracted from both x and y of the [Vector2].
*/
inline operator fun Vector2.minusAssign(value: Float) {
this.x -= value
this.y -= value
}
/**
* Value will be subtracted from both x and y of the [Vector2].
*/
inline operator fun Vector2.minusAssign(value: Int) {
minusAssign(value.toFloat())
}
/**
* Subtract a [Vector2] by another [Vector2]
*/
inline operator fun Vector2.minus(v2: Vector2) {
this.x -= v2.x
this.y -= v2.y
}
/**
* Value will be multiplied by a [Vector2]
*/
inline operator fun Vector2.times(v2: Vector2) {
this.x *= v2.x
this.y *= v2.y
}
/**
* Get a reminder of [Vector2]
*/
inline operator fun Vector2.rem(v2: Vector2) {
this.x %= v2.x
this.y %= v2.y
}
/**
* Value will be compared with another [Vector2]
*/
inline operator fun Vector2.compareTo(other: Vector2): Int = when {
this.y != other.y -> (this.y - other.y).toInt()
else -> (this.x - other.x).toInt()
}
/**
* Due to inability to override toString function, this is the current workaround printing the values of a [Vector2]
* @return [String] values of [Vector2]
*/
inline fun Vector2.asString(): String {
return "X: ${x.toString()}\nY: ${y.toString()}"
}
/**
* Due to inability to override equals operator, this is the current workaround checking for equality of the values of a [Vector2]
* @return [Boolean]
*/
inline fun Vector2.equalsTo(v: Vector2): Boolean {
return (abs(this.x - v.x) <= EPSILON * max(
1.0f,
max(abs(this.x), abs(v.x))
) && abs(this.y - v.y) <= EPSILON * max(1.0f, max(abs(this.y), abs(v.y))))
}
/**
* Set value of a [Vector2] with another provided value of same type.
* This is useful when dealing with cinterop CStruct that holds nested CStructs which are marked as immutable (val).
* NOTE: While the CStruct is immutable itself, the inner members of that CStruct are mutable.
*/
inline fun Vector2.set(other: Vector2) {
this.x = other.x
this.y = other.y
}

View file

@ -0,0 +1,788 @@
package kaylibkit.kMath
import kaylibc.*
import kotlinx.cinterop.*
import kotlin.math.*
// -- Module: kMath
//=======================================================//
// VECTOR3 DATA TYPE
//=======================================================//
/**
* Constructor function for [Vector3]
* @param [allocator] Uses `MemScope()` by default.
* @return [Vector3]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kVector3(x: Float = 0F, y: Float = 0F, z: Float = 0F, allocator: AutofreeScope = MemScope()): Vector3 {
return allocator.alloc<Vector3> {
this.x = x
this.y = y
this.z = z
}
}
/**
* [Vector3] with components value 0
* @return zero values Y, X and Z of passed [Vector3]
*/
inline fun Vector3.setZero() : Vector3 = this.apply { this.x = 0F; this.y = 0F; this.z = 0F }
/**
* [Vector3] with components value 1
* @return values of 1 in Y and X of passed [Vector3]
*/
inline fun Vector3.setOne() : Vector3 = this.apply { this.x = 1F; this.y = 1F; this.z = 1F }
/**
* Calculate [Vector3] length
*/
inline fun Vector3.length() : Float = sqrt((this.x*this.x + this.y*this.y + this.z*this.z))
/**
* Calculate [Vector3] square length
*/
inline fun Vector3.lengthSqr() = (this.x*this.x + this.y*this.y + this.z*this.z)
/**
* Calculate two [Vector3] dot product
*/
inline fun Vector3.dot(v3: Vector3) : Float = (this.x*v3.x + this.y*v3.y + this.z*v3.z)
/**
* Calculate distance between two [Vector3]
*/
inline fun Vector3.distance(v3: Vector3) : Float = sqrt((this.x - v3.x)*(this.x - v3.x) + (this.y - v3.y)*(this.y - v3.y) + (this.z - v3.z)*(this.z - v3.z))
/**
* Calculate square distance between two [Vector3]
*/
inline fun Vector3.distanceSqr(v3: Vector3) : Float = ((this.x - v3.x)*(this.x - v3.x) + (this.y - v3.y)*(this.y - v3.y) + (this.z - v3.z)*(this.z - v3.z))
/**
* Calculate angle from two [Vector3]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Vector3.angle(v3: Vector3) : Float {
val result: Float
val cross = kVector3(this.y*v3.z - this.z*v3.y, this.z*v3.x - this.x*v3.z, this.x*v3.y - this.y*v3.x )
val len = sqrt(cross.x*cross.x + cross.y*cross.y + cross.z*cross.z)
val dot = (this.x*v3.x + this.y*v3.y + this.z*v3.z)
result = atan2(len, dot)
return result
}
/**
* Calculate two [Vector3] cross product
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Vector3.crossProduct(v2: Vector3): Vector3 {
return kVector3(this.y * v2.z - this.z * v2.y, this.z * v2.x - this.x * v2.z, this.x * v2.y - this.y * v2.x)
}
/**
* Scale [Vector3] (multiply by value)
*/
inline fun Vector3.times(scale: Float) {
this.x *= scale
this.y *= scale
this.z *= scale
}
/**
* Add two [Vector3]
*/
inline fun Vector3.add(v3: Vector3) {
this.x += v3.x
this.y += v3.y
this.z += v3.z
}
/**
* Multiply [Vector3] by [Vector3]
*/
inline fun Vector3.multiply(v3: Vector3) {
this.x *= v3.x
this.y *= v3.y
this.z *= v3.z
}
/**
* Add [Vector3] and float value
*/
inline fun Vector3.addValue(add: Float) {
this.x += add
this.y += add
this.z += add
}
/**
* Subtract two [Vector3]
*/
inline fun Vector3.subtract(v3: Vector3) {
this.x -= v3.x
this.y -= v3.y
this.z -= v3.z
}
/**
* Subtract [Vector3] by float value
*/
inline fun Vector3.subtractValue(sub: Float) {
this.x -= sub
this.y -= sub
this.z -= sub
}
/**
* Get reminder of [Vector3]
*/
inline fun Vector3.rem(v3: Vector3) {
this.x %= v3.x
this.y %= v3.y
this.z %= v3.z
}
/**
* Negate [Vector3]
*/
inline fun Vector3.negate() {
this.x = this.x.unaryMinus()
this.y = this.y.unaryMinus()
this.z = this.z.unaryMinus()
}
/**
* Normalize provided [Vector3]
*/
inline fun Vector3.normalize() : Vector3 {
val result = this
var length = sqrt(this.x*this.x + this.y*this.y + this.z*this.z)
if (length == 0F) { length = 1F }
val iLength = 1F/length
result.x = this.x*iLength
result.y = this.y*iLength
result.z = this.z*iLength
return result
}
/**
* Transforms a [Vector3] by a given [Matrix]
*/
inline fun Vector3.transform(mat: Matrix) : Vector3 {
val result = this
val x = this.x
val y = this.y
val z = this.z
result.x = mat.m0*x + mat.m4*y + mat.m8*z + mat.m12
result.y = mat.m1*x + mat.m5*y + mat.m9*z + mat.m13
result.z = mat.m2*x + mat.m6*y + mat.m10*z + mat.m14;
return result
}
/**
* Transform a [Vector3] by [Quaternion] rotation
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Vector3.rotateByQuaternion(q: Quaternion): Vector3 {
val result: Vector3 = kVector3()
result.x =
this.x * (q.x * q.x + q.w * q.w - q.y * q.y - q.z * q.z) + this.y * (2 * q.x * q.y - 2 * q.w * q.z) + this.z * (2 * q.x * q.z + 2 * q.w * q.y)
result.y =
this.x * (2 * q.w * q.z + 2 * q.x * q.y) + this.y * (q.w * q.w - q.x * q.x + q.y * q.y - q.z * q.z) + this.z * (-2 * q.w * q.x + 2 * q.y * q.z)
result.z =
this.x * (-2 * q.w * q.y + 2 * q.x * q.z) + this.y * (2 * q.w * q.x + 2 * q.y * q.z) + this.z * (q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z)
return result
}
/**
* Rotates a [Vector3] around an [axis]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Vector3.rotateByAxisAngle(axis: Vector3, angle: Float): Vector3 {
// Using Euler-Rodrigues Formula
// Ref.: https://en.wikipedia.org/w/index.php?title=Euler%E2%80%93Rodrigues_formula
// Using Euler-Rodrigues Formula
// Ref.: https://en.wikipedia.org/w/index.php?title=Euler%E2%80%93Rodrigues_formula
val result: Vector3 = this
var ang = angle
// Vector3Normalize(axis);
// Vector3Normalize(axis);
var length: Float = sqrt(axis.x * axis.x + axis.y * axis.y + axis.z * axis.z)
if (length == 0.0f) length = 1.0f
val ilength = 1.0f / length
axis.x *= ilength
axis.y *= ilength
axis.z *= ilength
ang /= 2.0f
var a: Float = sin(angle)
val b = axis.x * a
val c = axis.y * a
val d = axis.z * a
a = cos(angle)
val w: Vector3 = kVector3(b, c, d)
// Vector3CrossProduct(w, v)
// Vector3CrossProduct(w, v)
val wv: Vector3 = kVector3(w.y * this.z - w.z * this.y, w.z * this.x - w.x * this.z, w.x * this.y - w.y * this.x)
// Vector3CrossProduct(w, wv)
// Vector3CrossProduct(w, wv)
val wwv: Vector3 = kVector3(w.y * wv.z - w.z * wv.y, w.z * wv.x - w.x * wv.z, w.x * wv.y - w.y * wv.x)
// Vector3Scale(wv, 2 * a)
// Vector3Scale(wv, 2 * a)
a *= 2f
wv.x *= a
wv.y *= a
wv.z *= a
// Vector3Scale(wwv, 2)
// Vector3Scale(wwv, 2)
wwv.x *= 2
wwv.y *= 2
wwv.z *= 2
result.x += wv.x
result.y += wv.y
result.z += wv.z
result.x += wwv.x
result.y += wwv.y
result.z += wwv.z
return result
}
/**
* Calculate linear interpolation between two [Vector3]
*/
inline fun Vector3.lerp(v3: Vector3, amount: Float) : Vector3 {
val result = this
result.x = this.x + amount*(v3.x - this.x)
result.y = this.y + amount*(v3.y - this.y)
result.z = this.z + amount*(v3.z - this.z)
return result
}
/**
* Orthonormalize provided [Vector3]
* Makes vectors normalized and orthogonal to each other
* Gram-Schmidt function implementation
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Vector3.orthoNormalize(v3: CPointer<Vector3>) {
return Vector3OrthoNormalize(this.ptr, v3)
}
/**
* Calculate reflected [Vector3] to normal
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Vector3.reflect(normal: Vector3) : Vector3 {
val result: Vector3 = kVector3()
// I is the original vector
// N is the normal of the incident plane
// R = I - (2*N*(DotProduct[I, N]))
// I is the original vector
// N is the normal of the incident plane
// R = I - (2*N*(DotProduct[I, N]))
val dotProduct: Float = this.x * normal.x + this.y * normal.y + this.z * normal.z
result.x = this.x - 2.0f * normal.x * dotProduct
result.y = this.y - 2.0f * normal.y * dotProduct
result.z = this.z - 2.0f * normal.z * dotProduct
return result
}
/**
* Calculate one [Vector3] perpendicular vector
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Vector3.perpendicular() : Vector3 {
val result: Vector3 = kVector3()
var min = abs(this.x)
var cardinalAxis: Vector3 = kVector3(1.0f, 0.0f, 0.0f)
if (abs(this.y) < min) {
min = abs(this.y)
val tmp: Vector3 = kVector3(0.0f, 1.0f, 0.0f)
cardinalAxis = tmp
}
if (abs(this.z) < min) {
val tmp: Vector3 = kVector3(0.0f, 0.0f, 1.0f)
cardinalAxis = tmp
}
// Cross product between vectors
// Cross product between vectors
result.x = this.y * cardinalAxis.z - this.z * cardinalAxis.y
result.y = this.z * cardinalAxis.x - this.x * cardinalAxis.z
result.z = this.x * cardinalAxis.y - this.y * cardinalAxis.x
return result
}
/**
* Invert the given [Vector3]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Vector3.invert(): Vector3 {
return kVector3(1.0f / this.x, 1.0f / this.y, 1.0f / this.z)
}
/**
* Get min value for each pair of components
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Vector3.min(v3: Vector3) : Vector3 {
val result: Vector3 = kVector3()
result.x = min(this.x, v3.x)
result.y = min(this.y, v3.y)
result.z = min(this.z, v3.z)
return result
}
/**
* Get max value for each pair of components
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Vector3.max(v3: Vector3) : Vector3 {
val result: Vector3 = kVector3()
result.x = max(this.x, v3.x)
result.y = max(this.y, v3.y)
result.z = max(this.z, v3.z)
return result
}
/**
* Get max value for each pair of components
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Vector3.barycenter(a: Vector3, b: Vector3, c: Vector3) : Vector3 {
val result: Vector3 = kVector3()
val v0: Vector3 = kVector3(b.x - a.x, b.y - a.y, b.z - a.z) // Vector3Subtract(b, a)
val v1: Vector3 = kVector3(c.x - a.x, c.y - a.y, c.z - a.z) // Vector3Subtract(c, a)
val v2: Vector3 = kVector3(this.x - a.x, this.y - a.y, this.z - a.z) // Vector3Subtract(p, a)
val d00 = v0.x * v0.x + v0.y * v0.y + v0.z * v0.z // Vector3DotProduct(v0, v0)
val d01 = v0.x * v1.x + v0.y * v1.y + v0.z * v1.z // Vector3DotProduct(v0, v1)
val d11 = v1.x * v1.x + v1.y * v1.y + v1.z * v1.z // Vector3DotProduct(v1, v1)
val d20 = v2.x * v0.x + v2.y * v0.y + v2.z * v0.z // Vector3DotProduct(v2, v0)
val d21 = v2.x * v1.x + v2.y * v1.y + v2.z * v1.z // Vector3DotProduct(v2, v1)
val denom = d00 * d11 - d01 * d01
result.y = (d11 * d20 - d01 * d21) / denom
result.z = (d00 * d21 - d01 * d20) / denom
result.x = 1.0f - (result.z + result.y)
return result
}
/**
* Projects a [Vector3] from screen space into object space
* NOTE: We are avoiding calling other raymath functions despite available
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Vector3.unproject(projection: Matrix, view: Matrix) : Vector3 {
val result: Vector3 = kVector3()
// Calculate unproject matrix (multiply view patrix by projection matrix) and invert it
// Calculate unproject matrix (multiply view patrix by projection matrix) and invert it
val matViewProj: Matrix = kMatrix( // MatrixMultiply(view, projection);
view.m0 * projection.m0 + view.m1 * projection.m4 + view.m2 * projection.m8 + view.m3 * projection.m12,
view.m0 * projection.m1 + view.m1 * projection.m5 + view.m2 * projection.m9 + view.m3 * projection.m13,
view.m0 * projection.m2 + view.m1 * projection.m6 + view.m2 * projection.m10 + view.m3 * projection.m14,
view.m0 * projection.m3 + view.m1 * projection.m7 + view.m2 * projection.m11 + view.m3 * projection.m15,
view.m4 * projection.m0 + view.m5 * projection.m4 + view.m6 * projection.m8 + view.m7 * projection.m12,
view.m4 * projection.m1 + view.m5 * projection.m5 + view.m6 * projection.m9 + view.m7 * projection.m13,
view.m4 * projection.m2 + view.m5 * projection.m6 + view.m6 * projection.m10 + view.m7 * projection.m14,
view.m4 * projection.m3 + view.m5 * projection.m7 + view.m6 * projection.m11 + view.m7 * projection.m15,
view.m8 * projection.m0 + view.m9 * projection.m4 + view.m10 * projection.m8 + view.m11 * projection.m12,
view.m8 * projection.m1 + view.m9 * projection.m5 + view.m10 * projection.m9 + view.m11 * projection.m13,
view.m8 * projection.m2 + view.m9 * projection.m6 + view.m10 * projection.m10 + view.m11 * projection.m14,
view.m8 * projection.m3 + view.m9 * projection.m7 + view.m10 * projection.m11 + view.m11 * projection.m15,
view.m12 * projection.m0 + view.m13 * projection.m4 + view.m14 * projection.m8 + view.m15 * projection.m12,
view.m12 * projection.m1 + view.m13 * projection.m5 + view.m14 * projection.m9 + view.m15 * projection.m13,
view.m12 * projection.m2 + view.m13 * projection.m6 + view.m14 * projection.m10 + view.m15 * projection.m14,
view.m12 * projection.m3 + view.m13 * projection.m7 + view.m14 * projection.m11 + view.m15 * projection.m15
)
// Calculate inverted matrix -> MatrixInvert(matViewProj);
// Cache the matrix values (speed optimization)
// Calculate inverted matrix -> MatrixInvert(matViewProj);
// Cache the matrix values (speed optimization)
val a00 = matViewProj.m0
val a01 = matViewProj.m1
val a02 = matViewProj.m2
val a03 = matViewProj.m3
val a10 = matViewProj.m4
val a11 = matViewProj.m5
val a12 = matViewProj.m6
val a13 = matViewProj.m7
val a20 = matViewProj.m8
val a21 = matViewProj.m9
val a22 = matViewProj.m10
val a23 = matViewProj.m11
val a30 = matViewProj.m12
val a31 = matViewProj.m13
val a32 = matViewProj.m14
val a33 = matViewProj.m15
val b00 = a00 * a11 - a01 * a10
val b01 = a00 * a12 - a02 * a10
val b02 = a00 * a13 - a03 * a10
val b03 = a01 * a12 - a02 * a11
val b04 = a01 * a13 - a03 * a11
val b05 = a02 * a13 - a03 * a12
val b06 = a20 * a31 - a21 * a30
val b07 = a20 * a32 - a22 * a30
val b08 = a20 * a33 - a23 * a30
val b09 = a21 * a32 - a22 * a31
val b10 = a21 * a33 - a23 * a31
val b11 = a22 * a33 - a23 * a32
// Calculate the invert determinant (inlined to avoid double-caching)
// Calculate the invert determinant (inlined to avoid double-caching)
val invDet = 1.0f / (b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06)
val matViewProjInv: Matrix = kMatrix(
(a11 * b11 - a12 * b10 + a13 * b09) * invDet,
(-a01 * b11 + a02 * b10 - a03 * b09) * invDet,
(a31 * b05 - a32 * b04 + a33 * b03) * invDet,
(-a21 * b05 + a22 * b04 - a23 * b03) * invDet,
(-a10 * b11 + a12 * b08 - a13 * b07) * invDet,
(a00 * b11 - a02 * b08 + a03 * b07) * invDet,
(-a30 * b05 + a32 * b02 - a33 * b01) * invDet,
(a20 * b05 - a22 * b02 + a23 * b01) * invDet,
(a10 * b10 - a11 * b08 + a13 * b06) * invDet,
(-a00 * b10 + a01 * b08 - a03 * b06) * invDet,
(a30 * b04 - a31 * b02 + a33 * b00) * invDet,
(-a20 * b04 + a21 * b02 - a23 * b00) * invDet,
(-a10 * b09 + a11 * b07 - a12 * b06) * invDet,
(a00 * b09 - a01 * b07 + a02 * b06) * invDet,
(-a30 * b03 + a31 * b01 - a32 * b00) * invDet,
(a20 * b03 - a21 * b01 + a22 * b00) * invDet
)
// Create quaternion from source point
// Create quaternion from source point
val quat: Quaternion = kQuaternion(this.x, this.y, this.z, 1.0f)
// Multiply quat point by unproject matrix
// Multiply quat point by unproject matrix
val qtransformed: Quaternion = kQuaternion( // QuaternionTransform(quat, matViewProjInv)
matViewProjInv.m0 * quat.x + matViewProjInv.m4 * quat.y + matViewProjInv.m8 * quat.z + matViewProjInv.m12 * quat.w,
matViewProjInv.m1 * quat.x + matViewProjInv.m5 * quat.y + matViewProjInv.m9 * quat.z + matViewProjInv.m13 * quat.w,
matViewProjInv.m2 * quat.x + matViewProjInv.m6 * quat.y + matViewProjInv.m10 * quat.z + matViewProjInv.m14 * quat.w,
matViewProjInv.m3 * quat.x + matViewProjInv.m7 * quat.y + matViewProjInv.m11 * quat.z + matViewProjInv.m15 * quat.w
)
// Normalized world points in vectors
// Normalized world points in vectors
result.x = qtransformed.x / qtransformed.w
result.y = qtransformed.y / qtransformed.w
result.z = qtransformed.z / qtransformed.w
return result
}
/**
* Get [Vector3] as float array
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Vector3.toFloatV() : float3 {
return Vector3ToFloatV(this.readValue()).getPointer(MemScope()).pointed
}
/**
* Clamp the components of the [Vector3] between
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Vector3.clamp(min: Vector3, max: Vector3) : Vector3 {
val result: Vector3 = kVector3()
result.x = min(max.x, max(min.x, this.x))
result.y = min(max.y, max(min.y, this.y))
result.z = min(max.z, max(min.z, this.z))
return result
}
@Deprecated("Use Kotlin's internal coerceIn function", ReplaceWith("coerceIn()"))
/**
* Clamp the magnitude of the vector between two values
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Vector3.clampValue(min: Float, max: Float) : Vector3 {
return Vector3ClampValue(this.readValue(), min, max).getPointer(MemScope()).pointed
}
/**
* Due to inability to override equals operator, this is the current workaround checking for equality of the values of a [Vector3]
* @return String values of [Vector3]
*/
inline fun Vector3.equalsTo(v: Vector3): Boolean {
return (abs(this.x - v.x) <= EPSILON * max(
1.0f,
max(abs(this.x), abs(v.x))
) && abs(this.y - v.y) <= EPSILON * max(
1.0f,
max(abs(this.y), abs(v.y))
) && abs(this.z - v.z) <= EPSILON * max(1.0f, max(abs(this.z), abs(v.z))))
}
/**
* Compute the direction of a refracted ray where v specifies the
* normalized direction of the incoming ray, n specifies the
* normalized normal vector of the interface of two optical media,
* and r specifies the ratio of the refractive index of the medium
* from where the ray comes to the refractive index of the medium
* on the other side of the surface
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Vector3.refract(n: Vector3, r: Float) : Vector3 {
var result: Vector3 = kVector3()
val dot: Float = this.x * n.x + this.y * n.y + this.z * n.z
var d = 1.0f - r * r * (1.0f - dot * dot)
if (d >= 0.0f) {
d = sqrt(d)
this.x = r * this.x - (r * dot + d) * n.x
this.y = r * this.y - (r * dot + d) * n.y
this.z = r * this.z - (r * dot + d) * n.z
result = this
}
return result
}
/**
* Divide vector by [Vector3]
*/
inline operator fun Vector3.div(v3: Vector3) {
this.x /= v3.x
this.y /= v3.y
this.z /= v3.z
}
/**
* Deconstruct this [Vector3].
*/
inline operator fun Vector3.component1(): Float = x
/**
* Deconstruct this [Vector3].
*/
inline operator fun Vector3.component2(): Float = y
/**
* Deconstruct this [Vector3].
*/
inline operator fun Vector3.component3(): Float = z
/**
* Divide vector by [Vector3]
*/
inline operator fun Vector3.divAssign(v3: Vector3) {
this.x /= v3.x
this.y /= v3.y
this.z /= v3.z
}
/**
* Divide both [Vector3] values by scalar.
*/
inline operator fun Vector3.divAssign(scalar: Float) {
this.x /= scalar
this.y /= scalar
this.z /= scalar
}
/**
* Divide both [Vector3] values by scalar.
*/
inline operator fun Vector3.divAssign(scalar: Int) {
this.x /= scalar
this.y /= scalar
this.z /= scalar
}
/**
* Multiply both [Vector3] values
*/
inline operator fun Vector3.timesAssign(scalar: Int) {
this.x *= scalar.toFloat()
this.y *= scalar.toFloat()
this.z *= scalar.toFloat()
}
/**
* Multiply both [Vector3] values
*/
inline operator fun Vector3.timesAssign(scalar: Float) {
this.x *= scalar
this.y *= scalar
this.z *= scalar
}
/**
* Multiply both [Vector3] values
*/
inline operator fun Vector3.timesAssign(v3: Vector3) {
this.x *= v3.x
this.y *= v3.y
this.z *= v3.z
}
/**
* Add two [Vector3]
*/
inline operator fun Vector3.plusAssign(v3: Vector3) {
this.add(v3)
}
/**
* Add two [Vector3]
*/
inline operator fun Vector3.plus(v3: Vector3) {
this.add(v3)
}
/**
* Add two [Vector3] to both x, y and z of the vector.
*/
inline operator fun Vector3.plusAssign(add: Float) {
this.x += add
this.y += add
this.z += add
}
/**
* Add two [Vector3] to both x, y and z of the vector.
*/
inline operator fun Vector3.plusAssign(addend: Int) {
plusAssign(addend.toFloat())
}
/**
* Subtract a [Vector3] by another Vector3
*/
inline operator fun Vector3.minusAssign(v3: Vector3) {
this.x -= v3.x
this.y -= v3.y
this.z -= v3.z
}
/**
* Subtract a [Vector3] by another Vector3
*/
inline operator fun Vector3.minus(v3: Vector3) {
this.x -= v3.x
this.y -= v3.y
this.z -= v3.z
}
/**
* Value will be subtracted from both x, y and z of the [Vector3].
*/
inline operator fun Vector3.minusAssign(value: Float) {
this.x -= value
this.y -= value
this.z -= value
}
/**
* Value will be subtracted from both x, y and z of the [Vector3].
*/
inline operator fun Vector3.minusAssign(value: Int) {
minusAssign(value.toFloat())
}
/**
* Value will be multiplied by a [Vector3]
*/
inline operator fun Vector3.times(v3: Vector3) {
this.x *= v3.x
this.y *= v3.y
this.z *= v3.z
}
/**
* Value will be compared with another [Vector3]
*/
inline operator fun Vector3.compareTo(other: Vector3): Int = when {
this.y != other.y -> (this.y - other.y).toInt()
this.z != other.z -> (this.z - other.z).toInt()
else -> (this.x - other.x).toInt()
}
/**
* Due to inability to override toString function, this is the current workaround printing the values of a [Vector3]
* @return [Vector3]
*/
inline fun Vector3.asString(): String {
return "X: ${x.toString()}\nY: ${y.toString()}\nZ: ${z.toString()}"
}
/**
* Set value of a [Vector3] with another provided value.
* This is useful when dealing with cinterop CStruct that holds nested CStructs which are marked as immutable (val).
* NOTE: While the CStruct is immutable itself, the inner members of that CStruct are mutable.
*/
inline fun Vector3.set(other: Vector3) {
this.x = other.x
this.y = other.y
this.z = other.z
}

View file

@ -0,0 +1,391 @@
package kaylibkit.kMath
import kaylibc.*
import kotlinx.cinterop.*
import kotlin.math.*
// -- Module: kMath
//=======================================================//
// Vector4 DATA TYPE
//=======================================================//
/**
* Constructor function for [Vector4]
* @param [allocator] Uses `MemScope()` by default.
* @return [Vector4]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kVector4(x: Float = 0F, y: Float = 0F, z: Float = 0F, w: Float = 0F, allocator: AutofreeScope = MemScope()): Vector4 {
return allocator.alloc<Vector4> {
this.x = x
this.y = y
this.z = z
this.w = w
}
}
/**
* Add [Vector4] and float value
*/
inline fun Vector4.addValue(add: Float) {
this.x += add
this.y += add
this.z += add
this.w += add
}
/**
* Subtract two [Vector4]
*/
inline fun Vector4.subtract(v4: Vector4) {
this.x -= v4.x
this.y -= v4.y
this.z -= v4.z
}
/**
* Subtract [Vector4] by float value
*/
inline fun Vector4.subtractValue(sub: Float) {
this.x -= sub
this.y -= sub
this.z -= sub
this.w -= sub
}
/**
* Negate [Vector4]
*/
inline fun Vector4.negate() {
this.x = this.x.unaryMinus()
this.y = this.y.unaryMinus()
this.z = this.z.unaryMinus()
this.w = this.w.unaryMinus()
}
/**
* Compute the length of a [Vector4]
* @return [Vector4]
*/
inline fun Vector4.length(): Float { return sqrt(this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w) }
/**
* Normalize provided [Vector4]
* @return [Vector4]
*/
inline fun Vector4.normalize() : Vector4 {
val result = this
var length = sqrt(this.x*this.x + this.y*this.y + this.z*this.z + this.w*this.w)
if (length == 0F) { length = 1F }
val iLength = 1F/length
result.x = this.x*iLength
result.y = this.y*iLength
result.z = this.z*iLength
result.w = this.w*iLength
return result
}
/**
* Invert provided [Quaternion]/[Vector4]
* @return [Vector4]
*/
inline fun Vector4.invert(): Vector4 {
val result: Vector4 = this
val lengthSq: Float = this.x * this.x + this.y * this.y + this.z * this.z + this.w * this.w
if (lengthSq != 0.0F) {
val invLength = 1.0F / lengthSq
result.x *= -invLength
result.y *= -invLength
result.z *= -invLength
result.w *= invLength
}
return result
}
/**
* Deconstruct this [Vector4].
*/
inline operator fun Vector4.component1(): Float = x
/**
* Deconstruct this [Vector4].
*/
inline operator fun Vector4.component2(): Float = y
/**
* Deconstruct this [Vector4].
*/
inline operator fun Vector4.component3(): Float = z
/**
* Deconstruct this [Vector4].
*/
inline operator fun Vector4.component4(): Float = w
/**
* Divide vector by [Vector4]
*/
inline operator fun Vector4.divAssign(v4: Vector4) {
this.x /= v4.x
this.y /= v4.y
this.z /= v4.z
this.w /= v4.w
}
/**
* Divide both [Vector4] values by scalar.
*/
inline operator fun Vector4.divAssign(scalar: Float) {
this.x /= scalar
this.y /= scalar
this.z /= scalar
this.w /= scalar
}
/**
* Divide both [Vector4] values by scalar.
*/
inline operator fun Vector4.divAssign(scalar: Int) {
this.x /= scalar
this.y /= scalar
this.z /= scalar
this.w /= scalar
}
/**
* Multiply both [Vector4] values
*/
inline operator fun Vector4.timesAssign(scalar: Int) {
this.x *= scalar.toFloat()
this.y *= scalar.toFloat()
this.z *= scalar.toFloat()
this.w *= scalar.toFloat()
}
/**
* Multiply both [Vector4] values
*/
inline operator fun Vector4.timesAssign(scalar: Float) {
this.x *= scalar
this.y *= scalar
this.z *= scalar
this.w *= scalar
}
/**
* Multiply both [Vector4] values
*/
inline operator fun Vector4.timesAssign(v4: Vector4) {
this.x *= v4.x
this.y *= v4.y
this.z *= v4.z
this.w *= v4.w
}
/**
* Add two [Vector4]
*/
inline fun Vector4.add(v4: Vector4) {
this.x += v4.x
this.y += v4.y
this.z += v4.z
this.w += v4.w
}
/**
* Add two [Vector4]
*/
inline fun Vector4.plus(v4: Vector4) {
this.x += v4.x
this.y += v4.y
this.z += v4.z
this.w += v4.w
}
/**
* Calculate linear interpolation between two [Vector4]
* @return [Vector4]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Vector4.lerp(v4: Vector4, amount: Float): Vector4 {
val result: Vector4 = kVector4()
result.x = this.x + amount * (v4.x - this.x)
result.y = this.y + amount * (v4.y - this.y)
result.z = this.z + amount * (v4.z - this.z)
result.w = this.w + amount * (v4.w - this.w)
return result
}
/**
* Due to inability to override equals operator, this is the current workaround checking for equality of the values of a [Vector4]
* @return [Boolean]
*/
inline fun Vector4.equalsTo(v: Vector4): Boolean {
return (abs(this.x - v.x) <= EPSILON * max(
1.0f,
max(abs(this.x), abs(v.x))
) && abs(this.y - v.y) <= EPSILON * max(
1.0f,
max(abs(this.y), abs(v.y))
) && abs(this.z - v.z) <= EPSILON * max(
1.0f,
max(abs(this.z), abs(v.z))
) && abs(this.w - v.w) <= EPSILON * max(
1.0f,
max(abs(this.w), abs(v.w))
) || abs(this.x + v.x) <= EPSILON * max(
1.0f,
max(abs(this.x), abs(v.x))
) && abs(this.y + v.y) <= EPSILON * max(
1.0f,
max(abs(this.y), abs(v.y))
) && abs(this.z + v.z) <= EPSILON * max(
1.0f,
max(abs(this.z), abs(v.z))
) && abs(this.w + v.w) <= EPSILON * max(1.0f, max(abs(this.w), abs(v.w))))
}
/**
* Scale [Vector4] by float value
*/
inline fun Vector4.scale(mul: Float) {
this.x *= mul
this.y *= mul
this.z *= mul
this.w *= mul
}
/**
* Add two [Vector4]
*/
inline operator fun Vector4.plusAssign(v4: Vector4) {
this.add(v4)
}
/**
* Divide [Vector4] by [Vector4]
*/
inline operator fun Vector4.div(v4: Vector4) {
this.x /= v4.x
this.y /= v4.y
this.z /= v4.z
this.w /= v4.w
}
/**
* Add two [Vector4] to both x, y, z and w of the [Vector4].
*/
inline operator fun Vector4.plusAssign(add: Float) {
this.x += add
this.y += add
this.z += add
this.w += add
}
/**
* Add two [Vector4] to both x, y, z and w of the [Vector4].
*/
inline operator fun Vector4.plusAssign(addend: Int) {
plusAssign(addend.toFloat())
}
/**
* Subtract a [Vector4] by another [Vector4]
*/
inline operator fun Vector4.minusAssign(v4: Vector4) {
this.x -= v4.x
this.y -= v4.y
this.z -= v4.z
this.w -= v4.w
}
/**
* Subtract a [Vector4] by another [Vector4]
*/
inline operator fun Vector4.minus(v4: Vector4) {
this.x -= v4.x
this.y -= v4.y
this.z -= v4.z
this.w -= v4.w
}
/**
* Value will be subtracted from both x, y, z and w of the [Vector4].
*/
inline operator fun Vector4.minusAssign(value: Float) {
this.x -= value
this.y -= value
this.z -= value
this.w -= value
}
/**
* Value will be subtracted from both x, y, z and w of the [Vector4].
*/
inline operator fun Vector4.minusAssign(value: Int) {
minusAssign(value.toFloat())
}
/**
* Calculate two [Vector4] multiplication
* @return [Vector4]
*/
@OptIn(ExperimentalForeignApi::class)
inline operator fun Vector4.times(v4: Vector4): Vector4 {
val result: Vector4 = kVector4()
val qax: Float = this.x
val qay: Float = this.y
val qaz: Float = this.z
val qaw: Float = this.w
val qbx: Float = v4.x
val qby: Float = v4.y
val qbz: Float = v4.z
val qbw: Float = v4.w
result.x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby
result.y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz
result.z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx
result.w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz
return result
}
/**
* Value will be compared with another [Vector4]
*/
inline operator fun Vector4.compareTo(other: Vector4): Int = when {
this.y != other.y -> (this.y - other.y).toInt()
this.z != other.z -> (this.z - other.z).toInt()
this.w != other.w -> (this.w - other.w).toInt()
else -> (this.x - other.x).toInt()
}
/**
* Due to inability to override toString function, this is the current workaround printing the values of a [Vector4]
* @return [String] values of [Vector4]
*/
inline fun Vector4.asString(): String {
return "X: ${x.toString()}\nY: ${y.toString()}\nZ: ${z.toString()}\nW: ${w.toString()}"
}
/**
* Set value of a [Quaternion]/[Vector4] with another provided value.
* This is useful when dealing with cinterop CStruct that holds nested CStructs which are marked as immutable (val).
* NOTE: While the CStruct is immutable itself, the inner members of that CStruct are mutable.
*/
inline fun Vector4.set(other: Quaternion) {
this.x = other.x
this.y = other.y
this.z = other.z
this.w = other.w
}

View file

@ -0,0 +1,634 @@
package kaylibkit.kModels
import kaylibc.*
import kotlinx.cinterop.*
// -- Module: kModels
//=======================================================//
// BASIC 3D GEOMETRIC DRAWING FUNCTIONS
//=======================================================//
/**
* Draw a line in 3D world space
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawLine3D(startPos: Vector3, endPos: Vector3, color: Color) {
DrawLine3D(startPos.readValue(), endPos.readValue(), color.readValue())
}
/**
* Draw a point in 3D space, actually a small line
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawPoint3D(position: Vector3, color: Color) {
DrawPoint3D(position.readValue(), color.readValue())
}
/**
* Draw a circle in 3D world space
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawCircle3D(center: Vector3, radius: Float, rotationAxis: Vector3, rotationAngle: Float, color: Color) {
DrawCircle3D(center.readValue(), radius, rotationAxis.readValue(), rotationAngle, color.readValue())
}
/**
* Draw a capsule with the center of its sphere caps at startPos and endPos
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawCapsule(startPos: Vector3, endPos: Vector3, radius: Float, slices: Int, rings: Int, color: Color) {
DrawCapsule(startPos.readValue(), endPos.readValue(), radius, slices, rings, color.readValue())
}
/**
* Draw capsule wires with the center of its sphere caps at [startPos] and [endPos]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawCapsuleWires(startPos: Vector3, endPos: Vector3, radius: Float, slices: Int, rings: Int, color: Color) {
DrawCapsuleWires(startPos.readValue(), endPos.readValue(), radius, slices, rings, color.readValue())
}
/**
* Draw a [color]-filled triangle (vertex in counter-clockwise order!)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawTriangle3D(v1: Vector3, v2: Vector3, v3: Vector3, color: Color) {
DrawTriangle3D(v1.readValue(), v2.readValue(), v3.readValue(), color.readValue())
}
/**
* Draw a triangle strip defined by [points]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawTriangleStrip3D(points: Vector3, pointCount: Int, color: Color) {
DrawTriangleStrip3D(points.ptr, pointCount, color.readValue())
}
/**
* Draw cube
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawCube(position: Vector3, width: Float, height: Float, length: Float, color: Color) {
DrawCube(position.readValue(), width, height, length, color.readValue())
}
/**
* Draw cube ([Vector3] version)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawCubeV(position: Vector3, size: Vector3, color: Color) {
DrawCubeV(position.readValue(), size.readValue(), color.readValue())
}
/**
* Draw cube wires
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawCubeWires(position: Vector3, width: Float, height: Float, length: Float, color: Color) {
DrawCubeWires(position.readValue(), width, height, length, color.readValue())
}
/**
* Draw cube wires ([Vector3] version)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawCubeWiresV(position: Vector3, size: Vector3, color: Color) {
DrawCubeWiresV(position.readValue(), size.readValue(), color.readValue())
}
/**
* Draw sphere
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawSphere(centerPos: Vector3, radius: Float, color: Color) {
DrawSphere(centerPos.readValue(), radius, color.readValue())
}
/**
* Draw sphere with extended parameters
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawSphereEx(centerPos: Vector3, radius: Float, rings: Int, slices: Int, color: Color) {
DrawSphereEx(centerPos.readValue(), radius, rings, slices, color.readValue())
}
/**
* Draw sphere wires
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawSphereWires(centerPos: Vector3, radius: Float, rings: Int, slices: Int, color: Color) {
DrawSphereWires(centerPos.readValue(), radius, rings, slices, color.readValue())
}
/**
* Draw a cylinder/cone
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawCylinder(position: Vector3, radiusTop: Float, radiusBottom: Float, height: Float, slices: Int, color: Color) {
DrawCylinder(position.readValue(), radiusTop, radiusBottom, height, slices, color.readValue())
}
/**
* Draw a cylinder with base at [startPos] and top at [endPos]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawCylinderEx(startPos: Vector3, endPos: Vector3, startRadius: Float, endRadius: Float, sides: Int, color: Color) {
DrawCylinderEx(startPos.readValue(), endPos.readValue(), startRadius, endRadius, sides, color.readValue())
}
/**
* Draw a cylinder/cone wires
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawCylinderWires(position: Vector3, radiusTop: Float, radiusBottom: Float, height: Float, slices: Int, color: Color) {
DrawCylinderWires(position.readValue(), radiusTop, radiusBottom, height, slices, color.readValue())
}
/**
* Draw a cylinder wires with base at [startPos] and top at [endPos]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawCylinderWiresEx(startPos: Vector3, endPos: Vector3, startRadius: Float, endRadius: Float, sides: Int, color: Color) {
DrawCylinderWiresEx(startPos.readValue(), endPos.readValue(), startRadius, endRadius, sides, color.readValue())
}
/**
* Draw a plane XZ
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawPlane(centerPos: Vector3, size: Vector2, color: Color) {
DrawPlane(centerPos.readValue(), size.readValue(), color.readValue())
}
/**
* Draw a [ray] line
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawRay(ray: Ray, color: Color) {
DrawRay(ray.readValue(), color.readValue())
}
/**
* Draw a grid (centered at (0, 0, 0))
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawGrid(slices: Int, spacing: Float) {
DrawGrid(slices, spacing)
}
//=======================================================//
// 3D MODEL LOADING FUNCTIONS
//=======================================================//
/**
* Load model from files (meshes and materials)
* @return [Model]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadModel(fileName: String) : Model {
return LoadModel(fileName).getPointer(MemScope()).pointed
}
/**
* Load model from generated mesh (default material)
* @return [Model]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadModelFromMesh(mesh: Mesh) : Model {
return LoadModelFromMesh(mesh.readValue()).getPointer(MemScope()).pointed
}
/**
* Unload [model] (including meshes) from memory (RAM and/or VRAM)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun unloadModel(model: Model) {
UnloadModel(model.readValue())
}
/**
* Compute [model] bounding box limits (considers all meshes)
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getModelBoundingBox(model: Model) : BoundingBox {
return GetModelBoundingBox(model.readValue()).getPointer(MemScope()).pointed
}
//=======================================================//
// 3D MODEL DRAWING FUNCTIONS
//=======================================================//
/**
* Draw a [model] (with texture if set)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawModel(model: Model , position: Vector3, scale: Float, tint: Color) {
DrawModel(model.readValue(), position.readValue(), scale, tint.readValue())
}
/**
* Draw a [model] with extended parameters
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawModelEx(model: Model, position: Vector3, rotationAxis: Vector3, rotationAngle: Float, scale: Vector3, tint: Color) {
DrawModelEx(model.readValue(), position.readValue(), rotationAxis.readValue(), rotationAngle, scale.readValue(), tint.readValue())
}
/**
* Draw a [model] wires (with texture if set)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawModelWires(model: Model, position: Vector3, scale: Float, tint: Color) {
DrawModelWires(model.readValue(), position.readValue(), scale, tint.readValue())
}
/**
* Draw a [model] wires (with texture if set) with extended parameters
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawModelWiresEx(model: Model, position: Vector3, rotationAxis: Vector3, rotationAngle: Float, scale: Vector3, tint: Color) {
DrawModelWiresEx(model.readValue(), position.readValue(), rotationAxis.readValue(), rotationAngle, scale.readValue(), tint.readValue())
}
/**
* Draw bounding [box] (wires)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawBoundingBox(box: BoundingBox, color: Color) {
DrawBoundingBox(box.readValue(), color.readValue())
}
/**
* Draw a billboard [texture]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawBillboard(camera: Camera, texture: Texture2D, position: Vector3, size: Float, tint: Color) {
DrawBillboard(camera.readValue(), texture.readValue(), position.readValue(), size, tint.readValue())
}
/**
* Draw a billboard [texture] defined by [source]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawBillboardRec(camera: Camera, texture: Texture2D, source: Rectangle, position: Vector3, size: Vector2, tint: Color) {
DrawBillboardRec(camera.readValue(), texture.readValue(), source.readValue(), position.readValue(), size.readValue(), tint.readValue())
}
/**
* Draw a billboard [texture] defined by [source] and [rotation]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawBillboardPro(camera: Camera, texture: Texture2D, source: Rectangle, position: Vector3, up: Vector3, size: Vector2, origin: Vector2, rotation: Float, tint: Color) {
DrawBillboardPro(camera.readValue(), texture.readValue(), source.readValue(), position.readValue(), up.readValue(), size.readValue(), origin.readValue(), rotation, tint.readValue())
}
//=======================================================//
// MESH MANAGEMENT FUNCTIONS
//=======================================================//
/**
* Upload [mesh] vertex data in GPU and provide VAO/VBO ids
*/
@OptIn(ExperimentalForeignApi::class)
inline fun uploadMesh(mesh: Mesh, dynamic: Boolean) {
UploadMesh(mesh.ptr, dynamic)
}
/**
* Update [mesh] vertex data in GPU for a specific buffer index
*/
@OptIn(ExperimentalForeignApi::class)
inline fun updateMeshBuffer(mesh: Mesh, index: Int, data: COpaquePointer, dataSize: Int, offset: Int) {
UpdateMeshBuffer(mesh.readValue(), index, data, dataSize, offset)
}
/**
* Unload [mesh] data from CPU and GPU
*/
@OptIn(ExperimentalForeignApi::class)
inline fun unloadMesh(mesh: Mesh) {
UnloadMesh(mesh.readValue())
}
/**
* Draw a 3d [mesh] with [material] and [transform]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawMesh(mesh: Mesh, material: Material, transform: Matrix) {
DrawMesh(mesh.readValue(), material.readValue(), transform.readValue())
}
/**
* Draw multiple [mesh] instances with [material] and different [transforms]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawMeshInstanced(mesh: Mesh, material: Material, transforms: Matrix, instances: Int) {
DrawMeshInstanced(mesh.readValue(), material.readValue(), transforms.ptr, instances)
}
/**
* Export mesh data to file, returns true on success
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun exportMesh(mesh: Mesh, fileName: String) : Boolean {
return ExportMesh(mesh.readValue(), fileName)
}
/**
* Compute mesh bounding box limits
* @return [BoundingBox]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getMeshBoundingBox(mesh: Mesh) : BoundingBox {
return GetMeshBoundingBox(mesh.readValue()).getPointer(MemScope()).pointed
}
/**
* Compute mesh tangents
*/
@OptIn(ExperimentalForeignApi::class)
inline fun genMeshTangents(mesh: Mesh) {
GenMeshTangents(mesh.readValue())
}
//=======================================================//
// MESH GENERATION FUNCTIONS
//=======================================================//
/**
* Generate polygonal mesh
* @return [Mesh]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun genMeshPoly(sides: Int, radius: Float) : Mesh {
return GenMeshPoly(sides, radius).getPointer(MemScope()).pointed
}
/**
* Generate plane mesh (with subdivisions)
* @return [Mesh]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun genMeshPlane(width: Float, length: Float, resX: Int, resZ: Int) : Mesh {
return GenMeshPlane(width, length, resX, resZ).getPointer(MemScope()).pointed
}
/**
* Generate cuboid mesh
* @return [Mesh]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun genMeshCube(width: Float, height: Float, length: Float) : Mesh {
return GenMeshCube(width, height, length).getPointer(MemScope()).pointed
}
/**
* Generate sphere mesh (standard sphere)
* @return [Mesh]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun genMeshSphere(radius: Float, rings: Int, slices: Int) : Mesh {
return GenMeshSphere(radius, rings, slices).getPointer(MemScope()).pointed
}
/**
* Generate half-sphere mesh (no bottom cap)
* @return [Mesh]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun genMeshHemiSphere(radius: Float, rings: Int, slices: Int) : Mesh {
return GenMeshHemiSphere(radius, rings, slices).getPointer(MemScope()).pointed
}
/**
* Generate cylinder mesh
* @return [Mesh]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun genMeshCylinder(radius: Float, height: Float, slices: Int) : Mesh {
return GenMeshCylinder(radius, height, slices).getPointer(MemScope()).pointed
}
/**
* Generate cone/pyramid mesh
* @return [Mesh]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun genMeshCone(radius: Float, height: Float, slices: Int) : Mesh {
return GenMeshCone(radius, height, slices).getPointer(MemScope()).pointed
}
/**
* Generate torus mesh
* @return [Mesh]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun genMeshTorus(radius: Float, size: Float, radSeg: Int, sides: Int) : Mesh {
return GenMeshTorus(radius, size, radSeg, sides).getPointer(MemScope()).pointed
}
/**
* Generate trefoil knot mesh
* @return [Mesh]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun genMeshKnot(radius: Float, size: Float, radSeg: Int, sides: Int) : Mesh {
return GenMeshKnot(radius, size, radSeg, sides).getPointer(MemScope()).pointed
}
/**
* Generate heightmap mesh from image data
* @return [Mesh]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun genMeshHeightmap(heightmap: Image, size: Vector3) : Mesh {
return GenMeshHeightmap(heightmap.readValue(), size.readValue()).getPointer(MemScope()).pointed
}
/**
* Generate cubes-based map mesh from image data
* @return [Mesh]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun genMeshCubicmap(cubicmap: Image, cubeSize: Vector3) : Mesh {
return GenMeshCubicmap(cubicmap.readValue(), cubeSize.readValue()).getPointer(MemScope()).pointed
}
//=======================================================//
// MATERIAL LOADING/UNLOADING FUNCTIONS
//=======================================================//
/**
* Load materials from model file
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadMaterials(fileName: String, materialCount: IntVar) : Material? {
return LoadMaterials(fileName, materialCount.ptr)?.getPointer(MemScope())?.pointed
}
/**
* Load default [Material] (Supports: DIFFUSE, SPECULAR, NORMAL maps)
* @return [Material]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadMaterialDefault() : Material {
return LoadMaterialDefault().getPointer(MemScope()).pointed
}
/**
* Unload [material] from GPU memory (VRAM)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun unloadMaterial(material: Material) {
UnloadMaterial(material.readValue())
}
/**
* Set [texture] for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun setMaterialTexture(material: Material, mapType: kaylibkit.kEnums.MaterialMapIndex, texture: Texture2D) {
SetMaterialTexture(material.ptr, mapType.value, texture.readValue())
}
/**
* Set [Material] for a [Mesh]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun setModelMeshMaterial(model: Model, meshId: Int, materialId: Int) {
SetModelMeshMaterial(model.ptr, meshId, materialId)
}
//=======================================================//
// MODEL ANIMATION LOADING/UNLOADING FUNCTIONS
//=======================================================//
/**
* Load model animations from file
* @return [ModelAnimation]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadModelAnimations(fileName: String, animCount: UIntVar) : ModelAnimation? {
return LoadModelAnimations(fileName, animCount.ptr)?.getPointer(MemScope())?.pointed
}
/**
* Update model animation pose
*/
@OptIn(ExperimentalForeignApi::class)
inline fun updateModelAnimation(model: Model, anim: ModelAnimation, frame: Int) {
UpdateModelAnimation(model.readValue(), anim.readValue(), frame)
}
/**
* Unload animation data
*/
@OptIn(ExperimentalForeignApi::class)
inline fun unloadModelAnimation(anim: ModelAnimation) {
UnloadModelAnimation(anim.readValue())
}
/**
* Unload animation array data
*/
@OptIn(ExperimentalForeignApi::class)
inline fun unloadModelAnimations(animations: ModelAnimation) {
UnloadModelAnimation(animations.readValue())
}
/**
* Check model animation skeleton match
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun isModelAnimationValid(model: Model, anim: ModelAnimation) : Boolean {
return IsModelAnimationValid(model.readValue(), anim.readValue())
}
//=======================================================//
// COLLISION DETECTION FUNCTIONS
//=======================================================//
/**
* Check collision between two spheres
* @return [RayCollision]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun checkCollisionSpheres(center1: Vector3, radius1: Float, center2: Vector3, radius2: Float) : Boolean {
return CheckCollisionSpheres(center1.readValue(), radius1, center2.readValue(), radius2)
}
/**
* Check collision between two bounding boxes
* @return [RayCollision]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun checkCollisionBoxes(box1: BoundingBox, box2: BoundingBox) : Boolean {
return CheckCollisionBoxes(box1.readValue(), box2.readValue())
}
/**
* Check collision between [box] and sphere
* @return [RayCollision]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun checkCollisionBoxSphere(box: BoundingBox, center: Vector3, radius: Float) : Boolean {
return CheckCollisionBoxSphere(box.readValue(), center.readValue(), radius)
}
/**
* Get collision info between [ray] and sphere
* @return [RayCollision]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getRayCollisionSphere(ray: Ray, center: Vector3, radius: Float) : RayCollision {
return GetRayCollisionSphere(ray.readValue(), center.readValue(), radius).getPointer(MemScope()).pointed
}
/**
* Get collision info between [ray] and box ([BoundingBox])
* @return [RayCollision]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getRayCollisionBox(ray: Ray, box: BoundingBox) : RayCollision {
return GetRayCollisionBox(ray.readValue(), box.readValue()).getPointer(MemScope()).pointed
}
/**
* Get collision info between [ray] and [mesh]
* @return [RayCollision]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getRayCollisionMesh(ray: Ray, mesh: Mesh, transform: Matrix) : RayCollision {
return GetRayCollisionMesh(ray.readValue(), mesh.readValue(), transform.readValue()).getPointer(MemScope()).pointed
}
/**
* Get collision info between [ray] and triangle
* @return [RayCollision]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getRayCollisionTriangle(ray: Ray, p1: Vector3, p2: Vector3, p3: Vector3) : RayCollision {
return GetRayCollisionTriangle(ray.readValue(), p1.readValue(), p2.readValue(), p3.readValue()).getPointer(MemScope()).pointed
}
/**
* Get collision info between [ray] and quad
* @return [RayCollision]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getRayCollisionQuad(ray: Ray, p1: Vector3, p2: Vector3, p3: Vector3, p4: Vector3) : RayCollision {
return GetRayCollisionQuad(ray.readValue(), p1.readValue(), p2.readValue(), p3.readValue(), p4.readValue()).getPointer(MemScope()).pointed
}
/**
* Check if a model is ready
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun isModelReady(model: Model) : Boolean {
return IsModelReady(model.readValue())
}

View file

@ -0,0 +1,51 @@
package kaylibkit.kShapes
import kaylibkit.kMath.kVector2
import kaylibc.Rectangle
import kotlinx.cinterop.AutofreeScope
import kotlinx.cinterop.MemScope
import kotlinx.cinterop.alloc
import kaylibc.Vector2
import kotlinx.cinterop.ExperimentalForeignApi
/**
* Constructor function for Rectangle using X and Y floats for position.
* @param [allocator] Uses `MemScope()` by default.
* @return [Rectangle]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kRectangle(x: Float = 0F, y: Float = 0F, width: Float = 0F, height: Float = 0F, allocator: AutofreeScope = MemScope()) : Rectangle {
return allocator.alloc<Rectangle> {
this.x = x
this.y = y
this.width = width
this.height = height
}
}
/**
* Constructor function for Rectangle that take a kVector2 for position.
* @param [allocator] Uses `MemScope()` by default.
* @return [Rectangle]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kRectangle(vector2: Vector2 = kVector2(0F, 0F), width: Float = 0F, height: Float = 0F, allocator: AutofreeScope = MemScope()) : Rectangle {
return allocator.alloc<Rectangle> {
this.x = vector2.x
this.y = vector2.y
this.width = width
this.height = height
}
}
/**
* Set value of a Rectangle with another provided value of same type.
* This is useful when dealing with cinterop CStruct that holds nested CStructs which are marked as immutable (val).
* NOTE: While the CStruct is immutable itself, the inner members of that CStruct are mutable.
*/
inline fun Rectangle.set(other: Rectangle) {
this.x = other.x
this.y = other.y
this.width = other.width
this.height = other.height
}

View file

@ -0,0 +1,418 @@
package kaylibkit.kShapes
import kaylibc.*
import kotlinx.cinterop.*
// -- Module: kShapes
//=======================================================//
// BASIC SHAPES DRAWING FUNCTIONS
//=======================================================//
/**
* Set texture and [Rectangle] to be used on shapes drawing
*/
@OptIn(ExperimentalForeignApi::class)
inline fun setShapesTexture(texture: Texture2D, source: Rectangle) {
SetShapesTexture(texture.readValue(), source.readValue())
}
/**
* Draw a pixel
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawPixel(posX: Int, posY: Int, color: Color) {
DrawPixel(posX, posY, color.readValue())
}
/**
* Draw a pixel ([Vector2] version)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawPixel(position: Vector2, color: Color) {
DrawPixelV(position.readValue(), color.readValue())
}
/**
* Draw a line
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawLine(startPosX: Int, startPosY: Int, endPosX: Int, endPosY: Int, color: Color) {
DrawLine(startPosX, startPosY, endPosX, endPosY, color.readValue())
}
/**
* Draw a line ([Vector2] version)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawLine(startPos: Vector2, endPos: Vector2, color: Color) {
DrawLineV(startPos.readValue(), endPos.readValue(), color.readValue())
}
/**
* Draw a line defining thickness
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawLine(startPos: Vector2, endPos: Vector2, thick: Float, color: Color) {
DrawLineEx(startPos.readValue(), endPos.readValue(), thick, color.readValue())
}
/**
* Draw a line using cubic-bezier curves in-out
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawLineBezier(startPos: Vector2, endPos: Vector2, thick: Float, color: Color) {
DrawLineBezier(startPos.readValue(), endPos.readValue(), thick, color.readValue())
}
/**
* Draw line using quadratic bezier curves with a control point
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawLineBezierQuad(startPos: Vector2, endPos: Vector2, controlPos: Vector2, thick: Float, color: Color) {
DrawLineBezierQuad(startPos.readValue(), endPos.readValue(), controlPos.readValue(), thick, color.readValue())
}
/**
* Draw line using cubic bezier curves with 2 control points
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawLineBezierCubic(startPos: Vector2, endPos: Vector2, startControlPos: Vector2, endControlPos: Vector2, thick: Float, color: Color) {
DrawLineBezierCubic(startPos.readValue(), endPos.readValue(), startControlPos.readValue(), endControlPos.readValue(), thick, color.readValue())
}
/**
* Draw lines sequence
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawLine(points: Vector2, pointsCount: Int, color: Color) {
DrawLineStrip(points.ptr, pointsCount, color.readValue())
}
/**
* Draw a [color]-filled circle
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawCircle(centerX: Int, centerY: Int, radius: Float, color: Color) {
DrawCircle(centerX, centerY, radius, color.readValue())
}
/**
* Draw a piece of a circle
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawCircle(center: Vector2, radius: Float, startAngle: Float, endAngle: Float, segments: Int, color: Color) {
DrawCircleSector(center.readValue(), radius, startAngle, endAngle, segments, color.readValue())
}
/**
* Draw circle sector outline
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawCircleSectorLines(center: Vector2, radius: Float, startAngle: Float, endAngle: Float, segments: Int, color: Color) {
DrawCircleSectorLines(center.readValue(), radius, startAngle, endAngle, segments, color.readValue())
}
/**
* Draw a gradient-filled circle
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawCircle(centerX: Int, centerY: Int, radius: Float, color1: Color, color2: Color) {
DrawCircleGradient(centerX, centerY, radius, color1.readValue(), color2.readValue())
}
/**
* Draw a [color]-filled circle ([Vector2] version)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawCircle(center: Vector2, radius: Float, color: Color) {
DrawCircleV(center.readValue(), radius, color.readValue())
}
/**
* Draw circle outline
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawCircleLines(centerX: Int, centerY: Int, radius: Float, color: Color) {
DrawCircleLines(centerX, centerY, radius, color.readValue())
}
/**
* Draw ellipse
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawEllipse(centerX: Int, centerY: Int, radiusH: Float, radiusV: Float, color: Color) {
DrawEllipse(centerX, centerY, radiusH, radiusV, color.readValue())
}
/**
* Draw ellipse outline
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawEllipseLines(centerX: Int, centerY: Int, radiusH: Float, radiusV: Float, color: Color) {
DrawEllipseLines(centerX, centerY, radiusH, radiusV, color.readValue())
}
/**
* Draw ring
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawRing(center: Vector2, innerRadius: Float, outerRadius: Float, startAngle: Float, endAngle: Float, segments: Int, color: Color) {
DrawRing(center.readValue(), innerRadius, outerRadius, startAngle, endAngle, segments, color.readValue())
}
/**
* Draw ring outline
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawRingLines(center: Vector2, innerRadius: Float, outerRadius: Float, startAngle: Float, endAngle: Float, segments: Int, color: Color) {
DrawRingLines(center.readValue(), innerRadius, outerRadius, startAngle, endAngle, segments, color.readValue())
}
/**
* Draw a [color]-filled [Rectangle]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawRectangle(posX: Int, posY: Int, width: Int, height: Int, color: Color) {
DrawRectangle(posX, posY, width, height, color.readValue())
}
/**
* Draw a [color]-filled [Rectangle]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawRectangle(rec: Rectangle, color: Color) {
DrawRectangleRec(rec.readValue(), color.readValue())
}
/**
* Draw a [color]-filled [Rectangle] with pro parameters
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawRectangle(rec: Rectangle, origin: Vector2, rotation: Float, color: Color) {
DrawRectanglePro(rec.readValue(), origin.readValue(), rotation, color.readValue())
}
/**
* Draw a [color]-filled [Rectangle] ([Vector2] version)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawRectangle(position: Vector2, size: Vector2, color: Color) {
DrawRectangleV(position.readValue(), size.readValue(), color.readValue())
}
/**
* Draw a vertical-gradient-filled [Rectangle]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawRectangleGradientV(posX: Int, posY: Int, width: Int, height: Int, color1: Color, color2: Color) {
DrawRectangleGradientV(posX, posY, width, height, color1.readValue(), color2.readValue())
}
/**
* Draw a horizontal-gradient-filled [Rectangle]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawRectangleGradientH(posX: Int, posY: Int, width: Int, height: Int, color1: Color, color2: Color) {
DrawRectangleGradientH(posX, posY, width, height, color1.readValue(), color2.readValue())
}
/**
* Draw a gradient-filled rectangle with custom vertex colors
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawRectangle(rec: Rectangle, col1: Color, col2: Color, col3: Color, col4: Color) {
DrawRectangleGradientEx(rec.readValue(), col1.readValue(), col2.readValue(), col3.readValue(), col4.readValue())
}
/**
* Draw [Rectangle] outline
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawRectangleLines(posX: Int, posY: Int, width: Int, height: Int, color: Color) {
DrawRectangleLines(posX, posY, width, height, color.readValue())
}
/**
* Draw [Rectangle] outline with extended parameters
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawRectangleLinesEx(rec: Rectangle, lineThick: Float, color: Color) {
DrawRectangleLinesEx(rec.readValue(), lineThick, color.readValue())
}
/**
* Draw [Rectangle] with rounded edges
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawRectangle(rec: Rectangle, roundness: Float, segments: Int, color: Color) {
DrawRectangleRounded(rec.readValue(), roundness, segments, color.readValue())
}
/**
* Draw [Rectangle] with rounded edges outline
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawRectangle(rec: Rectangle, roundness: Float, segments: Int, lineThick: Float, color: Color) {
DrawRectangleRoundedLines(rec.readValue(), roundness, segments, lineThick, color.readValue())
}
/**
* Draw a [color]-filled triangle (vertex in counter-clockwise order!)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawTriangle(v1: Vector2, v2: Vector2, v3: Vector2, color: Color) {
DrawTriangle(v1.readValue(), v2.readValue(), v3.readValue(), color.readValue())
}
/**
* Draw triangle outline (vertex in counter-clockwise order!)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawTriangleLines(v1: Vector2, v2: Vector2, v3: Vector2, color: Color) {
DrawTriangleLines(v1.readValue(), v2.readValue(), v3.readValue(), color.readValue())
}
/**
* Draw a triangle fan defined by [points] (first vertex is the center)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawTriangleFan(points: Vector2, pointsCount: Int ,color: Color) {
DrawTriangleFan(points.ptr, pointsCount, color.readValue())
}
/**
* Draw a triangle strip defined by [points]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawTriangleStrip(points: Vector2, pointCount: Int, color: Color) {
DrawTriangleStrip(points.ptr, pointCount, color.readValue())
}
/**
* Draw a regular polygon ([Vector2] version)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawPoly(center: Vector2, sides: Int, radius: Float, rotation: Float, color: Color) {
DrawPoly(center.readValue(), sides, radius, rotation, color.readValue())
}
/**
* Draw a polygon outline of n [sides]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawPolyLines(center: Vector2, sides: Int, radius: Float, rotation: Float, color: Color) {
DrawPolyLines(center.readValue(), sides, radius, rotation, color.readValue())
}
/**
* Draw a polygon outline of n [sides] with extended parameters
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawPolyLinesEx(center: Vector2, sides: Int, radius: Float, rotation: Float, lineThick: Float, color: Color) {
DrawPolyLinesEx(center.readValue(), sides, radius, rotation, lineThick, color.readValue())
}
//=======================================================//
// BASIC SHAPES COLLISION DETECTION FUNCTIONS
//=======================================================//
/**
* Check collision between two [Rectangle]
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun checkCollision(rec1: Rectangle, rec2: Rectangle) : Boolean {
return CheckCollisionRecs(rec1.readValue(), rec2.readValue())
}
/**
* Check collision between two circles
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun checkCollision(center1: Vector2, radius1: Float, center2: Vector2, radius2: Float) : Boolean {
return CheckCollisionCircles(center1.readValue(), radius1, center2.readValue(), radius2)
}
/**
* Check collision between circle and [Rectangle]
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun checkCollision(center: Vector2, radius: Float, rec: Rectangle) : Boolean {
return CheckCollisionCircleRec(center.readValue(), radius, rec.readValue())
}
/**
* Check if [point] is inside [Rectangle]
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun checkCollision(point: Vector2, rec: Rectangle) : Boolean {
return CheckCollisionPointRec(point.readValue(), rec.readValue())
}
/**
* Check if [point] is inside circle
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun checkCollision(point: Vector2, center: Vector2, radius: Float) : Boolean {
return CheckCollisionPointCircle(point.readValue(), center.readValue(), radius)
}
/**
* Check if [point] is inside a triangle
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun checkCollision(point: Vector2, p1: Vector2, p2: Vector2, p3: Vector2) : Boolean {
return CheckCollisionPointTriangle(point.readValue(), p1.readValue(), p2.readValue(), p3.readValue())
}
/**
* Check the collision between two lines defined by two points each, returns collision point by reference
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun checkCollision(startPos1: Vector2, endPos1: Vector2, startPos2: Vector2, endPos2: Vector2, collisionPoint: CValuesRef<Vector2>) : Boolean {
return CheckCollisionLines(startPos1.readValue(), endPos1.readValue(), startPos2.readValue(), endPos2.readValue(), collisionPoint)
}
/**
* Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold]
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun checkCollision(point: Vector2, p1: Vector2, p2: Vector2, threshold: Int) : Boolean {
return CheckCollisionPointLine(point.readValue(), p1.readValue(), p2.readValue(), threshold)
}
/**
* Get collision [Rectangle] for two rectangles collision
* @return [Rectangle]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getCollision(rec1: Rectangle, rec2: Rectangle) : Rectangle {
return GetCollisionRec(rec1.readValue(), rec2.readValue()).getPointer(MemScope()).pointed
}
/**
* Check if point is within a polygon described by array of vertices
* NOTE: Based on http://jeffreythompson.org/collision-detection/poly-point.php
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun checkCollisionPointPoly(point: Vector2, points: CValuesRef<Vector2>, pointCount: Int) : Boolean {
return CheckCollisionPointPoly(point.readValue(), points, pointCount)
}
/**
* Check if point is inside rectangle
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun checkCollisionPointRec(point: Vector2, rec: Rectangle) : Boolean {
return CheckCollisionPointRec(point.readValue(), rec.readValue())
}

View file

@ -0,0 +1,222 @@
package kaylibkit.kText
import kaylibc.*
import kotlinx.cinterop.*
// -- Module: kText
//=======================================================//
// FONT LOADING FUNCTIONS
//=======================================================//
/**
* Get the default [Font]
* @return [Font]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getFontDefault() : Font {
return GetFontDefault().getPointer(MemScope()).pointed
}
/**
* Load [Font] from file into GPU memory (VRAM)
* @return [Font]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadFont(fileName: String) : Font {
return LoadFont(fileName).getPointer(MemScope()).pointed
}
/**
* Load font from file with extended parameters
* @return [Font]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadFont(fileName: String, fontSize: Int, fontChars: IntVar, glyphCount: Int) : Font {
return LoadFontEx(fileName, fontSize, fontChars.ptr, glyphCount).getPointer(MemScope()).pointed
}
/**
* Load [Font] from Image (XNA style)
* @return [Font]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadFontFromImage(image: Image, key: Color, firstChar: Int) : Font {
return LoadFontFromImage(image.readValue(), key.readValue(), firstChar).getPointer(MemScope()).pointed
}
/**
* Load [Font] from memory buffer, fileType refers to extension: i.e. ".ttf"
* @return [Font]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadFontFromMemory(fileType: String, fileData: UByteVar, dataSize: Int, fontSize: Int, fontChars: IntVar, glyphCount: Int) : Font {
return LoadFontFromMemory(fileType, fileData.ptr, dataSize, fontSize, fontChars.ptr, glyphCount).getPointer(MemScope()).pointed
}
/**
* Load font data for further use
* @return [GlyphInfo]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadFontData(fileData: UByteVar, dataSize: Int, fontSize: Int, fontChars: IntVar, glyphCount: Int, type: kaylibkit.kEnums.FontType) : GlyphInfo? {
return LoadFontData(fileData.ptr, dataSize, fontSize, fontChars.ptr, glyphCount, type.value)?.getPointer(MemScope())?.pointed
}
/**
* Generate [Image] font atlas using chars info
* @return [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun genImageFontAtlas(chars: GlyphInfo, recs: CPointerVar<Rectangle>, glyphCount: Int, fontSize: Int, padding: Int, packMethod: Int) : Image {
return GenImageFontAtlas(chars.ptr, recs.ptr, glyphCount, fontSize, padding, packMethod).getPointer(MemScope()).pointed
}
/**
* Unload font chars info data (RAM)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun unloadFontData(chars: GlyphInfo, glyphCount: Int) {
UnloadFontData(chars.ptr, glyphCount)
}
/**
* Unload Font from GPU memory (VRAM)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun unloadFont(font: Font) {
UnloadFont(font.readValue())
}
/**
* Export font as code file, returns true on success
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun exportFontAsCode(font: Font, fileName: String) : Boolean {
return ExportFontAsCode(font.readValue(), fileName)
}
//=======================================================//
// TEXT DRAWING FUNCTIONS
//=======================================================//
/**
* Draw current FPS
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawFPS(posX: Int, posY: Int) {
DrawFPS(posX, posY)
}
/**
* Draw current FPS
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawText(text: String, posX: Int, posY: Int, fontSize: Int, color: Color) {
DrawText(text, posX, posY, fontSize, color.readValue())
}
/**
* Draw text using font and additional parameters
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawText(font: Font, text: String, position: Vector2, fontSize: Float, spacing: Float, tint: Color) {
DrawTextEx(font.readValue(), text, position.readValue(), fontSize, spacing, tint.readValue())
}
/**
* Draw text using Font and pro parameters (rotation)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawText(font: Font, text: String, position: Vector2, origin: Vector2, rotation: Float, fontSize: Float, spacing: Float, tint: Color) {
DrawTextPro(font.readValue(), text, position.readValue(), origin.readValue(), rotation, fontSize, spacing, tint.readValue())
}
/**
* Draw one character (codepoint)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawText(font: Font, codepoint: Int, position: Vector2, fontSize: Float, tint: Color) {
DrawTextCodepoint(font.readValue(), codepoint, position.readValue(), fontSize, tint.readValue())
}
/**
* Draw one character (codepoint)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawText(font: Font, codepoint: IntVar, count: Int, position: Vector2, fontSize: Float, spacing: Float, tint: Color) {
DrawTextCodepoints(font.readValue(), codepoint.ptr, count, position.readValue(), fontSize, spacing, tint.readValue())
}
//=======================================================//
// TEXT FONT INFO FUNCTIONS
//=======================================================//
/**
* Measure string width for default [Font]
* @return [Int]
*/
inline fun measureText(text: String, fontSize: Int) : Int {
return MeasureText(text, fontSize)
}
/**
* Measure string size for [Font]
* @return [Vector2]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun measureText(font: Font, text: String, fontSize: Float, spacing: Float) : Vector2 {
return MeasureTextEx(font.readValue(), text, fontSize, spacing).getPointer(MemScope()).pointed
}
/**
* Get index position for a unicode character on font
* @return [Int]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getGlyphIndex(font: Font, codepoint: Int) : Int {
return GetGlyphIndex(font.readValue(), codepoint)
}
/**
* Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found
* @return [GlyphInfo]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getGlyphInfo(font: Font, codepoint: Int) : GlyphInfo {
return GetGlyphInfo(font.readValue(), codepoint).getPointer(MemScope()).pointed
}
/**
* Get glyph [Rectangle] in [font] atlas for a codepoint (unicode character), fallback to '?' if not found
* @return [Rectangle]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getGlyphAtlasRec(font: Font, codepoint: Int) : Rectangle {
return GetGlyphAtlasRec(font.readValue(), codepoint).getPointer(MemScope()).pointed
}
/**
* Check if a font is ready
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun isFontReady(font: Font) : Boolean {
return IsFontReady(font.readValue())
}
//=======================================================//
// TEXT STRINGS MANAGEMENT FUNCTIONS
//=======================================================//
// Internal Note: There is no need to bind other related text strings management functions. Use existing Kotlin API.
/**
* Get Pascal case notation version of provided string
*/
@OptIn(ExperimentalForeignApi::class)
inline fun textToPascal(text: String) : String {
return TextToPascal(text)?.toKString() ?: "WARNING: Unable to return requested string."
}

View file

@ -0,0 +1,235 @@
package kaylibkit.kTextures
import kaylibc.*
import kotlinx.cinterop.*
// -- Module: kTextures
//=======================================================//
// TEXTURE LOADING FUNCTIONS
//=======================================================//
/**
* Load [Texture] from file into GPU memory (VRAM)
* @return [Texture2D]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadTexture(fileName: String) : Texture2D {
return LoadTexture(fileName).getPointer(MemScope()).pointed
}
/**
* Load [Texture] from image data
* @return [Texture2D]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadTextureFromImage(image: Image) : Texture2D {
return LoadTextureFromImage(image.readValue()).getPointer(MemScope()).pointed
}
/**
* Load cubemap from image, multiple image cubemap layouts supported
* @return [TextureCubemap]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadTextureCubemap(image: Image, layout: CubemapLayout) : TextureCubemap {
return LoadTextureCubemap(image.readValue(), layout.toInt()).getPointer(MemScope()).pointed
}
/**
* Load [RenderTexture2D] for rendering (framebuffer)
* @return [RenderTexture2D]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadRenderTexture(width: Int, height: Int) : RenderTexture2D {
return LoadRenderTexture(width, height).getPointer(MemScope()).pointed
}
/**
* Unload [Texture] from GPU memory (VRAM)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun unloadTexture(texture: Texture2D) {
return UnloadTexture(texture.readValue())
}
/**
* Unload render [RenderTexture2D] from GPU memory (VRAM)
*/
@OptIn(ExperimentalForeignApi::class)
inline fun unloadRenderTexture(target: RenderTexture2D) {
return UnloadRenderTexture(target.readValue())
}
/**
* Update GPU [Texture] with new data
*/
@OptIn(ExperimentalForeignApi::class)
inline fun updateTexture(texture: Texture2D, pixels: COpaquePointer) {
UpdateTexture(texture.readValue(), pixels)
}
/**
* Update GPU [Texture] rectangle with new data
*/
@OptIn(ExperimentalForeignApi::class)
inline fun updateTextureRec(texture: Texture2D, rec: Rectangle, pixels: COpaquePointer) {
UpdateTextureRec(texture.readValue(), rec.readValue(), pixels)
}
//=======================================================//
// TEXTURE CONFIGURATION FUNCTIONS
//=======================================================//
/**
* Generate GPU mipmaps for a [Texture]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun genTextureMipmaps(texture: Texture2D) {
GenTextureMipmaps(texture.ptr)
}
/**
* Set [Texture] scaling filter mode
*/
@OptIn(ExperimentalForeignApi::class)
inline fun setTextureFilter(texture: Texture2D, filter: kaylibkit.kEnums.TextureFilter) {
SetTextureFilter(texture.readValue(), filter.value)
}
/**
* Set [Texture] wrapping mode
*/
@OptIn(ExperimentalForeignApi::class)
inline fun setTextureWrap(texture: Texture2D, wrap: kaylibkit.kEnums.TextureWrap) {
SetTextureWrap(texture.readValue(), wrap.value)
}
//=======================================================//
// TEXTURE DRAWING FUNCTIONS
//=======================================================//
/**
* Draw a [Texture2D]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawTexture(texture: Texture2D, posX: Int, posY: Int, tint: Color) {
return DrawTexture(texture.readValue(), posX, posY, tint.readValue())
}
/**
* Draw a [Texture2D] with position defined as Vector2
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawTextureV(texture: Texture2D, position: Vector2, tint: Color) {
DrawTextureV(texture.readValue(), position.readValue(), tint.readValue())
}
/**
* Draw a [Texture2D] with extended parameters
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawTextureEx(texture: Texture2D, position: Vector2, rotation: Float, scale: Float, tint: Color) {
DrawTextureEx(texture.readValue(), position.readValue(), rotation, scale, tint.readValue())
}
/**
* Draw a part of a [Texture] defined by a rectangle
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawTextureRec(texture: Texture2D, source: Rectangle, position: Vector2, tint: Color) {
DrawTextureRec(texture.readValue(), source.readValue(), position.readValue(), tint.readValue())
}
/**
* Draw a part of a [Texture] defined by a rectangle with 'pro' parameters
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawTexturePro(texture: Texture2D, source: Rectangle, dest: Rectangle, origin: Vector2, rotation: Float, tint: Color) {
DrawTexturePro(texture.readValue(), source.readValue(), dest.readValue(), origin.readValue(), rotation, tint.readValue())
}
/**
* Draws a [Texture] (or part of it) that stretches or shrinks nicely
*/
@OptIn(ExperimentalForeignApi::class)
inline fun drawTextureNPatch(texture: Texture2D, nPatchInfo: NPatchInfo, dest: Rectangle, origin: Vector2, rotation: Float, tint: Color) {
DrawTextureNPatch(texture.readValue(), nPatchInfo.readValue(), dest.readValue(), origin.readValue(), rotation, tint.readValue())
}
/**
* Get [Color] with brightness correction, brightness factor goes from -1.0f to 1.0f
* @return [Color]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun colorBrightness(color: Color, factor: Float) : Color {
return ColorBrightness(color.readValue(), factor).getPointer(MemScope()).pointed
}
/**
* Get color multiplied with another [Color]
* @return [Color]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun colorTint(color: Color, tint: Color) : Color {
return ColorTint(color.readValue(), tint.readValue()).getPointer(MemScope()).pointed
}
/**
* Get [Color] with contrast correction
* NOTE: Contrast values between -1.0f and 1.0f
* @return [Color]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun colorContrast(color: Color, contrast: Float) : Color {
return ColorContrast(color.readValue(), contrast).getPointer(MemScope()).pointed
}
/**
* Generate image: perlin noise
* @return [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun genImagePerlinNoise(width: Int, height: Int, offsetX: Int, offsetY: Int, scale: Float) : Image {
return GenImagePerlinNoise(width, height, offsetX, offsetY, scale).getPointer(MemScope()).pointed
}
/**
* Generate image: grayscale image from text data
* @return [Image]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun genImageText(width: Int, height: Int, text: String) : Image {
return GenImageText(width, height, text).getPointer(MemScope()).pointed
}
/**
* Check if a [Texture] is ready
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun isTextureReady(texture: Texture2D) : Boolean {
return IsTextureReady(texture.readValue())
}
/**
* Check if a [RenderTexture] is ready
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun isRenderTextureReady(target: RenderTexture2D) : Boolean {
return IsRenderTextureReady(target.readValue())
}
/**
* Set value of a Texture with another provided value of same type.
* This is useful when dealing with cinterop CStruct that holds nested CStructs which are marked as immutable (val).
* NOTE: While the CStruct is immutable itself, the inner members of that CStruct are mutable.
*/
inline fun Texture.set(other: Texture) {
this.format = other.format
this.height = other.height
this.id = other.id
this.mipmaps = other.mipmaps
this.width = other.width
}

View file

@ -0,0 +1,440 @@
package kaylibkit.kTypes
import kaylibkit.kMath.kVector2
import kaylibkit.kMath.kVector3
import kaylibkit.kMath.set
import kaylibkit.kShapes.set
import kaylibkit.kImage.set
import kaylibkit.kTextures.set
import kaylibc.*
import kotlinx.cinterop.*
// -- Module: kTypes
//=======================================================//
// Type Constructors
//=======================================================//
/**
* Constructor function for [Color].
* Important to note that this uses `MemScope()` by default.
* @return [Color]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kColor(r: UByte = 0U, g: UByte = 0U, b: UByte = 0U, a: UByte = 0U, allocator: AutofreeScope = MemScope()) : Color {
return allocator.alloc<Color> {
this.r = r
this.g = g
this.b = b
this.a = a
}
}
/**
* Set value of a [Color] with another provided value of same type.
* This is useful when dealing with cinterop CStruct that holds nested CStructs which are marked as immutable (val).
* NOTE: While the CStruct is immutable itself, the inner members of that CStruct are mutable.
*/
inline fun Color.set(other: Color) {
this.r = other.r
this.g = other.g
this.b = other.b
this.a = other.a
}
/**
* Constructor function for [Texture].
* Important to note that this uses `MemScope()` by default.
* @return [Texture]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kTexture(id: UInt, width:Int, height: Int, mipmaps: Int, format: Int, allocator: AutofreeScope = MemScope()) : Texture {
return allocator.alloc<Texture> {
this.id = id
this.width = width
this.height = height
this.mipmaps = mipmaps
this.format = format
}
}
/**
* Constructor function for RenderTexture].
* Important to note that this uses `MemScope()` by default.
* @return [RenderTexture]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kRenderTexture(id: UInt, texture: Texture, depth: Texture, allocator: AutofreeScope = MemScope()) : RenderTexture {
return allocator.alloc<RenderTexture> {
this.id = id
this.texture.set(texture)
this.depth.set(depth)
}
}
/**
* Constructor function for [NPatchInfo].
* Important to note that this uses `MemScope()` by default.
* @return [NPatchInfo]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kNPatchInfo(source: Rectangle, left: Int, top: Int, right: Int, bottom: Int, layout: Int, allocator: AutofreeScope = MemScope()) : NPatchInfo {
return allocator.alloc<NPatchInfo> {
this.source.set(source)
this.left = left
this.top = top
this.right = right
this.bottom = bottom
this.layout = layout
}
}
/**
* Constructor function for [GlyphInfo].
* Important to note that this uses `MemScope()` by default.
* @return [GlyphInfo]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kGlyphInfo(value: Int, offsetX: Int, offsetY: Int, advanceX: Int, image: Image, allocator: AutofreeScope = MemScope()) : GlyphInfo {
return allocator.alloc<GlyphInfo> {
this.value = value
this.offsetX = offsetX
this.offsetY = offsetY
this.advanceX = advanceX
this.image.set(image)
}
}
/**
* Constructor function for [Font].
* Important to note that this uses `MemScope()` by default.
* @return [Font]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kFont(baseSize: Int, glyphCount: Int, glyphPadding: Int, texture: Texture, recs: Rectangle, glyphs: GlyphInfo, allocator: AutofreeScope = MemScope()) : Font {
return allocator.alloc<Font> {
this.baseSize = baseSize
this.glyphCount = glyphCount
this.glyphPadding = glyphPadding
this.texture.set(texture)
this.recs = recs.ptr
this.glyphs = glyphs.ptr
}
}
/**
* Constructor function for [Camera3D].
* Important to note that this uses `MemScope()` by default.
* @return [Camera3D]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kCamera3D(position: Vector3 = kVector3(), target: Vector3 = kVector3(), up: Vector3 = kVector3(), fovy: Float = 0F, projection: kaylibkit.kEnums.CameraProjection, allocator: AutofreeScope = MemScope()) : Camera3D {
return allocator.alloc<Camera3D> {
this.position.set(position)
this.target.set(target)
this.up.set(up)
this.fovy = fovy
this.projection = projection.value
}
}
/**
* Constructor function for [Camera2D].
* Important to note that this uses `MemScope()` by default.
* @return [Camera2D]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kCamera2D(offset: Vector2 = kVector2(), target: Vector2 = kVector2(), rotation: Float = 0F, zoom: Float = 0F, allocator: AutofreeScope = MemScope()) : Camera2D {
return allocator.alloc<Camera2D> {
this.offset.set(offset)
this.target.set(target)
this.rotation = rotation
this.zoom = zoom
}
}
/**
* Constructor function for [Mesh].
* Important to note that this uses `MemScope()` by default.
* @return [Mesh]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kMesh(vertexCount: Int, triangleCount: Int, vertices: FloatVar, texcoords: FloatVar, texcoords2: FloatVar, normals: FloatVar, tangents: FloatVar, colors: UByteVar, indices: UShortVar, animVertices: FloatVar, animNormals: FloatVar, boneIds: UByteVar, boneWeights: FloatVar, vaoId: UInt, vboId: UIntVar , allocator: AutofreeScope = MemScope()) : Mesh {
return allocator.alloc<Mesh> {
this.vertexCount = vertexCount
this.triangleCount = triangleCount
this.vertices = vertices.ptr
this.texcoords = texcoords.ptr
this.texcoords2 = texcoords2.ptr
this.normals = normals.ptr
this.tangents = tangents.ptr
this.colors = colors.ptr
this.indices = indices.ptr
this.animVertices = animVertices.ptr
this.animNormals = animNormals.ptr
this.boneIds = boneIds.ptr
this.boneWeights = boneWeights.ptr
this.vaoId = vaoId
this.vboId = vboId.ptr
}
}
/**
* Set value of a Color with another provided value of same type.
* This is useful when dealing with cinterop CStruct that holds nested CStructs which are marked as immutable (val).
* NOTE: While the CStruct is immutable itself, the inner members of that CStruct are mutable.
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Mesh.set(other: Mesh) {
this.vertexCount = other.vertexCount
this.triangleCount = other.triangleCount
this.vertices = other.vertices
this.texcoords = other.texcoords
this.texcoords2 = other.texcoords2
this.normals = other.normals
this.tangents = other.tangents
this.colors = other.colors
this.indices = other.indices
this.animNormals = other.animNormals
this.animVertices = other.animVertices
this.boneIds = other.boneIds
this.boneWeights = other.boneWeights
this.vaoId = other.vaoId
this.vboId = other.vboId
}
/**
* Constructor function for [Shader].
* Important to note that this uses `MemScope(`) by default.
* @return [Shader]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kShader(id: UInt, locs: IntVar, allocator: AutofreeScope = MemScope()) : Shader {
return allocator.alloc<Shader> {
this.id = id
this.locs = locs.ptr
}
}
/**
* Set value of a Shader with another provided value of same type.
* This is useful when dealing with cinterop CStruct that holds nested CStructs which are marked as immutable (val).
* NOTE: While the CStruct is immutable itself, the inner members of that CStruct are mutable.
*/
@OptIn(ExperimentalForeignApi::class)
inline fun Shader.set(other: Shader) {
this.id = other.id
this.locs = other.locs
}
/**
* Constructor function for [MaterialMap].
* Important to note that this uses `MemScope()` by default.
* @return [MaterialMap]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kMaterialMap(texture: Texture2D, color: Color, value: Float, allocator: AutofreeScope = MemScope()) : MaterialMap {
return allocator.alloc<MaterialMap> {
this.texture.set(texture)
this.color.set(color)
this.value = value
}
}
/**
* Constructor function for [Material].
* Important to note that this uses `MemScope()` by default.
* @return [Material]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kMaterial(shader: Shader, maps: MaterialMap, params: CArrayPointer<FloatVar>, allocator: AutofreeScope = MemScope()) : Material {
return MaterialConstructor(shader.readValue(), maps.ptr, params).getPointer(allocator).pointed
}
/**
* Constructor function for [Transform].
* Important to note that this uses `MemScope()` by default.
* @return [Transform]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kTransform(translation: Vector3, rotation: Quaternion, scale: Vector3, allocator: AutofreeScope = MemScope()) : Transform {
return allocator.alloc<Transform> {
this.translation.set(translation)
this.rotation.set(rotation)
this.scale.set(scale)
}
}
/**
* Constructor function for [BoneInfo].
* Important to note that this uses `MemScope()` by default.
* @return [BoneInfo]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kBoneInfo(name: CArrayPointer<ByteVar>, parent: Int, allocator: AutofreeScope = MemScope()) : BoneInfo {
return BoneInfoConstructor(name, parent).getPointer(allocator).pointed
}
/**
* Constructor function for [Model].
* Important to note that this uses `MemScope()` by default.
* @return [Model]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kModel(transform: Matrix, meshCount: Int, materialCount: Int, meshes: Mesh, materials: Material, meshMaterial: IntVar, boneCount: Int, bones: BoneInfo, bindPose: Transform, allocator: AutofreeScope = MemScope()) : Model {
return allocator.alloc<Model> {
this.transform.set(transform)
this.meshCount = meshCount
this.materialCount = materialCount
this.meshes = meshes.ptr
this.materials = materials.ptr
this.meshMaterial = meshMaterial.ptr
this.boneCount = boneCount
this.bones = bones.ptr
this.bindPose = bindPose.ptr
}
}
/**
* Constructor function for [ModelAnimation].
* Important to note that this uses `MemScope()` by default.
* @return [ModelAnimation]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kModelAnimation(boneCount: Int, frameCount: Int, bones: BoneInfo, framePoses: CPointer<CPointerVar<Transform>>, allocator: AutofreeScope = MemScope()) : ModelAnimation {
return allocator.alloc<ModelAnimation> {
this.boneCount = boneCount
this.frameCount = frameCount
this.bones = bones.ptr
this.framePoses = framePoses
}
}
/**
* Constructor function for [Ray].
* Important to note that this uses `MemScope()` by default.
* @return [Ray]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kRay(position: Vector3 = kVector3(), direction: Vector3 = kVector3(), allocator: AutofreeScope = MemScope()) : Ray {
return allocator.alloc<Ray> {
this.position.set(position)
this.direction.set(direction)
}
}
/**
* Constructor function for [RayCollision].
* Important to note that this uses `MemScope()` by default.
* @return [RayCollision]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kRayCollision(hit: Boolean = false, distance: Float = 0F, point: Vector3 = kVector3(), normal: Vector3 = kVector3(), allocator: AutofreeScope = MemScope()) : RayCollision {
return allocator.alloc<RayCollision> {
this.hit = hit
this.distance = distance
this.point.set(point)
this.normal.set(normal)
}
}
/**
* Constructor function for [BoundingBox].
* Important to note that this uses `MemScope()` by default.
* @return [BoundingBox]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kBoundingBox(min: Vector3 = kVector3(), max: Vector3 = kVector3(), allocator: AutofreeScope = MemScope()) : BoundingBox {
return allocator.alloc<BoundingBox> {
this.min.set(min)
this.max.set(max)
}
}
/**
* Constructor function for [Wave].
* Important to note that this uses `MemScope()` by default.
* @return [Wave]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kWave(frameCount: UInt, sampleRate: UInt, sampleSize: UInt, channels: UInt, data: COpaquePointer?, allocator: AutofreeScope = MemScope()) : Wave {
return allocator.alloc<Wave> {
this.frameCount = frameCount
this.sampleRate = sampleRate
this.sampleSize = sampleSize
this.channels = channels
this.data = data
}
}
/**
* Constructor function for [AudioStream].
* Important to note that this uses `MemScope()` by default.
* @return [AudioStream]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kAudioStream(buffer: rAudioBuffer, processor: rAudioProcessor, sampleRate: UInt, sampleSize: UInt, channels: UInt, allocator: AutofreeScope = MemScope()) : AudioStream {
return allocator.alloc<AudioStream> {
this.buffer = buffer.ptr
this.processor = processor.ptr
this.sampleRate = sampleRate
this.sampleSize = sampleSize
this.channels = channels
}
}
/**
* Constructor function for [Sound].
* Important to note that this uses `MemScope()` by default.
* @return [Sound]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kSound(stream: AudioStream, frameCount: UInt, allocator: AutofreeScope = MemScope()) : Sound {
return SoundConstructor(stream.readValue(), frameCount).getPointer(allocator).pointed
}
/**
* Constructor function for [Music].
* Important to note that this uses `MemScope()` by default.
* @return [Music]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kMusic(stream: AudioStream, frameCount: UInt, looping: Boolean, ctxType: Int, data: COpaquePointer?, allocator: AutofreeScope = MemScope()) : Music {
return MusicConstructor(stream.readValue(), frameCount, looping, ctxType, data).getPointer(allocator).pointed
}
/**
* Constructor function for [VrDeviceInfo].
* Important to note that this uses `MemScope()` by default.
* @return [VrDeviceInfo]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kVrDeviceInfo(hResolution: Int, vResolution: Int, hScreenSize: Float, vScreenSize: Float, vScreenCenter: Float, eyeToScreenDistance: Float, lensSeparationDistance: Float, interpupillaryDistance: Float, lensDistortionValues: CArrayPointer<FloatVar>, chromaAbCorrection: CArrayPointer<FloatVar>, allocator: AutofreeScope = MemScope()) : VrDeviceInfo {
return VrDeviceInfoConstructor(hResolution, vResolution, hScreenSize, vScreenSize, vScreenCenter, eyeToScreenDistance, lensSeparationDistance, interpupillaryDistance, lensDistortionValues, chromaAbCorrection).getPointer(allocator).pointed
}
/**
* Constructor function for [VrStereoConfig].
* Important to note that this uses `MemScope()` by default.
* @return [VrStereoConfig]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kVrStereoConfig(projection: CArrayPointer<Matrix>, viewOffset: CArrayPointer<Matrix>, leftLensCenter: CArrayPointer<FloatVar>, rightLensCenter: CArrayPointer<FloatVar>, leftScreenCenter: CArrayPointer<FloatVar>, rightScreenCenter: CArrayPointer<FloatVar>, scale: CArrayPointer<FloatVar>, scaleIn: CArrayPointer<FloatVar>, allocator: AutofreeScope = MemScope()) : VrStereoConfig {
return VrStereoConfigConstructor(projection, viewOffset, leftLensCenter, rightLensCenter, leftScreenCenter, rightScreenCenter, scale, scaleIn).getPointer(allocator).pointed
}
/**
* Constructor function for [FilePathList].
* Important to note that this uses `MemScope()` by default.
* @return [FilePathList]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun kFilePathList(capacity: UInt, count: UInt, paths: CPointer<CPointerVar<ByteVar>>, allocator: AutofreeScope = MemScope()) : FilePathList {
return allocator.alloc<FilePathList> {
this.capacity = capacity
this.count = count
this.paths = paths
}
}

View file

@ -0,0 +1,201 @@
package kaylibkit.kUtils
import kaylibc.*
import kotlinx.cinterop.*
// -- Module: kUtils
//=======================================================//
// Raylib Utility Functions - Don't belong anywhere else
//=======================================================//
/**
* Encode text codepoint into UTF-8 text
* REQUIRES: memcpy()
* WARNING: Allocated memory must be manually freed
* @return [ByteVar]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun loadUTF8(codepoints: IntVar, length: Int): ByteVar? {
return LoadUTF8(codepoints.ptr, length)?.getPointer(MemScope())?.pointed
}
/**
* Unload UTF-8 text encoded from codepoints array
*/
@OptIn(ExperimentalForeignApi::class)
inline fun unloadUTF8(text: String) {
UnloadUTF8(text.cstr)
}
/**
* Unload codepoints data from memory
*/
@OptIn(ExperimentalForeignApi::class)
inline fun unloadCodepoints(codepoints: IntVar) {
UnloadCodepoints(codepoints.ptr)
}
/**
* Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure
* @return [Int]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getCodepoint(text: String, codepointSize: IntVar) : Int {
return GetCodepoint(text, codepointSize.ptr)
}
/**
* Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure
* @return [Int]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getCodepointNext(text: String, codepointSize: IntVar) : Int {
return GetCodepointNext(text, codepointSize.ptr)
}
/**
* Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure
* @return [Int]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getCodepointPrevious(text: String, codepointSize: IntVar) : Int {
return GetCodepointPrevious(text, codepointSize.ptr)
}
/**
* Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure
*/
@OptIn(ExperimentalForeignApi::class)
inline fun codepointToUTF8(codepoint: Int, utf8Size: IntVar) : ByteVar? {
return CodepointToUTF8(codepoint, utf8Size.ptr)?.getPointer(MemScope())?.pointed
}
//=======================================================//
// COLOR/PIXEL FUNCTIONS
//=======================================================//
/**
* Get [Color] with alpha applied, alpha goes from 0.0f to 1.0f
* @return [Color]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun fade(color: Color, alpha: Float) : Color {
return Fade(color.readValue(), alpha).getPointer(MemScope()).pointed
}
/**
* Get hexadecimal value for a [Color]
* @return [Int]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun colorToInt(color: Color) : Int {
return ColorToInt(color.readValue())
}
/**
* Get [Color] normalized as float [0..1]
* @return [Vector4]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun colorNormalize(color: Color) : Vector4 {
return ColorNormalize(color.readValue()).getPointer(MemScope()).pointed
}
/**
* Get [Color] from normalized values [0..1]
* @return [Color]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun colorFromNormalized(normalized: Vector4) : Color {
return ColorFromNormalized(normalized.readValue()) .getPointer(MemScope()).pointed
}
/**
* Get HSV values for a [Color], hue [0..360], saturation/value [0..1]
* @return [Vector3]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun colorToHSV(color: Color) : Vector3 {
return ColorToHSV(color.readValue()).getPointer(MemScope()).pointed
}
/**
* Get a [Color] from HSV values, hue [0..360], saturation/value [0..1]
* @return [Color]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun colorFromHSV(hue: Float, saturation: Float, value: Float) : Color {
return ColorFromHSV(hue, saturation, value).getPointer(MemScope()).pointed
}
/**
* Get [Color] with alpha applied, alpha goes from .0F to 1.0F
* @return [Color]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun colorAlpha(color: Color, alpha: Float) : Color {
return ColorAlpha(color.readValue(), alpha).getPointer(MemScope()).pointed
}
/**
* Get src alpha-blended into dst [Color] with tint
* @return [Color]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun colorAlphaBlend(dst: Color, src: Color, tint: Color) : Color {
return ColorAlphaBlend(dst.readValue(), src.readValue(), tint.readValue()).getPointer(MemScope()).pointed
}
/**
* Get [Color] structure from hexadecimal value
* @return [Color]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getColor(hexValue: UInt) : Color {
return GetColor(hexValue).getPointer(MemScope()).pointed
}
/**
* Get [Color] from a source pixel pointer of certain format
* @return [Color]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getPixelColor(srcPtr: COpaquePointer, format: kaylibkit.kEnums.PixelFormat) : Color {
return GetPixelColor(srcPtr, format.value).getPointer(MemScope()).pointed
}
/**
* Set [Color] formatted into destination pixel pointer
*/
@OptIn(ExperimentalForeignApi::class)
inline fun setPixelColor(dstPtr: COpaquePointer, color: Color, format: kaylibkit.kEnums.PixelFormat) {
return SetPixelColor(dstPtr, color.readValue(), format.value)
}
/**
* Get pixel data size in bytes for certain format
* @return [Int]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun getPixelDataSize(width: Int, height: Int, format: kaylibkit.kEnums.PixelFormat) : Int {
return GetPixelDataSize(width, height, format.value)
}
/**
* Checks if shader is ready
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun isShaderReady(shader: Shader) : Boolean {
return IsShaderReady(shader.readValue())
}
/**
* Check if a material is ready
* @return [Boolean]
*/
@OptIn(ExperimentalForeignApi::class)
inline fun isMaterialReady(material: Material) : Boolean {
return IsMaterialReady(material.readValue())
}

View file

@ -0,0 +1,9 @@
package kaylibkit.kCore
import kaylibc.TraceLogCallback
import kotlinx.cinterop.ExperimentalForeignApi
@OptIn(ExperimentalForeignApi::class)
actual fun setTraceLogCallbackInternal(callback: TraceLogCallback) {
kaylibc.SetTraceLogCallback(callback)
}

View file

@ -0,0 +1,8 @@
package kaylibkit.kCore
import kotlinx.cinterop.ExperimentalForeignApi
@OptIn(ExperimentalForeignApi::class)
actual fun setTraceLogCallbackInternal(callback: TraceLogCallback) {
kaylibc.SetTraceLogCallback(callback)
}

View file

@ -0,0 +1,8 @@
package kaylibkit.kCore
import kotlinx.cinterop.ExperimentalForeignApi
@OptIn(ExperimentalForeignApi::class)
actual fun setTraceLogCallbackInternal(callback: TraceLogCallback) {
kaylibc.SetTraceLogCallback(callback)
}

View file

@ -0,0 +1,66 @@
LIB_DIR := $(CURDIR)/lib
INC_DIR := $(CURDIR)/include
LIB_LINUX := $(CURDIR)/lib/linux
LIB_OSX := $(CURDIR)/lib/osx
LIB_MINGW := $(CURDIR)/lib/mingw
VERSION ?= 4.5.0
URL := https://github.com/raysan5/raylib/releases/download/
ARCHIVE_LINUX := raylib-$(VERSION)_linux_amd64.tar.gz
ARCHIVE_OSX := raylib-$(VERSION)_macos.tar.gz
ARCHIVE_MINGW := raylib-$(VERSION)_win64_mingw-w64.zip
ARCHIVE_LINUX_EXT := raylib-$(VERSION)_linux_amd64
ARCHIVE_OSX_EXT := raylib-$(VERSION)_macos
ARCHIVE_MINGW_EXT := raylib-$(VERSION)_win64_mingw-w64
HEADER_NAMES := raylib.h raymath.h rcamera.h
.PHONY: all
all: download extract download_headers
.PHONY: download
download:
@echo "Downloading Raylib files..."
@wget -q $(URL)$(VERSION)/$(ARCHIVE_LINUX)
@wget -q $(URL)$(VERSION)/$(ARCHIVE_OSX)
@wget -q $(URL)$(VERSION)/$(ARCHIVE_MINGW)
.PHONY: extract
extract:
@echo "Extracting Raylib files..."
@tar -C $(LIB_LINUX) -xf $(CURDIR)/$(ARCHIVE_LINUX) --strip-components=2 $(ARCHIVE_LINUX_EXT)/lib/libraylib.a
@tar -C $(LIB_OSX) -xf $(CURDIR)/$(ARCHIVE_OSX) --strip-components=2 $(ARCHIVE_OSX_EXT)/lib/libraylib.a
@unzip -q -j -d $(LIB_MINGW) $(CURDIR)/$(ARCHIVE_MINGW) $(ARCHIVE_MINGW_EXT)/lib/libraylib.a
@unzip -q -j -d $(LIB_MINGW) $(CURDIR)/$(ARCHIVE_MINGW) $(ARCHIVE_MINGW_EXT)/lib/libraylibdll.a
@echo "Cleaning up archive files..."
@rm -rf $(CURDIR)/*.tar.gz $(CURDIR)/*.zip
.PHONY: download_headers
download_headers:
@echo "Downloading header files..."
@for header in $(HEADER_NAMES); do \
wget -q https://raw.githubusercontent.com/raysan5/raylib/18a36b3e066f5743757cfa9ecbe784bbe20d529e/src/$$header -P $(INC_DIR); \
done
.PHONY: clean
clean:
@echo "Cleaning up static libraries..."
@if [ "$(OS)" == "Windows_NT" ]; then \
del /Q $(LIB_LINUX)\libraylib.a; \
del /Q $(LIB_OSX)\libraylib.a; \
del /Q $(LIB_MINGW)\libraylib.a; \
del /Q $(LIB_MINGW)\libraylibdll.a; \
del /Q $(INC_DIR)\raylib.h; \
del /Q $(INC_DIR)\rcamera.h; \
del /Q $(INC_DIR)\raymath.h; \
else \
rm -f $(LIB_LINUX)/libraylib.a; \
rm -f $(LIB_OSX)/libraylib.a; \
rm -f $(LIB_MINGW)/libraylib.a; \
rm -f $(LIB_MINGW)/libraylibdll.a; \
fi
@echo "Cleaning up header files..."
@for header in $(HEADER_NAMES); do \
rm -f $(INC_DIR)/$$header; \
done

View file

@ -0,0 +1,73 @@
headers = raylib.h raymath.h rcamera.h
package = kaylibc
staticLibraries = libraylib.a
libraryPaths.osx = src/nativeInterop/cinterop/lib/osx
libraryPaths.linux = src/nativeInterop/cinterop/lib/linux
libraryPaths.mingw = src/nativeInterop/cinterop/lib/mingw
compilerOpts = -Isrc/nativeInterop/cinterop/include
linkerOpts.mingw = -lwinmm -lgdi32 -lopengl32 -lkernel32
linkerOpts.osx = -framework CoreVideo -framework IOKit -framework Cocoa -framework GLUT -framework OpenGL
---
static Color lightGray = (Color){ 200, 200, 200, 255 };
static Color gray = (Color){ 130, 130, 130, 255 };
static Color darkGray = (Color){ 80, 80, 80, 255 };
static Color yellow = (Color){ 253, 249, 0, 255 };
static Color gold = (Color){ 255, 203, 0, 255 };
static Color orange = (Color){ 255, 161, 0, 255 };
static Color pink = (Color){ 255, 109, 194, 255 };
static Color red = (Color){ 230, 41, 55, 255 };
static Color maroon = (Color){ 190, 33, 55, 255 };
static Color green = (Color){ 0, 228, 48, 255 };
static Color lime = (Color){ 0, 158, 47, 255 };
static Color darkGreen = (Color){ 0, 117, 44, 255 };
static Color skyBlue = (Color){ 102, 191, 255, 255 };
static Color blue = (Color){ 0, 121, 241, 255 };
static Color darkBlue = (Color){ 0, 82, 172, 255 };
static Color purple = (Color){ 200, 122, 255, 255 };
static Color violet = (Color){ 135, 60, 190, 255 };
static Color darkPurple = (Color){ 112, 31, 126, 255 };
static Color beige = (Color){ 211, 176, 131, 255 };
static Color brown = (Color){ 127, 106, 79, 255 };
static Color darkBrown = (Color){ 76, 63, 47, 255 };
static Color white = (Color){ 255, 255, 255, 255 };
static Color black = (Color){ 0, 0, 0, 255 };
static Color blank = (Color){ 0, 0, 0, 0 };
static Color magenta = (Color){ 255, 0, 255, 255 };
static Color rayWhite = (Color){ 245, 245, 245, 255 };
// CONSTRUCTOR HELPERS
static inline struct BoneInfo BoneInfoConstructor(char name[32], int parent) {
struct BoneInfo r = { name[32], parent };
return r;
}
static inline struct Material MaterialConstructor(Shader shader, MaterialMap *maps, float params[4]) {
struct Material r = { shader, maps, params[4] };
return r;
}
static inline struct Sound SoundConstructor(AudioStream stream, unsigned int frameCount) {
struct Sound r = { stream, frameCount };
return r;
}
static inline struct Music MusicConstructor(AudioStream stream, unsigned int frameCount, bool looping, int ctxType, void *ctxData) {
struct Music r = { stream, frameCount, looping, ctxType, ctxData };
return r;
}
static inline struct VrDeviceInfo VrDeviceInfoConstructor(int hResolution, int vResolution, float hScreenSize, float vScreenSize, float vScreenCenter, float eyeToScreenDistance, float lensSeparationDistance, float interpupillaryDistance, float lensDistortionValues[4], float chromaAbCorrection[4]) {
struct VrDeviceInfo r = { hResolution, vResolution, hScreenSize, vScreenSize, vScreenCenter, eyeToScreenDistance, lensSeparationDistance, interpupillaryDistance, lensDistortionValues[4], chromaAbCorrection[4] };
return r;
}
static inline struct VrStereoConfig VrStereoConfigConstructor(Matrix projection[2], Matrix viewOffset[2], float leftLensCenter[2], float rightLensCenter[2], float leftScreenCenter[2], float rightScreenCenter[2], float scale[2], float scaleIn[2]) {
struct VrStereoConfig r = { projection[2], viewOffset[2], leftLensCenter[2], rightLensCenter[2], leftScreenCenter[2], rightScreenCenter[2], scale[2], scaleIn[2] };
return r;
}

View file