Viewports can be used by users now.

This commit is contained in:
Kapendev 2024-10-26 16:44:09 +03:00
parent 969775ff5c
commit ecc33657a8
13 changed files with 221 additions and 162 deletions

View file

@ -93,6 +93,8 @@ bool isReleased(Mouse key);
bool isReleased(Gamepad key, int id = 0); bool isReleased(Gamepad key, int id = 0);
Vec2 wasd(); Vec2 wasd();
Vec2 wasdPressed();
Vec2 wasdReleased();
``` ```
## Sound ## Sound
@ -128,6 +130,9 @@ void drawTexture(TextureId texture, Vec2 position, DrawOptions options = DrawOpt
void drawTextureArea(Texture texture, Rect area, Vec2 position, DrawOptions options = DrawOptions()); void drawTextureArea(Texture texture, Rect area, Vec2 position, DrawOptions options = DrawOptions());
void drawTextureArea(TextureId texture, Rect area, Vec2 position, DrawOptions options = DrawOptions()); void drawTextureArea(TextureId texture, Rect area, Vec2 position, DrawOptions options = DrawOptions());
void drawViewport(Viewport viewport, Vec2 position, DrawOptions options = DrawOptions());
void drawViewportArea(Viewport viewport, Rect area, Vec2 position, DrawOptions options = DrawOptions());
void drawRune(Font font, dchar rune, Vec2 position, DrawOptions options = DrawOptions()); void drawRune(Font font, dchar rune, Vec2 position, DrawOptions options = DrawOptions());
void drawRune(FontId font, dchar rune, Vec2 position, DrawOptions options = DrawOptions()); void drawRune(FontId font, dchar rune, Vec2 position, DrawOptions options = DrawOptions());
void drawText(Font font, IStr text, Vec2 position, DrawOptions options = DrawOptions()); void drawText(Font font, IStr text, Vec2 position, DrawOptions options = DrawOptions());

View file

@ -11,7 +11,6 @@ auto walkAnimation = SpriteAnimation(0, 2, 6);
void ready() { void ready() {
lockResolution(320, 180); lockResolution(320, 180);
setBackgroundColor(toRgb(0x0b0b0b)); setBackgroundColor(toRgb(0x0b0b0b));
setIsPixelPerfect(true);
setIsCursorVisible(false); setIsCursorVisible(false);
// Load the `atlas.png` file from the assets folder. // Load the `atlas.png` file from the assets folder.
atlas = loadTexture("atlas.png"); atlas = loadTexture("atlas.png");

View file

@ -10,7 +10,7 @@ enum header = "// ---
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// Email: alexandroskapretsos@gmail.com // Email: alexandroskapretsos@gmail.com
// Project: https://github.com/Kapendev/parin // Project: https://github.com/Kapendev/parin
// Version: v0.0.23 // Version: v0.0.24
// ---"; // ---";
int main(string[] args) { int main(string[] args) {

View file

@ -3,7 +3,7 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// Email: alexandroskapretsos@gmail.com // Email: alexandroskapretsos@gmail.com
// Project: https://github.com/Kapendev/parin // Project: https://github.com/Kapendev/parin
// Version: v0.0.23 // Version: v0.0.24
// --- // ---
// TODO: The DialogueCommandRunner should work with gc functions too. Think about how to do it. // TODO: The DialogueCommandRunner should work with gc functions too. Think about how to do it.

View file

@ -3,7 +3,7 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// Email: alexandroskapretsos@gmail.com // Email: alexandroskapretsos@gmail.com
// Project: https://github.com/Kapendev/parin // Project: https://github.com/Kapendev/parin
// Version: v0.0.23 // Version: v0.0.24
// --- // ---
// TODO: Test the resource loading code. // TODO: Test the resource loading code.
@ -188,130 +188,6 @@ struct DrawOptions {
} }
} }
/// A structure representing a camera.
struct Camera {
Vec2 position; /// The position of the cammera.
float rotation = 0.0f; /// The rotation angle of the camera, in degrees.
float scale = 1.0f; /// The zoom level of the camera.
bool isAttached; /// Indicates whether the camera is currently in use.
bool isCentered; /// Determines if the camera's origin is at the center instead of the top left.
@safe @nogc nothrow:
/// Initializes the camera with the given position and optional centering.
this(float x, float y, bool isCentered = false) {
this.position.x = x;
this.position.y = y;
this.isCentered = isCentered;
}
/// Returns the current hook associated with the camera.
Hook hook() {
return isCentered ? Hook.center : Hook.topLeft;
}
/// Returns the origin of the camera.
Vec2 origin() {
return Rect(position, resolution / Vec2(scale)).origin(hook);
}
/// Returns the area covered by the camera.
Rect area() {
return Rect(position, resolution / Vec2(scale)).area(hook);
}
/// Returns the top left point of the camera.
Vec2 topLeftPoint() {
return area.topLeftPoint;
}
/// Returns the top point of the camera.
Vec2 topPoint() {
return area.topPoint;
}
/// Returns the top right point of the camera.
Vec2 topRightPoint() {
return area.topRightPoint;
}
/// Returns the left point of the camera.
Vec2 leftPoint() {
return area.leftPoint;
}
/// Returns the center point of the camera.
Vec2 centerPoint() {
return area.centerPoint;
}
/// Returns the right point of the camera.
Vec2 rightPoint() {
return area.rightPoint;
}
/// Returns the bottom left point of the camera.
Vec2 bottomLeftPoint() {
return area.bottomLeftPoint;
}
/// Returns the bottom point of the camera.
Vec2 bottomPoint() {
return area.bottomPoint;
}
/// Returns the bottom right point of the camera.
Vec2 bottomRightPoint() {
return area.bottomRightPoint;
}
/// Moves the camera to follow the target position at the specified speed.
void followPosition(Vec2 target, float speed) {
position = position.moveTo(target, Vec2(speed));
}
/// Moves the camera to follow the target position with gradual slowdown.
void followPositionWithSlowdown(Vec2 target, float slowdown) {
position = position.moveToWithSlowdown(target, Vec2(deltaTime), slowdown);
}
/// Adjusts the cameras zoom level to follow the target value at the specified speed.
void followScale(float target, float speed) {
scale = scale.moveTo(target, speed);
}
/// Adjusts the cameras zoom level to follow the target value with gradual slowdown.
void followScaleWithSlowdown(float target, float slowdown) {
scale = scale.moveToWithSlowdown(target, deltaTime, slowdown);
}
/// Attaches the camera, making it active.
@trusted
void attach() {
if (isAttached) {
return;
}
isAttached = true;
auto temp = this.toRl();
if (isPixelSnapped || isPixelPerfect) {
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);
}
rl.BeginMode2D(temp);
}
/// Detaches the camera, making it inactive.
@trusted
void detach() {
if (isAttached) {
isAttached = false;
rl.EndMode2D();
}
}
}
/// Represents an identifier for a managed resource. /// Represents an identifier for a managed resource.
struct TextId { struct TextId {
GenerationalIndex data; GenerationalIndex data;
@ -670,9 +546,16 @@ struct SoundId {
/// Represents the viewing area for rendering. /// Represents the viewing area for rendering.
struct Viewport { struct Viewport {
rl.RenderTexture2D data; rl.RenderTexture2D data;
Color color;
bool isAttached;
@safe @nogc nothrow: @safe @nogc nothrow:
this(int width, int height, Color color = gray) {
this.color = color;
resize(width, height);
}
/// Checks if the viewport is not loaded. /// Checks if the viewport is not loaded.
bool isEmpty() { bool isEmpty() {
return data.texture.id <= 0; return data.texture.id <= 0;
@ -693,6 +576,32 @@ struct Viewport {
return Vec2(width, height); return Vec2(width, height);
} }
/// Resizes the viewport to the given width and height.
/// Internally, this allocates a new render texture, so avoid calling it while the viewport is in use.
@trusted
void resize(int width, int height) {
if (!isEmpty) rl.UnloadRenderTexture(data);
data = rl.LoadRenderTexture(width, height);
setFilter(engineState.defaultFilter);
}
/// Attaches the viewport, making it active.
@trusted
void attach() {
if (isAttached) return;
isAttached = true;
rl.BeginTextureMode(data);
rl.ClearBackground(color.toRl());
}
/// Detaches the viewport, making it inactive.
@trusted
void detach() {
if (!isAttached) return;
isAttached = false;
rl.EndTextureMode();
}
/// Sets the filter mode of the viewport. /// Sets the filter mode of the viewport.
@trusted @trusted
void setFilter(Filter value) { void setFilter(Filter value) {
@ -703,14 +612,133 @@ struct Viewport {
/// Frees the loaded viewport. /// Frees the loaded viewport.
@trusted @trusted
void free() { void free() {
if (isEmpty) { if (isEmpty) return;
return;
}
rl.UnloadRenderTexture(data); rl.UnloadRenderTexture(data);
this = Viewport(); this = Viewport();
} }
} }
/// A structure representing a camera.
struct Camera {
Vec2 position; /// The position of the cammera.
float rotation = 0.0f; /// The rotation angle of the camera, in degrees.
float scale = 1.0f; /// The zoom level of the camera.
bool isAttached; /// Indicates whether the camera is currently in use.
bool isCentered; /// Determines if the camera's origin is at the center instead of the top left.
@safe @nogc nothrow:
/// Initializes the camera with the given position and optional centering.
this(float x, float y, bool isCentered = false) {
this.position.x = x;
this.position.y = y;
this.isCentered = isCentered;
}
/// Returns the current hook associated with the camera.
Hook hook() {
return isCentered ? Hook.center : Hook.topLeft;
}
/// Returns the origin of the camera.
Vec2 origin() {
return Rect(position, resolution / Vec2(scale)).origin(hook);
}
/// Returns the area covered by the camera.
Rect area() {
return Rect(position, resolution / Vec2(scale)).area(hook);
}
/// Returns the top left point of the camera.
Vec2 topLeftPoint() {
return area.topLeftPoint;
}
/// Returns the top point of the camera.
Vec2 topPoint() {
return area.topPoint;
}
/// Returns the top right point of the camera.
Vec2 topRightPoint() {
return area.topRightPoint;
}
/// Returns the left point of the camera.
Vec2 leftPoint() {
return area.leftPoint;
}
/// Returns the center point of the camera.
Vec2 centerPoint() {
return area.centerPoint;
}
/// Returns the right point of the camera.
Vec2 rightPoint() {
return area.rightPoint;
}
/// Returns the bottom left point of the camera.
Vec2 bottomLeftPoint() {
return area.bottomLeftPoint;
}
/// Returns the bottom point of the camera.
Vec2 bottomPoint() {
return area.bottomPoint;
}
/// Returns the bottom right point of the camera.
Vec2 bottomRightPoint() {
return area.bottomRightPoint;
}
/// Moves the camera to follow the target position at the specified speed.
void followPosition(Vec2 target, float speed) {
position = position.moveTo(target, Vec2(speed));
}
/// Moves the camera to follow the target position with gradual slowdown.
void followPositionWithSlowdown(Vec2 target, float slowdown) {
position = position.moveToWithSlowdown(target, Vec2(deltaTime), slowdown);
}
/// Adjusts the cameras zoom level to follow the target value at the specified speed.
void followScale(float target, float speed) {
scale = scale.moveTo(target, speed);
}
/// Adjusts the cameras zoom level to follow the target value with gradual slowdown.
void followScaleWithSlowdown(float target, float slowdown) {
scale = scale.moveToWithSlowdown(target, deltaTime, slowdown);
}
/// Attaches the camera, making it active.
@trusted
void attach() {
if (isAttached) return;
isAttached = true;
auto temp = this.toRl();
if (isPixelSnapped || isPixelPerfect) {
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);
}
rl.BeginMode2D(temp);
}
/// Detaches the camera, making it inactive.
@trusted
void detach() {
if (!isAttached) return;
isAttached = false;
rl.EndMode2D();
}
}
struct EngineFlags { struct EngineFlags {
bool isUpdating; bool isUpdating;
bool isPixelSnapped; bool isPixelSnapped;
@ -802,6 +830,7 @@ struct EngineResources {
} }
} }
// NOTE: Maybe look at the locking and unlocking code again. Works, but maybe could be more nice looking.
struct EngineViewport { struct EngineViewport {
Viewport data; Viewport data;
int targetWidth; int targetWidth;
@ -839,7 +868,6 @@ struct EngineState {
LStr assetsPath; LStr assetsPath;
Color borderColor; Color borderColor;
Color backgroundColor;
Filter defaultFilter; Filter defaultFilter;
Sz tickCount; Sz tickCount;
@ -1174,16 +1202,6 @@ SoundId loadSound(IStr path, float volume, float pitch, Sz tag = 0) {
} }
} }
/// Loads a viewport.
/// The resource must be manually freed.
/// Supports both forward slashes and backslashes in file paths.
@trusted
Result!Viewport loadRawViewport(int width, int height) {
auto value = rl.LoadRenderTexture(width, height).toParin();
value.setFilter(engineState.defaultFilter);
return Result!Viewport(value, value.isEmpty.toFault());
}
/// 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) {
@ -1209,7 +1227,7 @@ void openWindow(int width, int height, IStr appPath, IStr title = "Parin") {
rl.SetExitKey(rl.KEY_NULL); rl.SetExitKey(rl.KEY_NULL);
rl.SetTargetFPS(60); rl.SetTargetFPS(60);
engineState.borderColor = black; engineState.borderColor = black;
engineState.backgroundColor = gray2; engineState.viewport.color = gray;
engineState.fullscreenState.lastWindowWidth = width; engineState.fullscreenState.lastWindowWidth = width;
engineState.fullscreenState.lastWindowHeight = height; engineState.fullscreenState.lastWindowHeight = height;
engineState.assetsPath.append(pathConcat(appPath.pathDir, "assets")); engineState.assetsPath.append(pathConcat(appPath.pathDir, "assets"));
@ -1231,7 +1249,7 @@ void updateWindow(bool function(float dt) updateFunc) {
} else { } else {
rl.BeginDrawing(); rl.BeginDrawing();
} }
rl.ClearBackground(engineState.backgroundColor.toRl()); rl.ClearBackground(engineState.viewport.color.toRl());
// The main loop. // The main loop.
auto dt = deltaTime; auto dt = deltaTime;
@ -1276,8 +1294,7 @@ void updateWindow(bool function(float dt) updateFunc) {
// Main viewport code. // Main viewport code.
if (engineState.viewport.isLocking) { if (engineState.viewport.isLocking) {
engineState.viewport.free(); engineState.viewport.resize(engineState.viewport.targetWidth, engineState.viewport.targetHeight);
engineState.viewport.data = loadRawViewport(engineState.viewport.targetWidth, engineState.viewport.targetHeight).getOr();
} else if (engineState.viewport.isUnlocking) { } else if (engineState.viewport.isUnlocking) {
engineState.viewport.free(); engineState.viewport.free();
} }
@ -1423,7 +1440,7 @@ bool isWindowResized() {
/// Returns the current background color. /// Returns the current background color.
Color backgroundColor() { Color backgroundColor() {
return engineState.backgroundColor; return engineState.viewport.color;
} }
void setBorderColor(Color value) { void setBorderColor(Color value) {
@ -1432,7 +1449,7 @@ void setBorderColor(Color value) {
/// Sets the background color to the specified value. /// Sets the background color to the specified value.
void setBackgroundColor(Color value) { void setBackgroundColor(Color value) {
engineState.backgroundColor = value; engineState.viewport.color = value;
} }
/// Returns the default filter mode for textures. /// Returns the default filter mode for textures.
@ -1472,8 +1489,7 @@ bool isResolutionLocked() {
void lockResolution(int width, int height) { void lockResolution(int width, int height) {
engineState.viewport.startLocking(width, height); engineState.viewport.startLocking(width, height);
if (!engineState.flags.isUpdating) { if (!engineState.flags.isUpdating) {
engineState.viewport.free(); engineState.viewport.resize(width, height);
engineState.viewport.data = loadRawViewport(width, height).get();
} }
} }
@ -1746,14 +1762,36 @@ bool isReleased(Gamepad key, int id = 0) {
return rl.IsGamepadButtonReleased(id, key); return rl.IsGamepadButtonReleased(id, key);
} }
/// Returns the directional input based on the WASD and arrow keys. /// Returns the directional input based on the WASD and arrow keys when they are down.
/// The vector is not normalized. /// The vector is not normalized.
Vec2 wasd() { Vec2 wasd() {
auto result = Vec2(); auto result = Vec2();
if (Keyboard.a.isDown || Keyboard.left.isDown) result.x += -1.0f; if (Keyboard.w.isDown || Keyboard.up.isDown) result.y -= 1.0f;
if (Keyboard.d.isDown || Keyboard.right.isDown) result.x += 1.0f; if (Keyboard.a.isDown || Keyboard.left.isDown) result.x -= 1.0f;
if (Keyboard.w.isDown || Keyboard.up.isDown) result.y += -1.0f;
if (Keyboard.s.isDown || Keyboard.down.isDown) result.y += 1.0f; if (Keyboard.s.isDown || Keyboard.down.isDown) result.y += 1.0f;
if (Keyboard.d.isDown || Keyboard.right.isDown) result.x += 1.0f;
return result;
}
/// Returns the directional input based on the WASD and arrow keys when they are pressed.
/// The vector is not normalized.
Vec2 wasdPressed() {
auto result = Vec2();
if (Keyboard.w.isPressed || Keyboard.up.isPressed) result.y -= 1.0f;
if (Keyboard.a.isPressed || Keyboard.left.isPressed) result.x -= 1.0f;
if (Keyboard.s.isPressed || Keyboard.down.isPressed) result.y += 1.0f;
if (Keyboard.d.isPressed || Keyboard.right.isPressed) result.x += 1.0f;
return result;
}
/// Returns the directional input based on the WASD and arrow keys when they are released.
/// The vector is not normalized.
Vec2 wasdReleased() {
auto result = Vec2();
if (Keyboard.w.isReleased || Keyboard.up.isReleased) result.y -= 1.0f;
if (Keyboard.a.isReleased || Keyboard.left.isReleased) result.x -= 1.0f;
if (Keyboard.s.isReleased || Keyboard.down.isReleased) result.y += 1.0f;
if (Keyboard.d.isReleased || Keyboard.right.isReleased) result.x += 1.0f;
return result; return result;
} }
@ -1949,6 +1987,23 @@ void drawTexture(TextureId texture, Vec2 position, DrawOptions options = DrawOpt
drawTexture(texture.getOr(), position, options); drawTexture(texture.getOr(), position, options);
} }
/// Draws a portion of the specified viewport at the given position with the specified draw options.
void drawViewportArea(Viewport viewport, Rect area, Vec2 position, DrawOptions options = DrawOptions()) {
// Some basic rules to make viewports noob friendly.
final switch (options.flip) {
case Flip.none: options.flip = Flip.y; break;
case Flip.x: options.flip = Flip.xy; break;
case Flip.y: options.flip = Flip.none; break;
case Flip.xy: options.flip = Flip.x; break;
}
drawTextureArea(viewport.data.texture.toParin(), area, position, options);
}
/// Draws the viewport at the given position with the specified draw options.
void drawViewport(Viewport viewport, Vec2 position, DrawOptions options = DrawOptions()) {
drawViewportArea(viewport, Rect(viewport.size), position, options);
}
/// Draws a single character from the specified font at the given position with the specified draw options. /// Draws a single character from the specified font at the given position with the specified draw options.
@trusted @trusted
void drawRune(Font font, dchar rune, Vec2 position, DrawOptions options = DrawOptions()) { void drawRune(Font font, dchar rune, Vec2 position, DrawOptions options = DrawOptions()) {

View file

@ -3,7 +3,7 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// Email: alexandroskapretsos@gmail.com // Email: alexandroskapretsos@gmail.com
// Project: https://github.com/Kapendev/parin // Project: https://github.com/Kapendev/parin
// Version: v0.0.23 // Version: v0.0.24
// --- // ---
module parin; module parin;

View file

@ -3,7 +3,7 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// Email: alexandroskapretsos@gmail.com // Email: alexandroskapretsos@gmail.com
// Project: https://github.com/Kapendev/parin // Project: https://github.com/Kapendev/parin
// Version: v0.0.23 // Version: v0.0.24
// --- // ---
/// The `rl` module provides access to the raylib library. /// The `rl` module provides access to the raylib library.

View file

@ -3,7 +3,7 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// Email: alexandroskapretsos@gmail.com // Email: alexandroskapretsos@gmail.com
// Project: https://github.com/Kapendev/parin // Project: https://github.com/Kapendev/parin
// Version: v0.0.23 // Version: v0.0.24
// --- // ---
/// The `rayib` module provides access to the raylib.h functions. /// The `rayib` module provides access to the raylib.h functions.

View file

@ -3,7 +3,7 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// Email: alexandroskapretsos@gmail.com // Email: alexandroskapretsos@gmail.com
// Project: https://github.com/Kapendev/parin // Project: https://github.com/Kapendev/parin
// Version: v0.0.23 // Version: v0.0.24
// --- // ---
/// The `rlgl` module provides access to the rlgl.h functions. /// The `rlgl` module provides access to the rlgl.h functions.

View file

@ -3,7 +3,7 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// Email: alexandroskapretsos@gmail.com // Email: alexandroskapretsos@gmail.com
// Project: https://github.com/Kapendev/parin // Project: https://github.com/Kapendev/parin
// Version: v0.0.23 // Version: v0.0.24
// --- // ---
// TODO: Update all the doc comments here. // TODO: Update all the doc comments here.

View file

@ -3,7 +3,7 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// Email: alexandroskapretsos@gmail.com // Email: alexandroskapretsos@gmail.com
// Project: https://github.com/Kapendev/parin // Project: https://github.com/Kapendev/parin
// Version: v0.0.23 // Version: v0.0.24
// --- // ---
// TODO: Think about gaps in an atlas texture. // TODO: Think about gaps in an atlas texture.

View file

@ -3,7 +3,7 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// Email: alexandroskapretsos@gmail.com // Email: alexandroskapretsos@gmail.com
// Project: https://github.com/Kapendev/parin // Project: https://github.com/Kapendev/parin
// Version: v0.0.23 // Version: v0.0.24
// --- // ---
// TODO: Think about gaps in an atlas texture. // TODO: Think about gaps in an atlas texture.

View file

@ -3,7 +3,7 @@
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
// Email: alexandroskapretsos@gmail.com // Email: alexandroskapretsos@gmail.com
// Project: https://github.com/Kapendev/parin // Project: https://github.com/Kapendev/parin
// Version: v0.0.23 // Version: v0.0.24
// --- // ---
// TODO: Update all the doc comments here. // TODO: Update all the doc comments here.