proper anchor points
This commit is contained in:
1 parent
bdc1a4561d
commit
333ebca300
3 files changed
+108
-56
No files matched your search
+14
-1
@@ -24,6 +24,18 @@ enum easing_t {
|
||||
EASE_IN_OUT_CUBIC
|
||||
};
|
||||
|
||||
enum anchor_point_t {
|
||||
ANCHOR_TOP_LEFT,
|
||||
ANCHOR_TOP_CENTER,
|
||||
ANCHOR_TOP_RIGHT,
|
||||
ANCHOR_CENTER_LEFT,
|
||||
ANCHOR_CENTER,
|
||||
ANCHOR_CENTER_RIGHT,
|
||||
ANCHOR_BOTTOM_LEFT,
|
||||
ANCHOR_BOTTOM_CENTER,
|
||||
ANCHOR_BOTTOM_RIGHT,
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
typedef struct compositor_t compositor_t;
|
||||
|
||||
@@ -39,7 +51,8 @@ uint32_t compositor_insert(compositor_t* comp,
|
||||
uint32_t fb_width,
|
||||
uint32_t fb_height,
|
||||
int32_t x, int32_t y,
|
||||
int32_t z_order, float scale = 1.0f);
|
||||
int32_t z_order, float scale,
|
||||
anchor_point_t anchor);
|
||||
|
||||
// Remove element by ID
|
||||
void compositor_remove(compositor_t* comp, uint32_t id);
|
||||
|
||||
+58
-36
@@ -70,6 +70,7 @@ struct element_t {
|
||||
bool visible;
|
||||
float scale;
|
||||
float alpha = 1.0f;
|
||||
anchor_point_t anchor; // Anchor point for positioning and scaling
|
||||
uint32_t id; // Unique identifier
|
||||
std::vector<uint32_t> occupied_tiles; // List of occupied tiles
|
||||
animation_t anim;
|
||||
@@ -154,30 +155,36 @@ static inline void get_element_bounds(const element_t* elem,
|
||||
int32_t& out_x0, int32_t& out_y0,
|
||||
int32_t& out_x1, int32_t& out_y1)
|
||||
{
|
||||
if (elem->scale == 1.0f) {
|
||||
// Fast path for 1.0 scale
|
||||
out_x0 = elem->x;
|
||||
out_y0 = elem->y;
|
||||
out_x1 = elem->x + (int32_t)elem->fb_width;
|
||||
out_y1 = elem->y + (int32_t)elem->fb_height;
|
||||
// Calculate scaled dimensions, rounding to nearest integer to prevent jitter
|
||||
int32_t scaled_width = (int32_t)std::round(elem->fb_width * elem->scale);
|
||||
int32_t scaled_height = (int32_t)std::round(elem->fb_height * elem->scale);
|
||||
|
||||
float anchor_x_factor = 0.0f;
|
||||
float anchor_y_factor = 0.0f;
|
||||
|
||||
// Determine the anchor's proportional position within the element
|
||||
switch (elem->anchor) {
|
||||
case ANCHOR_TOP_LEFT: anchor_x_factor = 0.0f; anchor_y_factor = 0.0f; break;
|
||||
case ANCHOR_TOP_CENTER: anchor_x_factor = 0.5f; anchor_y_factor = 0.0f; break;
|
||||
case ANCHOR_TOP_RIGHT: anchor_x_factor = 1.0f; anchor_y_factor = 0.0f; break;
|
||||
case ANCHOR_CENTER_LEFT: anchor_x_factor = 0.0f; anchor_y_factor = 0.5f; break;
|
||||
case ANCHOR_CENTER: anchor_x_factor = 0.5f; anchor_y_factor = 0.5f; break;
|
||||
case ANCHOR_CENTER_RIGHT: anchor_x_factor = 1.0f; anchor_y_factor = 0.5f; break;
|
||||
case ANCHOR_BOTTOM_LEFT: anchor_x_factor = 0.0f; anchor_y_factor = 1.0f; break;
|
||||
case ANCHOR_BOTTOM_CENTER: anchor_x_factor = 0.5f; anchor_y_factor = 1.0f; break;
|
||||
case ANCHOR_BOTTOM_RIGHT: anchor_x_factor = 1.0f; anchor_y_factor = 1.0f; break;
|
||||
}
|
||||
else {
|
||||
// Scaled path
|
||||
int32_t scaled_width = (int32_t)(elem->fb_width * elem->scale);
|
||||
int32_t scaled_height = (int32_t)(elem->fb_height * elem->scale);
|
||||
|
||||
// Apply anchor point transformation (bottom-center)
|
||||
float x_offset = (elem->fb_width * 0.5f) * (1.0f - elem->scale);
|
||||
float y_offset = (elem->fb_height) * (1.0f - elem->scale);
|
||||
|
||||
// Use std::round to avoid 1-pixel jiggle
|
||||
out_x0 = elem->x + (int32_t)std::round(x_offset);
|
||||
out_y0 = elem->y + (int32_t)std::round(y_offset);
|
||||
// The element's (x, y) represents the anchor point's position on screen.
|
||||
// We calculate the top-left corner (out_x0, out_y0) by offsetting
|
||||
// from the anchor's position.
|
||||
out_x0 = elem->x - (int32_t)std::round(scaled_width * anchor_x_factor);
|
||||
out_y0 = elem->y - (int32_t)std::round(scaled_height * anchor_y_factor);
|
||||
|
||||
// The bottom-right corner is the top-left plus the scaled dimensions.
|
||||
out_x1 = out_x0 + scaled_width;
|
||||
out_y1 = out_y0 + scaled_height;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Spatial Hashing & Dirty Rects ---
|
||||
|
||||
@@ -256,8 +263,6 @@ static void update_spatial_hash(compositor_t* comp, element_t* elem) {
|
||||
}
|
||||
}
|
||||
|
||||
// --- Draw Order ---
|
||||
|
||||
// Uses an incremental insertion sort to place an element in the correct z-order position.
|
||||
static void update_draw_order(compositor_t* comp, uint32_t id) {
|
||||
element_t* elem = find_element(comp, id);
|
||||
@@ -286,12 +291,8 @@ static void update_draw_order(compositor_t* comp, uint32_t id) {
|
||||
comp->draw_order.insert(it_insert, id);
|
||||
}
|
||||
|
||||
// --- Public C-Style API Functions ---
|
||||
|
||||
extern "C" {
|
||||
|
||||
// --- Lifecycle ---
|
||||
|
||||
// Advances all animations by a given time delta.
|
||||
void compositor_tick(compositor_t* comp, float delta_time_sec) {
|
||||
if (!comp) return;
|
||||
@@ -448,15 +449,14 @@ void compositor_destroy(compositor_t* comp) {
|
||||
delete comp; // Uses the default C++ destructor for cleanup
|
||||
}
|
||||
|
||||
// --- Element Management ---
|
||||
|
||||
// Inserts a new element into the compositor and returns a unique ID.
|
||||
uint32_t compositor_insert(compositor_t* comp,
|
||||
const uint32_t* fb,
|
||||
uint32_t fb_width,
|
||||
uint32_t fb_height,
|
||||
int32_t x, int32_t y,
|
||||
int32_t z_order, float scale) {
|
||||
int32_t z_order, float scale,
|
||||
anchor_point_t anchor) {
|
||||
if (!comp) {
|
||||
// Cannot set error if comp is NULL, must return 0
|
||||
return 0;
|
||||
@@ -483,6 +483,7 @@ uint32_t compositor_insert(compositor_t* comp,
|
||||
.z_order = z_order,
|
||||
.visible = true,
|
||||
.scale = scale,
|
||||
.anchor = anchor,
|
||||
.id = id
|
||||
};
|
||||
comp->elements_map[id] = new_elem;
|
||||
@@ -567,9 +568,7 @@ void compositor_update(compositor_t* comp, uint32_t id,
|
||||
comp->last_error.clear();
|
||||
}
|
||||
|
||||
// --- Optimized Setters ---
|
||||
|
||||
// Optimized setter for an element's (x, y) position.
|
||||
// setter for an element's (x, y) position.
|
||||
void compositor_set_position(compositor_t* comp, uint32_t id,
|
||||
int32_t x, int32_t y) {
|
||||
if (!comp) return;
|
||||
@@ -589,7 +588,7 @@ void compositor_set_position(compositor_t* comp, uint32_t id,
|
||||
comp->last_error.clear();
|
||||
}
|
||||
|
||||
// Optimized setter for an element's z-order.
|
||||
// setter for an element's z-order.
|
||||
void compositor_set_zorder(compositor_t* comp, uint32_t id,
|
||||
int32_t z_order) {
|
||||
if (!comp) return;
|
||||
@@ -606,7 +605,7 @@ void compositor_set_zorder(compositor_t* comp, uint32_t id,
|
||||
comp->last_error.clear();
|
||||
}
|
||||
|
||||
// Optimized setter for an element's visibility.
|
||||
// setter for an element's visibility.
|
||||
void compositor_set_visible(compositor_t* comp, uint32_t id,
|
||||
bool visible) {
|
||||
if (!comp) return;
|
||||
@@ -629,7 +628,7 @@ void compositor_set_visible(compositor_t* comp, uint32_t id,
|
||||
comp->last_error.clear();
|
||||
}
|
||||
|
||||
// Optimized setter for an element's scale.
|
||||
// setter for an element's scale.
|
||||
void compositor_set_scale(compositor_t* comp, uint32_t id,
|
||||
float scale) {
|
||||
if (!comp) return;
|
||||
@@ -654,7 +653,7 @@ void compositor_set_scale(compositor_t* comp, uint32_t id,
|
||||
comp->last_error.clear();
|
||||
}
|
||||
|
||||
// Optimized setter for an element's alpha.
|
||||
// setter for an element's alpha.
|
||||
void compositor_set_alpha(compositor_t* comp, uint32_t id, float alpha) {
|
||||
if (!comp) return;
|
||||
element_t* elem = find_element(comp, id);
|
||||
@@ -679,7 +678,30 @@ void compositor_set_alpha(compositor_t* comp, uint32_t id, float alpha) {
|
||||
}
|
||||
}
|
||||
|
||||
// --- Animation ---
|
||||
// setter for an element's anchor point.
|
||||
void compositor_set_anchor(compositor_t* comp, uint32_t id, anchor_point_t anchor) {
|
||||
if (!comp) return;
|
||||
element_t* elem = find_element(comp, id);
|
||||
if (!elem) {
|
||||
set_error(comp, "Element with ID " + std::to_string(id) + " not found to set anchor.");
|
||||
return;
|
||||
}
|
||||
if (elem->anchor == anchor) {
|
||||
comp->last_error.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// Changing anchor changes bounds if scale != 1.0, so treat it like a move/scale.
|
||||
mark_element_dirty(comp, elem);
|
||||
clear_spatial_hash(comp, id);
|
||||
|
||||
elem->anchor = anchor;
|
||||
|
||||
update_spatial_hash(comp, elem);
|
||||
|
||||
// Clear last error on success
|
||||
comp->last_error.clear();
|
||||
}
|
||||
|
||||
void compositor_animate_move(compositor_t* comp, uint32_t id,
|
||||
int32_t to_x, int32_t to_y,
|
||||
@@ -1060,7 +1082,7 @@ const uint32_t* compositor_compose(compositor_t* comp) {
|
||||
// --- Blending Loop (with SIMD) ---
|
||||
int32_t x = blend_x0;
|
||||
|
||||
#if defined(__SSE2__) || (defined(_M_AMD64) || defined(_M_X64))
|
||||
#if defined(__x86_64__) || defined(__i386__) || defined(_M_X64) || defined(_M_IX86)
|
||||
// Pre-calculate fixed values for the SIMD loop
|
||||
const __m128i v_zero = _mm_setzero_si128();
|
||||
const __m128i v_one = _mm_set1_epi16(1);
|
||||
|
||||
+36
-19
@@ -13,8 +13,16 @@ int main() {
|
||||
const int HEIGHT = 720;
|
||||
const int CHAR_WIDTH = 400;
|
||||
const int CHAR_HEIGHT = 600;
|
||||
const int CHAR_Y_OFFSET = 140;
|
||||
const int POS_Y = HEIGHT - CHAR_HEIGHT - CHAR_Y_OFFSET;
|
||||
|
||||
// --- New Character Positioning Constants ---
|
||||
// The vertical baseline for character sprites, making positioning independent of sprite height.
|
||||
const int CHAR_BASELINE_Y = HEIGHT - 140;
|
||||
// The horizontal center for the character on the left.
|
||||
const int LEFT_CHAR_CENTER_X = 60 + (CHAR_WIDTH / 2);
|
||||
// The horizontal center for the character on the right.
|
||||
const int RIGHT_CHAR_CENTER_X = WIDTH - 60 - (CHAR_WIDTH / 2);
|
||||
// An off-screen position to the left for transitions, based on the final position.
|
||||
const int OFFSCREEN_LEFT_X = LEFT_CHAR_CENTER_X - 660;
|
||||
|
||||
window_t* window = window_create(WIDTH, HEIGHT, u8"The Obwarzanek Narrative Engine");
|
||||
if (!window) {
|
||||
@@ -31,59 +39,65 @@ int main() {
|
||||
|
||||
uint32_t* bg = image_load_resized("assets/background.png", WIDTH, HEIGHT);
|
||||
if (bg) {
|
||||
uint32_t bg_id = compositor_insert(compositor, bg, WIDTH, HEIGHT, 0, 0, 0);
|
||||
uint32_t bg_id = compositor_insert(compositor, bg, WIDTH, HEIGHT, 0, 0, 0, 1.0f, ANCHOR_TOP_LEFT);
|
||||
compositor_set_alpha(compositor, bg_id, 1.0f);
|
||||
}
|
||||
|
||||
uint32_t* textbox = image_load_resized("assets/textbox.png", WIDTH, 180);
|
||||
if (textbox) {
|
||||
compositor_insert(compositor, textbox, WIDTH, 180, 0, HEIGHT - 180, 3);
|
||||
// Position via the bottom-left corner for easier alignment with screen bottom
|
||||
compositor_insert(compositor, textbox, WIDTH, 180, 0, HEIGHT, 3, 1.0f, ANCHOR_BOTTOM_LEFT);
|
||||
}
|
||||
|
||||
uint32_t* maksw_happy = image_load_resized("assets/maksw.png", CHAR_WIDTH, CHAR_HEIGHT);
|
||||
// Insert off-screen left, anchored at bottom-center for natural scaling
|
||||
uint32_t maksw_happy_id = compositor_insert(compositor, maksw_happy,
|
||||
CHAR_WIDTH, CHAR_HEIGHT, -600, POS_Y, 1);
|
||||
CHAR_WIDTH, CHAR_HEIGHT, OFFSCREEN_LEFT_X, CHAR_BASELINE_Y, 1, 1.0f, ANCHOR_BOTTOM_CENTER);
|
||||
|
||||
uint32_t* maksw_neutral = image_load_resized("assets/maksw2.png", CHAR_WIDTH, CHAR_HEIGHT);
|
||||
// Insert at the final left position, anchored at bottom-center
|
||||
uint32_t maksw_neutral_id = compositor_insert(compositor, maksw_neutral,
|
||||
CHAR_WIDTH, CHAR_HEIGHT, 60, POS_Y, 1);
|
||||
CHAR_WIDTH, CHAR_HEIGHT, LEFT_CHAR_CENTER_X, CHAR_BASELINE_Y, 1, 1.0f, ANCHOR_BOTTOM_CENTER);
|
||||
compositor_set_visible(compositor, maksw_neutral_id, false);
|
||||
|
||||
uint32_t* wikor = image_load_resized("assets/wikor.png", CHAR_WIDTH, CHAR_HEIGHT);
|
||||
// Insert at the final right position, anchored at bottom-center
|
||||
uint32_t wikor_id = compositor_insert(compositor, wikor,
|
||||
CHAR_WIDTH, CHAR_HEIGHT, WIDTH - CHAR_WIDTH - 60, POS_Y, 2);
|
||||
CHAR_WIDTH, CHAR_HEIGHT, RIGHT_CHAR_CENTER_X, CHAR_BASELINE_Y, 2, 1.0f, ANCHOR_BOTTOM_CENTER);
|
||||
|
||||
text_t* name_text = text_init(300, 100);
|
||||
text_set_font(name_text, "assets/Aller_Rg.ttf", 40.0f);
|
||||
// Text is anchored at top-left, positioned relative to textbox
|
||||
uint32_t name_id = compositor_insert(compositor, text_getfb(name_text),
|
||||
300, 100, 60, HEIGHT - 180, 4);
|
||||
300, 100, 60, HEIGHT - 180, 4, 1.0f, ANCHOR_TOP_LEFT);
|
||||
|
||||
text_t* dialogue_text = text_init(WIDTH - 140, 160);
|
||||
text_set_font(dialogue_text, "assets/Vera.ttf", 30.0f);
|
||||
// Text is anchored at top-left, positioned relative to textbox
|
||||
uint32_t dialogue_id = compositor_insert(compositor, text_getfb(dialogue_text),
|
||||
WIDTH - 140, 160, 60, HEIGHT - 130, 4);
|
||||
WIDTH - 140, 160, 60, HEIGHT - 130, 4, 1.0f, ANCHOR_TOP_LEFT);
|
||||
|
||||
uint32_t* mask = image_load_resized("assets/mask.png", 1280, 1080);
|
||||
// okay so this requires some math
|
||||
// inserting at 0,0 (top-left anchor) means it will cover the whole screen
|
||||
// we need to insert it above the screen, so -1080
|
||||
uint32_t mask_id = compositor_insert(compositor, mask, 1280, 1080, 0, -1080, 5);
|
||||
// Use top-left anchor for full-screen wipes
|
||||
uint32_t mask_id = compositor_insert(compositor, mask, 1280, 1080, 0, -1080, 5, 1.0f, ANCHOR_TOP_LEFT);
|
||||
compositor_set_visible(compositor, mask_id, false);
|
||||
|
||||
uint32_t* mask2 = image_load_resized("assets/mask2.png", 1280, 1080);
|
||||
// here, we put it in the center, as this is the slide out
|
||||
uint32_t mask2_id = compositor_insert(compositor, mask2, 1280, 1080, 0, 0, 5);
|
||||
// Use top-left anchor for full-screen wipes
|
||||
uint32_t mask2_id = compositor_insert(compositor, mask2, 1280, 1080, 0, 0, 5, 1.0f, ANCHOR_TOP_LEFT);
|
||||
compositor_set_visible(compositor, mask2_id, false);
|
||||
|
||||
uint32_t* black = (uint32_t*)malloc(WIDTH * HEIGHT * sizeof(uint32_t));
|
||||
for (int i = 0; i < WIDTH * HEIGHT; i++) {
|
||||
black[i] = 0xFF000000;
|
||||
}
|
||||
uint32_t black_id = compositor_insert(compositor, black, WIDTH, HEIGHT, 0, 0, 6);
|
||||
// Use top-left anchor for full-screen fade
|
||||
uint32_t black_id = compositor_insert(compositor, black, WIDTH, HEIGHT, 0, 0, 6, 1.0f, ANCHOR_TOP_LEFT);
|
||||
compositor_set_visible(compositor, black_id, false);
|
||||
compositor_set_alpha(compositor, black_id, 0.0f);
|
||||
|
||||
compositor_animate_move(compositor, maksw_happy_id, 60, POS_Y, 1.0f, EASE_OUT_CUBIC);
|
||||
// Animate character into position using the new centered coordinates
|
||||
compositor_animate_move(compositor, maksw_happy_id, LEFT_CHAR_CENTER_X, CHAR_BASELINE_Y, 1.0f, EASE_OUT_CUBIC);
|
||||
compositor_animate_scale(compositor, maksw_happy_id, 1.05f, 0.3f, EASE_IN_OUT_CUBIC, true);
|
||||
text_push(name_text, "maksw", 0.0f, true);
|
||||
text_push(dialogue_text,
|
||||
@@ -152,11 +166,12 @@ int main() {
|
||||
text_show_all_current(dialogue_text);
|
||||
}
|
||||
else {
|
||||
// this will bite me
|
||||
switch (lines) {
|
||||
case 0:
|
||||
// line 2
|
||||
compositor_animation_complete_all(compositor);
|
||||
compositor_animate_scale(compositor, maksw_happy_id, 1.0f, 0.2f, EASE_IN_OUT_CUBIC);
|
||||
compositor_animate_scale(compositor, maksw_happy_id, 1.0f, 0.3f, EASE_IN_OUT_CUBIC);
|
||||
compositor_animate_scale(compositor, wikor_id, 1.1f, 0.3f, EASE_IN_OUT_CUBIC);
|
||||
text_clear(dialogue_text);
|
||||
text_clear(name_text);
|
||||
@@ -177,6 +192,7 @@ int main() {
|
||||
case 2:
|
||||
// (line 4), fade out
|
||||
compositor_animation_complete_all(compositor);
|
||||
compositor_animate_scale(compositor, maksw_neutral_id, 1.0f, 0.3f, EASE_IN_OUT_CUBIC, false);
|
||||
|
||||
compositor_set_visible(compositor, black_id, true);
|
||||
compositor_animate_fade(compositor, black_id, 1.0f, 1.0f, EASE_IN_CUBIC, false);
|
||||
@@ -197,7 +213,7 @@ int main() {
|
||||
if (compositor_is_element_animating(compositor, mask_id) == false) {
|
||||
compositor_set_visible(compositor, maksw_happy_id, false);
|
||||
compositor_set_visible(compositor, maksw_neutral_id, true);
|
||||
compositor_animate_scale(compositor, maksw_neutral_id, 1.05f, 0.5f, EASE_IN_OUT_CUBIC);
|
||||
compositor_set_scale(compositor, maksw_neutral_id, 1.05f);
|
||||
text_clear(dialogue_text);
|
||||
text_clear(name_text);
|
||||
text_push(name_text, "maksw", 0.0f, true);
|
||||
@@ -227,6 +243,7 @@ int main() {
|
||||
window_blit(window, fb);
|
||||
}
|
||||
|
||||
// no raii because i'm tired already
|
||||
if (maksw_happy) image_free(maksw_happy);
|
||||
if (maksw_neutral) image_free(maksw_neutral);
|
||||
if (wikor) image_free(wikor);
|
||||
|
||||
Reference in new issue
Block a user