Render magic gadgets (WIP)

This commit is contained in:
Edgaru089 2024-03-19 11:59:09 +08:00
parent e58e82b21b
commit 953f1d89ff
9 changed files with 315 additions and 7 deletions

View File

@ -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;
}

View File

@ -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

View File

@ -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");

82
render_pattern.c Normal file
View File

@ -0,0 +1,82 @@
#include "render_pattern.h"
#include <math.h>
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

10
render_pattern.h Normal file
View File

@ -0,0 +1,10 @@
#pragma once
// for BYTE
#include <minwindef.h>
// 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];

View File

@ -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 <string.h>
#include <stdio.h>
#include <math.h>
#include <wingdi.h>
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));
}
}

View File

@ -1,5 +1,8 @@
#pragma once
#include "types.h"
#include <stdbool.h>
#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

View File

@ -0,0 +1,95 @@
// g++ test_generate_patterns.cpp -lsfml-graphics -lsfml-window -lsfml-system -lX11 -lGL
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <SFML/Graphics.hpp>
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();
}
}

View File

@ -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);