Compare commits

...

12 Commits

Author SHA1 Message Date
veclavtalica
f90b973d86 /apps/demos/scenery: more than almost 2025-03-04 09:45:49 +03:00
veclavtalica
32675c012c /apps/demos/scenery: ...almost...🚬 2025-03-04 03:30:01 +03:00
veclavtalica
a97515e948 twn_textures.c: fix atlas packing, allow out of order population 2025-03-03 00:57:19 +03:00
veclavtalica
ed8e826b94 /apps/demos/scenery: something... 2025-03-02 23:19:27 +03:00
veclavtalica
4e5ff9433c /apps/demos/scenery: box is almost coherent... 2025-03-02 03:37:02 +03:00
veclavtalica
55829a1bef /apps/demos/scenery: fix world origin relation 2025-03-02 01:04:09 +03:00
veclavtalica
119bd52c51 /apps/demos/scenery: some friction 2025-03-02 00:02:32 +03:00
veclavtalica
5abd1ced1c /apps/demos/scenery: vehicle... 2025-03-01 16:42:14 +03:00
veclavtalica
80db96672d stb_ds.h: fix implicit casts to int, resulting in bogus bitshift 2025-03-01 12:15:41 +03:00
veclavtalica
2f6f7852be twn_api.json: add draw_line_3d() 2025-03-01 03:59:55 +03:00
veclavtalica
307d5552f6 twn_lines.c: 3d case 2025-03-01 03:46:11 +03:00
veclavtalica
5911cbd980 take care of warnings 2025-03-01 01:06:32 +03:00
12 changed files with 350 additions and 67 deletions

View File

@ -19,10 +19,215 @@
#define PLAYER_HEIGHT 0.6f
#define TREE_DENSITY 0.03f
#define G_CONST 10.0f
/* TODO: pregenerate grid of levels of detail */
static float heightmap[TERRAIN_DISTANCE][TERRAIN_DISTANCE];
/* vehicle sim ! */
/* https://www.youtube.com/watch?v=pwbwFdWBkU0 */
/* representation is a "jelly" box with spring configuration trying to make it coherent */
/* == springs == */
/* damped spring: F = -kx - cv */
/* x = length(p1-p0) - at_rest_length */
/* v = dot(v1 - v0, unit(p1-p0)) */
/* F = (-kx - cv) * unit(p1-p0) */
/* v += (F/m)*t */
/* one points gains positive F, other negative F, to come together */
/* == ground interaction == */
/* if point is under terrain, then apply this: */
/* x = y difference on point and ground */
/* -x(n) = x * normal */
/* -v(n) = dot(v, normal) */
/* -F = (-kx(n)-cv(n)) * normal */
/* == friction == */
/* v(o)/F(o) are perpendicular to slope (x - x(n)) */
/* if v(o) == 0, then */
/* -- at rest, static friction overcomes */
/* if F(o) <= f(s)*x(n), F(o) = 0 */
/* else, F(o) -= f(k) * x(n) */
/* else if length(v(o) + (F(o)/m)*t) <= (f(k)*x(n)*t), v(o) = 0 */
/* else, F = -unit(v(o)*f(k)*x(n)) */
#define VEHICLE_MASS 200.0f
#define VEHICLE_LENGTH 3.0f
#define VEHICLE_WIDTH 1.7f
#define VEHICLE_HEIGHT 1.3f
/* spring constant */
#define VEHICLE_SPRING_K 20000.0f
#define VEHICLE_SPRING_GK 80000.0f
/* damping constant */
#define VEHICLE_SPRING_C 800.0f
#define VEHICLE_SPRING_GC 100.0f
#define VEHICLE_FRICTION_S 1000.0f
#define VEHICLE_FRICTION_K 100.0f
/* TODO: shock springs, that are more loose, which are used to simulate the wheels */
/* initial, ideal corner positions */
static const Vec3 vbpi[8] = {
[0] = { 0, 0, 0 },
[1] = { VEHICLE_LENGTH, 0, 0 },
[2] = { VEHICLE_LENGTH, 0, VEHICLE_WIDTH },
[3] = { 0, 0, VEHICLE_WIDTH },
[4] = { 0, VEHICLE_HEIGHT, 0 },
[5] = { VEHICLE_LENGTH, VEHICLE_HEIGHT, 0 },
[6] = { VEHICLE_LENGTH, VEHICLE_HEIGHT, VEHICLE_WIDTH },
[7] = { 0, VEHICLE_HEIGHT, VEHICLE_WIDTH },
};
/* corner positions in simulation */
static Vec3 vbp[8] = { vbpi[0], vbpi[1], vbpi[2], vbpi[3],
vbpi[4], vbpi[5], vbpi[6], vbpi[7], };
/* corner velocities */
static Vec3 vbv[8];
/* springs */
static uint8_t vbs[28][2] = {
{0, 1}, {4, 5}, {0, 4},
{1, 2}, {5, 6}, {1, 5},
{2, 3}, {6, 7}, {2, 6},
{3, 0}, {7, 4}, {3, 7},
{0, 2}, {0, 5}, {0, 7},
{1, 3}, {1, 6}, {1, 4},
{4, 6}, {2, 7}, {2, 5},
{5, 7}, {3, 4}, {3, 6},
{0, 6}, {1, 7}, {2, 4}, {3, 5},
};
static float height_at(SceneIngame *scn, Vec2 position);
static Vec3 normal_at(SceneIngame *scn, Vec2 position);
static void draw_vehicle(SceneIngame *scn) {
for (size_t i = 0; i < 12; ++i)
draw_line_3d(vbp[vbs[i][0]], vbp[vbs[i][1]], 1, (Color){255, 255, 255, 255});
for (size_t i = 12; i < 24; ++i)
draw_line_3d(vbp[vbs[i][0]], vbp[vbs[i][1]], 1, (Color){200, 200, 200, 255});
for (size_t i = 24; i < 28; ++i)
draw_line_3d(vbp[vbs[i][0]], vbp[vbs[i][1]], 1, (Color){255, 125, 125, 255});
}
static void process_vehicle(SceneIngame *scn) {
/* apply gravity */
Vec3 Facc[8] = {0};
Vec3 const Fg = { .y = -VEHICLE_MASS * G_CONST };
for (size_t i = 0; i < 8; ++i)
Facc[i] = vec3_add(Facc[i], Fg);
/* apply springs */
for (size_t i = 0; i < 28; ++i) {
Vec3 const p0 = vbp[vbs[i][0]];
Vec3 const p1 = vbp[vbs[i][1]];
Vec3 const v0 = vbv[vbs[i][0]];
Vec3 const v1 = vbv[vbs[i][1]];
Vec3 const pd = vec3_sub(p1, p0);
Vec3 const pn = vec3_norm(pd);
/* TODO: length at rest could be precalculated */
float const lar = vec3_length(vec3_sub(vbpi[vbs[i][1]], vbpi[vbs[i][0]]));
float const x = vec3_length(pd) - lar;
float const v = vec3_dot(vec3_sub(v1, v0), pn);
Vec3 const Fs = vec3_scale(pn, -VEHICLE_SPRING_K * x - VEHICLE_SPRING_C * v);
Facc[vbs[i][0]] = vec3_sub(Facc[vbs[i][0]], Fs);
Facc[vbs[i][1]] = vec3_add(Facc[vbs[i][1]], Fs);
}
/* spring and friction against the ground */
for (size_t i = 0; i < 8; ++i) {
Vec3 const p = vbp[i];
Vec3 const v = vbv[i];
float const h = height_at(scn, (Vec2){ p.x, p.z });
if (h >= p.y) {
/* wheel processing */
if (i == 0 || i == 3 || i == 4 || i == 7) {
if (scn->camera_mode == 2 && input_action_pressed("player_forward")) {
Vec3 dir = i == 0 ? vec3_sub(vbp[1], vbp[0]) : vec3_sub(vbp[2], vbp[3]);
Facc[i] = vec3_add(Facc[i], vec3_scale(vec3_norm(dir), 6500));
}
}
/* normal force, for displacement */
Vec3 const n = normal_at(scn, (Vec2){ p.x, p.z });
float const xn = (h - p.y) * n.y;
float const vn = vec3_dot(v, n);
Vec3 const Fn = vec3_scale(n, -VEHICLE_SPRING_GK * xn - VEHICLE_SPRING_GC * vn);
Facc[i] = vec3_sub(Facc[i], Fn);
/* friction force, perpendicular to normal force */
/* TODO: is it right? aren't vn+vol should be = |v| */
Vec3 const von = vec3_norm(vec3_cross(n, vec3_cross(v, n)));
draw_line_3d(vbp[i], vec3_add(vbp[i], von), 1, (Color){255,255,0,255});
Vec3 const vo = vec3_scale(vec3_scale(von, vec3_length(v) - vn), -1);
float const vol = vec3_length(vo);
Vec3 const Fon = vec3_norm(vec3_cross(n, vec3_cross(Facc[i], n)));
Vec3 Fo = vec3_scale(vec3_scale(Fon, vec3_length(Facc[i]) - vec3_dot(Facc[i], n)), -1);
draw_line_3d(vbp[i], vec3_add(vbp[i], Fon), 1, (Color){255,255,0,255});
/* portion of total force along the surface */
float const Fol = vec3_length(Fo);
float const fkxn = VEHICLE_FRICTION_K * xn;
/* at rest, might want to start moving */
if (fabsf(0.0f - vol) <= 0.0001f) {
/* cannot overcome static friction, force along the surface is zeroed */
if (Fol <= VEHICLE_FRICTION_S * xn) {log_info("test 0"); Fo = vec3_scale(Fo, -1);}
/* resist the force by friction, while starting to move */
else {log_info("test 1"); Fo = vec3_sub(Fo, vec3_scale(von, fkxn));}
/* not at rest, stop accelerating along the surface */
} else if (vol + (Fol / VEHICLE_MASS) * ctx.frame_duration <= fkxn * ctx.frame_duration * 2) {
/* ugh ... */
vbv[i] = vec3_add(vbv[i], vo);
log_info("test 2");
/* just apply friction */
} else {
Fo = vec3_scale(von, -fkxn * 400);
}
Facc[i] = vec3_add(Facc[i], Fo);
// Facc[i] = vec3_sub(Fo, Fn);
// Facc[i] = vec3_sub(Fo, Fn);
Vec3 vd = vec3_scale(vec3_scale(Facc[i], (1.0f / VEHICLE_MASS)), ctx.frame_duration);
vbv[i] = vec3_add(vbv[i], vd);
vbp[i] = vec3_add(vbp[i], vec3_scale(vbv[i], ctx.frame_duration));
} else {
Vec3 vd = vec3_scale(vec3_scale(Facc[i], (1.0f / VEHICLE_MASS)), ctx.frame_duration);
vbv[i] = vec3_add(vbv[i], vd);
vbp[i] = vec3_add(vbp[i], vec3_scale(vbv[i], ctx.frame_duration));
}
}
}
static void process_vehicle_mode(State *state) {
SceneIngame *scn = (SceneIngame *)state->scene;
Vec3 const top_center = vec3_sub(vbp[4], vec3_scale(vec3_sub(vbp[4], vbp[6]), 1.0f / 2.0f));
// Vec3 const front_center = vec3_add(vbp[4], vec3_scale(vec3_sub(vbp[4], vbp[7]), 1.0f / 2.0f));
// Vec3 const facing_direction = vec3_sub(top_center, front_center);
float yawc, yaws, pitchc, pitchs;
sincosf(scn->yaw + (float)M_PI_2, &yaws, &yawc);
sincosf(scn->pitch, &pitchs, &pitchc);
Vec3 const looking_direction = vec3_norm(((Vec3){
yawc * pitchc,
pitchs,
yaws * pitchc,
}));
Vec3 const orbit = vec3_sub(top_center, vec3_scale(looking_direction, 7.5));
draw_camera(orbit, looking_direction, (Vec3){0,1,0}, (float)M_PI_2 * 0.8f, 1, TERRAIN_RADIUS * sqrtf(3));
scn->looking_direction = looking_direction;
scn->pos = top_center;
}
static void process_fly_mode(State *state) {
SceneIngame *scn = (SceneIngame *)state->scene;
@ -32,7 +237,7 @@ static void process_fly_mode(State *state) {
scn->looking_direction = dir_and_up.direction;
const Vec3 right = m_vec_norm(m_vec_cross(dir_and_up.direction, dir_and_up.up));
const float speed = 0.04f; /* TODO: put this in a better place */
const float speed = 0.1f; /* TODO: put this in a better place */
if (input_action_pressed("player_left"))
scn->pos = vec3_sub(scn->pos, m_vec_scale(right, speed));
@ -52,33 +257,39 @@ static void process_fly_mode(State *state) {
scn->pos.y -= speed;
}
/* TODO: could be baked in map format */
static Vec3 normal_at(SceneIngame *scn, Vec2 position) {
int const x = (int)(floorf(position.x - scn->world_center.x));
int const y = (int)(floorf(position.y - scn->world_center.y));
float const height0 = heightmap[x][y];
float const height1 = heightmap[x + 1][y];
float const height2 = heightmap[x][y + 1];
Vec3 const a = { .x = -1, .y = height0 - height1, .z = 0 };
Vec3 const b = { .x = 0, .y = height0 - height2, .z = -1 };
return vec3_scale(vec3_norm(vec3_cross(a, b)), -1);
}
/* TODO: don't operate on triangles, instead interpolate on quads */
static float height_at(SceneIngame *scn, Vec2 position) {
float height0, height1, height2, weight0, weight1, weight2;
int const x = (int)(floorf(position.x - scn->world_center.x));
int const y = (int)(floorf(position.y - scn->world_center.y));
int const x = (int)(HALF_TERRAIN_DISTANCE + (position.x - scn->pos.x));
int const y = (int)(HALF_TERRAIN_DISTANCE + (position.y - scn->pos.z));
height0 = heightmap[x][y];
height1 = heightmap[x + 1][y + 1];
float const height0 = heightmap[x][y];
float const height1 = heightmap[x + 1][y];
float const height2 = heightmap[x][y + 1];
float const height3 = heightmap[x + 1][y + 1];
Vec2 incell = { position.x - floorf(position.x), position.y - floorf(position.y) };
/* who needs barycentric coordinates, am i right? */
weight0 = 1 / sqrtf(powf(incell.x, 2) + powf(incell.y, 2));
weight1 = 1 / sqrtf(powf(1 - incell.x, 2) + powf(1 - incell.y, 2));
float const weight0 = (1 - incell.x) * (1 - incell.y);
float const weight1 = ( incell.x) * (1 - incell.y);
float const weight2 = (1 - incell.x) * ( incell.y);
float const weight3 = ( incell.x) * ( incell.y);
/* find which triangle we're directly under */
/* for this manhattan distance is sufficient */
if (incell.x + (1 - incell.y) < (1 - incell.x) + incell.y) {
height2 = heightmap[x][y + 1];
weight2 = 1 / sqrtf(powf(incell.x, 2) + powf(1 - incell.y, 2));
} else {
height2 = heightmap[x + 1][y];
weight2 = 1 / sqrtf(powf(1 - incell.x, 2) + powf(incell.y, 2));
}
return (height0 * weight0 + height1 * weight1 + height2 * weight2) / (weight0 + weight1 + weight2);
return (height0 * weight0 + height1 * weight1 + height2 * weight2 + height3 * weight3) / (weight0 + weight1 + weight2 + weight3);
}
@ -147,6 +358,8 @@ static void generate_terrain(SceneIngame *scn) {
heightmap[lx][ly] = height;
}
}
scn->world_center = (Vec2){ floorf(scn->pos.x - HALF_TERRAIN_DISTANCE), floorf(scn->pos.z - HALF_TERRAIN_DISTANCE) };
}
@ -264,12 +477,14 @@ static void ingame_tick(State *state) {
}
if (input_action_just_pressed("toggle_camera_mode"))
scn->flying_camera = !scn->flying_camera;
scn->camera_mode = scn->camera_mode == 2 ? 0 : scn->camera_mode + 1;
if (scn->flying_camera) {
if (scn->camera_mode == 1) {
process_fly_mode(state);
} else {
} else if (scn->camera_mode == 0) {
process_ground_mode(state);
} else if (scn->camera_mode) {
process_vehicle_mode(state);
}
/* toggle mouse capture with end key */
@ -279,7 +494,10 @@ static void ingame_tick(State *state) {
ctx.mouse_capture = scn->mouse_captured;
generate_terrain(scn);
process_vehicle(scn);
draw_terrain(scn);
draw_vehicle(scn);
draw_skybox("/assets/miramar/miramar_*.tga");

View File

@ -13,6 +13,7 @@ typedef struct SceneIngame {
Scene base;
Vec3 looking_direction;
Vec2 world_center;
Vec3 pos;
float yaw;
@ -20,7 +21,7 @@ typedef struct SceneIngame {
float roll;
bool mouse_captured;
bool flying_camera;
int camera_mode;
} SceneIngame;

View File

@ -47,6 +47,13 @@ TWN_API void draw_line(Vec2 start,
float thickness, /* optional, default: 1 */
Color color); /* optional, default: all 255 */
/* intended for debugging and spatial reference */
TWN_API void draw_line_3d(Vec3 start,
Vec3 finish,
float thickness, /* optional, default: 1 */
Color color); /* optional, default: all 255 */
/* TODO: combine with draw_rectangle()? */
TWN_API void draw_box(Rect rect,
float thickness, /* optional, default: 1 */

View File

@ -144,6 +144,18 @@
]
},
"draw_line_3d": {
"module": "draw",
"symbol": "line_3d",
"header": "twn_draw.h",
"params": [
{ "name": "start", "type": "Vec3" },
{ "name": "finish", "type": "Vec3" },
{ "name": "thickness", "type": "float", "default": 1 },
{ "name": "color", "type": "Color", "default": { "r": 255, "g": 255, "b": 255, "a": 255 } }
]
},
"draw_triangle": {
"module": "draw",
"symbol": "triangle",

View File

@ -56,6 +56,9 @@ void render_clear(void) {
for (size_t i = 0; i < hmlenu(ctx.quad_batches); ++i)
arrsetlen(ctx.quad_batches[i].value.primitives, 0);
for (size_t i = 0; i < hmlenu(ctx.line_batches); ++i)
arrsetlen(ctx.line_batches[i].value.vertices, 0);
}
@ -328,7 +331,8 @@ static void render_2d(void) {
render_circle(&invocation.primitive->circle);
break;
case PRIMITIVE_2D_LINES:
render_lines(&invocation.primitive->line);
render_lines(&invocation.primitive->line, false);
stbds_arrfreef(invocation.primitive->line.vertices);
break;
case PRIMITIVE_2D_TEXT:
default:
@ -360,7 +364,7 @@ static void render_2d(void) {
render_text(&invocation.primitive->text);
break;
case PRIMITIVE_2D_LINES:
render_lines(&invocation.primitive->line);
render_lines(&invocation.primitive->line, false);
break;
default:
SDL_assert(false);
@ -387,6 +391,9 @@ static void render_space(void) {
for (size_t i = 0; i < hmlenu(ctx.billboard_batches); ++i)
finally_draw_billboard_batch(&ctx.billboard_batches[i].value, ctx.billboard_batches[i].key);
for (size_t i = 0; i < hmlenu(ctx.line_batches); ++i)
render_lines(&ctx.line_batches[i].value, true);
render_skybox(); /* after everything else, as to use depth buffer for early z rejection */
}

View File

@ -322,7 +322,7 @@ IndexBuffer get_circle_element_buffer(void);
void render_circle(const CirclePrimitive *circle);
void render_lines(LinePrimitive *line);
void render_lines(const LinePrimitive *line, bool is_3d);
void render_rectangle(const RectPrimitive *rectangle);

View File

@ -14,6 +14,11 @@ void draw_line(Vec2 start,
if (fabsf(1.0f - thickness) >= 0.00001f)
log_warn("Thickness isn't yet implemented for line drawing (got %f)", (double)thickness);
if (thickness < 1.0f) {
log_warn("Invalid thickness given.");
return;
}
struct LineVertex const v0 = { .position = (Vec3){start.x, start.y, 0}, .color = color };
struct LineVertex const v1 = { .position = (Vec3){finish.x, finish.y, 0}, .color = color };
@ -46,7 +51,36 @@ void draw_line(Vec2 start,
}
void render_lines(LinePrimitive *line) {
void draw_line_3d(Vec3 start,
Vec3 finish,
float thickness,
Color color)
{
if (fabsf(1.0f - thickness) >= 0.00001f)
log_warn("Thickness isn't yet implemented for line drawing (got %f)", (double)thickness);
if (thickness < 1.0f) {
log_warn("Invalid thickness given.");
return;
}
struct LineVertex const v0 = { .position = start, .color = color };
struct LineVertex const v1 = { .position = finish, .color = color };
/* 3d case, unordered depth based draw */
struct LineBatchItemKey const key = { .color = color, .thickness = (uint8_t)(floorf(thickness)) };
struct LineBatchItem *batch_p = hmgetp_null(ctx.line_batches, key);
if (!batch_p) {
hmput(ctx.line_batches, key, ((struct LinePrimitive){.thickness = thickness, .color = color}));
batch_p = &ctx.line_batches[hmlenu(ctx.line_batches) - 1];
}
arrput(batch_p->value.vertices, v0);
arrput(batch_p->value.vertices, v1);
return;
}
void render_lines(LinePrimitive const *line, bool is_3d) {
DeferredCommandDraw command = {0};
VertexBuffer buffer = get_scratch_vertex_array();
@ -68,10 +102,12 @@ void render_lines(LinePrimitive *line) {
.buffer = buffer
};
command.primitive_count = arrlenu(line->vertices);
command.primitive_count = (uint32_t)arrlenu(line->vertices);
command.geometry_mode = DEFERRED_COMMAND_DRAW_GEOMETRY_MODE_LINES;
command.pipeline = PIPELINE_2D;
command.pipeline = is_3d ? PIPELINE_SPACE : PIPELINE_2D;
command.texture_mode = line->color.a == 255 ? TEXTURE_MODE_OPAQUE : TEXTURE_MODE_GHOSTLY;
command.depth_range_high = depth_range_high;
command.depth_range_low = depth_range_low;
@ -81,7 +117,5 @@ void render_lines(LinePrimitive *line) {
.draw = command
};
/* TODO: should it be deleted here? */
arrfree(line->vertices);
arrpush(deferred_commands, final_command);
}

View File

@ -50,6 +50,13 @@ typedef struct EngineContext {
MeshBatchItem *uncolored_mesh_batches;
MeshBatchItem *billboard_batches;
MeshBatchItem *quad_batches;
struct LineBatchItem {
struct LineBatchItemKey {
Color color;
uint8_t thickness;
} key;
struct LinePrimitive value;
} *line_batches;
TextCache text_cache;
TextureCache texture_cache;

View File

@ -255,17 +255,17 @@ static void upload_texture_from_surface(GPUTexture texture, SDL_Surface *surface
}
static void recreate_current_atlas_texture(TextureCache *cache) {
static void recreate_current_atlas_texture(TextureCache *cache, int atlas_index) {
profile_start("atlas recreation");
/* TODO: should surfaces be freed after they cannot be referenced in atlas builing? */
/* for example, if full page of 64x64 tiles was already filled, there's no real reason to process them further */
SDL_Surface *atlas_surface = cache->atlas_surfaces[cache->atlas_index];
SDL_Surface *atlas_surface = cache->atlas_surfaces[atlas_index];
/* blit the texture surfaces onto the atlas */
for (size_t i = 0; i < shlenu(cache->hash); ++i) {
/* skip all that aren't part of currently built one */
if (cache->hash[i].value.atlas_index != cache->atlas_index)
if (cache->hash[i].value.atlas_index != atlas_index)
continue;
/* skip loners */
@ -284,7 +284,7 @@ static void recreate_current_atlas_texture(TextureCache *cache) {
}
/* texturize it! */
upload_texture_from_surface(cache->atlas_textures[cache->atlas_index], atlas_surface);
upload_texture_from_surface(cache->atlas_textures[atlas_index], atlas_surface);
profile_end("atlas recreation");
}
@ -337,16 +337,18 @@ static stbrp_rect *filter_unpacked_rects(stbrp_rect *rects) {
/* updates the original rects array with the data from packed_rects */
/* returns true if all rects were packed successfully */
static bool update_rects(TextureCache *cache, stbrp_rect *rects, stbrp_rect *packed_rects) {
static bool update_rects(TextureCache *cache, stbrp_rect *rects, stbrp_rect *packed_rects, int atlas_index) {
/* !!! do not grow either of the arrays !!! */
/* the reallocation will try to reassign the array pointer, to no effect. */
/* see stb_ds.h */
bool packed_all = true;
for (size_t i = 0; i < arrlenu(packed_rects); ++i) {
/* we can check if any rects failed to be packed right here */
/* it's not ideal, but it avoids another iteration */
if (!packed_rects[i].was_packed) {
/* mark it as potentially filled in next atlas page */
cache->hash[packed_rects[i].id].value.atlas_index = atlas_index + 1;
packed_all = false;
continue;
}
@ -355,7 +357,7 @@ static bool update_rects(TextureCache *cache, stbrp_rect *rects, stbrp_rect *pac
/* while the order of the elements in the hash map is unknown to us, */
/* their equivalents in `rects` are in that same (unknown) order, which means */
/* we can use the index we had saved to find the original texture struct */
cache->hash[packed_rects[i].id].value.atlas_index = cache->atlas_index;
cache->hash[packed_rects[i].id].value.atlas_index = atlas_index;
}
return packed_all;
@ -566,9 +568,6 @@ void textures_update_atlas(TextureCache *cache) {
upload_texture_from_surface(response.loner_texture, response.data);
response.srcrect = (Rect) { .w = (float)response.data->w, .h = (float)response.data->h };
} else {
/* will be fully populated as the atlas updates */
response.atlas_index = cache->atlas_index;
}
cache->hash[texture_load_queue[i].index].value = response;
@ -576,40 +575,42 @@ void textures_update_atlas(TextureCache *cache) {
arrsetlen(texture_load_queue, 0);
/* this function makes a lot more sense if you read stb_rect_pack.h */
stbrp_context pack_ctx; /* target info */
stbrp_init_target(&pack_ctx,
(int)ctx.texture_atlas_size,
(int)ctx.texture_atlas_size,
cache->node_buffer,
(int)ctx.texture_atlas_size);
stbrp_rect *rects = create_rects_from_cache(cache);
int current_atlas_index = 0;
/* we have to keep packing, and creating atlases if necessary, */
/* until all rects have been packed. */
/* ideally, this will not iterate more than once. */
bool textures_remaining = true;
while (textures_remaining) {
/* this function makes a lot more sense if you read stb_rect_pack.h */
stbrp_context pack_ctx; /* target info */
stbrp_init_target(&pack_ctx,
(int)ctx.texture_atlas_size,
(int)ctx.texture_atlas_size,
cache->node_buffer,
(int)ctx.texture_atlas_size);
stbrp_rect *rects_to_pack = filter_unpacked_rects(rects);
stbrp_pack_rects(&pack_ctx, rects_to_pack, (int)arrlen(rects_to_pack));
textures_remaining = !update_rects(cache, rects, rects_to_pack);
textures_remaining = !update_rects(cache, rects, rects_to_pack, current_atlas_index);
arrfree(rects_to_pack); /* got what we needed */
/* some textures couldn't be packed */
if (textures_remaining) {
update_texture_rects_in_atlas(cache, rects);
recreate_current_atlas_texture(cache);
recreate_current_atlas_texture(cache, current_atlas_index);
/* need a new atlas for next time */
add_new_atlas(cache);
++cache->atlas_index;
if (current_atlas_index >= cache->atlas_index) ++cache->atlas_index;
++current_atlas_index;
}
}
update_texture_rects_in_atlas(cache, rects);
recreate_current_atlas_texture(cache);
recreate_current_atlas_texture(cache, current_atlas_index);
cache->is_dirty = false;

View File

@ -181,13 +181,10 @@ bool file_exists(const char *path) {
void textures_dump_atlases(void) {
PHYSFS_mkdir("/dump");
/* TODO: png instead of bmp */
const char string_template[] = "/dump/atlas%zd.bmp";
size_t i = 0;
for (; i < arrlenu(ctx.texture_cache.atlas_surfaces); ++i) {
char *buf = NULL;
SDL_asprintf(&buf, string_template, i);
SDL_asprintf(&buf, "/dump/atlas%zd.bmp", (ssize_t)i);
SDL_RWops *handle = PHYSFSRWOPS_openWrite(buf);

View File

@ -1,9 +1,8 @@
#include "twn_util.h"
#include "twn_workers_c.h"
#include "rendering/twn_draw_c.h"
#ifndef __EMSCRIPTEN__
SDL_sem *workers_job_semaphore;
static SDL_sem *workers_job_semaphore;
static SDL_mutex *workers_mutex;
static SDL_sem *workers_exit_semaphore; /* should come to count of `workers_pool_size` */

View File

@ -1079,8 +1079,8 @@ static size_t stbds_siphash_bytes(void *p, size_t len, size_t seed)
} while (0)
for (i=0; i+sizeof(size_t) <= len; i += sizeof(size_t), d += sizeof(size_t)) {
data = d[0] | (d[1] << 8) | (d[2] << 16) | (d[3] << 24);
data |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | (d[7] << 24)) << 16 << 16; // discarded if size_t == 4
data = d[0] | (d[1] << 8) | (d[2] << 16) | ((size_t) d[3] << 24);
data |= (size_t) (d[4] | (d[5] << 8) | (d[6] << 16) | ((size_t) d[7] << 24)) << 16 << 16; // discarded if size_t == 4
v3 ^= data;
for (j=0; j < STBDS_SIPHASH_C_ROUNDS; ++j)
@ -1092,10 +1092,10 @@ static size_t stbds_siphash_bytes(void *p, size_t len, size_t seed)
case 7: data |= ((size_t) d[6] << 24) << 24; // fall through
case 6: data |= ((size_t) d[5] << 20) << 20; // fall through
case 5: data |= ((size_t) d[4] << 16) << 16; // fall through
case 4: data |= (d[3] << 24); // fall through
case 3: data |= (d[2] << 16); // fall through
case 2: data |= (d[1] << 8); // fall through
case 1: data |= d[0]; // fall through
case 4: data |= ((size_t) d[3] << 24); // fall through
case 3: data |= ((size_t) d[2] << 16); // fall through
case 2: data |= ((size_t) d[1] << 8); // fall through
case 1: data |= (size_t) d[0]; // fall through
case 0: break;
}
v3 ^= data;