This commit is contained in:
Kapendev 2024-03-05 07:59:55 +02:00
commit bd2d04caee
22 changed files with 7170 additions and 0 deletions

16
.gitignore vendored Normal file
View file

@ -0,0 +1,16 @@
.dub
docs.json
__dummy.html
docs/
/popka
popka.so
popka.dylib
popka.dll
popka.a
popka.lib
popka-test-*
*.exe
*.pdb
*.o
*.obj
*.lst

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Alexandros F. G. Kapretsos
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

29
README.md Normal file
View file

@ -0,0 +1,29 @@
# 🍂 Popka
Popka is a lightweight and beginner-friendly 2D game engine for the D programming language.
It focuses on providing a simple and easy-to-understand foundation for building 2D games.
The game engine is currently under development and is not yet ready for use.
```d
import popka.basic;
void main() {
openWindow(800, 600);
while (isWindowOpen) {
if (Keyboard.q.isPressed) {
closeWindow();
}
}
freeWindow();
}
```
## Dependencies
Popka requires the raylib library to be installed for full functionality.
Please install raylib following the official instructions before using this game engine.
## License
The project is released under the terms of the MIT License.
Please refer to the LICENSE file.

6
TODO.md Normal file
View file

@ -0,0 +1,6 @@
# TODO
- [ ] Dialogue functionality in dialogue.d must be implemented.
- [ ] Collision functionality must be implemented.
- [ ] Add more parsing options for fmt.d and strconv.d.
- [ ] Write tests.

13
basic.d Normal file
View file

@ -0,0 +1,13 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
module popka.basic;
/// The basic module acts as a central hub,
/// bundling together numerous specialized modules
/// to provide an extensive array of tools catering to a variety of programming tasks.
public import popka.core.basic;
public import popka.game.basic;
unittest {}

100
core/ascii.d Normal file
View file

@ -0,0 +1,100 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
module popka.core.ascii;
/// The ascii module assists in handling ASCII characters.
enum {
spaceChars = " \t\v\r\n\f",
}
bool isLower(char c) {
return c >= 'a' && c <= 'z';
}
bool isLower(const(char)[] str) {
foreach (c; str) {
if (!isLower(c)) return false;
}
return true;
}
bool isUpper(char c) {
return c >= 'A' && c <= 'Z';
}
bool isUpper(const(char)[] str) {
foreach (c; str) {
if (!isUpper(c)) return false;
}
return true;
}
bool isAlpha(char c) {
return isLower(c) || isUpper(c);
}
bool isAlpha(const(char)[] str) {
foreach (c; str) {
if (!isAlpha(c)) return false;
}
return true;
}
bool isDigit(char c) {
return c >= '0' && c <= '9';
}
bool isDigit(const(char)[] str) {
foreach (c; str) {
if (!isDigit(c)) return false;
}
return true;
}
bool isSpace(char c) {
foreach (sc; spaceChars) {
if (c == sc) return true;
}
return false;
}
bool isSpace(const(char)[] str) {
foreach (c; str) {
if (!isSpace(c)) return false;
}
return true;
}
char toLower(char c) {
return isUpper(c) ? cast(char) (c + 32) : c;
}
void toLower(char[] str) {
foreach (ref c; str) {
c = toLower(c);
}
}
char toUpper(char c) {
return isLower(c) ? cast(char) (c - 32) : c;
}
void toUpper(char[] str) {
foreach (ref c; str) {
c = toUpper(c);
}
}
char toDigit(char c) {
return isDigit(c) ? cast(char) (c - 48) : '0';
}
void toDigit(char[] str) {
foreach (ref c; str) {
c = toDigit(c);
}
}
unittest {}

20
core/basic.d Normal file
View file

@ -0,0 +1,20 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
module popka.core.basic;
/// The basic module acts as a central hub,
/// bundling together numerous specialized modules
/// to provide an extensive array of tools catering to a variety of programming tasks.
public import popka.core.ascii;
public import popka.core.color;
public import popka.core.container;
public import popka.core.fmt;
public import popka.core.io;
public import popka.core.math;
public import popka.core.strconv;
public import popka.core.strutils;
public import popka.core.sumtype;
unittest {}

48
core/color.d Normal file
View file

@ -0,0 +1,48 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
module popka.core.color;
/// The color module specializes in handling color-related operations,
/// offering a suite of procedures tailored for manipulating and managing color properties within a program.
enum {
blank = Color(),
black = Color(0),
gray = Color(100),
white = Color(255),
red = Color(255, 0, 0),
green = Color(0, 255, 0),
blue = Color(0, 0, 255),
yellow = Color(255, 255, 0),
magenta = Color(255, 0, 255),
cyan = Color(0, 255, 255),
}
struct Color {
ubyte r;
ubyte g;
ubyte b;
ubyte a;
this(ubyte r, ubyte g, ubyte b, ubyte a) {
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}
this(ubyte r, ubyte g, ubyte b) {
this(r, g, b, 255);
}
this(ubyte r) {
this(r, r, r, 255);
}
this(ubyte[4] rgba) {
this(rgba[0], rgba[1], rgba[2], rgba[3]);
}
}
unittest {}

419
core/container.d Normal file
View file

@ -0,0 +1,419 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
module popka.core.container;
/// The container module serves as a hub for various data structures,
/// offering a cohesive set of tools for efficient storage and retrieval of information within a program.
import lib = core.stdc.stdlib;
import popka.core.fmt;
struct List(T) {
T[] items;
size_t capacity;
this(size_t length) {
foreach (i; 0 .. length) {
append(T.init);
}
}
this(TT)(const(TT)[] args) {
foreach (arg; args) {
append(arg);
}
}
this(TT)(List!TT list) {
foreach (item; list.items) {
append(item);
}
}
this(TT)(FlagList!TT list) {
foreach (item; list.items) {
append(item);
}
}
T[] opSlice(size_t dim)(size_t i, size_t j) {
return items[i .. j];
}
T[] opIndex() {
return items[];
}
T[] opIndex(T[] slice) {
// D calls this when the slice op is used and it works like this: opIndex(opSlice(i, j))
return slice;
}
ref T opIndex(size_t i) {
// Returning a ref will let people take a pointer to that value.
return items[i];
}
void opIndexAssign(TT)(TT rhs, size_t i) {
items[i] = rhs;
}
void opIndexOpAssign(string op, TT)(TT rhs, size_t i) {
mixin("items[i] " ~ op ~ "= rhs;");
}
size_t opDollar(size_t dim)() {
return items.length;
}
size_t length() {
return items.length;
}
void append(TT)(const(TT)[] args...) {
foreach (arg; args) {
size_t newLength = length + 1;
if (newLength > capacity) {
capacity = findListCapacity(newLength);
items = (cast(T*) lib.realloc(items.ptr, capacity * T.sizeof))[0 .. newLength];
} else {
items = items.ptr[0 .. newLength];
}
// We cast here because of the type system.
items[$ - 1] = cast(TT) arg;
}
}
void remove(size_t i) {
items[i] = items[$ - 1];
items = items[0 .. $ - 1];
}
T pop() {
T temp = items[$ - 1];
remove(length - 1);
return temp;
}
void resize(size_t length) {
if (length < this.length) {
items = items[0 .. length];
} else {
foreach (i; 0 .. length - this.length) {
this.append(T.init);
}
}
}
void fill(TT)(TT value) {
foreach (ref item; items) {
item = value;
}
}
void clear() {
items = items[0 .. 0];
}
void free() {
if (items.ptr != null) {
lib.free(items.ptr);
items = [];
capacity = 0;
}
}
}
struct FlagList(T) {
List!T data;
List!bool flags;
size_t hotIndex;
size_t openIndex;
this(size_t length) {
foreach (i; 0 .. length) {
append(T.init);
}
}
this(TT)(const(TT)[] args...) {
foreach (arg; args) {
append(arg);
}
}
this(TT)(List!TT list) {
foreach (item; list.items) {
append(item);
}
}
this(TT)(FlagList!TT list) {
data.resize(list.data.length);
flags.resize(list.flags.length);
foreach (i; 0 .. flags.length) {
data[i] = list.data[i];
flags[i] = list.flags[i];
}
hotIndex = list.hotIndex;
openIndex = list.openIndex;
}
ref T opIndex(size_t i, const(char)[] file = __FILE__, size_t line = __LINE__) {
if (!flags[i]) {
assert(0, "{}({}): ID {} doesn't exist.".fmt(file, line, i));
}
return data[i];
}
void opIndexAssign(TT)(TT rhs, size_t i) {
data[i] = rhs;
flags[i] = true;
hotIndex = i;
}
void opIndexOpAssign(string op, TT)(TT rhs, size_t i, const(char)[] file = __FILE__, size_t line = __LINE__) {
if (!flags[i]) {
assert(0, "{}({}): ID {} doesn't exist.".fmt(file, line, i));
}
mixin("data[i] " ~ op ~ "= rhs;");
}
size_t length() {
size_t result = 0;
foreach (id; ids) {
result += 1;
}
return result;
}
void append(TT)(const(TT)[] args...) {
foreach (arg; args) {
if (openIndex == flags.length) {
data.append(arg);
flags.append(true);
hotIndex = openIndex;
openIndex += 1;
} else {
foreach (i; openIndex .. flags.length) {
if (!flags[i]) {
data[i] = arg;
flags[i] = true;
hotIndex = i;
openIndex = i + 1;
break;
}
}
}
}
}
void remove(size_t i, const(char)[] file = __FILE__, size_t line = __LINE__) {
if (!flags[i]) {
assert(0, "{}({}): ID {} doesn't exist.".fmt(file, line, i));
}
flags[i] = false;
hotIndex = i;
if (i < openIndex) {
openIndex = i;
}
}
void resize(size_t length) {
if (length < flags.length) {
// I could write more code but I don't think that people will really resize.
hotIndex = 0;
openIndex = 0;
}
data.resize(length);
flags.resize(length);
}
void fill(TT)(TT value) {
data.fill(value);
flags.fill(true);
hotIndex = flags.length - 1;
openIndex = flags.length;
}
void clear() {
data.clear();
flags.clear();
hotIndex = 0;
openIndex = 0;
}
void free() {
data.free();
flags.free();
hotIndex = 0;
openIndex = 0;
}
auto ids() {
struct Range {
bool[] flags;
size_t id;
bool empty() {
return id == flags.length;
}
size_t front() {
return id;
}
void popFront() {
id += 1;
while (id != flags.length && !flags[id]) {
id += 1;
}
}
}
size_t id = 0;
while (id < flags.length && !flags[id]) {
id += 1;
}
return Range(flags.items, id);
}
auto items() {
struct Range {
T[] data;
bool[] flags;
size_t id;
bool empty() {
return id == flags.length;
}
ref T front() {
return data[id];
}
void popFront() {
id += 1;
while (id != flags.length && !flags[id]) {
id += 1;
}
}
}
size_t id = 0;
while (id < flags.length && !flags[id]) {
id += 1;
}
return Range(data.items, flags.items, id);
}
}
size_t findListCapacity(size_t length) {
enum defaultCapacity = 64;
size_t result = defaultCapacity;
while (result < length) {
result *= 2;
}
return result;
}
struct Grid(T) {
List!T cells;
float cellWidth;
float cellHeight;
size_t rowCount;
size_t colCount;
this(size_t rowCount, size_t colCount, size_t cellWidth, size_t cellHeight) {
this.cellWidth = cellWidth;
this.cellHeight = cellHeight;
resize(rowCount, colCount);
}
this(size_t rowCount, size_t colCount) {
resize(rowCount, colCount);
}
// TODO: Learn how to slice only rows, ...
T[] opSlice(size_t dim)(size_t i, size_t j) {
return items[i .. j];
}
T[] opIndex() {
return cells[];
}
T[] opIndex(T[] slice) {
return slice;
}
ref T opIndex(size_t i) {
return cells[i];
}
ref T opIndex(size_t row, size_t col) {
return cells[colCount * row + col];
}
void opIndexAssign(TT)(TT rhs, size_t i) {
cells[i] = rhs;
}
void opIndexAssign(TT)(TT rhs, size_t row, size_t col) {
cells[colCount * row + col] = rhs;
}
void opIndexOpAssign(string op, TT)(TT rhs, size_t i) {
mixin("cells[i] " ~ op ~ "= rhs;");
}
void opIndexOpAssign(string op, TT)(TT rhs, size_t row, size_t col) {
mixin("cells[colCount * row + col] " ~ op ~ "= rhs;");
}
size_t opDollar(size_t dim)() {
static if (dim == 0) {
return rowCount;
} else {
return colCount;
}
}
size_t length() {
return cells.length;
}
float width() {
return cellWidth * colCount;
}
float height() {
return cellHeight * rowCount;
}
void resize(size_t rowCount, size_t colCount) {
this.cells.resize(rowCount * colCount);
this.rowCount = rowCount;
this.colCount = colCount;
}
void fill(TT)(TT value) {
cells.fill(value);
}
void clear() {
cells.clear();
rowCount = 0;
colCount = 0;
}
void free() {
cells.free();
rowCount = 0;
colCount = 0;
}
}
unittest {}

78
core/fmt.d Normal file
View file

@ -0,0 +1,78 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
module popka.core.fmt;
/// The fmt module simplifies value formatting,
/// enabling conversion of data to strings and providing control over output appearance.
import io = core.stdc.stdio;
import popka.core.strconv;
const(char)[] fmt(A...)(const(char)[] str, A args) {
static char[1024][4] bufs = void;
static auto bufi = 0;
bufi = (bufi + 1) % bufs.length;
auto result = bufs[bufi][];
auto resi = 0;
auto stri = 0;
auto argi = 0;
while (stri < str.length) {
auto c1 = str[stri];
auto c2 = stri + 1 >= str.length ? '+' : str[stri + 1];
if (c1 == '{' && c2 == '}' && argi < args.length) {
static foreach (i, arg; args) {
if (i == argi) {
auto temp = toStr(arg);
result[resi .. resi + temp.length] = temp;
resi += temp.length;
stri += 2;
argi += 1;
goto exitLoopEarly;
}
}
exitLoopEarly:
} else {
result[resi] = c1;
resi += 1;
stri += 1;
}
}
result = result[0 .. resi];
return result;
}
void printf(A...)(const(char)[] str, A args) {
io.fputs(fmt("{}\0", fmt(str, args)).ptr, io.stdout);
}
void printfln(A...)(const(char)[] str, A args) {
io.fputs(fmt("{}\n\0", fmt(str, args)).ptr, io.stdout);
}
void print(A...)(A args) {
static foreach (arg; args) {
printf("{}", arg);
}
}
void println(A...)(A args) {
static foreach (arg; args) {
printf("{}", arg);
}
printf("\n");
}
unittest {
assert(fmt("") == "");
assert(fmt("{}") == "{}");
assert(fmt("{}", "1") == "1");
assert(fmt("{} {}", "1", "2") == "1 2");
assert(fmt("{} {} {}", "1", "2", "3") == "1 2 3");
assert(fmt("{} {} {}", 1, -2, 3.69) == "1 -2 3.69");
assert(fmt("{}", 420, 320, 220, 120, 20) == "420");
assert(fmt("", 1, -2, 3.69) == "");
assert(fmt("({})", fmt("({}, {})", false, true)) == "((false, true))");
}

50
core/io.d Normal file
View file

@ -0,0 +1,50 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
module popka.core.io;
/// The io module facilitates input and output operations,
/// offering functionalities such as file reading and writing.
import io = core.stdc.stdio;
import popka.core.container;
import popka.core.strconv;
List!char readText(const(char)[] path) {
auto f = io.fopen(toStrz(path), "rb");
if (f == null) {
return List!char();
}
if (io.fseek(f, 0, io.SEEK_END) != 0) {
io.fclose(f);
return List!char();
};
auto fsize = io.ftell(f);
if (fsize == -1) {
io.fclose(f);
return List!char();
}
if (io.fseek(f, 0, io.SEEK_SET) != 0) {
io.fclose(f);
return List!char();
}
auto result = List!char(fsize);
io.fread(result.items.ptr, fsize, 1, f);
io.fclose(f);
return result;
}
void writeText(const(char)[] path, List!char content) {
auto f = io.fopen(toStrz(path), "w");
if (f == null) {
return;
}
content.append('\0');
io.fputs(content.items.ptr, f);
io.fclose(f);
content.pop();
}
unittest {}

488
core/math.d Normal file
View file

@ -0,0 +1,488 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
module popka.core.math;
/// The math module covers essential mathematical operations, vectors, and shapes like rectangles.
import math = core.stdc.math;
enum Flip {
none, x, y, xy,
}
enum Hook {
topLeft, top, topRight,
left, center, right,
bottomLeft, bottom, bottomRight,
}
struct Vec2 {
float x = 0.0f;
float y = 0.0f;
this(float x, float y) {
this.x = x;
this.y = y;
}
this(float x) {
this(x, x);
}
this(float[2] xy) {
this(xy[0], xy[1]);
}
Vec2 opUnary(string op)() {
Vec2 result = void;
result.x = mixin(op ~ "x");
result.y = mixin(op ~ "y");
return result;
}
Vec2 opBinary(string op)(Vec2 rhs) {
Vec2 result = void;
result.x = mixin("x" ~ op ~ "rhs.x");
result.y = mixin("y" ~ op ~ "rhs.y");
return result;
}
void opOpAssign(string op)(Vec2 rhs) {
mixin("x" ~ op ~ "=" ~ "rhs.x;");
mixin("y" ~ op ~ "=" ~ "rhs.y;");
}
Vec2 floor() {
return Vec2(x.floor, y.floor);
}
float length() {
return sqrt(x * x + y * y);
}
Vec2 normalize() {
float l = length;
if (l == 0.0f) {
return Vec2();
} else {
return this / Vec2(l);
}
}
Vec2 directionTo(Vec2 to) {
return (to - this).normalize();
}
Vec2 moveTo(Vec2 to, Vec2 delta) {
Vec2 result;
Vec2 offset = this.directionTo(to) * delta;
if (abs(to.x - x) > abs(offset.x)) {
result.x = x + offset.x;
} else {
result.x = to.x;
}
if (abs(to.y - y) > abs(offset.y)) {
result.y = y + offset.y;
} else {
result.y = to.y;
}
return result;
}
Vec2 moveTo(Vec2 to, Vec2 delta, float slowdown) {
return Vec2(
.moveTo(x, to.x, delta.x, slowdown),
.moveTo(y, to.y, delta.y, slowdown),
);
}
}
struct Vec3 {
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
this(float x, float y, float z) {
this.x = x;
this.y = y;
this.z = z;
}
this(float x) {
this(x, x, x);
}
this(float[3] xyz) {
this(xyz[0], xyz[1], xyz[2]);
}
this(Vec2 xy, float z) {
this(xy.x, xy.y, z);
}
Vec3 opUnary(string op)() {
Vec3 result = void;
result.x = mixin(op ~ "x");
result.y = mixin(op ~ "y");
result.z = mixin(op ~ "z");
return result;
}
Vec3 opBinary(string op)(Vec3 rhs) {
Vec3 result = void;
result.x = mixin("x" ~ op ~ "rhs.x");
result.y = mixin("y" ~ op ~ "rhs.y");
result.z = mixin("z" ~ op ~ "rhs.z");
return result;
}
void opOpAssign(string op)(Vec3 rhs) {
mixin("x" ~ op ~ "=" ~ "rhs.x;");
mixin("y" ~ op ~ "=" ~ "rhs.y;");
mixin("z" ~ op ~ "=" ~ "rhs.z;");
}
Vec3 floor() {
return Vec3(x.floor, y.floor, z.floor);
}
}
struct Vec4 {
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
float w = 0.0f;
this(float x, float y, float z, float w) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
this(float x) {
this(x, x, x, x);
}
this(float[4] xyzw) {
this(xyzw[0], xyzw[1], xyzw[2], xyzw[3]);
}
Vec4 opUnary(string op)() {
Vec4 result = void;
result.x = mixin(op ~ "x");
result.y = mixin(op ~ "y");
result.z = mixin(op ~ "z");
result.w = mixin(op ~ "w");
return result;
}
Vec4 opBinary(string op)(Vec4 rhs) {
Vec4 result = void;
result.x = mixin("x" ~ op ~ "rhs.x");
result.y = mixin("y" ~ op ~ "rhs.y");
result.z = mixin("z" ~ op ~ "rhs.z");
result.w = mixin("w" ~ op ~ "rhs.w");
return result;
}
void opOpAssign(string op)(Vec4 rhs) {
mixin("x" ~ op ~ "=" ~ "rhs.x;");
mixin("y" ~ op ~ "=" ~ "rhs.y;");
mixin("z" ~ op ~ "=" ~ "rhs.z;");
mixin("w" ~ op ~ "=" ~ "rhs.w;");
}
Vec4 floor() {
return Vec4(x.floor, y.floor, z.floor, w.floor);
}
}
struct Rect {
Vec2 position;
Vec2 size;
this(Vec2 position, Vec2 size) {
this.position = position;
this.size = size;
}
this(Vec2 size) {
this.size = size;
}
this(float x, float y, float w, float h) {
this.position.x = x;
this.position.y = y;
this.size.x = w;
this.size.y = h;
}
this(float w, float h) {
this.size.x = w;
this.size.y = h;
}
void fix() {
if (size.x < 0.0f) {
position.x = position.x + size.x;
size.x = -size.x;
}
if (size.y < 0.0f) {
position.y = position.y + size.y;
size.y = -size.y;
}
}
Rect floor() {
Rect result = void;
result.position = position.floor;
result.size = size.floor;
return result;
}
Vec2 origin(Hook hook) {
final switch (hook) {
case Hook.topLeft: return size * Vec2(0.0f, 0.0f);
case Hook.top: return size * Vec2(0.5f, 0.0f);
case Hook.topRight: return size * Vec2(1.0f, 0.0f);
case Hook.left: return size * Vec2(0.0f, 0.5f);
case Hook.center: return size * Vec2(0.5f, 0.5f);
case Hook.right: return size * Vec2(1.0f, 0.5f);
case Hook.bottomLeft: return size * Vec2(0.0f, 1.0f);
case Hook.bottom: return size * Vec2(0.5f, 1.0f);
case Hook.bottomRight: return size * Vec2(1.0f, 1.0f);
}
}
Rect rect(Hook hook) {
Rect result = void;
result.position = position - origin(hook);
result.size = size;
return result;
}
Vec2 point(Hook hook) {
Vec2 result = void;
result = position + origin(hook);
return result;
}
bool hasPoint(Vec2 point) {
return (
point.x >= position.x &&
point.x <= position.x + size.x &&
point.y >= position.y &&
point.y <= position.y + size.y
);
}
bool hasIntersection(Rect area) {
return (
position.x + size.x >= area.position.x &&
position.x <= area.position.x + area.size.x &&
position.y + size.y >= area.position.y &&
position.y <= area.position.y + area.size.y
);
}
Rect intersection(Rect area) {
Rect result = void;
if (!this.hasIntersection(area)) {
result = Rect();
} else {
float maxY = max(position.x, area.position.x);
float maxX = max(position.y, area.position.y);
result.position.x = maxX;
result.position.y = maxY;
result.size.x = min(position.x + size.x, area.position.x + area.size.x) - maxX;
result.size.y = min(position.y + size.y, area.position.y + area.size.y) - maxY;
}
return result;
}
Rect merger(Rect area) {
Rect result = void;
float minX = min(position.x, area.position.x);
float minY = min(position.y, area.position.y);
result.position.x = minX;
result.position.y = minY;
result.size.x = max(position.x + size.x, area.position.x + area.size.x) - minX;
result.size.y = max(position.y + size.y, area.position.y + area.size.y) - minY;
return result;
}
Rect addLeft(float amount) {
position.x -= amount;
size.x += amount;
return Rect(position.x, position.y, amount, size.y);
}
Rect addRight(float amount) {
float w = size.x;
size.x += amount;
return Rect(w, position.y, amount, size.y);
}
Rect addTop(float amount) {
position.y -= amount;
size.y += amount;
return Rect(position.x, position.y, size.x, amount);
}
Rect addBottom(float amount) {
float h = size.y;
size.y += amount;
return Rect(position.x, h, size.x, amount);
}
Rect subLeft(float amount) {
float x = position.x;
position.x = min(position.x + amount, position.x + size.x);
size.x = max(size.x - amount, 0.0f);
return Rect(x, position.y, amount, size.y);
}
Rect subRight(float amount) {
size.x = max(size.x - amount, 0.0f);
return Rect(position.x + size.x, position.y, amount, size.y);
}
Rect subTop(float amount) {
float y = position.y;
position.y = min(position.y + amount, position.y + size.y);
size.y = max(size.y - amount, 0.0f);
return Rect(position.x, y, size.x, amount);
}
Rect subBottom(float amount) {
size.y = max(size.y - amount, 0.0f);
return Rect(position.x, position.y + size.y, size.x, amount);
}
void addLeftRight(float amount) {
this.addLeft(amount);
this.addRight(amount);
}
void addTopBottom(float amount) {
this.addTop(amount);
this.addBottom(amount);
}
void addAll(float amount) {
this.addLeftRight(amount);
this.addTopBottom(amount);
}
void subLeftRight(float amount) {
this.subLeft(amount);
this.subRight(amount);
}
void subTopBottom(float amount) {
this.subTop(amount);
this.subBottom(amount);
}
void subAll(float amount) {
this.subLeftRight(amount);
this.subTopBottom(amount);
}
Rect left(float amount) {
Rect temp = this;
return temp.subLeft(amount);
}
Rect right(float amount) {
Rect temp = this;
return temp.subRight(amount);
}
Rect top(float amount) {
Rect temp = this;
return temp.subTop(amount);
}
Rect bottom(float amount) {
Rect temp = this;
return temp.subBottom(amount);
}
}
float sqrt(float x) {
return math.sqrtf(x);
}
float min(float a, float b) {
return a <= b ? a : b;
}
float max(float a, float b) {
return a <= b ? b : a;
}
float sign(float x) {
return x <= 0.0f ? -1.0f : 1.0f;
}
float abs(float x) {
return x <= 0.0f ? -x : x;
}
float floor(float x) {
float xx = cast(float) cast(int) x;
return (x <= 0.0f && xx != x) ? xx - 1.0f : xx;
}
float ceil(float x) {
float xx = cast(float) cast(int) x;
return (x <= 0.0f || xx == x) ? xx : xx + 1.0f;
}
float round(float x) {
return x <= 0.0f ? cast(float) cast(int) (x - 0.5f) : cast(float) cast(int) (x + 0.5f);
}
float clamp(float x, float a, float b) {
return x <= a ? a : x >= b ? b : x;
}
float wrap(float x, float a, float b) {
auto result = x;
while (result < a) {
result += b - a;
}
while (result > b) {
result -= b - a;
}
return result;
}
float lerp(float from, float to, float weight) {
return from + (to - from) * weight;
}
float smoothstep(float from, float to, float weight) {
float v = weight * weight * (3.0f - 2.0f * weight);
return (to * v) + (from * (1.0f - v));
}
float smootherstep(float from, float to, float weight) {
float v = weight * weight * weight * (weight * (weight * 6.0f - 15.0f) + 10.0f);
return (to * v) + (from * (1.0f - v));
}
float moveTo(float from, float to, float delta) {
if (abs(to - from) > abs(delta)) return from + sign(to - from) * delta;
else return to;
}
float moveTo(float from, float to, float delta, float slowdown) {
float target = ((from * (slowdown - 1.0f)) + to) / slowdown;
return from + (target - from) * delta;
}
unittest {}

279
core/strconv.d Normal file
View file

@ -0,0 +1,279 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
module popka.core.strconv;
/// The strconv module offers procedures for both
/// parsing strings into numeric data types
/// and formatting numbers into string representations.
struct ToStrOptions {
uint floatPrecision = 2;
}
struct ToNumResult(T) {
alias Error = ubyte;
enum : Error {
ok,
invalid,
overflow,
}
T value;
Error error;
}
const(char)[] boolToStr(bool value) {
static char[64] buf = void;
auto result = buf[];
if (value) {
result[0 .. 4] = "true";
result = result[0 .. 4];
} else {
result[0 .. 5] = "false";
result = result[0 .. 5];
}
return result;
}
const(char)[] unsignedToStr(ulong value) {
static char[64] buf = void;
auto result = buf[];
if (value == 0) {
result[0] = '0';
result = result[0 .. 1];
} else {
auto digitCount = 0;
for (auto temp = value; temp != 0; temp /= 10) {
result[$ - 1 - digitCount] = (temp % 10) + '0';
digitCount += 1;
}
result = result[$ - digitCount .. $];
}
return result;
}
const(char)[] signedToStr(long value) {
static char[64] buf = void;
auto result = buf[];
if (value < 0) {
auto temp = unsignedToStr(-value);
result[0] = '-';
result[1 .. 1 + temp.length] = temp;
result = result[0 .. 1 + temp.length];
} else {
auto temp = unsignedToStr(value);
result[0 .. temp.length] = temp;
result = result[0 .. temp.length];
}
return result;
}
const(char)[] floatToStr(double value, uint precision = 2) {
static char[64] buf = void;
auto result = buf[];
auto fractionalDigitCount = 0;
auto cleanNumber = value;
while (cleanNumber != cast(double) (cast(long) cleanNumber)) {
fractionalDigitCount += 1;
cleanNumber *= 10;
}
auto temp = signedToStr(cast(long) cleanNumber);
auto i = result.length;
if (temp.length <= fractionalDigitCount) {
i -= temp.length;
result[i .. i + temp.length] = temp;
if (temp.length < fractionalDigitCount) {
i -= fractionalDigitCount - temp.length;
result[i .. i + fractionalDigitCount - temp.length] = '0';
}
i -= 2;
result[i .. i + 2] = "0.";
} else {
if (fractionalDigitCount == 0) {
i -= (precision == 0 ? 1 : precision);
result[i .. i + (precision == 0 ? 1 : precision)] = '0';
i -= 1;
result[i] = '.';
i -= temp.length;
result[i .. i + temp.length] = temp;
} else {
i -= fractionalDigitCount;
result[i .. i + fractionalDigitCount] = temp[$ - fractionalDigitCount .. $];
i -= 1;
result[i] = '.';
i -= (temp.length - fractionalDigitCount);
result[i .. i + (temp.length - fractionalDigitCount)] = temp[0 .. $ - fractionalDigitCount];
}
}
if (precision == 0) {
result = result[i .. $];
} else {
result = result[i .. $ - fractionalDigitCount + (precision > fractionalDigitCount ? fractionalDigitCount : precision)];
}
return result;
}
const(char)[] charToStr(char value) {
static char[1] buf = void;
auto result = buf[];
result[0] = value;
result = result[0 .. 1];
return result;
}
const(char)[] strzToStr(const(char)* value) {
static char[1024] buf = void;
auto result = buf[];
size_t strzLength = 0;
while (value[strzLength] != '\0') {
strzLength += 1;
}
result[0 .. strzLength] = value[0 .. strzLength];
result = result[0 .. strzLength];
return result;
}
const(char)[] strToStr(const(char)[] value) {
static char[1024] buf = void;
auto result = buf[];
result[0 .. value.length] = value;
result = result[0 .. value.length];
return result;
}
const(char)[] toStr(T)(T value, ToStrOptions opt = ToStrOptions()) {
enum isBool = is(T == bool);
enum isUnsigned = is(T == ubyte) || is(T == ushort) || is(T == uint) || is(T == ulong);
enum isSigned = is(T == byte) || is(T == short) || is(T == int) || is(T == long);
enum isFloat = is(T == float) || is(T == double);
enum isChar = is(T == char) || is(T == const(char)) || is(T == immutable(char));
enum isStrz = is(T : const(char)*);
enum isStr = is(T : const(char)[]);
static if (isBool) {
return boolToStr(value);
} else static if (isUnsigned) {
return unsignedToStr(value);
} else static if (isSigned) {
return signedToStr(value);
} else static if (isFloat) {
return floatToStr(value, opt.floatPrecision);
} else static if (isChar) {
return charToStr(value);
} else static if (isStrz) {
return strzToStr(value);
} else static if (isStr) {
return strToStr(value);
} else {
static assert(0, "The 'toStr' function exclusively handles numerical valueues, booleans, characters and strings.");
}
}
ToNumResult!bool toBool(const(char)[] str) {
auto result = ToNumResult!bool();
if (str == "true") {
result.value = true;
} else if (str == "false") {
result.value = false;
} else {
result.error = result.invalid;
}
return result;
}
ToNumResult!ulong toUnsigned(const(char)[] str) {
auto result = ToNumResult!ulong();
if (str.length == 0) {
result.error = result.invalid;
} else if (str.length >= 18) {
result.error = result.overflow;
} else {
ulong level = 1;
foreach_reverse (i, c; str) {
if (c < '0' || c > '9') {
result.error = result.invalid;
break;
}
auto digit = c - '0';
result.value += digit * level;
level *= 10;
}
}
return result;
}
ToNumResult!long toSigned(const(char)[] str) {
auto result = ToNumResult!long();
if (str.length == 0) {
result.error = result.invalid;
} else if (str.length >= 18) {
result.error = result.overflow;
} else {
if (str[0] == '-') {
auto conv = toUnsigned(str[1 .. $]);
if (conv.error) {
result.error = conv.error;
} else {
result.value = -conv.value;
}
} else {
auto conv = toUnsigned(str);
if (conv.error) {
result.error = conv.error;
} else {
result.value = conv.value;
}
}
}
return result;
}
ToNumResult!double toFloat(const(char)[] str) {
auto result = ToNumResult!double();
foreach (i, c; str) {
if (c == '.') {
auto lhs = toSigned(str[0 .. i]);
if (lhs.error) {
result.error = lhs.error;
} else {
auto rhs = toSigned(str[i + 1 .. $]);
if (rhs.error) {
result.error = rhs.error;
} else {
auto rhsLevel = 10;
foreach (_; 1 .. str[i + 1 .. $].length) {
rhsLevel *= 10;
}
result.value = lhs.value + ((lhs.value < 0 ? -1 : 1) * rhs.value / cast(double) rhsLevel);
}
}
break;
}
}
return result;
}
const(char)* toStrz(const(char)[] str) {
static char[1024] buf = void;
auto result = buf[];
result[0 .. str.length] = str;
result[str.length] = '\0';
return result.ptr;
}
unittest {}

149
core/strutils.d Normal file
View file

@ -0,0 +1,149 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
module popka.core.strutils;
/// The strutils module contains handy procedures designed to assist with various string manipulation tasks.
import popka.core.ascii;
bool isStrz(const(char)[] str) {
return str.length != 0 && str[$ - 1] == '\0';
}
bool equals(const(char)[] str, const(char)[] other) {
return str == other;
}
bool equalsi(const(char)[] str, const(char)[] other) {
if (str.length != other.length) return false;
foreach (i; 0 .. str.length) {
if (toUpper(str[i]) != toUpper(other[i])) return false;
}
return true;
}
bool startsWith(const(char)[] str, const(char)[] start) {
if (str.length < start.length) return false;
return str[0 .. start.length] == start;
}
bool startsWithi(const(char)[] str, const(char)[] start) {
if (str.length < start.length) return false;
foreach (i; 0 .. start.length) {
if (toUpper(str[i]) != toUpper(start[i])) return false;
}
return true;
}
bool endsWith(const(char)[] str, const(char)[] end) {
if (str.length < end.length) return false;
return str[$ - end.length - 1 .. end.length] == end;
}
bool endsWithi(const(char)[] str, const(char)[] end) {
if (str.length < end.length) return false;
foreach (i; str.length - end.length - 1 .. end.length) {
if (toUpper(str[i]) != toUpper(end[i])) return false;
}
return true;
}
int count(const(char)[] str, const(char)[] item) {
int result = 0;
if (str.length < item.length || item.length == 0) return result;
foreach (i; 0 .. str.length - item.length) {
if (str[i .. i + item.length] == item) {
result += 1;
i += item.length - 1;
}
}
return result;
}
int counti(const(char)[] str, const(char)[] item) {
int result = 0;
if (str.length < item.length || item.length == 0) return result;
foreach (i; 0 .. str.length - item.length) {
if (equalsi(str[i .. i + item.length], item)) {
result += 1;
i += item.length - 1;
}
}
return result;
}
int findStart(const(char)[] str, const(char)[] item) {
if (str.length < item.length || item.length == 0) return -1;
foreach (i; 0 .. str.length - item.length) {
if (str[i + item.length .. item.length] == item) return cast(int) i;
}
return -1;
}
int findStarti(const(char)[] str, const(char)[] item) {
if (str.length < item.length || item.length == 0) return -1;
foreach (i; 0 .. str.length - item.length) {
if (equalsi(str[i + item.length .. item.length], item)) return cast(int) i;
}
return -1;
}
int findEnd(const(char)[] str, const(char)[] item) {
if (str.length < item.length || item.length == 0) return -1;
foreach_reverse (i; 0 .. str.length - item.length) {
if (str[i + item.length .. item.length] == item) return cast(int) i;
}
return -1;
}
int findEndi(const(char)[] str, const(char)[] item) {
if (str.length < item.length || item.length == 0) return -1;
foreach_reverse (i; 0 .. str.length - item.length) {
if (equalsi(str[i + item.length .. item.length], item)) return cast(int) i;
}
return -1;
}
const(char)[] trimStart(const(char)[] str) {
const(char)[] result = str;
while (result.length > 0) {
if (isSpace(result[0])) result = result[1 .. $];
else break;
}
return result;
}
const(char)[] trimEnd(const(char)[] str) {
const(char)[] result = str;
while (result.length > 0) {
if (isSpace(result[$ - 1])) result = result[0 .. $ - 1];
else break;
}
return result;
}
const(char)[] trim(const(char)[] str) {
return str.trimStart().trimEnd();
}
const(char)[] skipValue(ref const(char)[] str, char sep = ',') {
foreach (i; 0 .. str.length) {
if (str[i] == sep) {
auto line = str[0 .. i];
str = str[i + 1 .. $];
return line;
} else if (i == str.length - 1) {
auto line = str[0 .. i + 1];
str = str[i + 1 .. $];
return line;
}
}
return "";
}
const(char)[] skipLine(ref const(char)[] str) {
return skipValue(str, '\n');
}
unittest {}

82
core/sumtype.d Normal file
View file

@ -0,0 +1,82 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
module popka.core.sumtype;
/// The sumtype module defines a data structure that can hold one of several possible types.
/// It provides functionalities to construct, access, and manipulate these values.
struct None {};
struct SumType(A...) {
template memberName(T) {
mixin("enum memberName = \"" ~ ((T.stringof[0] >= 'A' && T.stringof[0] <= 'Z') ? cast(char) (T.stringof[0] + 32) : T.stringof[0]) ~ T.stringof[1 .. $] ~ "\";");
}
template dataName(T) {
mixin("enum dataName = \"" ~ memberName!T ~ "Data\";");
}
template kindName(T) {
mixin("enum kindName = \"" ~ memberName!T ~ "Kind\";");
}
union Data {
static foreach (T; A) {
mixin("T ", dataName!T, ";");
}
}
alias Kind = ushort;
static foreach (i, T; A) {
mixin("enum ", kindName!T, " = ", i, ";");
}
Data data;
Kind kind;
alias this = data;
static foreach (i, T; A) {
this(T data) {
this.data = *(cast(Data*) &data);
this.kind = i;
}
}
static foreach (i, T; A) {
void opAssign(T rhs) {
data = *(cast(Data*) &rhs);
kind = i;
}
}
auto call(string f, AA...)(AA args) {
final switch (kind) {
static foreach (T; A) {
mixin("case ", kindName!T, ": return ", dataName!T, ".", f ~ "(args);");
}
}
}
}
alias Optional(T) = SumType!(None, T);
bool hasCommonBase(T)() {
alias Base = typeof(T.init.data.tupleof[0]);
static foreach (member; T.init.data.tupleof[1 .. $]) {
static if (member.tupleof.length == 0) {
return false;
} else static if (!is(Base == typeof(member.tupleof[0]))) {
return false;
}
}
return true;
}
mixin template AddBase(T) {
T base;
alias this = base;
}
unittest {}

10
dub.json Normal file
View file

@ -0,0 +1,10 @@
{
"authors": [
"Alexandros F. G. Kapretsos"
],
"copyright": "Copyright © 2024, Alexandros F. G. Kapretsos",
"description": "A lightweight and beginner-friendly 2D game engine for the D programming language.",
"license": "MIT",
"name": "popka",
"sourcePaths": ["."]
}

13
game/basic.d Normal file
View file

@ -0,0 +1,13 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
module popka.game.basic;
/// The basic module acts as a central hub,
/// bundling together numerous specialized modules
/// to provide an extensive array of tools catering to a variety of programming tasks.
public import popka.game.dialogue;
public import popka.game.engine;
unittest {}

86
game/dialogue.d Normal file
View file

@ -0,0 +1,86 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
module popka.game.dialogue;
/// The dialogue module is a versatile dialogue system for games,
/// enabling the creation of interactive conversations and branching narratives.
import popka.core.basic;
struct DialogueUnit {
alias Kind = char;
enum : Kind {
pause = '-',
comment = '#',
point = '*',
target = '@',
actor = '>',
line = '|',
}
List!char content;
Kind kind = pause;
bool isOneOf(const(Kind)[] args...) {
foreach (arg; args) {
if (arg == kind) {
return true;
}
}
return false;
}
}
struct Dialogue {
List!DialogueUnit units;
size_t index;
this(const(char)[] path) {
read(path);
}
DialogueUnit* now() {
return &units[index];
}
void update() {
if (units.length != 0 && index < units.length - 1) {
index += 1;
}
}
void free() {
foreach (ref unit; units.items) {
unit.content.free();
}
units.free();
}
void read(const(char)[] path) {
free();
units.append(DialogueUnit(List!char(), DialogueUnit.pause));
auto file = readText(path);
const(char)[] view = file.items;
auto lineNumber = 0;
while (view.length != 0) {
auto line = skipLine(view);
if (line.length == 0) {
continue;
}
lineNumber += 1;
if (lineNumber == 1 && line[0] == DialogueUnit.pause) {
continue;
}
units.append(DialogueUnit(List!char(trimStart(line[1 .. $])), line[0]));
}
if (units.items[$ - 1].kind != DialogueUnit.pause) {
units.append(DialogueUnit(List!char(), DialogueUnit.pause));
}
file.free();
}
}
unittest {}

838
game/engine.d Normal file
View file

@ -0,0 +1,838 @@
// Copyright 2024 Alexandros F. G. Kapretsos
// SPDX-License-Identifier: MIT
module popka.game.engine;
/// The engine module functions as a lightweight 2D game engine,
/// designed to provide essential tools and functionalities for developing games with ease and efficiency.
import ray = popka.vendor.ray.raylib;
import popka.core.basic;
bool popkaState;
Color popkaBackgroundColor = gray;
View popkaView;
float popkaViewWidth = 320.0f;
float popkaViewHeight = 180.0f;
bool popkaViewLockFlag;
bool popkaViewUnlockFlag;
bool popkaFullscreenFlag;
Vec2 popkaFullscreenLastWindowSize;
float popkaFullscreenTime = 0.0f;
enum popkaFullscreenWaitTime = 0.175f;
struct Sprite {
ray.Texture2D data;
this(const(char)[] path) {
load(path);
}
bool isEmpty() {
return data.id == 0;
}
float width() {
return data.width;
}
float height() {
return data.height;
}
Vec2 size() {
return Vec2(width, height);
}
Rect rect() {
return Rect(size);
}
void load(const(char)[] path) {
free();
data = ray.LoadTexture(toStrz(path));
}
void free() {
if (!isEmpty) {
ray.UnloadTexture(data);
data = ray.Texture2D();
}
}
}
struct Font {
ray.Font data;
this(const(char)[] path, uint size) {
load(path, size);
}
bool isEmpty() {
return data.texture.id == 0;
}
float size() {
return data.baseSize;
}
void load(const(char)[] path, uint size) {
free();
data = ray.LoadFontEx(toStrz(path), size, null, 0);
}
void free() {
if (data.texture.id != 0) {
ray.UnloadFont(data);
data = ray.Font();
}
}
}
struct View {
ray.RenderTexture2D data;
this(float width, float height) {
load(width, height);
}
bool isEmpty() {
return data.texture.id == 0;
}
float width() {
return data.texture.width;
}
float height() {
return data.texture.height;
}
Vec2 size() {
return Vec2(width, height);
}
Rect rect() {
return Rect(size);
}
void load(float width, float height) {
free();
data = ray.LoadRenderTexture(cast(int) width, cast(int) height);
}
void free() {
if (!isEmpty) {
ray.UnloadRenderTexture(data);
data = ray.RenderTexture();
}
}
}
struct TileMap {
Grid!short data;
alias this = data;
Vec2 cellSize() {
return Vec2(cellWidth, cellHeight);
}
void cellSize(Vec2 value) {
cellWidth = value.x;
cellHeight = value.y;
}
Vec2 size() {
return Vec2(width, height);
}
void load(const(char)[] path) {
free();
auto file = readText(path);
const(char)[] view = file.items;
while (view.length != 0) {
auto line = view.skipLine();
rowCount += 1;
colCount = 0;
while (line.length != 0) {
auto value = line.skipValue();
colCount += 1;
}
}
resize(rowCount, colCount);
view = file.items;
foreach (row; 0 .. rowCount) {
auto line = view.skipLine();
foreach (col; 0 .. colCount) {
auto value = line.skipValue();
auto conv = value.toSigned();
if (conv.error) {
data[row, col] = cast(short) -1;
} else {
data[row, col] = cast(short) conv.value;
}
}
}
file.free();
}
}
struct Camera {
Vec2 position;
float rotation = 0.0f;
float scale = 1.0f;
Hook hook;
bool isAttached;
this(Vec2 position) {
this.position = position;
}
float width() {
return resolution.x * scale;
}
float height() {
return resolution.y * scale;
}
Vec2 size() {
return Vec2(width, height);
}
Vec2 origin() {
return Rect(size).origin(hook);
}
Rect rect() {
return Rect(position - origin, size);
}
Vec2 point(Hook hook) {
return rect.point(hook);
}
void attach() {
if (!isAttached) {
isAttached = true;
auto temp = this.toRay();
temp.target.x = floor(temp.target.x);
temp.target.y = floor(temp.target.y);
temp.offset.x = floor(temp.offset.x);
temp.offset.y = floor(temp.offset.y);
ray.BeginMode2D(temp);
}
}
void detach() {
if (isAttached) {
isAttached = false;
ray.EndMode2D();
}
}
void follow(Vec2 target, float slowdown = 0.14f) {
if (slowdown <= 0.0f) {
position = target;
} else {
position = position.moveTo(target, Vec2(deltaTime), slowdown);
}
}
}
enum Keyboard {
a = ray.KEY_A,
b = ray.KEY_B,
c = ray.KEY_C,
d = ray.KEY_D,
e = ray.KEY_E,
f = ray.KEY_F,
g = ray.KEY_G,
h = ray.KEY_H,
i = ray.KEY_I,
j = ray.KEY_J,
k = ray.KEY_K,
l = ray.KEY_L,
m = ray.KEY_M,
n = ray.KEY_N,
o = ray.KEY_O,
p = ray.KEY_P,
q = ray.KEY_Q,
r = ray.KEY_R,
s = ray.KEY_S,
t = ray.KEY_T,
u = ray.KEY_U,
v = ray.KEY_V,
w = ray.KEY_W,
x = ray.KEY_X,
y = ray.KEY_Y,
z = ray.KEY_Z,
n0 = ray.KEY_ZERO,
n1 = ray.KEY_ONE,
n2 = ray.KEY_TWO,
n3 = ray.KEY_THREE,
n4 = ray.KEY_FOUR,
n5 = ray.KEY_FIVE,
n6 = ray.KEY_SIX,
n7 = ray.KEY_SEVEN,
n8 = ray.KEY_EIGHT,
n9 = ray.KEY_NINE,
f1 = ray.KEY_F1,
f2 = ray.KEY_F2,
f3 = ray.KEY_F3,
f4 = ray.KEY_F4,
f5 = ray.KEY_F5,
f6 = ray.KEY_F6,
f7 = ray.KEY_F7,
f8 = ray.KEY_F8,
f9 = ray.KEY_F9,
f10 = ray.KEY_F10,
f11 = ray.KEY_F11,
f12 = ray.KEY_F12,
left = ray.KEY_LEFT,
right = ray.KEY_RIGHT,
up = ray.KEY_UP,
down = ray.KEY_DOWN,
esc = ray.KEY_ESCAPE,
enter = ray.KEY_ENTER,
tab = ray.KEY_TAB,
space = ray.KEY_SPACE,
backspace = ray.KEY_BACKSPACE,
shift = ray.KEY_LEFT_SHIFT,
ctrl = ray.KEY_LEFT_CONTROL,
alt = ray.KEY_LEFT_ALT,
win = ray.KEY_LEFT_SUPER,
}
enum Mouse {
left = ray.MOUSE_BUTTON_LEFT,
right = ray.MOUSE_BUTTON_RIGHT,
middle = ray.MOUSE_BUTTON_MIDDLE,
}
enum Gamepad {
left = ray.GAMEPAD_BUTTON_LEFT_FACE_LEFT,
right = ray.GAMEPAD_BUTTON_LEFT_FACE_RIGHT,
up = ray.GAMEPAD_BUTTON_LEFT_FACE_UP,
down = ray.GAMEPAD_BUTTON_LEFT_FACE_DOWN,
y = ray.GAMEPAD_BUTTON_RIGHT_FACE_UP,
x = ray.GAMEPAD_BUTTON_RIGHT_FACE_RIGHT,
a = ray.GAMEPAD_BUTTON_RIGHT_FACE_DOWN,
b = ray.GAMEPAD_BUTTON_RIGHT_FACE_LEFT,
lb = ray.GAMEPAD_BUTTON_LEFT_TRIGGER_1,
lt = ray.GAMEPAD_BUTTON_LEFT_TRIGGER_2,
lsb = ray.GAMEPAD_BUTTON_LEFT_THUMB,
rb = ray.GAMEPAD_BUTTON_RIGHT_TRIGGER_1,
rt = ray.GAMEPAD_BUTTON_RIGHT_TRIGGER_2,
rsb = ray.GAMEPAD_BUTTON_RIGHT_THUMB,
back = ray.GAMEPAD_BUTTON_MIDDLE_LEFT,
start = ray.GAMEPAD_BUTTON_MIDDLE_RIGHT,
center = ray.GAMEPAD_BUTTON_MIDDLE,
}
// # Converters
Color toPopka(ray.Color from) {
return Color(from.r, from.g, from.b, from.a);
}
Vec2 toPopka(ray.Vector2 from) {
return Vec2(from.x, from.y);
}
Vec3 toPopka(ray.Vector3 from) {
return Vec3(from.x, from.y, from.z);
}
Vec4 toPopka(ray.Vector4 from) {
return Vec4(from.x, from.y, from.z, from.w);
}
Rect toPopka(ray.Rectangle from) {
return Rect(from.x, from.y, from.width, from.height);
}
Sprite toPopka(ray.Texture2D from) {
Sprite result;
result.data = from;
return result;
}
Font toPopka(ray.Font from) {
Font result;
result.data = from;
return result;
}
View toPopka(ray.RenderTexture2D from) {
View result;
result.data = from;
return result;
}
Camera toPopka(ray.Camera2D from) {
Camera result;
result.position = from.target.toPopka();
result.rotation = from.rotation;
result.scale = from.zoom;
return result;
}
ray.Color toRay(Color from) {
return ray.Color(from.r, from.g, from.b, from.a);
}
ray.Vector2 toRay(Vec2 from) {
return ray.Vector2(from.x, from.y);
}
ray.Vector3 toRay(Vec3 from) {
return ray.Vector3(from.x, from.y, from.z);
}
ray.Vector4 toRay(Vec4 from) {
return ray.Vector4(from.x, from.y, from.z, from.w);
}
ray.Rectangle toRay(Rect from) {
return ray.Rectangle(from.position.x, from.position.y, from.size.x, from.size.y);
}
ray.Texture2D toRay(Sprite from) {
return from.data;
}
ray.Font toRay(Font from) {
return from.data;
}
ray.RenderTexture2D toRay(View from) {
return from.data;
}
ray.Camera2D toRay(Camera from) {
return ray.Camera2D(from.origin.toRay(), from.position.toRay(), from.rotation, from.scale);
}
void gprintf(size_t line = __LINE__, A...)(const(char)[] str, A args) {
static auto timer = 0.0f;
enum waitTime = 0.5f;
timer += deltaTime;
if (timer > waitTime) {
timer -= waitTime;
printf(str, args);
}
}
void gprintfln(size_t line = __LINE__, A...)(const(char)[] str, A args) {
static auto timer = 0.0f;
enum waitTime = 0.5f;
timer += deltaTime;
if (timer > waitTime) {
timer -= waitTime;
printfln(str, args);
}
}
void gprint(size_t line = __LINE__, A...)(A args) {
static auto timer = 0.0f;
enum waitTime = 0.5f;
timer += deltaTime;
if (timer > waitTime) {
timer -= waitTime;
print(args);
}
}
void gprintln(size_t line = __LINE__, A...)(A args) {
static auto timer = 0.0f;
enum waitTime = 0.5f;
timer += deltaTime;
if (timer > waitTime) {
timer -= waitTime;
println(args);
}
}
int randi() {
return ray.GetRandomValue(0, int.max);
}
float randf() {
return ray.GetRandomValue(0, cast(int) float.max) / cast(float) cast(int) float.max;
}
void randomize(uint seed) {
ray.SetRandomSeed(seed);
}
void randomize() {
randomize(randi);
}
void openWindow(float width, float height, const(char)[] title = "Popka", Color backgroundColor = gray) {
ray.SetConfigFlags(ray.FLAG_VSYNC_HINT | ray.FLAG_WINDOW_RESIZABLE);
ray.SetTraceLogLevel(ray.LOG_ERROR);
ray.InitWindow(cast(int) width, cast(int) height, toStrz(title));
ray.SetWindowMinSize(cast(int) (width * 0.2f), cast(int) (height * 0.2f));
ray.SetExitKey(ray.KEY_NULL);
ray.SetTargetFPS(60);
popkaState = true;
popkaFullscreenLastWindowSize = Vec2(width, height);
popkaBackgroundColor = backgroundColor;
}
void closeWindow() {
popkaState = false;
}
void freeWindow() {
ray.CloseWindow();
}
bool isWindowOpen() {
static auto isFirstCall = true;
auto result = !(ray.WindowShouldClose() || !popkaState);
if (result) {
if (isFirstCall) {
// Begin drawing.
if (isResolutionLocked) {
ray.BeginTextureMode(popkaView.data);
} else {
ray.BeginDrawing();
}
ray.ClearBackground(popkaBackgroundColor.toRay());
isFirstCall = false;
} else {
// End drawing.
if (isResolutionLocked) {
auto minSize = popkaView.size;
auto maxSize = windowSize;
auto ratio = maxSize / minSize;
auto minRatio = min(ratio.x, ratio.y);
auto targetSize = minSize * Vec2(minRatio);
auto targetPos = maxSize * Vec2(0.5f) - targetSize * Vec2(0.5f);
ray.EndTextureMode();
ray.BeginDrawing();
ray.ClearBackground(ray.BLACK);
ray.DrawTexturePro(
popkaView.data.texture,
ray.Rectangle(0.0f, 0.0f, minSize.x, -minSize.y),
ray.Rectangle(
ratio.x == minRatio ? targetPos.x : targetPos.x.floor,
ratio.y == minRatio ? targetPos.y : targetPos.y.floor,
ratio.x == minRatio ? targetSize.x : targetSize.x.floor,
ratio.y == minRatio ? targetSize.y : targetSize.y.floor,
),
ray.Vector2(0.0f, 0.0f),
0.0f,
ray.WHITE,
);
ray.EndDrawing();
} else {
ray.EndDrawing();
}
// Check if the resolution was locked or unlocked.
if (popkaViewLockFlag) {
popkaView.load(popkaViewWidth, popkaViewHeight);
popkaViewLockFlag = false;
}
if (popkaViewUnlockFlag) {
popkaView.free();
popkaViewUnlockFlag = false;
}
// Begin drawing.
if (isResolutionLocked) {
ray.BeginTextureMode(popkaView.data);
} else {
ray.BeginDrawing();
}
ray.ClearBackground(popkaBackgroundColor.toRay());
// Fullscreen code to fix a bug on KDE.
if (popkaFullscreenFlag) {
popkaFullscreenTime += deltaTime;
if (popkaFullscreenTime >= popkaFullscreenWaitTime) {
popkaFullscreenTime = 0.0f;
popkaFullscreenFlag = false;
ray.ToggleFullscreen();
if (!isFullscreen) {
windowSize(popkaFullscreenLastWindowSize);
}
}
}
}
}
return result;
}
void lockFPS(uint target) {
ray.SetTargetFPS(target);
}
void unlockFPS() {
ray.SetTargetFPS(0);
}
bool isResolutionLocked() {
return !popkaView.isEmpty;
}
void lockResolution(float width, float height) {
popkaViewWidth = width;
popkaViewHeight = height;
popkaViewLockFlag = true;
}
void unlockResolution() {
popkaViewUnlockFlag = true;
}
bool isFullscreen() {
return ray.IsWindowFullscreen;
}
void toggleFullscreen() {
if (!popkaFullscreenFlag) {
popkaFullscreenFlag = true;
if (!isFullscreen) {
popkaFullscreenLastWindowSize = windowSize;
windowSize(screenSize);
}
}
}
Vec2 screenSize(uint id) {
return Vec2(ray.GetMonitorWidth(id), ray.GetMonitorHeight(id));
}
Vec2 screenSize() {
return screenSize(ray.GetCurrentMonitor());
}
Vec2 windowSize() {
if (isFullscreen) {
return screenSize;
} else {
return Vec2(ray.GetScreenWidth(), ray.GetScreenHeight());
}
}
void windowSize(Vec2 size) {
auto screen = screenSize;
ray.SetWindowSize(cast(int) size.x, cast(int) size.y);
ray.SetWindowPosition(cast(int) (screen.x * 0.5f - size.x * 0.5f), cast(int) (screen.y * 0.5f - size.y * 0.5f));
}
Vec2 resolution() {
if (isResolutionLocked) {
return popkaView.size;
} else {
return windowSize;
}
}
int fps() {
return ray.GetFPS();
}
float deltaTime() {
return ray.GetFrameTime();
}
bool isPressed(Keyboard key) {
return ray.IsKeyPressed(key);
}
bool isPressed(Mouse key) {
return ray.IsMouseButtonPressed(key);
}
bool isPressed(Gamepad key, uint id = 0) {
return ray.IsGamepadButtonPressed(id, key);
}
bool isDown(Keyboard key) {
return ray.IsKeyDown(key);
}
bool isDown(Mouse key) {
return ray.IsMouseButtonDown(key);
}
bool isDown(Gamepad key, uint id = 0) {
return ray.IsGamepadButtonDown(id, key);
}
bool isReleased(Keyboard key) {
return ray.IsKeyReleased(key);
}
bool isReleased(Mouse key) {
return ray.IsMouseButtonReleased(key);
}
bool isReleased(Gamepad key, uint id = 0) {
return ray.IsGamepadButtonReleased(id, key);
}
void drawRect(Rect rect, Color color = white) {
ray.DrawRectanglePro(rect.floor.toRay(), ray.Vector2(0.0f, 0.0f), 0.0f, color.toRay());
}
void drawSprite(Sprite sprite, Rect region, Vec2 position = Vec2(), float rotation = 0.0f, Vec2 scale = Vec2(1.0f), Color color = white, Flip flip = Flip.none, Hook hook = Hook.topLeft) {
if (sprite.isEmpty) return;
Rect target = Rect(position, region.size * scale);
Rect source = region;
if (region.size.x <= 0.0f || region.size.y <= 0.0f) {
target = Rect(position, sprite.size * scale);
source = Rect(sprite.size);
} else {
target = Rect(position, region.size * scale);
source = region;
}
final switch (flip) {
case Flip.none: break;
case Flip.x: source.size.x *= -1.0f; break;
case Flip.y: source.size.y *= -1.0f; break;
case Flip.xy: source.size *= Vec2(-1.0f); break;
}
ray.DrawTexturePro(
sprite.data,
source.floor().toRay(),
target.floor().toRay(),
target.origin(hook).floor().toRay(),
rotation,
color.toRay(),
);
}
void drawTile(Sprite sprite, uint tileID, Vec2 cellSize, Vec2 position = Vec2(), float rotation = 0.0f, Vec2 scale = Vec2(1.0f), Color color = white, Flip flip = Flip.none, Hook hook = Hook.topLeft) {
uint gridWidth = cast(uint) (sprite.width / cellSize.x).floor();
uint gridHeight = cast(uint) (sprite.height / cellSize.y).floor();
drawSprite(
sprite,
Rect((tileID % gridWidth) * cellSize.x, (tileID / gridHeight) * cellSize.y, cellSize.x, cellSize.y),
position,
rotation,
scale,
color,
flip,
hook,
);
}
void drawTileMap(Sprite sprite, TileMap map, Camera camera = Camera(), Vec2 position = Vec2(), float rotation = 0.0f, Vec2 scale = Vec2(1.0f), Color color = white, Flip flip = Flip.none, Hook hook = Hook.topLeft) {
size_t col1, col2, row1, row2;
if (camera.isAttached) {
col1 = cast(size_t) clamp((camera.point(Hook.topLeft).x - position.x) / map.cellWidth - 4.0f, 0, map.colCount).floor();
col2 = cast(size_t) clamp((camera.point(Hook.bottomRight).x - position.x) / map.cellWidth + 4.0f, 0, map.colCount).floor();
row1 = cast(size_t) clamp((camera.point(Hook.topLeft).y - position.y) / map.cellHeight - 4.0f, 0, map.rowCount).floor();
row2 = cast(size_t) clamp((camera.point(Hook.bottomRight).y - position.y) / map.cellHeight + 4.0f, 0, map.rowCount).floor();
} else {
col1 = 0;
col2 = map.colCount;
row1 = 0;
row2 = map.rowCount;
}
foreach (row; row1 .. row2) {
foreach (col; col1 .. col2) {
if (map[row, col] == -1) {
continue;
}
drawTile(
sprite,
map[row, col],
map.cellSize,
Vec2(position.x + col * map.cellSize.x, position.y + row * map.cellSize.y),
rotation,
scale,
color,
flip,
hook,
);
}
}
}
Font raylibFont() {
return toPopka(ray.GetFontDefault());
}
// -----------------
// TODO: Text drawing and measuring need rethinking. Do it later...
// NOTE: drawing sprites, text, tiles.
// NOTE: drawign text will be the raylib func copy-pasted but will take popka font. This will also let us remove the global spacing.
// NOTE: Maybe we can measure the text without using the raylib function???
Vec2 measureText(const(char)[] text, Font font, float size, Vec2 spacing) {
static char[1024] buf = void;
const(char)[] strz;
if (text.isStrz) {
strz = text;
} else {
buf[0 .. text.length] = text;
buf[text.length] = '\0';
strz = buf[0 .. text.length];
}
bool hasNewLineChar;
foreach (c; strz) {
if (c == '\n') {
hasNewLineChar = true;
break;
}
}
ray.SetTextLineSpacing(cast(int) spacing.y);
Vec2 trueSize = ray.MeasureTextEx(font.data, &strz[0], size, spacing.x).toPopka();
return Vec2(trueSize.x, trueSize.y - (hasNewLineChar ? spacing.y : 0.0f));
}
Vec2 measureText(const(char)[] text) {
return measureText(text, raylibFont, 10.0f, Vec2(1.0f, 12.0f));
}
// TODO: Needs thinking of how text drawing works.
void drawText(const(char)[] text, Font font, float size, Vec2 spacing, Vec2 positionition, Vec2 origin, float rotation, Color color) {
static char[1024] buf = void;
const(char)[] strz;
if (text.isStrz) {
strz = text;
} else {
buf[0 .. text.length] = text;
buf[text.length] = '\0';
strz = buf[0 .. text.length];
}
ray.SetTextLineSpacing(cast(int) spacing.y);
ray.DrawTextPro(font.data, &strz[0], positionition.toRay(), origin.toRay(), rotation, size, spacing.x, color.toRay());
}
// TODO: Needs thinking of how drawing works.
void drawText(const(char)[] text, Vec2 positionition, Color color) {
drawText(text, raylibFont, 10.0f, Vec2(1.0f, 12.0f), positionition, Vec2(), 0.0f, color);
}
// TODO: Needs thinking of how drawing works.
void drawText(const(char)[] text) {
drawText(text, Vec2(8.0f), white);
}
// TODO: Needs thinking of how drawing works.
void drawFPS(float x, float y) {
drawText("FPS: {}".fmt(fps));
}
unittest {}

1695
vendor/ray/raylib.d vendored Normal file

File diff suppressed because it is too large Load diff

678
vendor/ray/raymath.d vendored Normal file
View file

@ -0,0 +1,678 @@
/**********************************************************************************************
*
* raymath v1.5 - Math functions to work with Vector2, Vector3, Matrix and Quaternions
*
* CONVENTIONS:
* - Matrix structure is defined as row-major (memory layout) but parameters naming AND all
* math operations performed by the library consider the structure as it was column-major
* It is like transposed versions of the matrices are used for all the maths
* It benefits some functions making them cache-friendly and also avoids matrix
* transpositions sometimes required by OpenGL
* Example: In memory order, row0 is [m0 m4 m8 m12] but in semantic math row0 is [m0 m1 m2 m3]
* - Functions are always self-contained, no function use another raymath function inside,
* required code is directly re-implemented inside
* - Functions input parameters are always received by value (2 unavoidable exceptions)
* - Functions use always a "result" variable for return
* - Functions are always defined inline
* - Angles are always in radians (DEG2RAD/RAD2DEG macros provided for convenience)
* - No compound literals used to make sure libray is compatible with C++
*
* CONFIGURATION:
* #define RAYMATH_IMPLEMENTATION
* Generates the implementation of the library into the included file.
* If not defined, the library is in header only mode and can be included in other headers
* or source files without problems. But only ONE file should hold the implementation.
*
* #define RAYMATH_STATIC_INLINE
* Define static inline functions code, so #include header suffices for use.
* This may use up lots of memory.
*
*
* LICENSE: zlib/libpng
*
* 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.
*
**********************************************************************************************/
module popka.vendor.ray.raymath;
extern (C):
// Function specifiers definition
// We are building raylib as a Win32 shared library (.dll).
// We are using raylib as a Win32 shared library (.dll)
// Provide external definition
// Functions may be inlined, no external out-of-line definition
// plain inline not supported by tinycc (See issue #435) // Functions may be inlined or external definition used
//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
enum PI = 3.14159265358979323846f;
enum EPSILON = 0.000001f;
enum DEG2RAD = PI / 180.0f;
enum RAD2DEG = 180.0f / PI;
// Get float vector for Matrix
extern (D) auto MatrixToFloat(T)(auto ref T mat)
{
return MatrixToFloatV(mat).v;
}
// Get float vector for Vector3
extern (D) auto Vector3ToFloat(T)(auto ref T vec)
{
return Vector3ToFloatV(vec).v;
}
//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
// Vector2 type
struct Vector2
{
float x;
float y;
}
// Vector3 type
struct Vector3
{
float x;
float y;
float z;
}
// Vector4 type
struct Vector4
{
float x;
float y;
float z;
float w;
}
// Quaternion type
alias Quaternion = Vector4;
// Matrix type (OpenGL style 4x4 - right handed, column major)
struct Matrix
{
float m0;
float m4;
float m8;
float m12; // Matrix first row (4 components)
float m1;
float m5;
float m9;
float m13; // Matrix second row (4 components)
float m2;
float m6;
float m10;
float m14; // Matrix third row (4 components)
float m3;
float m7;
float m11;
float m15; // Matrix fourth row (4 components)
}
// NOTE: Helper types to be used instead of array return types for *ToFloat functions
struct float3
{
float[3] v;
}
struct float16
{
float[16] v;
}
// Required for: sinf(), cosf(), tan(), atan2f(), sqrtf(), floor(), fminf(), fmaxf(), fabs()
//----------------------------------------------------------------------------------
// Module Functions Definition - Utils math
//----------------------------------------------------------------------------------
// Clamp float value
float Clamp (float value, float min, float max);
// Calculate linear interpolation between two floats
float Lerp (float start, float end, float amount);
// Normalize input value within input range
float Normalize (float value, float start, float end);
// Remap input value within input range to output range
float Remap (
float value,
float inputStart,
float inputEnd,
float outputStart,
float outputEnd);
// Wrap input value from min to max
float Wrap (float value, float min, float max);
// Check whether two given floats are almost equal
int FloatEquals (float x, float y);
//----------------------------------------------------------------------------------
// Module Functions Definition - Vector2 math
//----------------------------------------------------------------------------------
// Vector with components value 0.0f
Vector2 Vector2Zero ();
// Vector with components value 1.0f
Vector2 Vector2One ();
// Add two vectors (v1 + v2)
Vector2 Vector2Add (Vector2 v1, Vector2 v2);
// Add vector and float value
Vector2 Vector2AddValue (Vector2 v, float add);
// Subtract two vectors (v1 - v2)
Vector2 Vector2Subtract (Vector2 v1, Vector2 v2);
// Subtract vector by float value
Vector2 Vector2SubtractValue (Vector2 v, float sub);
// Calculate vector length
float Vector2Length (Vector2 v);
// Calculate vector square length
float Vector2LengthSqr (Vector2 v);
// Calculate two vectors dot product
float Vector2DotProduct (Vector2 v1, Vector2 v2);
// Calculate distance between two vectors
float Vector2Distance (Vector2 v1, Vector2 v2);
// Calculate square distance between two vectors
float Vector2DistanceSqr (Vector2 v1, Vector2 v2);
// Calculate angle between two vectors
// NOTE: Angle is calculated from origin point (0, 0)
float Vector2Angle (Vector2 v1, Vector2 v2);
// Calculate angle defined by a two vectors line
// NOTE: Parameters need to be normalized
// Current implementation should be aligned with glm::angle
// TODO(10/9/2023): Currently angles move clockwise, determine if this is wanted behavior
float Vector2LineAngle (Vector2 start, Vector2 end);
// Scale vector (multiply by value)
Vector2 Vector2Scale (Vector2 v, float scale);
// Multiply vector by vector
Vector2 Vector2Multiply (Vector2 v1, Vector2 v2);
// Negate vector
Vector2 Vector2Negate (Vector2 v);
// Divide vector by vector
Vector2 Vector2Divide (Vector2 v1, Vector2 v2);
// Normalize provided vector
Vector2 Vector2Normalize (Vector2 v);
// Transforms a Vector2 by a given Matrix
Vector2 Vector2Transform (Vector2 v, Matrix mat);
// Calculate linear interpolation between two vectors
Vector2 Vector2Lerp (Vector2 v1, Vector2 v2, float amount);
// Calculate reflected vector to normal
// Dot product
Vector2 Vector2Reflect (Vector2 v, Vector2 normal);
// Rotate vector by angle
Vector2 Vector2Rotate (Vector2 v, float angle);
// Move Vector towards target
Vector2 Vector2MoveTowards (Vector2 v, Vector2 target, float maxDistance);
// Invert the given vector
Vector2 Vector2Invert (Vector2 v);
// Clamp the components of the vector between
// min and max values specified by the given vectors
Vector2 Vector2Clamp (Vector2 v, Vector2 min, Vector2 max);
// Clamp the magnitude of the vector between two min and max values
Vector2 Vector2ClampValue (Vector2 v, float min, float max);
// Check whether two given vectors are almost equal
int Vector2Equals (Vector2 p, Vector2 q);
//----------------------------------------------------------------------------------
// Module Functions Definition - Vector3 math
//----------------------------------------------------------------------------------
// Vector with components value 0.0f
Vector3 Vector3Zero ();
// Vector with components value 1.0f
Vector3 Vector3One ();
// Add two vectors
Vector3 Vector3Add (Vector3 v1, Vector3 v2);
// Add vector and float value
Vector3 Vector3AddValue (Vector3 v, float add);
// Subtract two vectors
Vector3 Vector3Subtract (Vector3 v1, Vector3 v2);
// Subtract vector by float value
Vector3 Vector3SubtractValue (Vector3 v, float sub);
// Multiply vector by scalar
Vector3 Vector3Scale (Vector3 v, float scalar);
// Multiply vector by vector
Vector3 Vector3Multiply (Vector3 v1, Vector3 v2);
// Calculate two vectors cross product
Vector3 Vector3CrossProduct (Vector3 v1, Vector3 v2);
// Calculate one vector perpendicular vector
// Cross product between vectors
Vector3 Vector3Perpendicular (Vector3 v);
// Calculate vector length
float Vector3Length (const Vector3 v);
// Calculate vector square length
float Vector3LengthSqr (const Vector3 v);
// Calculate two vectors dot product
float Vector3DotProduct (Vector3 v1, Vector3 v2);
// Calculate distance between two vectors
float Vector3Distance (Vector3 v1, Vector3 v2);
// Calculate square distance between two vectors
float Vector3DistanceSqr (Vector3 v1, Vector3 v2);
// Calculate angle between two vectors
float Vector3Angle (Vector3 v1, Vector3 v2);
// Negate provided vector (invert direction)
Vector3 Vector3Negate (Vector3 v);
// Divide vector by vector
Vector3 Vector3Divide (Vector3 v1, Vector3 v2);
// Normalize provided vector
Vector3 Vector3Normalize (Vector3 v);
//Calculate the projection of the vector v1 on to v2
Vector3 Vector3Project (Vector3 v1, Vector3 v2);
//Calculate the rejection of the vector v1 on to v2
Vector3 Vector3Reject (Vector3 v1, Vector3 v2);
// Orthonormalize provided vectors
// Makes vectors normalized and orthogonal to each other
// Gram-Schmidt function implementation
// Vector3Normalize(*v1);
// Vector3CrossProduct(*v1, *v2)
// Vector3Normalize(vn1);
// Vector3CrossProduct(vn1, *v1)
void Vector3OrthoNormalize (Vector3* v1, Vector3* v2);
// Transforms a Vector3 by a given Matrix
Vector3 Vector3Transform (Vector3 v, Matrix mat);
// Transform a vector by quaternion rotation
Vector3 Vector3RotateByQuaternion (Vector3 v, Quaternion q);
// Rotates a vector around an axis
// Using Euler-Rodrigues Formula
// Ref.: https://en.wikipedia.org/w/index.php?title=Euler%E2%80%93Rodrigues_formula
// Vector3Normalize(axis);
// Vector3CrossProduct(w, v)
// Vector3CrossProduct(w, wv)
// Vector3Scale(wv, 2*a)
// Vector3Scale(wwv, 2)
Vector3 Vector3RotateByAxisAngle (Vector3 v, Vector3 axis, float angle);
// Calculate linear interpolation between two vectors
Vector3 Vector3Lerp (Vector3 v1, Vector3 v2, float amount);
// Calculate reflected vector to normal
// I is the original vector
// N is the normal of the incident plane
// R = I - (2*N*(DotProduct[I, N]))
Vector3 Vector3Reflect (Vector3 v, Vector3 normal);
// Get min value for each pair of components
Vector3 Vector3Min (Vector3 v1, Vector3 v2);
// Get max value for each pair of components
Vector3 Vector3Max (Vector3 v1, Vector3 v2);
// Compute barycenter coordinates (u, v, w) for point p with respect to triangle (a, b, c)
// NOTE: Assumes P is on the plane of the triangle
// Vector3Subtract(b, a)
// Vector3Subtract(c, a)
// Vector3Subtract(p, a)
// Vector3DotProduct(v0, v0)
// Vector3DotProduct(v0, v1)
// Vector3DotProduct(v1, v1)
// Vector3DotProduct(v2, v0)
// Vector3DotProduct(v2, v1)
Vector3 Vector3Barycenter (Vector3 p, Vector3 a, Vector3 b, Vector3 c);
// Projects a Vector3 from screen space into object space
// NOTE: We are avoiding calling other raymath functions despite available
// Calculate unprojected matrix (multiply view matrix by projection matrix) and invert it
// MatrixMultiply(view, projection);
// Calculate inverted matrix -> MatrixInvert(matViewProj);
// Cache the matrix values (speed optimization)
// Calculate the invert determinant (inlined to avoid double-caching)
// Create quaternion from source point
// Multiply quat point by unprojecte matrix
// QuaternionTransform(quat, matViewProjInv)
// Normalized world points in vectors
Vector3 Vector3Unproject (Vector3 source, Matrix projection, Matrix view);
// Get Vector3 as float array
float3 Vector3ToFloatV (Vector3 v);
// Invert the given vector
Vector3 Vector3Invert (Vector3 v);
// Clamp the components of the vector between
// min and max values specified by the given vectors
Vector3 Vector3Clamp (Vector3 v, Vector3 min, Vector3 max);
// Clamp the magnitude of the vector between two values
Vector3 Vector3ClampValue (Vector3 v, float min, float max);
// Check whether two given vectors are almost equal
int Vector3Equals (Vector3 p, Vector3 q);
// Compute the direction of a refracted ray
// v: normalized direction of the incoming ray
// n: normalized normal vector of the interface of two optical media
// r: 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
Vector3 Vector3Refract (Vector3 v, Vector3 n, float r);
//----------------------------------------------------------------------------------
// Module Functions Definition - Matrix math
//----------------------------------------------------------------------------------
// Compute matrix determinant
// Cache the matrix values (speed optimization)
float MatrixDeterminant (Matrix mat);
// Get the trace of the matrix (sum of the values along the diagonal)
float MatrixTrace (Matrix mat);
// Transposes provided matrix
Matrix MatrixTranspose (Matrix mat);
// Invert provided matrix
// Cache the matrix values (speed optimization)
// Calculate the invert determinant (inlined to avoid double-caching)
Matrix MatrixInvert (Matrix mat);
// Get identity matrix
Matrix MatrixIdentity ();
// Add two matrices
Matrix MatrixAdd (Matrix left, Matrix right);
// Subtract two matrices (left - right)
Matrix MatrixSubtract (Matrix left, Matrix right);
// Get two matrix multiplication
// NOTE: When multiplying matrices... the order matters!
Matrix MatrixMultiply (Matrix left, Matrix right);
// Get translation matrix
Matrix MatrixTranslate (float x, float y, float z);
// Create rotation matrix from axis and angle
// NOTE: Angle should be provided in radians
Matrix MatrixRotate (Vector3 axis, float angle);
// Get x-rotation matrix
// NOTE: Angle must be provided in radians
// MatrixIdentity()
Matrix MatrixRotateX (float angle);
// Get y-rotation matrix
// NOTE: Angle must be provided in radians
// MatrixIdentity()
Matrix MatrixRotateY (float angle);
// Get z-rotation matrix
// NOTE: Angle must be provided in radians
// MatrixIdentity()
Matrix MatrixRotateZ (float angle);
// Get xyz-rotation matrix
// NOTE: Angle must be provided in radians
// MatrixIdentity()
Matrix MatrixRotateXYZ (Vector3 angle);
// Get zyx-rotation matrix
// NOTE: Angle must be provided in radians
Matrix MatrixRotateZYX (Vector3 angle);
// Get scaling matrix
Matrix MatrixScale (float x, float y, float z);
// Get perspective projection matrix
Matrix MatrixFrustum (
double left,
double right,
double bottom,
double top,
double near,
double far);
// Get perspective projection matrix
// NOTE: Fovy angle must be provided in radians
// MatrixFrustum(-right, right, -top, top, near, far);
Matrix MatrixPerspective (
double fovY,
double aspect,
double nearPlane,
double farPlane);
// Get orthographic projection matrix
Matrix MatrixOrtho (
double left,
double right,
double bottom,
double top,
double nearPlane,
double farPlane);
// Get camera look-at matrix (view matrix)
// Vector3Subtract(eye, target)
// Vector3Normalize(vz)
// Vector3CrossProduct(up, vz)
// Vector3Normalize(x)
// Vector3CrossProduct(vz, vx)
// Vector3DotProduct(vx, eye)
// Vector3DotProduct(vy, eye)
// Vector3DotProduct(vz, eye)
Matrix MatrixLookAt (Vector3 eye, Vector3 target, Vector3 up);
// Get float array of matrix data
float16 MatrixToFloatV (Matrix mat);
//----------------------------------------------------------------------------------
// Module Functions Definition - Quaternion math
//----------------------------------------------------------------------------------
// Add two quaternions
Quaternion QuaternionAdd (Quaternion q1, Quaternion q2);
// Add quaternion and float value
Quaternion QuaternionAddValue (Quaternion q, float add);
// Subtract two quaternions
Quaternion QuaternionSubtract (Quaternion q1, Quaternion q2);
// Subtract quaternion and float value
Quaternion QuaternionSubtractValue (Quaternion q, float sub);
// Get identity quaternion
Quaternion QuaternionIdentity ();
// Computes the length of a quaternion
float QuaternionLength (Quaternion q);
// Normalize provided quaternion
Quaternion QuaternionNormalize (Quaternion q);
// Invert provided quaternion
Quaternion QuaternionInvert (Quaternion q);
// Calculate two quaternion multiplication
Quaternion QuaternionMultiply (Quaternion q1, Quaternion q2);
// Scale quaternion by float value
Quaternion QuaternionScale (Quaternion q, float mul);
// Divide two quaternions
Quaternion QuaternionDivide (Quaternion q1, Quaternion q2);
// Calculate linear interpolation between two quaternions
Quaternion QuaternionLerp (Quaternion q1, Quaternion q2, float amount);
// Calculate slerp-optimized interpolation between two quaternions
// QuaternionLerp(q1, q2, amount)
// QuaternionNormalize(q);
Quaternion QuaternionNlerp (Quaternion q1, Quaternion q2, float amount);
// Calculates spherical linear interpolation between two quaternions
Quaternion QuaternionSlerp (Quaternion q1, Quaternion q2, float amount);
// Calculate quaternion based on the rotation from one vector to another
// Vector3DotProduct(from, to)
// Vector3CrossProduct(from, to)
// QuaternionNormalize(q);
// NOTE: Normalize to essentially nlerp the original and identity to 0.5
Quaternion QuaternionFromVector3ToVector3 (Vector3 from, Vector3 to);
// Get a quaternion for a given rotation matrix
Quaternion QuaternionFromMatrix (Matrix mat);
// Get a matrix for a given quaternion
// MatrixIdentity()
Matrix QuaternionToMatrix (Quaternion q);
// Get rotation quaternion for an angle and axis
// NOTE: Angle must be provided in radians
// Vector3Normalize(axis)
// QuaternionNormalize(q);
Quaternion QuaternionFromAxisAngle (Vector3 axis, float angle);
// Get the rotation angle and axis for a given quaternion
// QuaternionNormalize(q);
// This occurs when the angle is zero.
// Not a problem: just set an arbitrary normalized axis.
void QuaternionToAxisAngle (Quaternion q, Vector3* outAxis, float* outAngle);
// Get the quaternion equivalent to Euler angles
// NOTE: Rotation order is ZYX
Quaternion QuaternionFromEuler (float pitch, float yaw, float roll);
// Get the Euler angles equivalent to quaternion (roll, pitch, yaw)
// NOTE: Angles are returned in a Vector3 struct in radians
// Roll (x-axis rotation)
// Pitch (y-axis rotation)
// Yaw (z-axis rotation)
Vector3 QuaternionToEuler (Quaternion q);
// Transform a quaternion given a transformation matrix
Quaternion QuaternionTransform (Quaternion q, Matrix mat);
// Check whether two given quaternions are almost equal
int QuaternionEquals (Quaternion p, Quaternion q);
// RAYMATH_H

2052
vendor/ray/rlgl.d vendored Normal file

File diff suppressed because it is too large Load diff