mirror of
https://github.com/Kapendev/parin.git
synced 2025-04-25 12:39:54 +03:00
First.
This commit is contained in:
commit
bd2d04caee
22 changed files with 7170 additions and 0 deletions
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal 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
21
LICENSE
Normal 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
29
README.md
Normal 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
6
TODO.md
Normal 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
13
basic.d
Normal 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
100
core/ascii.d
Normal 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
20
core/basic.d
Normal 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
48
core/color.d
Normal 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
419
core/container.d
Normal 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
78
core/fmt.d
Normal 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
50
core/io.d
Normal 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
488
core/math.d
Normal 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
279
core/strconv.d
Normal 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
149
core/strutils.d
Normal 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
82
core/sumtype.d
Normal 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
10
dub.json
Normal 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
13
game/basic.d
Normal 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
86
game/dialogue.d
Normal 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
838
game/engine.d
Normal 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
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
678
vendor/ray/raymath.d
vendored
Normal 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
2052
vendor/ray/rlgl.d
vendored
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue