static void _control_camera(Game* G, float delta_time) { if(G->num_points == 1) { Vec2 curr = G->points[0].pos; Vec2 delta = vec2_sub(curr, G->prev_single); /* L-R rotation */ Quaternion q = quat_from_axis_anglef(0, 1, 0, delta_time*delta.x*0.2f); G->camera.orientation = quat_multiply(G->camera.orientation, q); /* U-D rotation */ q = quat_from_axis_anglef(1, 0, 0, delta_time*delta.y*0.2f); G->camera.orientation = quat_multiply(q, G->camera.orientation); G->prev_single = curr; } else if(G->num_points == 2) { float camera_speed = 0.1f; Vec3 look = quat_get_z_axis(G->camera.orientation); Vec3 right = quat_get_x_axis(G->camera.orientation); Vec2 avg = vec2_add(G->points[0].pos, G->points[1].pos); Vec2 delta; avg = vec2_mul_scalar(avg, 0.5f); delta = vec2_sub(avg, G->prev_double); look = vec3_mul_scalar(look, -delta.y*camera_speed); right = vec3_mul_scalar(right, delta.x*camera_speed); G->camera.position = vec3_add(G->camera.position, look); G->camera.position = vec3_add(G->camera.position, right); G->prev_double = avg; } }
bool _get_touch(const Vector2* pos, float radius, Vector2* touch) { assert(pos); Touch* touches = touches_get(); if(touches) { uint count = touches_count(); for(uint i = 0; i < count; ++i) { if(vec2_length_sq(vec2_sub(*pos, touches[i].pos)) > radius*radius) continue; if(touch) *touch = touches[i].pos; return true; } } if(mouse_pressed(MBTN_LEFT)) { uint x, y; mouse_pos(&x, &y); Vector2 mpos = {(float)x, (float)y}; if(vec2_length_sq(vec2_sub(*pos, mpos)) <= radius*radius) { if(touch) *touch = mpos; return true; } } return false; }
static bool powerup_help_render(float t) { if(t != 0.0f) game_update_empty(); UIElement* element = uidesc_get("powerup_help"); UIElement* text_elem2 = uidesc_get_child(element, "text2"); UIElement* text_elem3 = uidesc_get_child(element, "text3"); UIElement* star = uidesc_get_child(element, "star"); UIElement* item = uidesc_get_child(element, "item"); UIElement* button_quit = uidesc_get_child(element, "button_quit"); float state_alpha = 1.0f-fabsf(t); byte a = lrintf(255.0f * state_alpha); Color col = COLOR_RGBA(255, 255, 255, a); spr_draw("blue_shade", hud_layer-1, rectf(0.0f, 0.0f, v_width, v_height), col); static float xpos = 0.0f; static float inc = 600.0f; xpos = hud_scroll(xpos, inc, item_count, t); for(uint i = 0; i < item_count; ++i) { Vector2 off = vec2(xpos + i * inc, 0.0f); float d = normalize(fabsf(off.x), 0.0f, inc * (item_count-1)); float scroll_alpha = 1.0f / exp(PI*d); byte a2 = lrintf(255.0f * scroll_alpha * state_alpha); Color col2 = COLOR_RGBA(255, 255, 255, a2); // Star spr_draw_cntr_h(star->spr, hud_layer, vec2_add(off, star->vec2), time_s()/10.0f, 1.0f, col2); // Item const char* item_img = powerup_params[i].unlocked_spr; spr_draw_cntr(item_img, hud_layer, vec2_add(off, item->vec2), 0.0f, 1.0f, col2); // Description text const char* text3 = powerup_params[i].description; vfont_select(FONT_NAME, 32.0f); Vector2 half_size3 = vec2_scale(vfont_size(text3), 0.5f); vfont_draw(text3, hud_layer+1, vec2_add(off, vec2_sub(text_elem3->vec2,half_size3)), col2); // Item name const char* text2 = powerup_params[i].name; vfont_select(FONT_NAME, 48.0f); Vector2 half_size2 = vec2_scale(vfont_size(text2), 0.5f); vfont_draw(text2, hud_layer+1, vec2_add(off, vec2_sub(text_elem2->vec2,half_size2)), col2); } // Quit button if(hud_button(button_quit, col, t)) { malka_states_pop(); } return true; }
static float get_angle(const float a0[2], const float a1[2], const float b0[2], const float b1[2]) { float u[2], v[2], dot, det; vec2_sub(a1, a0, u); vec2_normalize(u, u); vec2_sub(b1, b0, v); vec2_normalize(v, v); dot = vec2_dot(u, v); det = vec2_cross(u, v); return atan2(det, dot); }
float ai_navmesh_distance(NavMesh* navmesh, Vector2 p1, Vector2 p2) { assert(navmesh); uint navpoint1 = ai_nearest_navpoint(navmesh, p1); uint navpoint2 = ai_nearest_navpoint(navmesh, p2); float distance = vec2_length(vec2_sub(p1, navmesh->navpoints[navpoint1])); distance += vec2_length(vec2_sub(p2, navmesh->navpoints[navpoint2])); uint idx = IDX_2D(navpoint1, navpoint2, navmesh->n_nodes); distance += navmesh->distance[idx]; return distance; }
void render_animated(vec3 color, vec2 offset, vec2 size, animation_state * animation){ UNUSED(size); u64 tex = get_animation_texture(animation->animation); if(tex == 0) return; animation_frame * frame = NULL; u64 * key = NULL; get_refs2_animation_frames(&animation->animation, &frame, &key, 1); if(frame == NULL || key == NULL) return; ASSERT(key[animation->frame] == animation->animation); while(animation->time > frame[animation->frame].time){ animation->time -= frame[animation->frame].time; animation->frame += 1; if(key[animation->frame] != animation->animation) animation->frame = 0; } texture_section sec[20]; u64 idx = 0; iter_texture_sections(tex, sec, 20, &idx); u64 ts = frame[animation->frame].section; i32 gltex = get_animation_gltexture(tex); vec2 render_size = vec2_scale(sec[ts].pixel_size, 3); vec2 center = vec2_add(offset, vec2_scale(size, 0.5)); vec2 offset2 = vec2_sub(center, vec2_scale(render_size, 0.5)); rect_render2(color, vec2_add(offset2, sec[ts].render_offset), render_size , gltex, sec[ts].uv_offset, sec[ts].uv_size); }
static int _llfunc_vec2_sub(lua_State *L) { vec2 *a = (vec2*)userdata_get_or_die(L, 1); vec2 *b = (vec2*)userdata_get_or_die(L, 2); vec2 *r = (vec2*)userdata_get_or_new(L, 3, sizeof(vec2)); vec2_sub(a, b, r); return 1; }
Vector2 touch_joystick(TexHandle tex, uint layer, const RectF* back_src, const RectF* nub_src, const Vector2* pos) { assert(back_src); assert(nub_src); assert(pos); Color nub_color = COLOR_WHITE; float back_width = rectf_width(back_src); float back_height = rectf_height(back_src); // Draw static back RectF dest = { pos->x - back_width / 2.0f, pos->y - back_height / 2.0f, 0.0f, 0.0f }; video_draw_rect(tex, layer, back_src, &dest, COLOR_WHITE); Vector2 touch; if(!_get_touch(pos, joystick_area_radius, &touch)) { touch = vec2(0.0f, 0.0f); } else { touch = vec2_sub(touch, *pos); nub_color = pressed_color; } float nub_width = rectf_width(nub_src); float nub_height = rectf_width(nub_src); assert(feql(back_width, back_height) && feql(nub_width, nub_height)); float back_radius = back_width / 2.0f; float nub_radius = nub_width / 2.0f; float move_radius = back_radius - nub_radius; assert(move_radius > 4.0f); float offset_len = vec2_length(touch); if(offset_len > move_radius) { touch = vec2_scale(touch, move_radius / offset_len); offset_len = move_radius; nub_color = COLOR_WHITE; } // Draw nub dest.left = pos->x + touch.x - nub_width / 2.0f; dest.top = pos->y + touch.y - nub_height / 2.0f; video_draw_rect(tex, layer, nub_src, &dest, nub_color); if(offset_len < joystick_deadzone_radius) return vec2(0.0f, 0.0f); float norm_len = normalize(offset_len, joystick_deadzone_radius, move_radius); return vec2_scale(touch, norm_len / offset_len); }
void OBSBasicPreview::MoveItems(const vec2 &pos) { Qt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers(); OBSBasic *main = reinterpret_cast<OBSBasic*>(App()->GetMainWindow()); OBSScene scene = main->GetCurrentScene(); vec2 offset, moveOffset; vec2_sub(&offset, &pos, &startPos); vec2_sub(&moveOffset, &offset, &lastMoveOffset); if (!(modifiers & Qt::ControlModifier)) SnapItemMovement(moveOffset); vec2_add(&lastMoveOffset, &lastMoveOffset, &moveOffset); obs_scene_enum_items(scene, move_items, &moveOffset); }
static struct AABB aabb_from_transform(vec2 center, vec2 size) { vec2 half_size = vec2_div(size, 2.0f); struct AABB aabb; aabb.min = vec2_sub(center, half_size); aabb.max = vec2_add(center, half_size); return aabb; }
float _node_distance_sq(Vector2 p1, Vector2 p2) { float result = 10000000.0f; float d_sqr; for(uint i = 0; i < 5; ++i) { Vector2 wrap_p = vec2_add(p2, wraparound_offsets[i]); d_sqr = vec2_length_sq(vec2_sub(p1, wrap_p)); result = MIN(result, d_sqr); } return result; }
int prim_rect_oriented(SDL_Renderer* renderer, Rect* rect, f32 rotation) { Vec2 corners[4]; Vec2 x = vec2_init(cosf(rotation), sinf(rotation)); Vec2 y = vec2_init(-sinf(rotation), cosf(rotation)); vec2_scale(&x, rect->width / 2.f, &x); vec2_scale(&y, rect->height / 2.f, &y); Vec2 center; center.x = rect->position.x + rect->width / 2.f; center.y = rect->position.y + rect->height / 2.f; for (u32 i = 0; i < 4; ++i) { vec2_copy_to(¢er, &corners[i]); } vec2_sub(&corners[0], &x, &corners[0]); vec2_sub(&corners[0], &y, &corners[0]); vec2_add(&corners[1], &x, &corners[1]); vec2_sub(&corners[1], &y, &corners[1]); vec2_add(&corners[2], &x, &corners[2]); vec2_add(&corners[2], &y, &corners[2]); vec2_sub(&corners[3], &x, &corners[3]); vec2_add(&corners[3], &y, &corners[3]); int result = 0; for (u32 i = 0; i < 4; ++i) { Vec2 c1 = corners[i]; Vec2 c2 = (i < 3) ? corners[i + 1] : corners[0]; result |= SDL_RenderDrawLine(renderer, (int)c1.x, (int)c1.y, (int)c2.x, (int)c2.y); } return result; }
Segment ai_shortest_path(Vector2 p1, Vector2 p2) { uint id = 5; float min_d = 10000000.0f; float d_sqr; for(uint i = 0; i < 5; ++i) { Vector2 wrap_p = vec2_add(p2, wraparound_offsets[i]); d_sqr = vec2_length_sq(vec2_sub(p1, wrap_p)); if(d_sqr < min_d) { min_d = d_sqr; id = i; } } return segment(p1, vec2_add(p2, wraparound_offsets[id])); }
static void fire_projectile(struct GameState *game_state, struct Ship *source, struct Ship *target, int32 damage) { ASSERT(source != target); source->fire_cooldown_timer = source->fire_cooldown; struct Projectile *projectile = create_projectile(game_state); projectile->owner = source->id; projectile->team = source->team; projectile->damage = damage; projectile->position = source->position; projectile->size = vec2_new(0.1f, 0.1f); vec2 direction = vec2_normalize(vec2_sub(target->position, projectile->position)); projectile->velocity = vec2_mul(direction, 5.0f); }
static bool line_line_intersection(vec2 a0, vec2 a1, vec2 b0, vec2 b1) { #if 1 // Implemented based on http://stackoverflow.com/a/17198094/4354008 vec2 v = vec2_sub(a0, a1); vec2 normal = vec2_new(v.y, -v.x); vec2 v0 = vec2_sub(b0, a0); vec2 v1 = vec2_sub(b1, a0); float proj0 = vec2_dot(v0, normal); float proj1 = vec2_dot(v1, normal); if ((proj0 == 0) || (proj1 == 0)) return true; // TODO: float_sign() if ((proj0 > 0) && (proj1 < 0)) return true; if ((proj0 < 0) && (proj1 > 0)) return true; return false; #else vec2 intersection = vec2_zero(); vec2 b = vec2_sub(a1, a0); vec2 d = vec2_sub(b1, b0); float b_dot_p_perp = (b.x * d.y) - (b.y * d.x); if (b_dot_p_perp == 0) return false; vec2 c = vec2_sub(b0, a0); float t = ((c.x * d.y) - (c.y * d.x)) / b_dot_p_perp; if ((t < 0) || (t > 1)) return false; float u = ((c.x * b.y) - (c.y * b.x)) / b_dot_p_perp; if ((u < 0) || (u > 1)) return false; // TODO: make this an output parameter intersection = vec2_add(a0, vec2_mul(b, t)); return true; #endif }
void Boat_m_touch(Ent *ent1, Ent *ent2) { Boat *boat = (Boat*)ent1; if(Ent_GET(flags, ent2) & EFLAGS_SOLID) { if(ent_class_is_subclass(ent2->eclass, &Enemy_CLASS)) { set_paused(1); set_game_over(1); } else { float dir_angle = vec2_to_angle(Ent_GET(move_direction, boat)), coll_angle; vec2 boat_pos = *Ent_GET(position, boat), coll_pos = *Ent_GET(position, ent2); vec2 coll_vec = coll_pos; vec2_sub(&coll_vec, &boat_pos); vec2_norm(&coll_vec); coll_angle = vec2_to_angle(&coll_vec); if(fabs(dir_angle - coll_angle) <= 45) Ent_SET(speed, boat, 0); } } }
static bool _vis_query(NavMesh* navmesh, Vector2 p1, Vector2 p2) { int step_x, step_y, bmap_x, bmap_y, bmap_end_x, bmap_end_y; bmap_x = MIN(floorf(p1.x / vis_cell_width), vis_bitmap_width-1); bmap_y = MIN(floorf(p1.y / vis_cell_height), vis_bitmap_height-1); bmap_end_x = MIN(floorf(p2.x / vis_cell_width), vis_bitmap_width-1); bmap_end_y = MIN(floorf(p2.y / vis_cell_height), vis_bitmap_height-1); Vector2 dir = vec2_sub(p2, p1); dir = vec2_normalize(dir); Vector2 side_dist, delta_dist = vec2( sqrtf(1.0f + (dir.y * dir.y) / (dir.x * dir.x)) * vis_cell_width, sqrtf(1.0f + (dir.x * dir.x) / (dir.y * dir.y)) * vis_cell_height ); if(dir.x > 0.0f) { step_x = 1; side_dist.x = (float)(bmap_x+1) * vis_cell_width - p1.x; } else { step_x = -1; side_dist.x = p1.x - (float)bmap_x * vis_cell_width; } side_dist.y *= delta_dist.x / vis_cell_width; if(dir.y > 0.0f) { step_y = 1; side_dist.y = (float)(bmap_y+1) * vis_cell_height - p1.y; } else { step_y = -1; side_dist.y = p1.y - (float)bmap_y * vis_cell_height; } side_dist.y *= delta_dist.y / vis_cell_height; // DDA while(!_query_vis_bitmap(navmesh, bmap_x, bmap_y)) { bool x_cond = (step_x > 0 && bmap_x >= bmap_end_x) || (step_x < 0 && bmap_x <= bmap_end_x); bool y_cond = (step_y > 0 && bmap_y >= bmap_end_y) || (step_y < 0 && bmap_y <= bmap_end_y); if(x_cond && y_cond) return true; if(side_dist.x < side_dist.y) { side_dist.x += delta_dist.x; bmap_x += step_x; } else { side_dist.y += delta_dist.y; bmap_y += step_y; } } return false; }
static void tick_physics(struct GameState *game_state, float dt) { // Projectile kinematics. for (uint32 i = 0; i < game_state->projectile_count; ++i) { struct Projectile *projectile = &game_state->projectiles[i]; // r = r0 + (v*t) + (a*t^2)/2 projectile->position = vec2_add(projectile->position, vec2_mul(projectile->velocity, dt)); } // Ship kinematics. for (uint32 i = 0; i < game_state->ship_count; ++i) { struct Ship *ship = &game_state->ships[i]; vec2 move_acceleration = vec2_zero(); // v = v0 + (a*t) ship->move_velocity = vec2_add(ship->move_velocity, vec2_mul(move_acceleration, dt)); // r = r0 + (v*t) + (a*t^2)/2 ship->position = vec2_add(vec2_add(ship->position, vec2_mul(ship->move_velocity, dt)), vec2_div(vec2_mul(move_acceleration, dt * dt), 2.0f)); } // // TODO: spatial hashing // // Projectile collision. for (uint32 i = 0; i < game_state->projectile_count; ++i) { struct Projectile *projectile = &game_state->projectiles[i]; struct AABB projectile_aabb = aabb_from_transform(projectile->position, projectile->size); // Projectile-building collision. for (uint32 j = 0; j < game_state->building_count; ++j) { struct Building *building = &game_state->buildings[j]; struct AABB building_aabb = aabb_from_transform(building->position, building->size); if (aabb_aabb_intersection(projectile_aabb, building_aabb)) { // TODO: damage building if not friendly destroy_projectile(game_state, projectile); break; } } // NOTE: owner may be dead and destroyed at this point, so checking for NULL might be required. struct Ship *owner = get_ship_by_id(game_state, projectile->owner); // Projectile-ship collision. for (uint32 j = 0; j < game_state->ship_count; ++j) { struct Ship *ship = &game_state->ships[j]; if (ship->id == projectile->owner) continue; #if 0 // Allow projectiles to pass through teammates. if (ship->team == projectile->team) continue; #endif struct AABB ship_aabb = aabb_from_transform(ship->position, ship->size); if (aabb_aabb_intersection(projectile_aabb, ship_aabb)) { // Disable friendly fire. if (ship->team != projectile->team) damage_ship(game_state, ship, projectile->damage); destroy_projectile(game_state, projectile); break; } } } // TODO: optimize // Ship collision. if (game_state->ship_count >= 2) { for (uint32 i = 0; i < game_state->ship_count - 1; ++i) { struct Ship *a = &game_state->ships[i]; struct AABB a_aabb = aabb_from_transform(a->position, a->size); vec2 a_center = vec2_div(vec2_add(a_aabb.min, a_aabb.max), 2.0f); vec2 a_half_extents = vec2_div(vec2_sub(a_aabb.max, a_aabb.min), 2.0f); // Ship-building collision. for (uint32 j = 0; j < game_state->building_count; ++j) { struct Building *building = &game_state->buildings[j]; struct AABB b_aabb = aabb_from_transform(building->position, building->size); if (aabb_aabb_intersection(a_aabb, b_aabb)) { vec2 b_center = vec2_div(vec2_add(b_aabb.min, b_aabb.max), 2.0f); vec2 b_half_extents = vec2_div(vec2_sub(b_aabb.max, b_aabb.min), 2.0f); vec2 intersection = vec2_sub(vec2_abs(vec2_sub(b_center, a_center)), vec2_add(a_half_extents, b_half_extents)); if (intersection.x > intersection.y) { a->move_velocity.x = 0.0f; if (a->position.x < building->position.x) a->position.x += intersection.x/2.0f; else a->position.x -= intersection.x/2.0f; } else { a->move_velocity.y = 0.0f; if (a->position.y < building->position.y) a->position.y += intersection.y/2.0f; else a->position.y -= intersection.y/2.0f; } } } // Ship-ship collision. for (uint32 j = i + 1; j < game_state->ship_count; ++j) { struct Ship *b = &game_state->ships[j]; struct AABB b_aabb = aabb_from_transform(b->position, b->size); if (aabb_aabb_intersection(a_aabb, b_aabb)) { vec2 b_center = vec2_div(vec2_add(b_aabb.min, b_aabb.max), 2.0f); vec2 b_half_extents = vec2_div(vec2_sub(b_aabb.max, b_aabb.min), 2.0f); vec2 intersection = vec2_sub(vec2_abs(vec2_sub(b_center, a_center)), vec2_add(a_half_extents, b_half_extents)); if (intersection.x > intersection.y) { if (abs_float(a->move_velocity.x) > abs_float(b->move_velocity.x)) a->move_velocity.x = 0.0f; else b->move_velocity.x = 0.0f; if (a->position.x < b->position.x) { a->position.x += intersection.x/2.0f; b->position.x -= intersection.x/2.0f; } else { a->position.x -= intersection.x/2.0f; b->position.x += intersection.x/2.0f; } } else { if (abs_float(a->move_velocity.y) > abs_float(b->move_velocity.y)) a->move_velocity.y = 0.0f; else b->move_velocity.y = 0.0f; if (a->position.y < b->position.y) { a->position.y += intersection.y/2.0f; b->position.y -= intersection.y/2.0f; } else { a->position.y -= intersection.y/2.0f; b->position.y += intersection.y/2.0f; } } } } } } }
void tick_game(struct GameMemory *memory, struct Input *input, uint32 screen_width, uint32 screen_height, float dt) { struct GameState *game_state = (struct GameState *)memory->game_memory; struct RenderBuffer *render_buffer = (struct RenderBuffer *)memory->render_memory; clear_render_buffer(render_buffer); for (uint32 i = 0; i < game_state->visibility_graph.vertex_count; ++i) { vec2 vertex = game_state->visibility_graph.vertices[i]; draw_world_quad_buffered(render_buffer, vertex, vec2_scalar(0.1f), vec4_zero(), vec3_new(0, 1, 1)); } for (uint32 i = 0; i < game_state->visibility_graph.edge_count; ++i) { uint32 *edge = &game_state->visibility_graph.edges[i][0]; vec2 p0 = game_state->visibility_graph.vertices[edge[0]]; vec2 p1 = game_state->visibility_graph.vertices[edge[1]]; draw_world_line_buffered(render_buffer, p0, p1, vec3_new(1, 1, 0)); } if (mouse_down(MOUSE_LEFT, input)) { struct AABB box = calc_mouse_selection_box(input, MOUSE_LEFT); vec2 size = vec2_sub(box.max, box.min); vec2 bottom_left = vec2_new(box.min.x, box.max.y); vec2 bottom_right = vec2_new(box.max.x, box.max.y); vec2 top_left = vec2_new(box.min.x, box.min.y); vec2 top_right = vec2_new(box.max.x, box.min.y); const uint32 outline_thickness = 1; draw_screen_quad_buffered(render_buffer, top_left, vec2_new(size.x, outline_thickness), vec4_zero(), vec3_new(1, 1, 0)); draw_screen_quad_buffered(render_buffer, bottom_left, vec2_new(size.x, outline_thickness), vec4_zero(), vec3_new(1, 1, 0)); draw_screen_quad_buffered(render_buffer, top_left, vec2_new(outline_thickness, size.y), vec4_zero(), vec3_new(1, 1, 0)); draw_screen_quad_buffered(render_buffer, top_right, vec2_new(outline_thickness, size.y), vec4_zero(), vec3_new(1, 1, 0)); } if (mouse_released(MOUSE_LEFT, input)) { // Clear previous selection. game_state->selected_ship_count = 0; struct AABB screen_selection_box = calc_mouse_selection_box(input, MOUSE_LEFT); // Because screen space y-values are inverted, these two results have conflicting // y-values, i.e. screen_to_world_min.y is actually the maximum y. These values // are then properly min/maxed and stored correctly in world_selection_box. vec2 screen_to_world_min = screen_to_world_coords(screen_selection_box.min, &game_state->camera, screen_width, screen_height); vec2 screen_to_world_max = screen_to_world_coords(screen_selection_box.max, &game_state->camera, screen_width, screen_height); struct AABB world_selection_box; world_selection_box.min = min_vec2(screen_to_world_min, screen_to_world_max); world_selection_box.max = max_vec2(screen_to_world_min, screen_to_world_max); // Add colliding ships to the selection list. // TODO: optimize, spatial grid hash? for (uint32 i = 0; i < game_state->ship_count; ++i) { struct Ship *ship = &game_state->ships[i]; struct AABB ship_aabb = aabb_from_transform(ship->position, ship->size); vec2 center = vec2_div(vec2_add(ship_aabb.min, ship_aabb.max), 2.0f); if (aabb_aabb_intersection(world_selection_box, ship_aabb)) game_state->selected_ships[game_state->selected_ship_count++] = ship->id; } if (game_state->selected_ship_count > 0) fprintf(stderr, "selected %u ships\n", game_state->selected_ship_count); } if (mouse_down(MOUSE_RIGHT, input)) { vec2 target_position = screen_to_world_coords(input->mouse_position, &game_state->camera, screen_width, screen_height); for (uint32 i = 0; i < game_state->selected_ship_count; ++i) { uint32 id = game_state->selected_ships[i]; struct Ship *ship = get_ship_by_id(game_state, id); ship->target_position = target_position; ship->move_order = true; } } // Outline selected ships. for (uint32 i = 0; i < game_state->selected_ship_count; ++i) { uint32 id = game_state->selected_ships[i]; struct Ship *ship = get_ship_by_id(game_state, id); draw_world_quad_buffered(render_buffer, ship->position, vec2_mul(ship->size, 1.1f), vec4_zero(), vec3_new(0, 1, 0)); if (ship->move_order) draw_world_quad_buffered(render_buffer, ship->target_position, vec2_new(0.5f, 0.5f), vec4_zero(), vec3_new(0, 1, 0)); } // Handle move orders. for (uint32 i = 0; i < game_state->ship_count; ++i) { struct Ship *ship = &game_state->ships[i]; if (ship->move_order) { vec2 direction = vec2_zero(); /* if (vec2_length2(direction) < FLOAT_EPSILON) direction = vec2_normalize(vec2_sub(ship->target_position, ship->position)); */ ship->move_velocity = vec2_mul(direction, 2.0f); /* vec2 direction = vec2_normalize(vec2_sub(ship->target_position, ship->position)); ship->move_velocity = vec2_mul(direction, 2.0f); */ if (vec2_distance2(ship->position, ship->target_position) < 0.1f) { ship->move_velocity = vec2_zero(); ship->move_order = false; } } } tick_camera(input, &game_state->camera, dt); tick_combat(game_state, dt); tick_physics(game_state, dt); }
int main(int argc, char** argv) { Settings settings; ConstructSettings(&settings); LoadSettingsFile(&settings, "settings.ini"); if(Init(&settings) < 0) return -1; GLuint program; if(SetupProgram(&program) < 0) return -2; GLuint grid_vbuf = CreateGridVertexBuffer(settings.graphics.viewdistance); int grid_icount; GLuint grid_ibuf = CreateGridIndexBuffer(&grid_icount, settings.graphics.viewdistance); glUseProgram(program); GLint grid_pos_loc = glGetAttribLocation(program, "grid_pos"); GLint grid_world_mat_loc = glGetUniformLocation(program, "world_mat"); GLint grid_view_mat_loc = glGetUniformLocation(program, "view_mat"); GLint grid_proj_mat_loc = glGetUniformLocation(program, "proj_mat"); GLint grid_color_loc = glGetUniformLocation(program, "color"); GLint grid_viewdistance_loc = glGetUniformLocation(program, "viewdistance"); glUniform3f(grid_color_loc, 0.f, 0.6f, 0.f); glUniform1f(grid_viewdistance_loc, settings.graphics.viewdistance); GLuint grid_vao; glGenVertexArrays(1, &grid_vao); glBindVertexArray(grid_vao); glBindBuffer(GL_ARRAY_BUFFER, grid_vbuf); glEnableVertexAttribArray(grid_pos_loc); glVertexAttribPointer(grid_pos_loc, 2, GL_FLOAT, GL_FALSE, 0, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, grid_ibuf); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glClearColor(0.5f, 0.5f, 0.5f, 1.f); glViewport(0, 0, settings.video.width, settings.video.height); Node grid_node; ConstructNode(&grid_node); Camera camera; ConstructCamera(&camera); mat4x4 projection_matrix; mat4x4_perspective(projection_matrix, settings.video.pfov, settings.video.width/(float)settings.video.height, settings.video.pnear, settings.video.pfar); glUniformMatrix4fv(grid_proj_mat_loc, 1, GL_FALSE, (const float*)projection_matrix); float speed = 10.f; Uint32 ticks = SDL_GetTicks(); State state = STATE_RUNNING | (settings.video.fullscreen ? STATE_FULLSCREEN : 0); while(state & STATE_RUNNING) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glUseProgram(program); glUniformMatrix4fv(grid_world_mat_loc, 1, GL_FALSE, (const float*)grid_node.world_matrix); glUniformMatrix4fv(grid_view_mat_loc, 1, GL_FALSE, (const float*)camera.view_matrix); glBindVertexArray(grid_vao); glDrawElements(GL_TRIANGLE_STRIP, grid_icount, GL_UNSIGNED_INT, NULL); SDL_GL_SwapWindow(window); SDL_Event event; while(SDL_PollEvent(&event)) { switch(event.type) { case SDL_QUIT: state &= ~STATE_RUNNING; break; case SDL_KEYUP: switch(event.key.keysym.scancode) { case SDL_SCANCODE_F11: state ^= STATE_FULLSCREEN; SDL_SetWindowFullscreen ( window, state & STATE_FULLSCREEN ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0 ); break; case SDL_SCANCODE_ESCAPE: state ^= STATE_MOUSE_GRABBED; SDL_SetRelativeMouseMode(state & STATE_MOUSE_GRABBED); break; case SDL_SCANCODE_LSHIFT: speed = settings.controls.speed1; break; default: break; } break; case SDL_KEYDOWN: switch(event.key.keysym.scancode) { case SDL_SCANCODE_LSHIFT: speed = settings.controls.speed2; break; default: break; } break; case SDL_WINDOWEVENT: if(event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { int w = event.window.data1; int h = event.window.data2; glViewport(0, 0, w, h); mat4x4_perspective(projection_matrix, settings.video.pfov, w/(float)h, settings.video.pnear, settings.video.pfar); glUniformMatrix4fv(grid_proj_mat_loc, 1, GL_FALSE, (const float*)projection_matrix); } break; case SDL_MOUSEMOTION: if(state & STATE_MOUSE_GRABBED) { camera.yaw -= settings.controls.xsensitivity * event.motion.xrel; camera.pitch -= settings.controls.xsensitivity * event.motion.yrel; } break; default: break; } } Uint32 nticks = SDL_GetTicks(); float delta = (nticks - ticks)/1000.f; ticks = nticks; const Uint8* keys = SDL_GetKeyboardState(NULL); vec3 movement; int i; for(i = 0; i < 3; i++) movement[i] = 0.f; SDL_bool moved = SDL_FALSE; if(keys[SDL_SCANCODE_W] && !keys[SDL_SCANCODE_S]) { vec3_add(movement, movement, camera.direction); moved = SDL_TRUE; } else if(keys[SDL_SCANCODE_S] && !keys[SDL_SCANCODE_W]) { vec3_sub(movement, movement, camera.direction); moved = SDL_TRUE; } if(keys[SDL_SCANCODE_A] && !keys[SDL_SCANCODE_D]) { vec3_sub(movement, movement, camera.right); moved = SDL_TRUE; } else if(keys[SDL_SCANCODE_D] && !keys[SDL_SCANCODE_A]) { vec3_add(movement, movement, camera.right); moved = SDL_TRUE; } if(keys[SDL_SCANCODE_Q] && !keys[SDL_SCANCODE_E]) { vec3_sub(movement, movement, camera.up); moved = SDL_TRUE; } else if(keys[SDL_SCANCODE_E] && !keys[SDL_SCANCODE_Q]) { vec3_add(movement, movement, camera.up); moved = SDL_TRUE; } if(moved) { vec3_norm(movement, movement); vec3_scale(movement, movement, delta * speed); vec3_add(camera.node.translation, camera.node.translation, movement); } UpdateCamera(&camera); if(moved) { vec2 tmp1; tmp1[0] = camera.node.position[0]; tmp1[1] = camera.node.position[2]; vec2 tmp2; tmp2[0] = grid_node.position[0]; tmp2[1] = grid_node.position[2]; vec2_sub(tmp1, tmp1, tmp2); tmp1[0] = floor(tmp1[0]); tmp1[1] = floor(tmp1[1]); grid_node.translation[0] += tmp1[0]; grid_node.translation[2] += tmp1[1]; UpdateNode(&grid_node); } } glDeleteVertexArrays(1, &grid_vao); glDeleteBuffers(1, &grid_vbuf); glDeleteBuffers(1, &grid_ibuf); glDeleteProgram(program); return 0; }
NavMesh ai_precalc_navmesh(DArray geometry, DArray platforms, float width, float height) { NavMesh res; ai_precalc_bounds(width, height); DArray navpoints = _gen_navpoints(geometry, platforms); DArray edges = _gen_edges(navpoints, geometry); Vector2* navpoints_v = DARRAY_DATA_PTR(navpoints, Vector2); Edge* edges_e = DARRAY_DATA_PTR(edges, Edge); uint n = navpoints.size; res.n_nodes = n; res.navpoints = MEM_ALLOC(sizeof(Vector2) * n); res.neighbour_count = MEM_ALLOC(sizeof(uint) * n); res.neighbours_start = MEM_ALLOC(sizeof(uint) * n); float* adjacency = MEM_ALLOC(sizeof(float) * n*n); res.distance = MEM_ALLOC(sizeof(float) * n*n); res.radius = MEM_ALLOC(sizeof(float) * n); res.nn_grid_count = MEM_ALLOC(sizeof(uint) * nn_grid_cells); res.nn_grid_start = MEM_ALLOC(sizeof(uint) * nn_grid_cells); memset(res.neighbour_count, 0, sizeof(uint) * n); memset(adjacency, 0, sizeof(float) * n*n); memset(res.distance, 0, sizeof(float) * n*n); memset(res.nn_grid_count, 0, sizeof(uint) * nn_grid_cells); memset(res.nn_grid_start, 0, sizeof(uint) * nn_grid_cells); for(uint i = 0; i < navpoints.size; ++i) { res.navpoints[i] = navpoints_v[i]; res.radius[i] = ai_wall_distance(navpoints_v[i], geometry); } for(uint i = 0; i < edges.size; ++i) { float dist = vec2_length(vec2_sub( navpoints_v[edges_e[i].v1], navpoints_v[edges_e[i].v2])); adjacency[IDX_2D(edges_e[i].v1, edges_e[i].v2, n)] = dist; adjacency[IDX_2D(edges_e[i].v2, edges_e[i].v1, n)] = dist; res.neighbour_count[edges_e[i].v1]++; res.neighbour_count[edges_e[i].v2]++; } // Pracalc node neighbour lists uint total_neighbours = res.neighbour_count[0]; res.neighbours_start[0] = 0; for(uint i = 1; i < n; ++i) { res.neighbours_start[i] = res.neighbours_start[i-1] + res.neighbour_count[i-1]; total_neighbours += res.neighbour_count[i]; } res.neighbours = MEM_ALLOC(sizeof(uint) * total_neighbours); for(uint i = 0; i < n; ++i) { uint idx = res.neighbours_start[i]; for(uint j = 0; j < n; ++j) { if(adjacency[IDX_2D(i, j, n)] > 0.0f) res.neighbours[idx++] = j; } assert(idx == res.neighbours_start[i] + res.neighbour_count[i]); } memcpy(res.distance, adjacency, sizeof(float) * n*n); // Change zero distances to infinities for(uint i = 0; i < n*n; ++i) if(res.distance[i] == 0.0f) res.distance[i] = 10000000.0f; // Floyd-Warshall for(uint i = 0; i < n; ++i) for(uint j = 0; j < n; ++j) for(uint k = 0; k < n; ++k) res.distance[IDX_2D(j, k, n)] = MIN(res.distance[IDX_2D(j, k, n)], res.distance[IDX_2D(j, i, n)]+res.distance[IDX_2D(i, k, n)]); // Precalc nearest-navpoint grid DArray grid_cell; DArray nn_grid; res.nn_grid_start[0] = 0; nn_grid = darray_create(sizeof(uint), 0); for(uint i = 0; i < nn_grid_cells; ++i) { grid_cell = darray_create(sizeof(uint), 0); for(uint j = 0; j < nn_samples; ++j) { Vector2 p = _rand_point_in_nn_grid_cell(i); uint nearest_navpoint = _nearest_navpoint(p, geometry, navpoints); if(nearest_navpoint >= n) // Point was inside wall, skip continue; bool unique = true; uint* existing_navpoints = DARRAY_DATA_PTR(grid_cell, uint); for(uint k = 0; k < grid_cell.size; ++k) if(existing_navpoints[k] == nearest_navpoint) unique = false; if(unique) darray_append(&grid_cell, &nearest_navpoint); } res.nn_grid_count[i] = grid_cell.size; if(i > 0) res.nn_grid_start[i] = res.nn_grid_start[i-1] + res.nn_grid_count[i-1]; uint* cell_navpoints = DARRAY_DATA_PTR(grid_cell, uint); darray_append_multi(&nn_grid, cell_navpoints, grid_cell.size); darray_free(&grid_cell); } // Hack, works properly as long as darray_free only does // MEM_FREE(darray.data); res.nn_grid = (uint*)nn_grid.data; darray_free(&navpoints); darray_free(&edges); MEM_FREE(adjacency); _precalc_vis(geometry, &res); return res; }
vec2 ui_rectangle_size(ui_rectangle* r) { return vec2_sub(r->bottom_right, r->top_left); }
void crop_rotate::crop_outline(PapayaMemory* mem) { // TODO: Function lacks grace Vec2 mouse = mem->mouse.pos; Vec2 p[4]; p[0] = mem->doc.canvas_pos + mem->crop_rotate.top_left * mem->doc.canvas_zoom; p[2] = mem->doc.canvas_pos + mem->crop_rotate.bot_right * mem->doc.canvas_zoom; p[1] = Vec2(p[0].x, p[2].y); p[3] = Vec2(p[2].x, p[0].y); // Only change vertex selection if the left mouse button is not down if (!mem->mouse.is_down[0]) { mem->crop_rotate.crop_mode = 0; // Vertex selection { float min_dist = FLT_MAX; int32 min_index; for (int32 i = 0; i < 4; i++) { float dist = math::distance(p[i], mouse); if (min_dist > dist) { min_dist = dist; min_index = i; } } if (min_dist < 10.f) { mem->crop_rotate.crop_mode = 1 << min_index; goto dragging; } } // Edge selecton { float min_dist = FLT_MAX; int32 min_index; for (int32 i = 0; i < 4; i++) { int32 j = (i + 1) % 4; vec2 v1 = { p[i].x , p[i].y }; vec2 v2 = { p[j].x , p[j].y }; vec2 m = { mouse.x , mouse.y }; vec2 a, b; vec2_sub(a, v2, v1); vec2_sub(b, m , v1); float dot = vec2_mul_inner(a, b); // Continue if projection of mouse doesn't lie on the edge v1-v2 if (dot < 0 || dot > vec2_mul_inner(a, a)) { continue; } float dist = (i % 2 == 0) ? math::abs(p[i].x - mouse.x) // Vertical edge : math::abs(p[i].y - mouse.y); if (min_dist > dist) { min_dist = dist; min_index = i; } } if (min_dist < 10.f) { mem->crop_rotate.crop_mode = (1 << min_index) | (1 << (min_index + 1) % 4); goto dragging; } } // Entire rect selection { Vec2 v = (mouse - mem->doc.canvas_pos) / mem->doc.canvas_zoom; if (v.x >= mem->crop_rotate.top_left.x && v.x <= mem->crop_rotate.bot_right.x && v.y >= mem->crop_rotate.top_left.y && v.y <= mem->crop_rotate.bot_right.y) { mem->crop_rotate.crop_mode = 15; mem->crop_rotate.rect_drag_position = v - mem->crop_rotate.top_left; } } } dragging: if (mem->crop_rotate.crop_mode && mem->mouse.is_down[0]) { Vec2 v = (mouse - mem->doc.canvas_pos) / mem->doc.canvas_zoom; // TODO: Implement smart-bounds toggle for partial image rotational cropping // while maintaining aspect ratio // Whole rect if (mem->crop_rotate.crop_mode == 15) { Vec2 v = ((mouse - mem->doc.canvas_pos) / mem->doc.canvas_zoom) - mem->crop_rotate.top_left; Vec2 delta = v - mem->crop_rotate.rect_drag_position; delta.x = math::clamp((float)round(delta.x), -1.f * mem->crop_rotate.top_left.x, mem->doc.width - mem->crop_rotate.bot_right.x); delta.y = math::clamp((float)round(delta.y), -1.f * mem->crop_rotate.top_left.y, mem->doc.height - mem->crop_rotate.bot_right.y); mem->crop_rotate.top_left += delta; mem->crop_rotate.bot_right += delta; goto drawing; } // Edges if (mem->crop_rotate.crop_mode == 3) { mem->crop_rotate.top_left.x = v.x; } else if (mem->crop_rotate.crop_mode == 9) { mem->crop_rotate.top_left.y = v.y; } else if (mem->crop_rotate.crop_mode == 12) { mem->crop_rotate.bot_right.x = v.x; } else if (mem->crop_rotate.crop_mode == 6) { mem->crop_rotate.bot_right.y = v.y; } else { // Vertices if (mem->crop_rotate.crop_mode & 3) { mem->crop_rotate.top_left.x = v.x; } if (mem->crop_rotate.crop_mode & 9) { mem->crop_rotate.top_left.y = v.y; } if (mem->crop_rotate.crop_mode & 12) { mem->crop_rotate.bot_right.x = v.x; } if (mem->crop_rotate.crop_mode & 6) { mem->crop_rotate.bot_right.y = v.y; } } if (mem->crop_rotate.crop_mode & 3) { mem->crop_rotate.top_left.x = math::clamp((float)round(mem->crop_rotate.top_left.x), 0.f, mem->crop_rotate.bot_right.x - 1); } if (mem->crop_rotate.crop_mode & 9) { mem->crop_rotate.top_left.y = math::clamp((float)round(mem->crop_rotate.top_left.y), 0.f, mem->crop_rotate.bot_right.y - 1); } if (mem->crop_rotate.crop_mode & 12) { mem->crop_rotate.bot_right.x = math::clamp((float)round(mem->crop_rotate.bot_right.x), mem->crop_rotate.top_left.x + 1, (float)mem->doc.width); } if (mem->crop_rotate.crop_mode & 6) { mem->crop_rotate.bot_right.y = math::clamp((float)round(mem->crop_rotate.bot_right.y), mem->crop_rotate.top_left.y + 1, (float)mem->doc.height); } } drawing: ImDrawVert verts[4]; { glBindTexture(GL_TEXTURE_2D, 0); verts[0].pos = p[0]; verts[1].pos = p[1]; verts[2].pos = p[2]; verts[3].pos = p[3]; verts[0].uv = Vec2(0.0f, 0.0f); verts[1].uv = Vec2(0.0f, 1.0f); verts[2].uv = Vec2(1.0f, 1.0f); verts[3].uv = Vec2(1.0f, 0.0f); uint32 col1 = 0xffcc7a00; uint32 col2 = 0xff1189e6; uint32 col3 = 0xff36bb0a; uint8 Mode = mem->crop_rotate.crop_mode; if (Mode == 15) { verts[0].col = verts[1].col = verts[2].col = verts[3].col = col3; } else { verts[0].col = (Mode & 1) ? col2 : col1; verts[1].col = (Mode & 2) ? col2 : col1; verts[2].col = (Mode & 4) ? col2 : col1; verts[3].col = (Mode & 8) ? col2 : col1; } } GLCHK( glBindBuffer(GL_ARRAY_BUFFER, mem->meshes[PapayaMesh_CropOutline].vbo_handle) ); GLCHK( glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(verts), verts) ); gl::draw_mesh(mem->meshes[PapayaMesh_CropOutline], mem->shaders[PapayaShader_VertexColor], true, 1, UniformType_Matrix4, &mem->window.proj_mtx[0][0]); }
void ui_rectangle_move(ui_rectangle* r, vec2 pos) { vec2 size = vec2_sub(r->bottom_right, r->top_left); r->top_left = pos; r->bottom_right = vec2_add(pos, size); }