static image* perlin_noise_generate(int x_size, int y_size, int octaves) { unsigned char* data = malloc(sizeof(char) * x_size * y_size); int i, x, y; /* Clear data to average */ for(x = 0; x < x_size; x++) for(y = 0; y < y_size; y++) { data[x + (y * x_size)] = 128; } srand(time(NULL)); debug("Generating perlin noise."); for(i = 0; i < octaves; i++ ) { float wavelength = pow( 2, i); float amplitude = pow( 0.5, octaves-i ); vec2 seed = vec2_new(rand(),rand()); debug("Octave: %i Wavelength: %f Amplitude: %f", i, wavelength, amplitude); for(x = 0; x < x_size; x++) for(y = 0; y < y_size; y++) { /* Four positions are required for tiling behaviour */ vec2 pos, pos_x, pos_y, pos_xy; pos = vec2_div( vec2_new(x, y) , wavelength ); pos_x = vec2_div( vec2_new(x - x_size, y) , wavelength ); pos_y = vec2_div( vec2_new(x, y - y_size) , wavelength ); pos_xy = vec2_div( vec2_new(x - x_size, y - y_size) , wavelength ); pos = vec2_add( pos, seed ); pos_x = vec2_add( pos_x, seed ); pos_y = vec2_add( pos_y, seed ); pos_xy = vec2_add( pos_xy, seed ); float val = perlin_noise2D(pos) * amplitude; float val_x = perlin_noise2D(pos_x) * amplitude; float val_y = perlin_noise2D(pos_y) * amplitude; float val_xy = perlin_noise2D(pos_xy) * amplitude; val = bilinear_interp(val_x, val, val_xy, val_y, (float)x/x_size, (float)y/y_size); data[x + (y * x_size)] += val * 128; } } unsigned char* image_data = malloc(sizeof(char) * 4 * x_size * y_size); for(x = 0; x < x_size; x++) for(y = 0; y < y_size; y++) { int amount = data[x + y * x_size]; image_data[x * 4 + y * x_size * 4 + 0] = amount; image_data[x * 4 + y * x_size * 4 + 1] = amount; image_data[x * 4 + y * x_size * 4 + 2] = amount; image_data[x * 4 + y * x_size * 4 + 3] = 255; } free(data); return image_new(x_size, y_size, image_data); }
void OBSBasicPreview::StretchItem(const vec2 &pos) { Qt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers(); obs_bounds_type boundsType = obs_sceneitem_get_bounds_type(stretchItem); uint32_t stretchFlags = (uint32_t)stretchHandle; bool shiftDown = (modifiers & Qt::ShiftModifier); vec3 tl, br, pos3; vec3_zero(&tl); vec3_set(&br, stretchItemSize.x, stretchItemSize.y, 0.0f); vec3_set(&pos3, pos.x, pos.y, 0.0f); vec3_transform(&pos3, &pos3, &screenToItem); if (stretchFlags & ITEM_LEFT) tl.x = pos3.x; else if (stretchFlags & ITEM_RIGHT) br.x = pos3.x; if (stretchFlags & ITEM_TOP) tl.y = pos3.y; else if (stretchFlags & ITEM_BOTTOM) br.y = pos3.y; if (!(modifiers & Qt::ControlModifier)) SnapStretchingToScreen(tl, br); obs_source_t source = obs_sceneitem_getsource(stretchItem); vec2 baseSize; vec2_set(&baseSize, float(obs_source_getwidth(source)), float(obs_source_getheight(source))); vec2 size; vec2_set(&size,br. x - tl.x, br.y - tl.y); if (boundsType != OBS_BOUNDS_NONE) { if (shiftDown) ClampAspect(tl, br, size, baseSize); if (tl.x > br.x) std::swap(tl.x, br.x); if (tl.y > br.y) std::swap(tl.y, br.y); vec2_abs(&size, &size); obs_sceneitem_set_bounds(stretchItem, &size); } else { if (!shiftDown) ClampAspect(tl, br, size, baseSize); vec2_div(&size, &size, &baseSize); obs_sceneitem_setscale(stretchItem, &size); } pos3 = CalculateStretchPos(tl, br); vec3_transform(&pos3, &pos3, &itemToScreen); vec2 newPos; vec2_set(&newPos, std::round(pos3.x), std::round(pos3.y)); obs_sceneitem_setpos(stretchItem, &newPos); }
u64 load_pixel_frame(u64 texture, int x, int y, int x2, int y2){ texture_info texinfo; if(!try_get_texture_info(texture, &texinfo)){ char namebuf[100]; get_textures(texture, namebuf, sizeof(namebuf)); stbi_info(namebuf, &texinfo.width, &texinfo.height, &texinfo.comp); set_texture_info(texture, texinfo); } vec2 size = vec2_new(texinfo.width, texinfo.height); texture_section sec = {.uv_offset = vec2_div(vec2_new(x, y), size), .uv_size = vec2_div(vec2_new(x2 - x, y2 - y), size), .render_offset = vec2_new(0,0), .pixel_size = vec2_new(x2 - x, y2 - y)}; //sec.uv_offset.y = 1.0 - sec.uv_offset.y; add_texture_sections(texture, sec); return 0; } u64 load_pixel_frame_center_of_mass(u64 texture, int x, int y, int x2, int y2, int cx, int cy){ texture_info texinfo; if(!try_get_texture_info(texture, &texinfo)){ char namebuf[100]; get_textures(texture, namebuf, sizeof(namebuf)); stbi_info(namebuf, &texinfo.width, &texinfo.height, &texinfo.comp); set_texture_info(texture, texinfo); } vec2 size = vec2_new(texinfo.width, texinfo.height); int center_x = (x2 + x) / 2, center_y = (y2 + y) / 2; texture_section sec = {.uv_offset = vec2_div(vec2_new(x, y), size), .uv_size = vec2_div(vec2_new(x2 - x, y2 - y), size), .render_offset = vec2_new(center_x - cx, center_y - cy), .pixel_size = vec2_new(x2 - x, y2 - y)}; add_texture_sections(texture, sec); return 0; } CREATE_TABLE2(animation, u64, animation_state); struct{ u32 count; i32 * texture; u64 * texid; }loaded_textures; i32 get_animation_gltexture(u64 tex){ u64 * id = memmem(loaded_textures.texid, loaded_textures.count * sizeof(u64), &tex, sizeof(u64)); if(id != NULL){ ASSERT(*id == tex); return loaded_textures.texture[id - loaded_textures.texid]; } char buffer[100]; get_textures(tex, buffer, sizeof(buffer)); i32 newtex = load_gl_texture(buffer); list_push(loaded_textures.texture, loaded_textures.count, newtex); list_push2(loaded_textures.texid, loaded_textures.count, tex); return newtex; }
vec2 ui_rectangle_center(ui_rectangle* r) { return vec2_div(vec2_add(r->top_left, r->bottom_right), 2); }
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); }
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; } } } } } } }
static void tick_camera(struct Input *input, struct Camera *camera, float dt) { // // move // vec2 camera_up = vec2_direction(camera->rotation); vec2 camera_right = vec2_direction(camera->rotation - PI_OVER_TWO); vec2 move_acceleration = vec2_zero(); if (key_down(GLFW_KEY_W, input)) move_acceleration = vec2_add(move_acceleration, camera_up); if (key_down(GLFW_KEY_A, input)) move_acceleration = vec2_add(move_acceleration, vec2_negate(camera_right)); if (key_down(GLFW_KEY_S, input)) move_acceleration = vec2_add(move_acceleration, vec2_negate(camera_up)); if (key_down(GLFW_KEY_D, input)) move_acceleration = vec2_add(move_acceleration, camera_right); if (vec2_length2(move_acceleration) > FLOAT_EPSILON) { const float acceleration_magnitude = camera->zoom * 10.0f; move_acceleration = vec2_normalize(move_acceleration); move_acceleration = vec2_mul(move_acceleration, acceleration_magnitude); // v = v0 + (a*t) camera->move_velocity = vec2_add(camera->move_velocity, vec2_mul(move_acceleration, dt)); // Clamp move velocity. const float max_move_speed = 5.0f * sqrtf(camera->zoom); if (vec2_length2(camera->move_velocity) > (max_move_speed * max_move_speed)) camera->move_velocity = vec2_mul(vec2_normalize(camera->move_velocity), max_move_speed); } // Number of seconds required for friction to stop movement completely. // (because velocity is sampled each frame, actual stop time longer than this) float stop_time = 0.1f; float inv_stop_time = 1.0f / stop_time; if (move_acceleration.x == 0.0f) camera->move_velocity.x -= camera->move_velocity.x * inv_stop_time * dt; if (move_acceleration.y == 0.0f) camera->move_velocity.y -= camera->move_velocity.y * inv_stop_time * dt; // r = r0 + (v0*t) + (a*t^2)/2 camera->position = vec2_add(vec2_add(camera->position, vec2_mul(camera->move_velocity, dt)), vec2_div(vec2_mul(move_acceleration, dt * dt), 2.0f)); // // zoom // float zoom_acceleration = 0.0f; if (key_down(GLFW_KEY_SPACE, input)) zoom_acceleration += 1.0f; if (key_down(GLFW_KEY_LEFT_CONTROL, input)) zoom_acceleration -= 1.0f; zoom_acceleration *= camera->zoom * 50.0f; if (zoom_acceleration == 0.0f) camera->zoom_velocity -= camera->zoom_velocity * (1.0f / 0.1f) * dt; // v = v0 + (a*t) camera->zoom_velocity += zoom_acceleration * dt; // r = r0 + (v0*t) + (a*t^2)/2 camera->zoom += (camera->zoom_velocity * dt) + (zoom_acceleration * dt * dt) / 2.0f; // Clamp zoom velocity. const float max_zoom_speed = 3.0f * camera->zoom; camera->zoom_velocity = clamp_float(camera->zoom_velocity, -max_zoom_speed, max_zoom_speed); float unclamped_zoom = camera->zoom; camera->zoom = clamp_float(camera->zoom, 1.0f, 1000.0f); if (camera->zoom != unclamped_zoom) camera->zoom_velocity = 0.0f; }