toys/sww/test/texture.c

266 lines
8.5 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <math.h>
#include <stdio.h>
#include <stdlib.h> /**< rand qsort */
#include "sww/app.h"
#include "sww/color.h"
#include "sww/fps_helper.h"
#include "sww/rate_helper.h"
#include "sww/renderer.h"
#include "sww/texture.h"
#include "sww/util.h"
#include "sww/vmath.h"
#include "sww/window.h"
#define ROTATION_SPEED 0.015f
#define VIEW_DISTANCE 800.0f
#define ANTIALIASING 1 // 抗锯齿采样级别1为关闭
#define MAX_SCENE 2
#define TITLE "SWW Texture"
typedef struct
{
int w;
int h;
int method;
swwRenderer* renderer;
} UserData;
void OnKey(swwWindow* o, swwKeycode key, int pressed)
{
if (pressed) {
if (key == swwKeycode_ESCAPE) {
swwApp_PostQuitEvent();
} else {
UserData* ud = swwWindow_GetUserData(o);
ud->method = (ud->method + 1) % MAX_SCENE;
}
}
}
void OnButton(swwWindow* o, swwButton btn, int pressed)
{
if (btn == swwButton_R && pressed) {
// Save to png file
UserData* ud = swwWindow_GetUserData(o);
uint32_t buf_size = swwUtil_PngBufferSize(ud->w, ud->h, 1);
uint8_t* buf = (uint8_t*)malloc(buf_size);
uint8_t* wbuf = swwRenderer_LockBuffer(ud->renderer);
FILE* fp = NULL;
swwUtil_SaveBufferToPngBuffer(buf, buf_size, wbuf, ud->w, ud->h, 1);
fp = fopen("output.png", "wb+");
fwrite(buf, 1, buf_size, fp);
fclose(fp);
free(buf);
}
}
void OnScroll(swwWindow* o, float offset)
{}
typedef struct
{
int ori_x; // 原始X坐标
float depth; // 深度值Z坐标
int screen_x; // 投影后屏幕X坐标
float scale; // 垂直缩放因子
} ColumnInfo;
typedef struct
{
Point2i p;
int rx;
int ry;
} EyeGround;
int CompareDepth(const void* a, const void* b)
{
ColumnInfo* c1 = (ColumnInfo*)a;
ColumnInfo* c2 = (ColumnInfo*)b;
return c1->depth < c2->depth ? 1 : -1;
}
int main()
{
uint32_t w = 512, h = 512;
uint32_t img_w, img_h;
int cw, ch;
swwWindowCallback callback = {OnKey, OnButton, OnScroll};
swwFpsHelper fps;
swwApp_Initialize();
swwWindow* window = swwWindow_Create(TITLE, w, h);
UserData ud = {w, h, 0, NULL};
ud.renderer = swwRenderer_CreateAttachWindow(window, kSoftwareRenderer);
swwWindow_SetUserData(window, &ud);
swwWindow_SetCallback(window, &callback);
int ww2 = w / 2;
int hh2 = h / 2;
const int center_x = w / 2;
const int center_y = h / 2;
int i = 0;
swwRateHelper rh;
EyeGround left_eye_grd = {{ww2 + 147, hh2 + 166}, 87, 96};
EyeGround right_eye_grd = {{ww2 + 357, hh2 + 166}, 87, 96};
Point2i left_eye_pos[2] = {{ww2 + 110, hh2 + 120}, {ww2 + 110, hh2 + 80}};
Point2i right_eye_pos[2] = {{ww2 + 320, hh2 + 120}, {ww2 + 320, hh2 + 80}};
Rangef left_eye_range = {left_eye_pos[1].y, left_eye_pos[0].y};
Rangef right_eye_range = {right_eye_pos[1].y, right_eye_pos[0].y};
swwTexture* tex = swwTexture_LoadFromFile("eye.png");
swwTexture* rotated_tex = swwTexture_LoadFromFile("avatar.png");
swwTexture_GetSize(rotated_tex, &img_w, &img_h);
ColumnInfo* columns = (ColumnInfo*)malloc(img_w * sizeof(ColumnInfo));
float angle = 0.0f;
swwRenderer_EnablePerfMonitor(ud.renderer, 1);
swwFpsHelper_Construct(&fps, 1.f);
swwRateHelper_Construct(&rh, 30.f);
int cur_step = 0;
int step = 1;
Rangef r = {0, 30};
while (!swwApp_ShouldExit()) {
swwRenderer_ClearBlack(ud.renderer);
switch (ud.method) {
case 0: {
int lpy = (int)Mapf(cur_step, r, left_eye_range);
int rpy = (int)Mapf(cur_step, r, right_eye_range);
// Draw the eye ground
swwRenderer_DrawEllipse(ud.renderer, left_eye_grd.p, left_eye_grd.rx, left_eye_grd.ry,
kWhite, 1);
swwRenderer_DrawEllipse(ud.renderer, right_eye_grd.p, right_eye_grd.rx,
right_eye_grd.ry, kWhite, 1);
// Draw eyes
swwRenderer_DrawTexture(ud.renderer, tex, (Point2i){left_eye_pos[0].x, lpy});
swwRenderer_DrawTexture(ud.renderer, tex, (Point2i){right_eye_pos[0].x, rpy});
if (cur_step == 30) {
step = -1;
} else if (cur_step == 0) {
step = 1;
}
cur_step += step;
} break;
case 1:
default: {
angle += ROTATION_SPEED;
const float cos_theta = cos(angle);
const float sin_theta = sin(angle);
int valid_columns = 0;
uint32_t x = 0;
for (; x < img_w; x++) {
const float ori_x = (x - img_w / 2.0f);
const float rotated_z = -ori_x * sin_theta;
const float divisor = VIEW_DISTANCE + rotated_z;
if (divisor <= 0.1f)
continue; // 避免除以零
ColumnInfo* col = &columns[valid_columns];
col->ori_x = x;
col->depth = rotated_z;
// 计算投影参数
const float proj_x = (ori_x * cos_theta * VIEW_DISTANCE) / divisor;
col->screen_x = center_x + (int)round(proj_x);
col->scale = VIEW_DISTANCE / divisor; // 垂直缩放因子
valid_columns++;
}
qsort(columns, valid_columns, sizeof(ColumnInfo), CompareDepth);
for (int i = 0; i < valid_columns; i++) {
const ColumnInfo* col = &columns[i];
if (col->screen_x < 0 || col->screen_x >= w)
continue;
// 计算垂直缩放范围
const int draw_height = (int)(img_h * col->scale);
const int y_start = center_y - draw_height / 2;
const int y_end = y_start + draw_height;
int sy = y_start;
for (; sy < y_end; sy++) {
if (sy < 0 || sy >= h)
continue;
// 逆向计算原始Y坐标带抗锯齿
const float src_y = ((sy - y_start) / (float)draw_height) * img_h;
const int y0 = (int)src_y;
const int y1 = MIN(y0 + 1, img_h - 1);
const float fy = src_y - y0;
// 抗锯齿采样
if (ANTIALIASING > 1) {
uint32_t sum[3] = {0};
for (int dy = 0; dy < ANTIALIASING; dy++) {
for (int dx = 0; dx < ANTIALIASING; dx++) {
const float sample_y = src_y + dy / (float)ANTIALIASING;
const int y = (int)sample_y;
const int offset = y * img_w + col->ori_x;
uint32_t c = swwTexture_Get(rotated_tex, col->ori_x, y);
uint8_t r, g, b;
Color_GetRGB(c, r, g, b);
sum[0] += r;
sum[1] += g;
sum[2] += b;
}
}
const int divisor = ANTIALIASING * ANTIALIASING;
swwRenderer_DrawPixel(
ud.renderer, (Point2i){col->screen_x, sy},
Color_RGB(sum[0] / divisor, sum[1] / divisor, sum[2] / divisor));
} else {
// 双线性插值
uint32_t c1 = swwTexture_Get(rotated_tex, col->ori_x, y0);
uint32_t c2 = swwTexture_Get(rotated_tex, col->ori_x, y1);
uint8_t r1, g1, b1, r2, b2, g2;
Color_GetRGB(c1, r1, g1, b1);
Color_GetRGB(c2, r2, g2, b2);
swwRenderer_DrawPixel(
ud.renderer, (Point2i){col->screen_x, sy},
Color_RGB((1 - fy) * r1 + fy * r2, (1 - fy) * g1 + fy * g2,
(1 - fy) * b1 + fy * b2));
}
}
}
} break;
}
if (swwFpsHelper_Update(&fps, ud.renderer)) {
swwFpsHelper_SetTitleWithFps(&fps, window, TITLE);
}
swwRenderer_Present(ud.renderer);
swwApp_PollEvent();
swwRateHelper_Sleep(&rh);
}
free(columns);
swwTexture_Destroy(rotated_tex);
swwTexture_Destroy(tex);
swwRenderer_Destroy(ud.renderer);
swwWindow_Destroy(window);
swwApp_Cleanup();
return 0;
}