diff --git a/camera.c b/camera.c index 3ba24db..92adb96 100644 --- a/camera.c +++ b/camera.c @@ -15,7 +15,7 @@ System_Camera *camera_NewSystem(App *super) { Box2 default_screen = { .lefttop = {.x = 0.0, .y = 0.0}, - .size = {.x = 1280, .y = 720}}; + .size = {.x = SCREEN_WIDTH, .y = SCREEN_HEIGHT}}; sys->cam = sys->screen = default_screen; return sys; } @@ -69,7 +69,7 @@ void camera_Advance(System_Camera *sys, Duration deltaTime) { } -inline static Vec2 _camera_TransformSize(System_Camera *sys, Vec2 worldSize) { +Vec2 camera_TransformSize(System_Camera *sys, Vec2 worldSize) { Vec2 result = { .x = worldSize.x / sys->cam.size.x * sys->screen.size.x, .y = worldSize.y / sys->cam.size.y * sys->screen.size.y}; @@ -78,11 +78,11 @@ inline static Vec2 _camera_TransformSize(System_Camera *sys, Vec2 worldSize) { Vec2 camera_TransformVec2(System_Camera *sys, Vec2 world) { Vec2 relative = vec2_Minus(world, sys->cam.lefttop); - return vec2_Add(sys->screen.lefttop, _camera_TransformSize(sys, relative)); + return vec2_Add(sys->screen.lefttop, camera_TransformSize(sys, relative)); } Box2 camera_TransformBox2(System_Camera *sys, Box2 world) { Box2 result = { .lefttop = camera_TransformVec2(sys, world.lefttop), - .size = _camera_TransformSize(sys, world.size)}; + .size = camera_TransformSize(sys, world.size)}; return result; } diff --git a/camera.h b/camera.h index f504b1d..498da53 100644 --- a/camera.h +++ b/camera.h @@ -28,8 +28,9 @@ void camera_DeleteEntity(System_Camera *sys, uintptr_t id); void camera_Advance(System_Camera *sys, Duration deltaTime); -Vec2 camera_TransformVec2(System_Camera *sys, Vec2 world); -Box2 camera_TransformBox2(System_Camera *sys, Box2 world); +Vec2 camera_TransformVec2(System_Camera *sys, Vec2 world); // Transform a Point, accounted for offset +Box2 camera_TransformBox2(System_Camera *sys, Box2 world); // Transform a Rect, accounted for offset & zoom +Vec2 camera_TransformSize(System_Camera *sys, Vec2 worldSize); // Transform a certain size, accounted for only zoom #ifdef __cplusplus diff --git a/main.cpp b/main.cpp index 2ed2083..7fb846e 100644 --- a/main.cpp +++ b/main.cpp @@ -16,7 +16,7 @@ int main() { int frameCount = 0; SetProcessDPIAware(); - HWND win = initgraph(1280, 720); + HWND win = initgraph(SCREEN_WIDTH, SCREEN_HEIGHT); SetWindowTextA(win, "JacksEscape"); settextstyle(TEXTHEIGHT, 0, "Courier New"); diff --git a/render_pattern.c b/render_pattern.c new file mode 100644 index 0000000..b849075 --- /dev/null +++ b/render_pattern.c @@ -0,0 +1,82 @@ +#include "render_pattern.h" +#include + + +BYTE *render_DissolvePattern(double progress) { + if (progress < .0) + progress = .0; + if (progress > 1.0) + progress = 1.0; + + int index = round(progress * 65); + return (BYTE *)(render_DissolvePatternData + index * 8); +} + + +// generated from test_generate_pattern.cpp +char render_DissolvePatternData[520] = + "\xff\xff\xff\xff\xff\xff\xff\xff" + "\x7f\xff\xff\xff\xff\xff\xff\xff" + "\x3f\xff\xff\xff\xff\xff\xff\xff" + "\x3f\xbf\xff\xff\xff\xff\xff\xff" + "\x3f\x3f\xff\xff\xff\xff\xff\xff" + "\x3f\x3f\x7f\xff\xff\xff\xff\xff" + "\x3f\x3f\x3f\xff\xff\xff\xff\xff" + "\x3f\x3f\x1f\xff\xff\xff\xff\xff" + "\x3f\x1f\x1f\xff\xff\xff\xff\xff" + "\x1f\x1f\x1f\xff\xff\xff\xff\xff" + "\x0f\x1f\x1f\xff\xff\xff\xff\xff" + "\x0f\x0f\x1f\xff\xff\xff\xff\xff" + "\x0f\x0f\x0f\xff\xff\xff\xff\xff" + "\x0f\x0f\x0f\xef\xff\xff\xff\xff" + "\x0f\x0f\x0f\xcf\xff\xff\xff\xff" + "\x0f\x0f\x0f\x8f\xff\xff\xff\xff" + "\x0f\x0f\x0f\x0f\xff\xff\xff\xff" + "\x0f\x0f\x0f\x0f\x7f\xff\xff\xff" + "\x0f\x0f\x0f\x0f\x3f\xff\xff\xff" + "\x0f\x0f\x0f\x0f\x1f\xff\xff\xff" + "\x0f\x0f\x0f\x0f\x0f\xff\xff\xff" + "\x0f\x0f\x0f\x0f\x07\xff\xff\xff" + "\x0f\x0f\x0f\x07\x07\xff\xff\xff" + "\x0f\x0f\x07\x07\x07\xff\xff\xff" + "\x0f\x07\x07\x07\x07\xff\xff\xff" + "\x07\x07\x07\x07\x07\xff\xff\xff" + "\x03\x07\x07\x07\x07\xff\xff\xff" + "\x03\x03\x07\x07\x07\xff\xff\xff" + "\x03\x03\x03\x07\x07\xff\xff\xff" + "\x03\x03\x03\x03\x07\xff\xff\xff" + "\x03\x03\x03\x03\x03\xff\xff\xff" + "\x03\x03\x03\x03\x03\xfb\xff\xff" + "\x03\x03\x03\x03\x03\xf3\xff\xff" + "\x03\x03\x03\x03\x03\xe3\xff\xff" + "\x03\x03\x03\x03\x03\xc3\xff\xff" + "\x03\x03\x03\x03\x03\x83\xff\xff" + "\x03\x03\x03\x03\x03\x03\xff\xff" + "\x03\x03\x03\x03\x03\x03\x7f\xff" + "\x03\x03\x03\x03\x03\x03\x3f\xff" + "\x03\x03\x03\x03\x03\x03\x1f\xff" + "\x03\x03\x03\x03\x03\x03\x0f\xff" + "\x03\x03\x03\x03\x03\x03\x07\xff" + "\x03\x03\x03\x03\x03\x03\x03\xff" + "\x03\x03\x03\x03\x03\x03\x01\xff" + "\x03\x03\x03\x03\x03\x01\x01\xff" + "\x03\x03\x03\x03\x01\x01\x01\xff" + "\x03\x03\x03\x01\x01\x01\x01\xff" + "\x03\x03\x01\x01\x01\x01\x01\xff" + "\x03\x01\x01\x01\x01\x01\x01\xff" + "\x01\x01\x01\x01\x01\x01\x01\xff" + "\x00\x01\x01\x01\x01\x01\x01\xff" + "\x00\x00\x01\x01\x01\x01\x01\xff" + "\x00\x00\x00\x01\x01\x01\x01\xff" + "\x00\x00\x00\x00\x01\x01\x01\xff" + "\x00\x00\x00\x00\x00\x01\x01\xff" + "\x00\x00\x00\x00\x00\x00\x01\xff" + "\x00\x00\x00\x00\x00\x00\x00\xff" + "\x00\x00\x00\x00\x00\x00\x00\xfe" + "\x00\x00\x00\x00\x00\x00\x00\xfc" + "\x00\x00\x00\x00\x00\x00\x00\xf8" + "\x00\x00\x00\x00\x00\x00\x00\xf0" + "\x00\x00\x00\x00\x00\x00\x00\xe0" + "\x00\x00\x00\x00\x00\x00\x00\xc0" + "\x00\x00\x00\x00\x00\x00\x00\x80" + "\x00\x00\x00\x00\x00\x00\x00"; // And the last \0 in the string terminator diff --git a/render_pattern.h b/render_pattern.h new file mode 100644 index 0000000..42de8b9 --- /dev/null +++ b/render_pattern.h @@ -0,0 +1,10 @@ +#pragma once + +// for BYTE +#include + +// Returns a dissolve pattern for the given progress in [0,1] +BYTE *render_DissolvePattern(double progress); + +// The underlying data. 520 bytes +extern char render_DissolvePatternData[520]; diff --git a/render_util.cpp b/render_util.cpp index ca3043e..5f69ca0 100644 --- a/render_util.cpp +++ b/render_util.cpp @@ -1,9 +1,15 @@ #include "render_util.h" +#include "camera.h" +#include "render_pattern.h" #include "easyx.h" +#include "types.h" #include "util/vector.h" +#include "app.h" #include #include +#include +#include extern "C" { @@ -39,4 +45,65 @@ void render_DrawText(int x, int y, const char *str) { vector_Clear(tbuf); } } + + +const FillMode render_DefaultMode = { + .rop2 = R2_COPYPEN, + .style = BS_SOLID, + .hatch = 0, + .rotate = {.microseconds = 0}, + .dissolve = {.microseconds = 0}}; + + +void render_SetModes(FillMode mode, TimePoint since) { + if (mode.dissolve.microseconds != 0) { + // Dissolve mode + double progress = duration_Seconds(time_Since(since)) / duration_Seconds(mode.dissolve); + setfillstyle(render_DissolvePattern(progress)); // progress is capped into [0,1] in this func + setrop2(R2_COPYPEN); + return; + } + + if (mode.rotate.microseconds != 0) { + // Rotate mode + int steps = round(duration_Seconds(time_Since(since)) / duration_Seconds(mode.dissolve)); + + static const long hatches[] = {HS_HORIZONTAL, HS_FDIAGONAL, HS_VERTICAL, HS_BDIAGONAL}; + setfillstyle(BS_HATCHED, hatches[steps % 4], NULL); + setrop2(R2_COPYPEN); + return; + } + + // Normal mode + setfillstyle(mode.style, mode.hatch, NULL); + setrop2(mode.rop2); +} + +void render_FillScreen(FillMode mode, TimePoint since) { + fillrectangle(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); +} + +void render_FillRectW(App *app, Box2 rect, FillMode mode, TimePoint since) { + if (app->camera) + rect = camera_TransformBox2(app->camera, rect); + else + fprintf(stderr, "[WARN][render_FillRectW] called without an active camera system\n"); + fillrectangle( + (int)round(rect.lefttop.x), + (int)round(rect.lefttop.y), + (int)round(rect.lefttop.x + rect.size.x), + (int)round(rect.lefttop.y + rect.size.y)); +} + +void render_FillCircleW(App *app, Vec2 center, double radius, FillMode mode, TimePoint since) { + if (app->camera) { + center = camera_TransformVec2(app->camera, center); + radius = camera_TransformSize(app->camera, vec2(radius, 0)).x; // TODO non-aspect scaling + } else + fprintf(stderr, "[WARN][render_FillCircleW] called without an active camera system\n"); + fillcircle( + (int)round(center.x), + (int)round(center.y), + (int)round(radius)); +} } diff --git a/render_util.h b/render_util.h index 831c3b9..482ecfa 100644 --- a/render_util.h +++ b/render_util.h @@ -1,5 +1,8 @@ #pragma once +#include "types.h" +#include + #ifdef __cplusplus extern "C" { #endif @@ -15,6 +18,52 @@ extern "C" { void render_DrawText(int x, int y, const char *str); +// Fill modes. +typedef struct { + // Most often it's: + // R2_COPYPEN (Output = Pen color) + // R2_BLACK (Output = Black) + // R2_WHITE (Output = White) + // R2_NOT (Output = NOT Screen color) + // + // https://docs.easyx.cn/zh-cn/setrop2 + int rop2; + + // Style & hatch passed into setfillstyle() + // + // https://docs.easyx.cn/zh-cn/setfillstyle + int style; + long hatch; + + // Rotates in /-\| every this amount of time, if not 0 + // Overrides style & hatch + Duration rotate; + + // Dissolve the entire screen in an animation in this + // amount of time. Overrides rotate, style & hatch + Duration dissolve; +} FillMode; + +// Default fill mode. +// rop2 = R2_COPYPEN, +// style = BS_SOLID, +// others = 0 +extern const FillMode render_DefaultMode; + +typedef struct _App App; + +// Calls the required setfillstyle/setrop2 calls for the struct. +void render_SetModes(FillMode mode, TimePoint since); + +// Fills the entire screen +void render_FillScreen(FillMode mode, TimePoint since); + +// Fills a rectangle in world coordinates +void render_FillRectW(App *app, Box2 rect, FillMode mode, TimePoint since); +// Fills a circle in world coordinates +void render_FillCircleW(App *app, Vec2 center, double radius, FillMode mode, TimePoint since); + + #ifdef __cplusplus } #endif diff --git a/test_generate_patterns.cpp b/test_generate_patterns.cpp new file mode 100644 index 0000000..2c5937f --- /dev/null +++ b/test_generate_patterns.cpp @@ -0,0 +1,95 @@ +// g++ test_generate_patterns.cpp -lsfml-graphics -lsfml-window -lsfml-system -lX11 -lGL + +#include +#include +#include +#include + +#include + +using namespace std; +using namespace sf; + + +uint8_t pattern[8]; +int x = 0, y = 0; + +void outpattern() { + cout << "\""; + for (int i = 0; i < 8; i++) + printf("\\x%02x", pattern[i]); + cout << "\"" << endl; +} + +void setbit(int x, int y, bool value) { + if (x < 0 || x > 7 || y < 0 || y > 7) + return; + uint8_t &target = pattern[y]; + if (value) { // set to 1 + uint8_t mask = 1 << (7 - x); + target |= mask; + } else { // set to 0 + uint8_t mask = ~(1 << (7 - x)); + target &= mask; + } +} + +bool getbit(int x, int y) { + if (x < 0 || x > 7 || y < 0 || y > 7) + return false; + uint8_t &target = pattern[y]; + uint8_t mask = 1 << (7 - x); + return (target & mask) != 0; +} + +int main() { + memset(pattern, -1, sizeof(pattern)); + + RenderWindow win(VideoMode(400, 400), "Generator"); + win.setVerticalSyncEnabled(true); + win.setFramerateLimit(120); + + outpattern(); + setbit(0, 0, false); + outpattern(); + + + while (win.isOpen()) { + Event e; + while (win.pollEvent(e)) { + if (e.type == Event::KeyPressed) { + switch (e.key.code) { + case Keyboard::Up: + y--; + break; + case Keyboard::Down: + y++; + break; + case Keyboard::Left: + x--; + break; + case Keyboard::Right: + x++; + break; + } + setbit(x, y, false); + outpattern(); + } else if (e.type == Event::Closed) + win.close(); + } + + win.clear(); + + for (int i = 0; i < 8; i++) + for (int j = 0; j < 8; j++) { + if (getbit(i, j)) { + RectangleShape rect(Vector2f(50, 50)); + rect.setPosition(i * 50, j * 50); + rect.setFillColor(Color::White); + win.draw(rect); + } + } + + win.display(); + } +} diff --git a/types.h b/types.h index a9ca92b..8779280 100644 --- a/types.h +++ b/types.h @@ -10,6 +10,10 @@ extern "C" { #endif +#define SCREEN_WIDTH 1280 +#define SCREEN_HEIGHT 720 + + static inline void *zero_malloc(size_t size) { void *d = malloc(size); memset(d, 0, size);