Font and tile map fix.

This commit is contained in:
Kapendev 2024-12-07 14:22:21 +02:00
parent 1ee5d1a9aa
commit 0bfc42f18d
2 changed files with 154 additions and 25 deletions

View file

@ -206,6 +206,7 @@ struct DrawOptions {
/// Represents an identifier for a managed resource. /// Represents an identifier for a managed resource.
struct TextId { struct TextId {
GenerationalIndex data; GenerationalIndex data;
alias data this; alias data this;
@safe @nogc nothrow: @safe @nogc nothrow:
@ -295,6 +296,7 @@ struct Texture {
/// Represents an identifier for a managed resource. /// Represents an identifier for a managed resource.
struct TextureId { struct TextureId {
GenerationalIndex data; GenerationalIndex data;
alias data this; alias data this;
@safe @nogc nothrow: @safe @nogc nothrow:
@ -386,6 +388,7 @@ struct Font {
/// Represents an identifier for a managed resource. /// Represents an identifier for a managed resource.
struct FontId { struct FontId {
GenerationalIndex data; GenerationalIndex data;
alias data this; alias data this;
@safe @nogc nothrow: @safe @nogc nothrow:
@ -528,6 +531,7 @@ struct Sound {
/// Represents an identifier for a managed resource. /// Represents an identifier for a managed resource.
struct SoundId { struct SoundId {
GenerationalIndex data; GenerationalIndex data;
alias data this; alias data this;
@safe @nogc nothrow: @safe @nogc nothrow:
@ -811,6 +815,7 @@ struct EngineFlags {
bool isPixelSnapped; bool isPixelSnapped;
bool isPixelPerfect; bool isPixelPerfect;
bool isCursorVisible; bool isCursorVisible;
bool canUseAssetsPath;
} }
struct EngineFullscreenState { struct EngineFullscreenState {
@ -902,6 +907,7 @@ struct EngineViewport {
Viewport data; Viewport data;
int targetWidth; int targetWidth;
int targetHeight; int targetHeight;
alias data this; alias data this;
@safe @nogc nothrow: @safe @nogc nothrow:
@ -931,16 +937,16 @@ struct EngineState {
EngineResources resources; EngineResources resources;
EngineFullscreenState fullscreenState; EngineFullscreenState fullscreenState;
Color borderColor;
Sz tickCount;
LStr assetsPath; LStr assetsPath;
LStr tempText; LStr tempText;
Camera currentCamera; Camera currentCamera;
Viewport currentViewport; Viewport currentViewport;
Color borderColor;
Filter defaultFilter; Filter defaultFilter;
Wrap defaultWrap; Wrap defaultWrap;
Sz tickCount; rl.FilePathList filePathListBuffer;
@safe @nogc nothrow: @safe @nogc nothrow:
@ -1114,11 +1120,30 @@ IStr toAssetsPath(IStr path) {
return pathConcat(assetsPath, path).pathFormat(); return pathConcat(assetsPath, path).pathFormat();
} }
bool canUseAssetsPath() {
return engineState.flags.canUseAssetsPath;
}
void setCanUseAssetsPath(bool value) {
engineState.flags.canUseAssetsPath = value;
}
@trusted
IStr[] droppedFilePaths() {
static IStr[128] buffer;
foreach (i; 0 .. engineState.filePathListBuffer.count) {
buffer[i] = engineState.filePathListBuffer.paths[i].toStr();
}
return buffer[0 .. engineState.filePathListBuffer.count];
}
/// Loads a text file from the assets folder. /// Loads a text file from the assets folder.
/// The resource remains valid until this function is called again. /// The resource remains valid until this function is called again.
/// Supports both forward slashes and backslashes in file paths. /// Supports both forward slashes and backslashes in file paths.
Result!IStr loadTempText(IStr path) { Result!IStr loadTempText(IStr path) {
auto fault = readTextIntoBuffer(path.toAssetsPath(), engineState.tempText); auto targetPath = canUseAssetsPath ? path.toAssetsPath() : path;
auto fault = readTextIntoBuffer(targetPath, engineState.tempText);
return Result!IStr(engineState.tempText.items, fault); return Result!IStr(engineState.tempText.items, fault);
} }
@ -1126,7 +1151,8 @@ Result!IStr loadTempText(IStr path) {
/// The resource must be manually freed. /// The resource must be manually freed.
/// Supports both forward slashes and backslashes in file paths. /// Supports both forward slashes and backslashes in file paths.
Result!LStr loadRawText(IStr path) { Result!LStr loadRawText(IStr path) {
return readText(path.toAssetsPath()); auto targetPath = canUseAssetsPath ? path.toAssetsPath() : path;
return readText(targetPath);
} }
/// Loads a text file from the assets folder. /// Loads a text file from the assets folder.
@ -1157,7 +1183,8 @@ TextId loadText(IStr path, Sz tag = 0) {
/// Supports both forward slashes and backslashes in file paths. /// Supports both forward slashes and backslashes in file paths.
@trusted @trusted
Result!Texture loadRawTexture(IStr path) { Result!Texture loadRawTexture(IStr path) {
auto value = rl.LoadTexture(path.toAssetsPath().toCStr().getOr()).toParin(); auto targetPath = canUseAssetsPath ? path.toAssetsPath() : path;
auto value = rl.LoadTexture(targetPath.toCStr().getOr()).toParin();
value.setFilter(engineState.defaultFilter); value.setFilter(engineState.defaultFilter);
value.setWrap(engineState.defaultWrap); value.setWrap(engineState.defaultWrap);
return Result!Texture(value, value.isEmpty.toFault(Fault.cantFind)); return Result!Texture(value, value.isEmpty.toFault(Fault.cantFind));
@ -1191,7 +1218,8 @@ TextureId loadTexture(IStr path, Sz tag = 0) {
/// Supports both forward slashes and backslashes in file paths. /// Supports both forward slashes and backslashes in file paths.
@trusted @trusted
Result!Font loadRawFont(IStr path, int size, int runeSpacing, int lineSpacing, IStr32 runes = "") { Result!Font loadRawFont(IStr path, int size, int runeSpacing, int lineSpacing, IStr32 runes = "") {
auto value = rl.LoadFontEx(path.toAssetsPath().toCStr().getOr(), size, runes == "" ? null : cast(int*) runes.ptr, cast(int) runes.length).toParin(); auto targetPath = canUseAssetsPath ? path.toAssetsPath() : path;
auto value = rl.LoadFontEx(targetPath.toCStr().getOr(), size, runes == "" ? null : cast(int*) runes.ptr, cast(int) runes.length).toParin();
if (value.data.texture.id == engineFont.data.texture.id) { if (value.data.texture.id == engineFont.data.texture.id) {
value = Font(); value = Font();
} }
@ -1230,11 +1258,12 @@ FontId loadFont(IStr path, int size, int runeSpacing, int lineSpacing, IStr32 ru
/// Supports both forward slashes and backslashes in file paths. /// Supports both forward slashes and backslashes in file paths.
@trusted @trusted
Result!Sound loadRawSound(IStr path, float volume, float pitch) { Result!Sound loadRawSound(IStr path, float volume, float pitch) {
auto targetPath = canUseAssetsPath ? path.toAssetsPath() : path;
auto value = Sound(); auto value = Sound();
if (path.endsWith(".wav")) { if (path.endsWith(".wav")) {
value.data = rl.LoadSound(path.toAssetsPath().toCStr().getOr()); value.data = rl.LoadSound(targetPath.toCStr().getOr());
} else { } else {
value.data = rl.LoadMusicStream(path.toAssetsPath().toCStr().getOr()); value.data = rl.LoadMusicStream(targetPath.toCStr().getOr());
} }
value.setVolume(volume); value.setVolume(volume);
value.setPitch(pitch); value.setPitch(pitch);
@ -1267,7 +1296,8 @@ SoundId loadSound(IStr path, float volume, float pitch, Sz tag = 0) {
/// Saves a text file to the assets folder. /// Saves a text file to the assets folder.
/// Supports both forward slashes and backslashes in file paths. /// Supports both forward slashes and backslashes in file paths.
Fault saveText(IStr path, IStr text) { Fault saveText(IStr path, IStr text) {
return writeText(path.toAssetsPath(), text); auto targetPath = canUseAssetsPath ? path.toAssetsPath() : path;
return writeText(targetPath, text);
} }
/// Frees all managed resources associated with the given tag, or all if no tag is specified. /// Frees all managed resources associated with the given tag, or all if no tag is specified.
@ -1292,6 +1322,7 @@ void openWindow(int width, int height, IStr appPath, IStr title = "Parin") {
engineState.viewport.color = gray; engineState.viewport.color = gray;
engineState.fullscreenState.lastWindowWidth = width; engineState.fullscreenState.lastWindowWidth = width;
engineState.fullscreenState.lastWindowHeight = height; engineState.fullscreenState.lastWindowHeight = height;
engineState.flags.canUseAssetsPath = true;
engineState.assetsPath.append(pathConcat(appPath.pathDir, "assets")); engineState.assetsPath.append(pathConcat(appPath.pathDir, "assets"));
engineState.tempText.reserve(8192); engineState.tempText.reserve(8192);
// NOTE: This line is used for fixing an alpha bug with render textures. // NOTE: This line is used for fixing an alpha bug with render textures.
@ -1316,9 +1347,16 @@ void updateWindow(bool function(float dt) updateFunc) {
rl.ClearBackground(engineState.viewport.color.toRl()); rl.ClearBackground(engineState.viewport.color.toRl());
// The main loop. // The main loop.
if (rl.IsFileDropped) {
engineState.filePathListBuffer = rl.LoadDroppedFiles();
}
auto dt = deltaTime; auto dt = deltaTime;
auto result = _updateFunc(dt); auto result = _updateFunc(dt);
engineState.tickCount = (engineState.tickCount + 1) % engineState.tickCount.max; engineState.tickCount = (engineState.tickCount + 1) % engineState.tickCount.max;
if (rl.IsFileDropped) {
rl.UnloadDroppedFiles(engineState.filePathListBuffer);
engineState.filePathListBuffer = rl.FilePathList();
}
// End drawing. // End drawing.
if (isResolutionLocked) { if (isResolutionLocked) {
@ -1780,7 +1818,15 @@ bool isDown(char key) {
/// Returns true if the specified key is currently pressed. /// Returns true if the specified key is currently pressed.
@trusted @trusted
bool isDown(Keyboard key) { bool isDown(Keyboard key) {
return rl.IsKeyDown(key); if (key == Keyboard.shift) {
return rl.IsKeyDown(key) || rl.IsKeyDown(rl.KEY_RIGHT_SHIFT);
} else if (key == Keyboard.ctrl) {
return rl.IsKeyDown(key) || rl.IsKeyDown(rl.KEY_RIGHT_CONTROL);
} else if (key == Keyboard.alt) {
return rl.IsKeyDown(key) || rl.IsKeyDown(rl.KEY_RIGHT_ALT);
} else {
return rl.IsKeyDown(key);
}
} }
/// Returns true if the specified key is currently pressed. /// Returns true if the specified key is currently pressed.
@ -1804,7 +1850,15 @@ bool isPressed(char key) {
/// Returns true if the specified key was pressed. /// Returns true if the specified key was pressed.
@trusted @trusted
bool isPressed(Keyboard key) { bool isPressed(Keyboard key) {
return rl.IsKeyPressed(key); if (key == Keyboard.shift) {
return rl.IsKeyPressed(key) || rl.IsKeyPressed(rl.KEY_RIGHT_SHIFT);
} else if (key == Keyboard.ctrl) {
return rl.IsKeyPressed(key) || rl.IsKeyPressed(rl.KEY_RIGHT_CONTROL);
} else if (key == Keyboard.alt) {
return rl.IsKeyPressed(key) || rl.IsKeyPressed(rl.KEY_RIGHT_ALT);
} else {
return rl.IsKeyPressed(key);
}
} }
/// Returns true if the specified key was pressed. /// Returns true if the specified key was pressed.
@ -1828,7 +1882,15 @@ bool isReleased(char key) {
/// Returns true if the specified key was released. /// Returns true if the specified key was released.
@trusted @trusted
bool isReleased(Keyboard key) { bool isReleased(Keyboard key) {
return rl.IsKeyReleased(key); if (key == Keyboard.shift) {
return rl.IsKeyReleased(key) || rl.IsKeyReleased(rl.KEY_RIGHT_SHIFT);
} else if (key == Keyboard.ctrl) {
return rl.IsKeyReleased(key) || rl.IsKeyReleased(rl.KEY_RIGHT_CONTROL);
} else if (key == Keyboard.alt) {
return rl.IsKeyReleased(key) || rl.IsKeyReleased(rl.KEY_RIGHT_ALT);
} else {
return rl.IsKeyReleased(key);
}
} }
/// Returns true if the specified key was released. /// Returns true if the specified key was released.
@ -2094,11 +2156,7 @@ void drawRune(Font font, dchar rune, Vec2 position, DrawOptions options = DrawOp
} }
rl.rlRotatef(options.rotation, 0.0f, 0.0f, 1.0f); rl.rlRotatef(options.rotation, 0.0f, 0.0f, 1.0f);
rl.rlScalef(options.scale.x, options.scale.y, 1.0f); rl.rlScalef(options.scale.x, options.scale.y, 1.0f);
if (isPixelSnapped || isPixelPerfect) { rl.rlTranslatef(-origin.x.floor(), -origin.y.floor(), 0.0f);
rl.rlTranslatef(-origin.x.floor(), -origin.y.floor(), 0.0f);
} else {
rl.rlTranslatef(-origin.x, -origin.y, 0.0f);
}
rl.DrawTextCodepoint(font.data, rune, rl.Vector2(0.0f, 0.0f), font.size, options.color.toRl()); rl.DrawTextCodepoint(font.data, rune, rl.Vector2(0.0f, 0.0f), font.size, options.color.toRl());
rl.rlPopMatrix(); rl.rlPopMatrix();
} }
@ -2122,11 +2180,7 @@ void drawText(Font font, IStr text, Vec2 position, DrawOptions options = DrawOpt
} }
rl.rlRotatef(options.rotation, 0.0f, 0.0f, 1.0f); rl.rlRotatef(options.rotation, 0.0f, 0.0f, 1.0f);
rl.rlScalef(options.scale.x, options.scale.y, 1.0f); rl.rlScalef(options.scale.x, options.scale.y, 1.0f);
if (isPixelSnapped || isPixelPerfect) { rl.rlTranslatef(floor(-origin.x), floor(-origin.y), 0.0f);
rl.rlTranslatef(floor(-origin.x), floor(-origin.y), 0.0f);
} else {
rl.rlTranslatef(-origin.x, -origin.y, 0.0f);
}
auto textOffsetY = 0.0f; // Offset between lines (on linebreak '\n'). auto textOffsetY = 0.0f; // Offset between lines (on linebreak '\n').
auto textOffsetX = 0.0f; // Offset X to next character to draw. auto textOffsetX = 0.0f; // Offset X to next character to draw.
auto i = 0; auto i = 0;

View file

@ -71,7 +71,9 @@ struct TileMap {
int tileHeight = 16; int tileHeight = 16;
Vec2 position; Vec2 position;
alias data this; enum maxRowCount = data.maxRowCount;
enum maxColCount = data.maxColCount;
enum maxCapacity = data.maxCapacity;
@safe @nogc nothrow: @safe @nogc nothrow:
@ -84,6 +86,79 @@ struct TileMap {
this.data.fill(-1); this.data.fill(-1);
} }
ref short opIndex(Sz row, Sz col) {
if (!has(row, col)) {
assert(0, "Tile `[{}, {}]` does not exist.".format(row, col));
}
return data[row, col];
}
ref short opIndex(IVec2 position) {
return opIndex(position.y, position.x);
}
void opIndexAssign(short rhs, Sz row, Sz col) {
if (!has(row, col)) {
assert(0, "Tile `[{}, {}]` does not exist.".format(row, col));
}
data[row, col] = rhs;
}
void opIndexAssign(short rhs, IVec2 position) {
return opIndexAssign(rhs, position.y, position.x);
}
void opIndexOpAssign(IStr op)(T rhs, Sz row, Sz col) {
if (!has(row, col)) {
assert(0, "Tile `[{}, {}]` does not exist.".format(row, col));
}
mixin("tiles[colCount * row + col]", op, "= rhs;");
}
void opIndexOpAssign(IStr op)(T rhs, IVec2 position) {
return opIndexOpAssign!(op)(rhs, position.y, position.x);
}
Sz opDollar(Sz dim)() {
static if (dim == 0) {
return rowCount;
} else static if (dim == 1) {
return colCount;
} else {
assert(0, "WTF!");
}
}
Sz rowCount() {
return data.length == 0 ? 0 : softMaxRowCount;
}
Sz colCount() {
return data.length == 0 ? 0 : softMaxColCount;
}
bool isEmpty() {
return data.isEmpty;
}
bool has(Sz row, Sz col) {
return row < softMaxRowCount && col < softMaxColCount;
}
bool has(IVec2 position) {
return has(position.y, position.x);
}
@trusted
void fill(short value) {
data.fill(value);
}
@trusted
void free() {
data.free();
}
int width() { int width() {
return cast(int) (softMaxColCount * tileWidth); return cast(int) (softMaxColCount * tileWidth);
} }
@ -189,7 +264,7 @@ struct TileMap {
void popFront() { void popFront() {
position.x += 1; position.x += 1;
if (position.x >= TileMap.maxColCount) { if (position.x >= maxColCount) {
position.x = first.x; position.x = first.x;
position.y += 1; position.y += 1;
} }