From 8b3c16e6152a527f9aec1a88a9eed74119de7000 Mon Sep 17 00:00:00 2001 From: Magnus Auvinen Date: Wed, 22 Aug 2007 07:52:33 +0000 Subject: major engine cleanup. dependency on baselib removed. engine is now C code (not ansi tho). some other cruft removed aswell --- src/engine/client/client.c | 980 +++++++++++++++++++++++++++++++ src/engine/client/client.cpp | 1078 ----------------------------------- src/engine/client/gfx.c | 886 ++++++++++++++++++++++++++++ src/engine/client/gfx.cpp | 910 ----------------------------- src/engine/client/inp.c | 95 +++ src/engine/client/pnglite/pnglite.c | 877 ---------------------------- src/engine/client/pnglite/pnglite.h | 227 -------- src/engine/client/snd.c | 634 ++++++++++++++++++++ src/engine/client/snd.cpp | 550 ------------------ src/engine/client/ui.c | 117 ++++ src/engine/client/ui.cpp | 122 ---- src/engine/client/ui.h | 16 +- 12 files changed, 2720 insertions(+), 3772 deletions(-) create mode 100644 src/engine/client/client.c delete mode 100644 src/engine/client/client.cpp create mode 100644 src/engine/client/gfx.c delete mode 100644 src/engine/client/gfx.cpp create mode 100644 src/engine/client/inp.c delete mode 100644 src/engine/client/pnglite/pnglite.c delete mode 100644 src/engine/client/pnglite/pnglite.h create mode 100644 src/engine/client/snd.c delete mode 100644 src/engine/client/snd.cpp create mode 100644 src/engine/client/ui.c delete mode 100644 src/engine/client/ui.cpp (limited to 'src/engine/client') diff --git a/src/engine/client/client.c b/src/engine/client/client.c new file mode 100644 index 00000000..ab4b7109 --- /dev/null +++ b/src/engine/client/client.c @@ -0,0 +1,980 @@ + +#include +#include +#include +#include +#include + +#include +#include +#include "ui.h" + +#include + +#include +#include +#include +#include +#include +#include + +#include + +static int info_request_begin; +static int info_request_end; +static int snapshot_part; +static int64 local_start_time; +static int64 game_start_time; +static float latency = 0; +static int extra_polating = 0; +static int debug_font; +static float frametime = 0.0001f; +static NETCLIENT *net; +static NETADDR4 master_server; +static NETADDR4 server_address; +static int window_must_refocus = 0; +static int snaploss = 0; + +static int current_tick = 0; +static float intratick = 0; + +// --- input snapping --- +static int input_data[MAX_INPUT_SIZE] = {0}; +static int input_data_size; +static int input_is_changed = 1; +void snap_input(void *data, int size) +{ + if(input_data_size != size || memcmp(input_data, data, size)) + input_is_changed = 1; + mem_copy(input_data, data, size); + input_data_size = size; +} + +// -- snapshot handling --- +enum +{ + NUM_SNAPSHOT_TYPES=2, +}; + +SNAPSTORAGE snapshot_storage; +static SNAPSTORAGE_HOLDER *snapshots[NUM_SNAPSHOT_TYPES]; +static int recived_snapshots; +static char snapshot_incomming_data[MAX_SNAPSHOT_SIZE]; + +// --- + +const void *snap_get_item(int snapid, int index, SNAP_ITEM *item) +{ + dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); + SNAPSHOT_ITEM *i = snapshot_get_item(snapshots[snapid]->snap, index); + item->type = snapitem_type(i); + item->id = snapitem_id(i); + return (void *)snapitem_data(i); +} + +const void *snap_find_item(int snapid, int type, int id) +{ + // TODO: linear search. should be fixed. + int i; + for(i = 0; i < snapshots[snapid]->snap->num_items; i++) + { + SNAPSHOT_ITEM *itm = snapshot_get_item(snapshots[snapid]->snap, i); + if(snapitem_type(itm) == type && snapitem_id(itm) == id) + return (void *)snapitem_data(itm); + } + return 0x0; +} + +int snap_num_items(int snapid) +{ + dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); + return snapshots[snapid]->snap->num_items; +} + +static void snap_init() +{ + snapshots[SNAP_CURRENT] = 0; + snapshots[SNAP_PREV] = 0; + recived_snapshots = 0; + game_start_time = -1; + +} + +// ------ time functions ------ +float client_intratick() +{ + return intratick; +} + +int client_tick() +{ + return current_tick; +} + +int client_tickspeed() +{ + return SERVER_TICK_SPEED; +} + +float client_frametime() +{ + return frametime; +} + +float client_localtime() +{ + return (time_get()-local_start_time)/(float)(time_freq()); +} + +int menu_loop(); // TODO: what is this? + +// ----- send functions ----- +int client_send_msg() +{ + const MSG_INFO *info = msg_get_info(); + NETPACKET packet; + mem_zero(&packet, sizeof(NETPACKET)); + + packet.client_id = 0; + packet.data = info->data; + packet.data_size = info->size; + + if(info->flags&MSGFLAG_VITAL) + packet.flags = PACKETFLAG_VITAL; + + netclient_send(net, &packet); + return 0; +} + +static void client_send_info() +{ + recived_snapshots = 0; + game_start_time = -1; + + msg_pack_start_system(NETMSG_INFO, MSGFLAG_VITAL); + msg_pack_string(TEEWARS_NETVERSION_STRING, 64); + msg_pack_string(config.player_name, 128); + msg_pack_string(config.clan_name, 128); + msg_pack_string(config.password, 128); + msg_pack_string("myskin", 128); + msg_pack_end(); + client_send_msg(); +} + +static void client_send_entergame() +{ + msg_pack_start_system(NETMSG_ENTERGAME, MSGFLAG_VITAL); + msg_pack_end(); + client_send_msg(); +} + +static void client_send_error(const char *error) +{ + /* + packet p(NETMSG_CLIENT_ERROR); + p.write_str(error); + send_packet(&p); + //send_packet(&p); + //send_packet(&p); + */ +} + +static void client_send_input() +{ + msg_pack_start_system(NETMSG_INPUT, 0); + msg_pack_int(input_data_size); + int i; + for(i = 0; i < input_data_size/4; i++) + msg_pack_int(input_data[i]); + msg_pack_end(); + client_send_msg(); +} + + +// ------ server browse ---- +static struct +{ + SERVER_INFO infos[MAX_SERVERS]; + int64 request_times[MAX_SERVERS]; + NETADDR4 addresses[MAX_SERVERS]; + int num; +} servers; + +static int serverlist_lan = 1; + +int client_serverbrowse_getlist(SERVER_INFO **serverlist) +{ + *serverlist = servers.infos; + return servers.num; +} + +static void client_serverbrowse_init() +{ + servers.num = 0; +} + +void client_serverbrowse_refresh(int lan) +{ + serverlist_lan = lan; + + if(serverlist_lan) + { + if(config.debug) + dbg_msg("client", "broadcasting for servers"); + NETPACKET packet; + packet.client_id = -1; + mem_zero(&packet, sizeof(packet)); + packet.address.ip[0] = 0; + packet.address.ip[1] = 0; + packet.address.ip[2] = 0; + packet.address.ip[3] = 0; + packet.address.port = 8303; + packet.flags = PACKETFLAG_CONNLESS; + packet.data_size = sizeof(SERVERBROWSE_GETINFO); + packet.data = SERVERBROWSE_GETINFO; + netclient_send(net, &packet); + + // reset the list + servers.num = 0; + } + else + { + if(config.debug) + dbg_msg("client", "requesting server list"); + NETPACKET packet; + mem_zero(&packet, sizeof(packet)); + packet.client_id = -1; + packet.address = master_server; + packet.flags = PACKETFLAG_CONNLESS; + packet.data_size = sizeof(SERVERBROWSE_GETLIST); + packet.data = SERVERBROWSE_GETLIST; + netclient_send(net, &packet); + + // reset the list + servers.num = 0; + } +} + + +static void client_serverbrowse_request(int id) +{ + if(config.debug) + { + dbg_msg("client", "requesting server info from %d.%d.%d.%d:%d", + servers.addresses[id].ip[0], servers.addresses[id].ip[1], servers.addresses[id].ip[2], + servers.addresses[id].ip[3], servers.addresses[id].port); + } + NETPACKET packet; + packet.client_id = -1; + packet.address = servers.addresses[id]; + packet.flags = PACKETFLAG_CONNLESS; + packet.data_size = sizeof(SERVERBROWSE_GETINFO); + packet.data = SERVERBROWSE_GETINFO; + netclient_send(net, &packet); + servers.request_times[id] = time_get(); +} + +static void client_serverbrowse_update() +{ + int64 timeout = time_freq(); + int64 now = time_get(); + int max_requests = 10; + + // timeout old requests + while(info_request_begin < servers.num && info_request_begin < info_request_end) + { + if(now > servers.request_times[info_request_begin]+timeout) + info_request_begin++; + else + break; + } + + // send new requests + while(info_request_end < servers.num && info_request_end-info_request_begin < max_requests) + { + client_serverbrowse_request(info_request_end); + info_request_end++; + } +} + +// ------ state handling ----- +static int state; +int client_state() { return state; } +static void client_set_state(int s) +{ + if(config.debug) + dbg_msg("client", "state change. last=%d current=%d", state, s); + int old = state; + state = s; + if(old != s) + modc_statechange(state, old); +} + + +void client_connect(const char *server_address_str) +{ + dbg_msg("client", "connecting to '%s'", server_address_str); + char buf[512]; + strncpy(buf, server_address_str, 512); + + const char *port_str = 0; + int k; + for(k = 0; buf[k]; k++) + { + if(buf[k] == ':') + { + port_str = &(buf[k+1]); + buf[k] = 0; + break; + } + } + + int port = 8303; + if(port_str) + port = atoi(port_str); + + if(net_host_lookup(buf, port, &server_address) != 0) + dbg_msg("client", "could not find the address of %s, connecting to localhost", buf); + + netclient_connect(net, &server_address); + client_set_state(CLIENTSTATE_CONNECTING); + current_tick = 0; +} + +void client_disconnect() +{ + client_send_error("disconnected"); + netclient_disconnect(net, "disconnected"); + client_set_state(CLIENTSTATE_OFFLINE); + map_unload(); +} + +static int client_load_data() +{ + debug_font = gfx_load_texture("data/debug_font.png"); + return 1; +} + +static void client_debug_render() +{ + if(!config.debug) + return; + + gfx_blend_normal(); + gfx_texture_set(debug_font); + gfx_mapscreen(0,0,gfx_screenwidth(),gfx_screenheight()); + + static NETSTATS prev, current; + static int64 last_snap = 0; + if(time_get()-last_snap > time_freq()/10) + { + last_snap = time_get(); + prev = current; + netclient_stats(net, ¤t); + } + + static float frametime_avg = 0; + frametime_avg = frametime_avg*0.9f + frametime*0.1f; + char buffer[512]; + sprintf(buffer, "send: %6d recv: %6d snaploss: %4d latency: %4.0f %c gfxmem: %6dk fps: %3d", + (current.send_bytes-prev.send_bytes)*10, + (current.recv_bytes-prev.recv_bytes)*10, + snaploss, + latency*1000.0f, extra_polating?'E':' ', + gfx_memory_usage()/1024, + (int)(1.0f/frametime_avg)); + gfx_quads_text(2, 2, 16, buffer); + +} + +void client_quit() +{ + client_set_state(CLIENTSTATE_QUITING); +} + +const char *client_error_string() +{ + return netclient_error_string(net); +} + +static void client_render() +{ + gfx_clear(0.0f,0.0f,0.0f); + modc_render(); + client_debug_render(); +} + +static void client_error(const char *msg) +{ + dbg_msg("client", "error: %s", msg); + client_send_error(msg); + client_set_state(CLIENTSTATE_QUITING); +} + +static void client_process_packet(NETPACKET *packet) +{ + if(packet->client_id == -1) + { + // connectionlesss + if(packet->data_size >= (int)sizeof(SERVERBROWSE_LIST) && + memcmp(packet->data, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST)) == 0) + { + // server listing + int size = packet->data_size-sizeof(SERVERBROWSE_LIST); + mem_copy(servers.addresses, (char*)packet->data+sizeof(SERVERBROWSE_LIST), size); + servers.num = size/sizeof(NETADDR4); + + info_request_begin = 0; + info_request_end = 0; + + int i; + for(i = 0; i < servers.num; i++) + { + servers.infos[i].num_players = 0; + servers.infos[i].max_players = 0; + servers.infos[i].latency = 999; +#if defined(CONF_ARCH_ENDIAN_BIG) + const char *tmp = (const char *)&servers.addresses[i].port; + servers.addresses[i].port = (tmp[1]<<8) | tmp[0]; +#endif + sprintf(servers.infos[i].address, "%d.%d.%d.%d:%d", + servers.addresses[i].ip[0], servers.addresses[i].ip[1], servers.addresses[i].ip[2], + servers.addresses[i].ip[3], servers.addresses[i].port); + sprintf(servers.infos[i].name, "%d.%d.%d.%d:%d", + servers.addresses[i].ip[0], servers.addresses[i].ip[1], servers.addresses[i].ip[2], + servers.addresses[i].ip[3], servers.addresses[i].port); + } + } + + if(packet->data_size >= (int)sizeof(SERVERBROWSE_INFO) && + memcmp(packet->data, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)) == 0) + { + // we got ze info + UNPACKER up; + unpacker_reset(&up, (unsigned char*)packet->data+sizeof(SERVERBROWSE_INFO), packet->data_size-sizeof(SERVERBROWSE_INFO)); + + if(serverlist_lan) + { + if(servers.num != MAX_SERVERS) + { + int i = servers.num; + strncpy(servers.infos[i].name, unpacker_get_string(&up), 128); + strncpy(servers.infos[i].map, unpacker_get_string(&up), 128); + servers.infos[i].max_players = unpacker_get_int(&up); + servers.infos[i].num_players = unpacker_get_int(&up); + servers.infos[i].latency = 0; + + sprintf(servers.infos[i].address, "%d.%d.%d.%d:%d", + packet->address.ip[0], packet->address.ip[1], packet->address.ip[2], + packet->address.ip[3], packet->address.port); + + if(config.debug) + dbg_msg("client", "got server info"); + servers.num++; + + } + } + else + { + int i; + for(i = 0; i < servers.num; i++) + { + if(net_addr4_cmp(&servers.addresses[i], &packet->address) == 0) + { + strncpy(servers.infos[i].name, unpacker_get_string(&up), 128); + strncpy(servers.infos[i].map, unpacker_get_string(&up), 128); + servers.infos[i].max_players = unpacker_get_int(&up); + servers.infos[i].num_players = unpacker_get_int(&up); + servers.infos[i].latency = ((time_get() - servers.request_times[i])*1000)/time_freq(); + if(config.debug) + dbg_msg("client", "got server info"); + break; + } + } + } + } + } + else + { + + int sys; + int msg = msg_unpack_start(packet->data, packet->data_size, &sys); + if(sys) + { + // system message + if(msg == NETMSG_MAP) + { + const char *map = msg_unpack_string(); + dbg_msg("client/network", "connection accepted, map=%s", map); + client_set_state(CLIENTSTATE_LOADING); + + if(map_load(map)) + { + modc_entergame(); + client_send_entergame(); + dbg_msg("client/network", "loading done"); + // now we will wait for two snapshots + // to finish the connection + } + else + { + client_error("failure to load map"); + } + } + else if(msg == NETMSG_SNAP || msg == NETMSG_SNAPEMPTY) //|| msg == NETMSG_SNAPSMALL || msg == NETMSG_SNAPEMPTY) + { + //dbg_msg("client/network", "got snapshot"); + int game_tick = msg_unpack_int(); + int delta_tick = game_tick-msg_unpack_int(); + int num_parts = 1; + int part = 0; + int part_size = 0; + + if(msg != NETMSG_SNAPEMPTY) + part_size = msg_unpack_int(); + + if(snapshot_part == part && game_tick > current_tick) + { + // TODO: clean this up abit + const char *d = (const char *)msg_unpack_raw(part_size); + mem_copy((char*)snapshot_incomming_data + part*MAX_SNAPSHOT_PACKSIZE, d, part_size); + snapshot_part++; + + if(snapshot_part == num_parts) + { + snapshot_part = 0; + + // find snapshot that we should use as delta + static SNAPSHOT emptysnap; + emptysnap.data_size = 0; + emptysnap.num_items = 0; + + SNAPSHOT *deltashot = &emptysnap; + + // find delta + if(delta_tick >= 0) + { + //void *delta_data; + + int deltashot_size = snapstorage_get(&snapshot_storage, delta_tick, 0, &deltashot); + + if(deltashot_size < 0) + { + // couldn't find the delta snapshots that the server used + // to compress this snapshot. force the server to resync + if(config.debug) + dbg_msg("client", "error, couldn't find the delta snapshot"); + + // ack snapshot + msg_pack_start_system(NETMSG_SNAPACK, 0); + msg_pack_int(-1); + msg_pack_end(); + client_send_msg(); + return; + } + } + + // decompress snapshot + void *deltadata = snapshot_empty_delta(); + int deltasize = sizeof(int)*3; + + unsigned char tmpbuffer[MAX_SNAPSHOT_SIZE]; + unsigned char tmpbuffer2[MAX_SNAPSHOT_SIZE]; + if(part_size) + { + int compsize = zerobit_decompress(snapshot_incomming_data, part_size, tmpbuffer); + int intsize = intpack_decompress(tmpbuffer, compsize, tmpbuffer2); + deltadata = tmpbuffer2; + deltasize = intsize; + } + + //dbg_msg("UNPACK", "%d unpacked with %d", game_tick, delta_tick); + + unsigned char tmpbuffer3[MAX_SNAPSHOT_SIZE]; + int snapsize = snapshot_unpack_delta(deltashot, (SNAPSHOT*)tmpbuffer3, deltadata, deltasize); + + // purge old snapshots + int purgetick = delta_tick; + if(snapshots[SNAP_PREV] && snapshots[SNAP_PREV]->tick < purgetick) + purgetick = snapshots[SNAP_PREV]->tick; + if(snapshots[SNAP_CURRENT] && snapshots[SNAP_CURRENT]->tick < purgetick) + purgetick = snapshots[SNAP_PREV]->tick; + snapstorage_purge_until(&snapshot_storage, purgetick); + //client_snapshot_purge_until(game_tick-50); + + // add new + snapstorage_add(&snapshot_storage, game_tick, time_get(), snapsize, (SNAPSHOT*)tmpbuffer3); + //SNAPSTORAGE_HOLDER *snap = client_snapshot_add(game_tick, time_get(), tmpbuffer3, snapsize); + + //int ncrc = snapshot_crc((snapshot*)tmpbuffer3); + //if(crc != ncrc) + // dbg_msg("client", "client snapshot crc failure %d %d", crc, ncrc); + + // apply snapshot, cycle pointers + recived_snapshots++; + + + if(current_tick > 0) + snaploss += game_tick-current_tick-1; + current_tick = game_tick; + + // we got two snapshots until we see us self as connected + if(recived_snapshots == 2) + { + snapshots[SNAP_PREV] = snapshot_storage.first; + snapshots[SNAP_CURRENT] = snapshot_storage.last; + local_start_time = time_get(); + client_set_state(CLIENTSTATE_ONLINE); + } + + int64 now = time_get(); + int64 t = now - game_tick*time_freq()/50; + if(game_start_time == -1 || t < game_start_time) + { + if(config.debug) + dbg_msg("client", "adjusted time"); + game_start_time = t; + } + + int64 wanted = game_start_time+(game_tick*time_freq())/50; + float current_latency = (now-wanted)/(float)time_freq(); + latency = latency*0.95f+current_latency*0.05f; + + // ack snapshot + msg_pack_start_system(NETMSG_SNAPACK, 0); + msg_pack_int(game_tick); + msg_pack_end(); + client_send_msg(); + } + } + else + { + dbg_msg("client", "snapshot reset!"); + snapshot_part = 0; + } + } + } + else + { + // game message + modc_message(msg); + } + } +} + + +static void client_pump_network() +{ + netclient_update(net); + + // check for errors + if(client_state() != CLIENTSTATE_OFFLINE && netclient_state(net) == NETSTATE_OFFLINE) + { + // TODO: add message to the user there + client_set_state(CLIENTSTATE_OFFLINE); + dbg_msg("client", "offline error='%s'", netclient_error_string(net)); + } + + // + if(client_state() == CLIENTSTATE_CONNECTING && netclient_state(net) == NETSTATE_ONLINE) + { + // we switched to online + dbg_msg("client", "connected, sending info"); + client_set_state(CLIENTSTATE_LOADING); + client_send_info(); + } + + // process packets + NETPACKET packet; + while(netclient_recv(net, &packet)) + client_process_packet(&packet); +} + +static void client_run(const char *direct_connect_server) +{ + local_start_time = time_get(); + snapshot_part = 0; + info_request_begin = 0; + info_request_end = 0; + + client_serverbrowse_init(); + + // init graphics and sound + if(!gfx_init()) + return; + + snd_init(); // sound is allowed to fail + + // load data + if(!client_load_data()) + return; + + // init menu + modmenu_init(); // TODO: remove + + // init snapshotting + snap_init(); + + // init the mod + modc_init(); + + // open socket + NETADDR4 bindaddr; + mem_zero(&bindaddr, sizeof(bindaddr)); + net = netclient_open(bindaddr, 0); + + // + net_host_lookup(config.masterserver, MASTERSERVER_PORT, &master_server); + + // connect to the server if wanted + if(direct_connect_server) + client_connect(direct_connect_server); + + int64 game_starttime = time_get(); + int64 last_input = game_starttime; + + int64 reporttime = time_get(); + int64 reportinterval = time_freq()*1; + int frames = 0; + + inp_mouse_mode_relative(); + + while (1) + { + frames++; + int64 frame_start_time = time_get(); + + // switch snapshot + if(recived_snapshots >= 3) + { + int64 now = time_get(); + while(1) + { + SNAPSTORAGE_HOLDER *cur = snapshots[SNAP_CURRENT]; + int64 tickstart = game_start_time + (cur->tick+1)*time_freq()/50; + int64 t = tickstart; + if(latency > 0) + t += (int64)(time_freq()*(latency*1.1f)); + + if(t < now) + { + SNAPSTORAGE_HOLDER *next = snapshots[SNAP_CURRENT]->next; + if(next) + { + snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT]; + snapshots[SNAP_CURRENT] = next; + if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV]) + modc_newsnapshot(); + } + else + { + extra_polating = 1; + break; + } + } + else + { + extra_polating = 0; + break; + } + } + + if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV]) + { + int64 curtick_start = game_start_time + (snapshots[SNAP_CURRENT]->tick+1)*time_freq()/50; + if(latency > 0) + curtick_start += (int64)(time_freq()*(latency*1.1f)); + + int64 prevtick_start = game_start_time + (snapshots[SNAP_PREV]->tick+1)*time_freq()/50; + if(latency > 0) + prevtick_start += (int64)(time_freq()*(latency*1.1f)); + + intratick = (now - prevtick_start) / (float)(curtick_start-prevtick_start); + } + } + + // send input + if(client_state() == CLIENTSTATE_ONLINE) + { + if(config.stress&1 && client_localtime() > 10.0f) + client_disconnect(); + + if(input_is_changed || time_get() > last_input+time_freq()) + { + client_send_input(); + input_is_changed = 0; + last_input = time_get(); + } + } + + if(client_state() == CLIENTSTATE_OFFLINE && config.stress && (frames%100) == 0) + client_connect(config.cl_stress_server); + + // update input + inp_update(); + + // refocus + // TODO: fixme + + if(!gfx_window_active()) + { + if(window_must_refocus == 0) + inp_mouse_mode_absolute(); + window_must_refocus = 1; + } + + if(window_must_refocus && gfx_window_active()) + { + if(window_must_refocus < 3) + { + inp_mouse_mode_absolute(); + window_must_refocus++; + } + + if(inp_key_pressed(KEY_MOUSE_1)) + { + inp_mouse_mode_relative(); + window_must_refocus = 0; + } + } + + // screenshot button + if(inp_key_down(config.key_screenshot)) + gfx_screenshot(); + + // panic button + if(config.debug) + { + if(inp_key_pressed(KEY_F1)) + inp_mouse_mode_absolute(); + if(inp_key_pressed(KEY_F2)) + inp_mouse_mode_relative(); + + if(inp_key_pressed(KEY_LCTRL) && inp_key_pressed('Q')) + break; + + if(inp_key_pressed(KEY_F5)) + { + // ack snapshot + msg_pack_start_system(NETMSG_SNAPACK, 0); + msg_pack_int(-1); + msg_pack_end(); + client_send_msg(); + } + } + + // pump the network + client_pump_network(); + + // update the server browser + client_serverbrowse_update(); + + // render + if(config.stress) + { + if((frames%10) == 0) + { + client_render(); + gfx_swap(); + } + } + else + { + client_render(); + gfx_swap(); + } + + // check conditions + if(client_state() == CLIENTSTATE_QUITING) + break; + + // be nice + if(config.cpu_throttle || !gfx_window_active()) + thread_sleep(1); + + if(reporttime < time_get()) + { + if(config.debug) + { + dbg_msg("client/report", "fps=%.02f netstate=%d", + frames/(float)(reportinterval/time_freq()), netclient_state(net)); + } + frames = 0; + reporttime += reportinterval; + } + + // update frametime + frametime = (time_get()-frame_start_time)/(float)time_freq(); + } + + modc_shutdown(); + client_disconnect(); + + modmenu_shutdown(); // TODO: remove this + + gfx_shutdown(); + snd_shutdown(); +} + + +int editor_main(int argc, char **argv); + +//client main_client; + +int main(int argc, char **argv) +{ + dbg_msg("client", "starting..."); + + config_reset(); + +#ifdef CONF_PLATFORM_MACOSX + const char *config_filename = "~/.teewars"; +#else + const char *config_filename = "default.cfg"; +#endif + + int i; + for(i = 1; i < argc; i++) + { + if(argv[i][0] == '-' && argv[i][1] == 'f' && argv[i][2] == 0 && argc - i > 1) + { + config_filename = argv[i+1]; + i++; + } + } + + config_load(config_filename); + + const char *direct_connect_server = 0x0; + snd_set_master_volume(config.volume / 255.0f); + int editor = 0; + + // init network, need to be done first so we can do lookups + net_init(); + + // parse arguments + for(i = 1; i < argc; i++) + { + if(argv[i][0] == '-' && argv[i][1] == 'c' && argv[i][2] == 0 && argc - i > 1) + { + // -c SERVER:PORT + i++; + direct_connect_server = argv[i]; + } + else if(argv[i][0] == '-' && argv[i][1] == 'e' && argv[i][2] == 0) + { + editor = 1; + } + else + config_set(argv[i]); + } + + if(editor) + editor_main(argc, argv); + else + { + // start the client + client_run(direct_connect_server); + } + return 0; +} diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp deleted file mode 100644 index a4cb4509..00000000 --- a/src/engine/client/client.cpp +++ /dev/null @@ -1,1078 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include "ui.h" - -#include -#include -#include -#include - -#include - -using namespace baselib; - -static int info_request_begin; -static int info_request_end; -static int snapshot_part; -static int64 local_start_time; -static int64 game_start_time; -static float latency = 0; -static int extra_polating = 0; -static int debug_font; -static float frametime = 0.0001f; -static net_client net; -static netaddr4 master_server; -static netaddr4 server_address; -static int window_must_refocus = 0; -static int snaploss = 0; - -static int current_tick = 0; -static float intratick = 0; - - -// --- input wrappers --- -static int keyboard_state[2][input::last]; -static int keyboard_current = 0; -static int keyboard_first = 1; - -void inp_mouse_relative(int *x, int *y) { input::mouse_position(x, y); } -int inp_mouse_scroll() { return input::mouse_scroll(); } -int inp_key_pressed(int key) { return keyboard_state[keyboard_current][key]; } -int inp_key_was_pressed(int key) { return keyboard_state[keyboard_current^1][key]; } -int inp_key_down(int key) { return inp_key_pressed(key)&&!inp_key_was_pressed(key); } -int inp_button_pressed(int button) { return input::pressed(button); } - -void inp_update() -{ - if(keyboard_first) - { - // make sure to reset - keyboard_first = 0; - inp_update(); - } - - keyboard_current = keyboard_current^1; - for(int i = 0; i < input::last; i++) - keyboard_state[keyboard_current][i] = input::pressed(i); -} - -// --- input snapping --- -static int input_data[MAX_INPUT_SIZE] = {0}; -static int input_data_size; -static int input_is_changed = 1; -void snap_input(void *data, int size) -{ - if(input_data_size != size || memcmp(input_data, data, size)) - input_is_changed = 1; - mem_copy(input_data, data, size); - input_data_size = size; -} - -// -- snapshot handling --- -enum -{ - NUM_SNAPSHOT_TYPES=2, -}; - -struct snapshot_info -{ - snapshot_info *prev; - snapshot_info *next; - - int tick; - int64 recvtime; - snapshot *snap; -}; - -static snapshot_info *first_snapshot = 0; -static snapshot_info *last_snapshot = 0; - -static snapshot_info *client_snapshot_add(int tick, int64 time, void *data, int data_size) -{ - snapshot_info *holder = (snapshot_info*)mem_alloc(sizeof(snapshot_info) + data_size, 1); - holder->tick = tick; - holder->recvtime = time; - holder->snap = (snapshot *)(holder+1); - mem_copy(holder->snap, data, data_size); - - holder->next =0x0; - holder->prev = last_snapshot; - if(last_snapshot) - last_snapshot->next = holder; - else - first_snapshot = holder; - last_snapshot = holder; - - return holder; -} - -static snapshot_info *client_snapshot_find(int tick) -{ - snapshot_info *current = first_snapshot; - while(current) - { - if(current->tick == tick) - return current; - current = current->next; - } - - return 0; -} - -static void client_snapshot_purge_until(int tick) -{ - snapshot_info *current = first_snapshot; - while(current) - { - snapshot_info *next = current->next; - if(current->tick < tick) - mem_free(current); - else - break; - - current = next; - current->prev = 0; - first_snapshot = current; - } - - if(!first_snapshot) - last_snapshot = 0; -} - -static snapshot_info *snapshots[NUM_SNAPSHOT_TYPES]; -static int recived_snapshots; -static char snapshot_incomming_data[MAX_SNAPSHOT_SIZE]; - -// --- - -const void *snap_get_item(int snapid, int index, snap_item *item) -{ - dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); - snapshot::item *i = snapshots[snapid]->snap->get_item(index); - item->type = i->type(); - item->id = i->id(); - return (void *)i->data(); -} - -const void *snap_find_item(int snapid, int type, int id) -{ - // TODO: linear search. should be fixed. - for(int i = 0; i < snapshots[snapid]->snap->num_items; i++) - { - snapshot::item *itm = snapshots[snapid]->snap->get_item(i); - if(itm->type() == type && itm->id() == id) - return (void *)itm->data(); - } - return 0x0; -} - -int snap_num_items(int snapid) -{ - dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); - return snapshots[snapid]->snap->num_items; -} - -static void snap_init() -{ - snapshots[SNAP_CURRENT] = 0; - snapshots[SNAP_PREV] = 0; - recived_snapshots = 0; - game_start_time = -1; - -} - -// ------ time functions ------ -float client_intratick() -{ - return intratick; -} - -int client_tick() -{ - return current_tick; -} - -int client_tickspeed() -{ - return SERVER_TICK_SPEED; -} - -float client_frametime() -{ - return frametime; -} - -float client_localtime() -{ - return (time_get()-local_start_time)/(float)(time_freq()); -} - -int menu_loop(); // TODO: what is this? - -// ----- send functions ----- -int client_send_msg() -{ - const msg_info *info = msg_get_info(); - NETPACKET packet; - mem_zero(&packet, sizeof(NETPACKET)); - - packet.client_id = 0; - packet.data = info->data; - packet.data_size = info->size; - - if(info->flags&MSGFLAG_VITAL) - packet.flags = PACKETFLAG_VITAL; - - net.send(&packet); - return 0; -} - -static void client_send_info() -{ - recived_snapshots = 0; - game_start_time = -1; - - msg_pack_start_system(NETMSG_INFO, MSGFLAG_VITAL); - msg_pack_string(TEEWARS_NETVERSION_STRING, 64); - msg_pack_string(config.player_name, 128); - msg_pack_string(config.clan_name, 128); - msg_pack_string(config.password, 128); - msg_pack_string("myskin", 128); - msg_pack_end(); - client_send_msg(); -} - -static void client_send_entergame() -{ - msg_pack_start_system(NETMSG_ENTERGAME, MSGFLAG_VITAL); - msg_pack_end(); - client_send_msg(); -} - -static void client_send_error(const char *error) -{ - /* - pack(NETMSG_CLIENT_ERROR, "s", error); - */ - /* - packet p(NETMSG_CLIENT_ERROR); - p.write_str(error); - send_packet(&p); - //send_packet(&p); - //send_packet(&p); - */ -} - -static void client_send_input() -{ - msg_pack_start_system(NETMSG_INPUT, 0); - msg_pack_int(input_data_size); - for(int i = 0; i < input_data_size/4; i++) - msg_pack_int(input_data[i]); - msg_pack_end(); - client_send_msg(); -} - - -// ------ server browse ---- -static struct -{ - server_info infos[MAX_SERVERS]; - int64 request_times[MAX_SERVERS]; - netaddr4 addresses[MAX_SERVERS]; - int num; -} servers; -static int serverlist_lan = 1; - -int client_serverbrowse_getlist(server_info **serverlist) -{ - *serverlist = servers.infos; - return servers.num; -} - -static void client_serverbrowse_init() -{ - servers.num = 0; -} - -void client_serverbrowse_refresh(int lan) -{ - serverlist_lan = lan; - - if(serverlist_lan) - { - if(config.debug) - dbg_msg("client", "broadcasting for servers"); - NETPACKET packet; - packet.client_id = -1; - mem_zero(&packet, sizeof(packet)); - packet.address.ip[0] = 0; - packet.address.ip[1] = 0; - packet.address.ip[2] = 0; - packet.address.ip[3] = 0; - packet.address.port = 8303; - packet.flags = PACKETFLAG_CONNLESS; - packet.data_size = sizeof(SERVERBROWSE_GETINFO); - packet.data = SERVERBROWSE_GETINFO; - net.send(&packet); - - // reset the list - servers.num = 0; - } - else - { - if(config.debug) - dbg_msg("client", "requesting server list"); - NETPACKET packet; - mem_zero(&packet, sizeof(packet)); - packet.client_id = -1; - packet.address = master_server; - packet.flags = PACKETFLAG_CONNLESS; - packet.data_size = sizeof(SERVERBROWSE_GETLIST); - packet.data = SERVERBROWSE_GETLIST; - net.send(&packet); - - // reset the list - servers.num = 0; - } -} - - -static void client_serverbrowse_request(int id) -{ - if(config.debug) - { - dbg_msg("client", "requesting server info from %d.%d.%d.%d:%d", - servers.addresses[id].ip[0], servers.addresses[id].ip[1], servers.addresses[id].ip[2], - servers.addresses[id].ip[3], servers.addresses[id].port); - } - NETPACKET packet; - packet.client_id = -1; - packet.address = servers.addresses[id]; - packet.flags = PACKETFLAG_CONNLESS; - packet.data_size = sizeof(SERVERBROWSE_GETINFO); - packet.data = SERVERBROWSE_GETINFO; - net.send(&packet); - servers.request_times[id] = time_get(); -} - -static void client_serverbrowse_update() -{ - int64 timeout = time_freq(); - int64 now = time_get(); - int max_requests = 10; - - // timeout old requests - while(info_request_begin < servers.num && info_request_begin < info_request_end) - { - if(now > servers.request_times[info_request_begin]+timeout) - info_request_begin++; - else - break; - } - - // send new requests - while(info_request_end < servers.num && info_request_end-info_request_begin < max_requests) - { - client_serverbrowse_request(info_request_end); - info_request_end++; - } -} - -// ------ state handling ----- -static int state; -int client_state() { return state; } -static void client_set_state(int s) -{ - if(config.debug) - dbg_msg("client", "state change. last=%d current=%d", state, s); - int old = state; - state = s; - if(old != s) - modc_statechange(state, old); -} - - -void client_connect(const char *server_address_str) -{ - dbg_msg("client", "connecting to '%s'", server_address_str); - char buf[512]; - strncpy(buf, server_address_str, 512); - - const char *port_str = 0; - for(int k = 0; buf[k]; k++) - { - if(buf[k] == ':') - { - port_str = &(buf[k+1]); - buf[k] = 0; - break; - } - } - - int port = 8303; - if(port_str) - port = atoi(port_str); - - if(net_host_lookup(buf, port, &server_address) != 0) - dbg_msg("client", "could not find the address of %s, connecting to localhost", buf); - - net.connect(&server_address); - client_set_state(CLIENTSTATE_CONNECTING); - current_tick = 0; -} - -void client_disconnect() -{ - client_send_error("disconnected"); - net.disconnect("disconnected"); - client_set_state(CLIENTSTATE_OFFLINE); - map_unload(); -} - -static bool client_load_data() -{ - debug_font = gfx_load_texture("data/debug_font.png"); - return true; -} - -static void client_debug_render() -{ - if(!config.debug) - return; - - gfx_blend_normal(); - gfx_texture_set(debug_font); - gfx_mapscreen(0,0,gfx_screenwidth(),gfx_screenheight()); - - static NETSTATS prev, current; - static int64 last_snap = 0; - if(time_get()-last_snap > time_freq()/10) - { - last_snap = time_get(); - prev = current; - net.stats(¤t); - } - - static float frametime_avg = 0; - frametime_avg = frametime_avg*0.9f + frametime*0.1f; - char buffer[512]; - sprintf(buffer, "send: %6d recv: %6d snaploss: %4d latency: %4.0f %c gfxmem: %6dk fps: %3d", - (current.send_bytes-prev.send_bytes)*10, - (current.recv_bytes-prev.recv_bytes)*10, - snaploss, - latency*1000.0f, extra_polating?'E':' ', - gfx_memory_usage()/1024, - (int)(1.0f/frametime_avg)); - gfx_quads_text(2, 2, 16, buffer); - -} - -void client_quit() -{ - client_set_state(CLIENTSTATE_QUITING); -} - -const char *client_error_string() -{ - return net.error_string(); -} - -static void client_render() -{ - gfx_clear(0.0f,0.0f,0.0f); - modc_render(); - client_debug_render(); -} - -static void client_error(const char *msg) -{ - dbg_msg("client", "error: %s", msg); - client_send_error(msg); - client_set_state(CLIENTSTATE_QUITING); -} - -static void client_process_packet(NETPACKET *packet) -{ - if(packet->client_id == -1) - { - // connectionlesss - if(packet->data_size >= (int)sizeof(SERVERBROWSE_LIST) && - memcmp(packet->data, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST)) == 0) - { - // server listing - int size = packet->data_size-sizeof(SERVERBROWSE_LIST); - mem_copy(servers.addresses, (char*)packet->data+sizeof(SERVERBROWSE_LIST), size); - servers.num = size/sizeof(NETADDR4); - - info_request_begin = 0; - info_request_end = 0; - - for(int i = 0; i < servers.num; i++) - { - servers.infos[i].num_players = 0; - servers.infos[i].max_players = 0; - servers.infos[i].latency = 999; -#if defined(CONF_ARCH_ENDIAN_BIG) - const char *tmp = (const char *)&servers.addresses[i].port; - servers.addresses[i].port = (tmp[1]<<8) | tmp[0]; -#endif - sprintf(servers.infos[i].address, "%d.%d.%d.%d:%d", - servers.addresses[i].ip[0], servers.addresses[i].ip[1], servers.addresses[i].ip[2], - servers.addresses[i].ip[3], servers.addresses[i].port); - sprintf(servers.infos[i].name, "%d.%d.%d.%d:%d", - servers.addresses[i].ip[0], servers.addresses[i].ip[1], servers.addresses[i].ip[2], - servers.addresses[i].ip[3], servers.addresses[i].port); - } - } - - if(packet->data_size >= (int)sizeof(SERVERBROWSE_INFO) && - memcmp(packet->data, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)) == 0) - { - // we got ze info - data_unpacker unpacker; - unpacker.reset((unsigned char*)packet->data+sizeof(SERVERBROWSE_INFO), packet->data_size-sizeof(SERVERBROWSE_INFO)); - - if(serverlist_lan) - { - if(servers.num != MAX_SERVERS) - { - int i = servers.num; - strncpy(servers.infos[i].name, unpacker.get_string(), 128); - strncpy(servers.infos[i].map, unpacker.get_string(), 128); - servers.infos[i].max_players = unpacker.get_int(); - servers.infos[i].num_players = unpacker.get_int(); - servers.infos[i].latency = 0; - - sprintf(servers.infos[i].address, "%d.%d.%d.%d:%d", - packet->address.ip[0], packet->address.ip[1], packet->address.ip[2], - packet->address.ip[3], packet->address.port); - - if(config.debug) - dbg_msg("client", "got server info"); - servers.num++; - - } - } - else - { - for(int i = 0; i < servers.num; i++) - { - if(net_addr4_cmp(&servers.addresses[i], &packet->address) == 0) - { - strncpy(servers.infos[i].name, unpacker.get_string(), 128); - strncpy(servers.infos[i].map, unpacker.get_string(), 128); - servers.infos[i].max_players = unpacker.get_int(); - servers.infos[i].num_players = unpacker.get_int(); - servers.infos[i].latency = ((time_get() - servers.request_times[i])*1000)/time_freq(); - if(config.debug) - dbg_msg("client", "got server info"); - break; - } - } - } - } - } - else - { - - int sys; - int msg = msg_unpack_start(packet->data, packet->data_size, &sys); - if(sys) - { - // system message - if(msg == NETMSG_MAP) - { - const char *map = msg_unpack_string(); - dbg_msg("client/network", "connection accepted, map=%s", map); - client_set_state(CLIENTSTATE_LOADING); - - if(map_load(map)) - { - modc_entergame(); - client_send_entergame(); - dbg_msg("client/network", "loading done"); - // now we will wait for two snapshots - // to finish the connection - } - else - { - client_error("failure to load map"); - } - } - else if(msg == NETMSG_SNAP || msg == NETMSG_SNAPEMPTY) //|| msg == NETMSG_SNAPSMALL || msg == NETMSG_SNAPEMPTY) - { - //dbg_msg("client/network", "got snapshot"); - int game_tick = msg_unpack_int(); - int delta_tick = game_tick-msg_unpack_int(); - int num_parts = 1; - int part = 0; - int part_size = 0; - - if(msg != NETMSG_SNAPEMPTY) - part_size = msg_unpack_int(); - - if(snapshot_part == part && game_tick > current_tick) - { - // TODO: clean this up abit - const char *d = (const char *)msg_unpack_raw(part_size); - mem_copy((char*)snapshot_incomming_data + part*MAX_SNAPSHOT_PACKSIZE, d, part_size); - snapshot_part++; - - if(snapshot_part == num_parts) - { - snapshot_part = 0; - - // find snapshot that we should use as delta - static snapshot emptysnap; - emptysnap.data_size = 0; - emptysnap.num_items = 0; - - snapshot *deltashot = &emptysnap; - - // find delta - if(delta_tick >= 0) - { - //void *delta_data; - snapshot_info *delta_info = client_snapshot_find(delta_tick); - //deltashot_size = snapshots_new.get(delta_tick, 0, &delta_data); - if(delta_info) - deltashot = delta_info->snap; - else - { - // couldn't find the delta snapshots that the server used - // to compress this snapshot. force the server to resync - if(config.debug) - dbg_msg("client", "error, couldn't find the delta snapshot"); - - // ack snapshot - msg_pack_start_system(NETMSG_SNAPACK, 0); - msg_pack_int(-1); - msg_pack_end(); - client_send_msg(); - return; - } - } - - // decompress snapshot - void *deltadata = snapshot_empty_delta(); - int deltasize = sizeof(int)*3; - - unsigned char tmpbuffer[MAX_SNAPSHOT_SIZE]; - unsigned char tmpbuffer2[MAX_SNAPSHOT_SIZE]; - if(part_size) - { - int compsize = zerobit_decompress(snapshot_incomming_data, part_size, tmpbuffer); - int intsize = intpack_decompress(tmpbuffer, compsize, tmpbuffer2); - deltadata = tmpbuffer2; - deltasize = intsize; - } - - //dbg_msg("UNPACK", "%d unpacked with %d", game_tick, delta_tick); - - unsigned char tmpbuffer3[MAX_SNAPSHOT_SIZE]; - int snapsize = snapshot_unpack_delta(deltashot, (snapshot*)tmpbuffer3, deltadata, deltasize); - - // purge old snapshots - int purgetick = delta_tick; - if(snapshots[SNAP_PREV] && snapshots[SNAP_PREV]->tick < purgetick) - purgetick = snapshots[SNAP_PREV]->tick; - if(snapshots[SNAP_CURRENT] && snapshots[SNAP_CURRENT]->tick < purgetick) - purgetick = snapshots[SNAP_PREV]->tick; - client_snapshot_purge_until(purgetick); - //client_snapshot_purge_until(game_tick-50); - - // add new - snapshot_info *snap = client_snapshot_add(game_tick, time_get(), tmpbuffer3, snapsize); - - //int ncrc = snapshot_crc((snapshot*)tmpbuffer3); - //if(crc != ncrc) - // dbg_msg("client", "client snapshot crc failure %d %d", crc, ncrc); - - // apply snapshot, cycle pointers - recived_snapshots++; - - - if(current_tick > 0) - snaploss += game_tick-current_tick-1; - - current_tick = game_tick; - - // we got two snapshots until we see us self as connected - if(recived_snapshots <= 2) - { - snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT]; - snapshots[SNAP_CURRENT] = snap; - } - - if(recived_snapshots == 2) - { - local_start_time = time_get(); - client_set_state(CLIENTSTATE_ONLINE); - } - - int64 now = time_get(); - int64 t = now - game_tick*time_freq()/50; - if(game_start_time == -1 || t < game_start_time) - { - if(config.debug) - dbg_msg("client", "adjusted time"); - game_start_time = t; - } - - int64 wanted = game_start_time+(game_tick*time_freq())/50; - float current_latency = (now-wanted)/(float)time_freq(); - latency = latency*0.95f+current_latency*0.05f; - - // ack snapshot - msg_pack_start_system(NETMSG_SNAPACK, 0); - msg_pack_int(game_tick); - msg_pack_end(); - client_send_msg(); - } - } - else - { - dbg_msg("client", "snapshot reset!"); - snapshot_part = 0; - } - } - } - else - { - // game message - modc_message(msg); - } - } -} - - -static void client_pump_network() -{ - net.update(); - - // check for errors - if(client_state() != CLIENTSTATE_OFFLINE && net.state() == NETSTATE_OFFLINE) - { - // TODO: add message to the user there - client_set_state(CLIENTSTATE_OFFLINE); - dbg_msg("client", "offline error='%s'", net.error_string()); - } - - // - if(client_state() == CLIENTSTATE_CONNECTING && net.state() == NETSTATE_ONLINE) - { - // we switched to online - dbg_msg("client", "connected, sending info"); - client_set_state(CLIENTSTATE_LOADING); - client_send_info(); - } - - // process packets - NETPACKET packet; - while(net.recv(&packet)) - client_process_packet(&packet); -} - -static void client_run(const char *direct_connect_server) -{ - local_start_time = time_get(); - snapshot_part = 0; - info_request_begin = 0; - info_request_end = 0; - - client_serverbrowse_init(); - - // init graphics and sound - if(!gfx_init()) - return; - - snd_init(); // sound is allowed to fail - - // load data - if(!client_load_data()) - return; - - // init menu - modmenu_init(); // TODO: remove - - // init snapshotting - snap_init(); - - // init the mod - modc_init(); - - // open socket - NETADDR4 bindaddr; - mem_zero(&bindaddr, sizeof(bindaddr)); - net.open(bindaddr, 0); - - // - net_host_lookup(config.masterserver, MASTERSERVER_PORT, &master_server); - - // connect to the server if wanted - if(direct_connect_server) - client_connect(direct_connect_server); - - //int64 inputs_per_second = 50; - //int64 time_per_input = time_freq()/inputs_per_second; - int64 game_starttime = time_get(); - int64 last_input = game_starttime; - - int64 reporttime = time_get(); - int64 reportinterval = time_freq()*1; - int frames = 0; - - input::set_mouse_mode(input::mode_relative); - - while (1) - { - frames++; - int64 frame_start_time = time_get(); - - // switch snapshot - if(recived_snapshots >= 3) - { - int64 now = time_get(); - while(1) - { - snapshot_info *cur = snapshots[SNAP_CURRENT]; - int64 tickstart = game_start_time + (cur->tick+1)*time_freq()/50; - int64 t = tickstart; - if(latency > 0) - t += (int64)(time_freq()*(latency*1.1f)); - - if(t < now) - { - snapshot_info *next = snapshots[SNAP_CURRENT]->next; - if(next) - { - snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT]; - snapshots[SNAP_CURRENT] = next; - if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV]) - modc_newsnapshot(); - } - else - { - extra_polating = 1; - break; - } - } - else - { - extra_polating = 0; - break; - } - } - - if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV]) - { - int64 curtick_start = game_start_time + (snapshots[SNAP_CURRENT]->tick+1)*time_freq()/50; - if(latency > 0) - curtick_start += (int64)(time_freq()*(latency*1.1f)); - - int64 prevtick_start = game_start_time + (snapshots[SNAP_PREV]->tick+1)*time_freq()/50; - if(latency > 0) - prevtick_start += (int64)(time_freq()*(latency*1.1f)); - - intratick = (now - prevtick_start) / (float)(curtick_start-prevtick_start); - } - } - - // send input - if(client_state() == CLIENTSTATE_ONLINE) - { - if(config.stress&1 && client_localtime() > 10.0f) - client_disconnect(); - - if(input_is_changed || time_get() > last_input+time_freq()) - { - client_send_input(); - input_is_changed = 0; - last_input = time_get(); - } - } - - if(client_state() == CLIENTSTATE_OFFLINE && config.stress && (frames%100) == 0) - client_connect(config.cl_stress_server); - - // update input - inp_update(); - - // refocus - if(!gfx_window_active()) - { - if(window_must_refocus == 0) - { - input::set_mouse_mode(input::mode_absolute); - } - window_must_refocus = 1; - } - - if(window_must_refocus && gfx_window_active()) - { - if(window_must_refocus < 3) - { - input::set_mouse_mode(input::mode_absolute); - window_must_refocus++; - } - - if(inp_button_pressed(input::mouse_1)) - { - input::set_mouse_mode(input::mode_relative); - window_must_refocus = 0; - } - } - - // screenshot button - if(inp_key_down(config.key_screenshot)) - gfx_screenshot(); - - // panic button - if(config.debug) - { - if(input::pressed(input::f1)) - input::set_mouse_mode(input::mode_absolute); - if(input::pressed(input::f2)) - input::set_mouse_mode(input::mode_relative); - - if(input::pressed(input::lctrl) && input::pressed('Q')) - break; - - if(input::pressed(input::f5)) - { - // ack snapshot - msg_pack_start_system(NETMSG_SNAPACK, 0); - msg_pack_int(-1); - msg_pack_end(); - client_send_msg(); - } - } - - // pump the network - client_pump_network(); - - // update the server browser - client_serverbrowse_update(); - - // render - if(config.stress) - { - if((frames%10) == 0) - { - client_render(); - gfx_swap(); - } - } - else - { - client_render(); - gfx_swap(); - } - - // check conditions - if(client_state() == CLIENTSTATE_QUITING) - break; - - // be nice - if(config.cpu_throttle || !gfx_window_active()) - thread_sleep(1); - - if(reporttime < time_get()) - { - if(config.debug) - { - dbg_msg("client/report", "fps=%.02f netstate=%d", - frames/(float)(reportinterval/time_freq()), net.state()); - } - frames = 0; - reporttime += reportinterval; - } - - // update frametime - frametime = (time_get()-frame_start_time)/(float)time_freq(); - } - - modc_shutdown(); - client_disconnect(); - - modmenu_shutdown(); // TODO: remove this - - gfx_shutdown(); - snd_shutdown(); -} - - -int editor_main(int argc, char **argv); - -//client main_client; - -int main(int argc, char **argv) -{ - dbg_msg("client", "starting..."); - - config_reset(); - -#ifdef CONF_PLATFORM_MACOSX - const char *config_filename = "~/.teewars"; -#else - const char *config_filename = "default.cfg"; -#endif - - for(int i = 1; i < argc; i++) - { - if(argv[i][0] == '-' && argv[i][1] == 'f' && argv[i][2] == 0 && argc - i > 1) - { - config_filename = argv[i+1]; - i++; - } - } - - config_load(config_filename); - - const char *direct_connect_server = 0x0; - snd_set_master_volume(config.volume / 255.0f); - bool editor = false; - - // init network, need to be done first so we can do lookups - net_init(); - - // parse arguments - for(int i = 1; i < argc; i++) - { - if(argv[i][0] == '-' && argv[i][1] == 'c' && argv[i][2] == 0 && argc - i > 1) - { - // -c SERVER:PORT - i++; - direct_connect_server = argv[i]; - } - else if(argv[i][0] == '-' && argv[i][1] == 'e' && argv[i][2] == 0) - { - editor = true; - } - else - config_set(argv[i]); - } - - if(editor) - editor_main(argc, argv); - else - { - // start the client - client_run(direct_connect_server); - } - return 0; -} diff --git a/src/engine/client/gfx.c b/src/engine/client/gfx.c new file mode 100644 index 00000000..b48740b6 --- /dev/null +++ b/src/engine/client/gfx.c @@ -0,0 +1,886 @@ + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +// compressed textures +#define GL_COMPRESSED_RGB_ARB 0x84ED +#define GL_COMPRESSED_RGBA_ARB 0x84EE + +// +typedef struct { float x, y, z; } VEC3; +typedef struct { float u, v; } TEXCOORD; +typedef struct { float r, g, b, a; } COLOR; + +typedef struct +{ + VEC3 pos; + TEXCOORD tex; + COLOR color; +} VERTEX; + +const int vertex_buffer_size = 32*1024; +static VERTEX *vertices = 0; +static int num_vertices = 0; + +static COLOR color[4]; +static TEXCOORD texture[4]; + +static int do_screenshot = 0; + +static int screen_width = -1; +static int screen_height = -1; +static float rotation = 0; +static int quads_drawing = 0; + +static float screen_x0 = 0; +static float screen_y0 = 0; +static float screen_x1 = 0; +static float screen_y1 = 0; + +typedef struct +{ + GLuint tex; + int memsize; + int flags; + int next; +} TEXTURE; + + +enum +{ + MAX_TEXTURES = 128 +}; + +static TEXTURE textures[MAX_TEXTURES]; +static int first_free_texture; +static int memory_usage = 0; + +static const unsigned char null_texture_data[] = { + 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, + 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, + 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, + 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, +}; + +static void draw_quad(int _bflush) +{ + if (!_bflush && ((num_vertices + 4) < vertex_buffer_size)) + { + // Just add + num_vertices += 4; + } + else if (num_vertices) + { + if (!_bflush) + num_vertices += 4; + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glVertexPointer(3, GL_FLOAT, + sizeof(VERTEX), + (char*)vertices); + glTexCoordPointer(2, GL_FLOAT, + sizeof(VERTEX), + (char*)vertices + sizeof(float)*3); + glColorPointer(4, GL_FLOAT, + sizeof(VERTEX), + (char*)vertices + sizeof(float)*5); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glDrawArrays(GL_QUADS, 0, num_vertices); + + // Reset pointer + num_vertices = 0; + } +} + +int gfx_init() +{ + screen_width = config.gfx_screen_width; + screen_height = config.gfx_screen_height; + + glfwInit(); + + if(config.stress) + { + screen_width = 320; + screen_height = 240; + } + + if(config.gfx_fullscreen) + { + int result = glfwOpenWindow(screen_width, screen_height, 8, 8, 8, 0, 0, 0, GLFW_FULLSCREEN); + if(result != GL_TRUE) + { + dbg_msg("game", "failed to create gl context"); + return 0; + } + } + else + { + int result = glfwOpenWindow(screen_width, screen_height, 0, 0, 0, 0, 0, 0, GLFW_WINDOW); + if(result != GL_TRUE) + { + dbg_msg("game", "failed to create gl context"); + return 0; + } + } + + glfwSetWindowTitle("Teewars"); + + // We don't want to see the window when we run the stress testing + if(config.stress) + glfwIconifyWindow(); + + // Init vertices + if (vertices) + mem_free(vertices); + vertices = (VERTEX*)mem_alloc(sizeof(VERTEX) * vertex_buffer_size, 1); + num_vertices = 0; + + + /* + dbg_msg("gfx", "OpenGL version %d.%d.%d", context.version_major(), + context.version_minor(), + context.version_rev());*/ + + gfx_mapscreen(0,0,config.gfx_screen_width, config.gfx_screen_height); + + // TODO: make wrappers for this + glEnable(GL_BLEND); + + // model + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + // Set all z to -5.0f + int i; + for (i = 0; i < vertex_buffer_size; i++) + vertices[i].pos.z = -5.0f; + + // init textures + first_free_texture = 0; + for(i = 0; i < MAX_TEXTURES; i++) + textures[i].next = i+1; + textures[MAX_TEXTURES-1].next = -1; + + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // init input + inp_init(); + + // create null texture, will get id=0 + gfx_load_texture_raw(4,4,IMG_RGBA,null_texture_data); + + // set vsync as needed + gfx_set_vsync(config.gfx_vsync); + + return 1; +} + + +int gfx_window_active() +{ + return glfwGetWindowParam(GLFW_ACTIVE) == GL_TRUE ? 1 : 0; +} + + +VIDEO_MODE fakemodes[] = { + {320,240,8,8,8}, {400,300,8,8,8}, {640,480,8,8,8}, + {720,400,8,8,8}, {768,576,8,8,8}, {800,600,8,8,8}, + {1024,600,8,8,8}, {1024,768,8,8,8}, {1152,864,8,8,8}, + {1280,768,8,8,8}, {1280,800,8,8,8}, {1280,960,8,8,8}, + {1280,1024,8,8,8}, {1368,768,8,8,8}, {1400,1050,8,8,8}, + {1440,900,8,8,8}, {1440,1050,8,8,8}, {1600,1000,8,8,8}, + {1600,1200,8,8,8}, {1680,1050,8,8,8}, {1792,1344,8,8,8}, + {1800,1440,8,8,8}, {1856,1392,8,8,8}, {1920,1080,8,8,8}, + {1920,1200,8,8,8}, {1920,1440,8,8,8}, {1920,2400,8,8,8}, + {2048,1536,8,8,8}, + + {320,240,5,6,5}, {400,300,5,6,5}, {640,480,5,6,5}, + {720,400,5,6,5}, {768,576,5,6,5}, {800,600,5,6,5}, + {1024,600,5,6,5}, {1024,768,5,6,5}, {1152,864,5,6,5}, + {1280,768,5,6,5}, {1280,800,5,6,5}, {1280,960,5,6,5}, + {1280,1024,5,6,5}, {1368,768,5,6,5}, {1400,1050,5,6,5}, + {1440,900,5,6,5}, {1440,1050,5,6,5}, {1600,1000,5,6,5}, + {1600,1200,5,6,5}, {1680,1050,5,6,5}, {1792,1344,5,6,5}, + {1800,1440,5,6,5}, {1856,1392,5,6,5}, {1920,1080,5,6,5}, + {1920,1200,5,6,5}, {1920,1440,5,6,5}, {1920,2400,5,6,5}, + {2048,1536,5,6,5} +}; + +int gfx_get_video_modes(VIDEO_MODE *list, int maxcount) +{ + if(config.gfx_display_all_modes) + { + mem_copy(list, fakemodes, sizeof(fakemodes)); + int count = sizeof(fakemodes)/sizeof(VIDEO_MODE); + if(maxcount < count) + count = maxcount; + return count; + } + + return glfwGetVideoModes((GLFWvidmode *)list, maxcount); +} + +void gfx_set_vsync(int val) +{ + glfwSwapInterval(val); +} + +int gfx_unload_texture(int index) +{ + glDeleteTextures(1, &textures[index].tex); + textures[index].next = first_free_texture; + memory_usage -= textures[index].memsize; + first_free_texture = index; + return 0; +} + +void gfx_blend_normal() +{ + // TODO: wrapper for this + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + +void gfx_blend_additive() +{ + // TODO: wrapper for this + glBlendFunc(GL_SRC_ALPHA, GL_ONE); +} + +int gfx_memory_usage() { return memory_usage; } + +static unsigned char sample(int w, int h, const unsigned char *data, int u, int v, int offset) +{ + return (data[(v*w+u)*4+offset]+ + data[(v*w+u+1)*4+offset]+ + data[((v+1)*w+u)*4+offset]+ + data[((v+1)*w+u+1)*4+offset])/4; +} + +int gfx_load_texture_raw(int w, int h, int format, const void *data) +{ + int mipmap = 1; + + // grab texture + int tex = first_free_texture; + first_free_texture = textures[tex].next; + textures[tex].next = -1; + + // resample if needed + unsigned char *texdata = (unsigned char *)data; + unsigned char *tmpdata = 0; + if(config.gfx_texture_quality==0) + { + if(w > 16 && h > 16 && format == IMG_RGBA) + { + w/=2; + h/=2; + unsigned char *tmpdata = (unsigned char *)mem_alloc(w*h*4, 1); + int c = 0; + int x, y; + for(y = 0; y < h; y++) + for(x = 0; x < w; x++, c++) + { + tmpdata[c*4] = sample(w*2, h*2, texdata, x*2,y*2, 0); + tmpdata[c*4+1] = sample(w*2, h*2, texdata, x*2,y*2, 1); + tmpdata[c*4+2] = sample(w*2, h*2, texdata, x*2,y*2, 2); + tmpdata[c*4+3] = sample(w*2, h*2, texdata, x*2,y*2, 3); + } + texdata = tmpdata; + } + } + + if(config.debug) + dbg_msg("gfx", "%d = %dx%d", tex, w, h); + + // upload texture + int oglformat = 0; + if(config.gfx_texture_compression) + { + oglformat = GL_COMPRESSED_RGBA_ARB; + if(format == IMG_RGB) + oglformat = GL_COMPRESSED_RGB_ARB; + } + else + { + oglformat = GL_RGBA; + if(format == IMG_RGB) + oglformat = GL_RGB; + } + + glGenTextures(1, &textures[tex].tex); + glBindTexture(GL_TEXTURE_2D, textures[tex].tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + gluBuild2DMipmaps(GL_TEXTURE_2D, oglformat, w, h, oglformat, GL_UNSIGNED_BYTE, texdata); + + // calculate memory usage + textures[tex].memsize = w*h*4; + if(mipmap) + { + while(w > 2 && h > 2) + { + w>>=1; + h>>=1; + textures[tex].memsize += w*h*4; + } + } + + memory_usage += textures[tex].memsize; + mem_free(tmpdata); + return tex; +} +/* +int gfx_load_mip_texture_raw(int w, int h, int format, const void *data) +{ + // grab texture + int tex = first_free_texture; + first_free_texture = textures[tex].next; + textures[tex].next = -1; + + // set data and return + // TODO: should be RGBA, not BGRA + dbg_msg("gfx", "%d = %dx%d", tex, w, h); + dbg_assert(format == IMG_RGBA, "not an RGBA image"); + + unsigned mip_w = w; + unsigned mip_h = h; + unsigned level = 0; + const unsigned char *ptr = (const unsigned char*)data; + while(mip_w > 0 && mip_h > 0) + { + dbg_msg("gfx mip", "%d = %dx%d", level, mip_w, mip_h); + textures[tex].tex.data2d_mip(mip_w, mip_h, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, level, ptr); + level++; + ptr = ptr + mip_w*mip_h*4; + mip_w = mip_w>>1; + mip_h = mip_h>>1; + } + return tex; +} +*/ +// simple uncompressed RGBA loaders +int gfx_load_texture(const char *filename) +{ + int l = strlen(filename); + if(l < 3) + return 0; + IMAGE_INFO img; + if(gfx_load_png(&img, filename)) + { + int id = gfx_load_texture_raw(img.width, img.height, img.format, img.data); + mem_free(img.data); + return id; + } + + return 0; +} + +int gfx_load_png(IMAGE_INFO *img, const char *filename) +{ + // open file for reading + png_init(0,0); + + png_t png; + if(png_open_file(&png, filename) != PNG_NO_ERROR) + { + dbg_msg("game/png", "failed to open file. filename='%s'", filename); + return 0; + } + + if(png.depth != 8 || (png.color_type != PNG_TRUECOLOR && png.color_type != PNG_TRUECOLOR_ALPHA)) + { + dbg_msg("game/png", "invalid format. filename='%s'", filename); + png_close_file(&png); + } + + unsigned char *buffer = (unsigned char *)mem_alloc(png.width * png.height * png.bpp, 1); + png_get_data(&png, buffer); + png_close_file(&png); + + img->width = png.width; + img->height = png.height; + if(png.color_type == PNG_TRUECOLOR) + img->format = IMG_RGB; + else if(png.color_type == PNG_TRUECOLOR_ALPHA) + img->format = IMG_RGBA; + img->data = buffer; + return 1; +} + +void gfx_shutdown() +{ + if (vertices) + mem_free(vertices); + glfwCloseWindow(); + glfwTerminate(); +} + +void gfx_screenshot() +{ + do_screenshot = 1; +} + +void gfx_swap() +{ + if(do_screenshot) + { + // fetch image data + int w = screen_width; + int h = screen_height; + unsigned char *pixel_data = (unsigned char *)mem_alloc(w*(h+1)*3, 1); + unsigned char *temp_row = pixel_data+w*h*3; + glReadPixels(0,0, w, h, GL_RGB, GL_UNSIGNED_BYTE, pixel_data); + + // flip the pixel because opengl works from bottom left corner + int y; + for(y = 0; y < h/2; y++) + { + mem_copy(temp_row, pixel_data+y*w*3, w*3); + mem_copy(pixel_data+y*w*3, pixel_data+(h-y-1)*w*3, w*3); + mem_copy(pixel_data+(h-y-1)*w*3, temp_row,w*3); + } + + // find filename + char filename[64]; + { + static int index = 1; + for(; index < 1000; index++) + { + sprintf(filename, "screenshot%04d.png", index); + IOHANDLE io = io_open(filename, IOFLAG_READ); + if(io) + io_close(io); + else + break; + } + } + + // save png + png_t png; + png_open_file_write(&png, filename); + png_set_data(&png, w, h, 8, PNG_TRUECOLOR, (unsigned char *)pixel_data); + png_close_file(&png); + + // clean up + mem_free(pixel_data); + do_screenshot = 0; + } + + glfwSwapBuffers(); + glfwPollEvents(); +} + +int gfx_screenwidth() +{ + return screen_width; +} + +int gfx_screenheight() +{ + return screen_height; +} + +void gfx_texture_set(int slot) +{ + dbg_assert(quads_drawing == 0, "called gfx_texture_set within quads_begin"); + if(slot == -1) + glDisable(GL_TEXTURE_2D); + else + { + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, textures[slot].tex); + } +} + +void gfx_clear(float r, float g, float b) +{ + glClearColor(r,g,b,1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +void gfx_mapscreen(float tl_x, float tl_y, float br_x, float br_y) +{ + screen_x0 = tl_x; + screen_y0 = tl_y; + screen_x1 = br_x; + screen_y1 = br_y; + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(tl_x, br_x, br_y, tl_y, 1.0f, 10.f); +} + +void gfx_getscreen(float *tl_x, float *tl_y, float *br_x, float *br_y) +{ + *tl_x = screen_x0; + *tl_y = screen_y0; + *br_x = screen_x1; + *br_y = screen_y1; +} + +void gfx_setoffset(float x, float y) +{ + //const float scale = 1.0f; + //mat4 mat = mat4::identity; + //mat.m[0] = scale; + //mat.m[5] = scale; + //mat.m[10] = scale; + //mat.m[12] = x*scale; + //mat.m[13] = y*scale; + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(x, y, 0); +} + + +void gfx_quads_begin() +{ + dbg_assert(quads_drawing == 0, "called quads_begin twice"); + quads_drawing++; + + gfx_quads_setsubset(0,0,1,1); + gfx_quads_setrotation(0); + gfx_quads_setcolor(1,1,1,1); +} + +void gfx_quads_end() +{ + dbg_assert(quads_drawing == 1, "called quads_end without quads_begin"); + draw_quad(1); + quads_drawing--; +} + + +void gfx_quads_setrotation(float angle) +{ + dbg_assert(quads_drawing == 1, "called gfx_quads_setrotation without quads_begin"); + rotation = angle; +} + +void gfx_quads_setcolorvertex(int i, float r, float g, float b, float a) +{ + dbg_assert(quads_drawing == 1, "called gfx_quads_setcolorvertex without quads_begin"); + color[i].r = r; + color[i].g = g; + color[i].b = b; + color[i].a = a; +} + +void gfx_quads_setcolor(float r, float g, float b, float a) +{ + dbg_assert(quads_drawing == 1, "called gfx_quads_setcolor without quads_begin"); + gfx_quads_setcolorvertex(0, r, g, b, a); + gfx_quads_setcolorvertex(1, r, g, b, a); + gfx_quads_setcolorvertex(2, r, g, b, a); + gfx_quads_setcolorvertex(3, r, g, b, a); +} + +void gfx_quads_setsubset(float tl_u, float tl_v, float br_u, float br_v) +{ + dbg_assert(quads_drawing == 1, "called gfx_quads_setsubset without quads_begin"); + + texture[0].u = tl_u; + texture[0].v = tl_v; + //g_pVertices[g_iVertexEnd].tex.u = tl_u; + //g_pVertices[g_iVertexEnd].tex.v = tl_v; + + texture[1].u = br_u; + texture[1].v = tl_v; + //g_pVertices[g_iVertexEnd + 2].tex.u = br_u; + //g_pVertices[g_iVertexEnd + 2].tex.v = tl_v; + + texture[2].u = br_u; + texture[2].v = br_v; + //g_pVertices[g_iVertexEnd + 1].tex.u = tl_u; + //g_pVertices[g_iVertexEnd + 1].tex.v = br_v; + + texture[3].u = tl_u; + texture[3].v = br_v; + //g_pVertices[g_iVertexEnd + 3].tex.u = br_u; + //g_pVertices[g_iVertexEnd + 3].tex.v = br_v; +} + +static void rotate(VEC3 *center, VEC3 *point) +{ + float x = point->x - center->x; + float y = point->y - center->y; + point->x = x * cosf(rotation) - y * sinf(rotation) + center->x; + point->y = x * sinf(rotation) + y * cosf(rotation) + center->y; +} + +void gfx_quads_draw(float x, float y, float w, float h) +{ + gfx_quads_drawTL(x-w/2, y-h/2,w,h); +} + +void gfx_quads_drawTL(float x, float y, float width, float height) +{ + dbg_assert(quads_drawing == 1, "called quads_draw without quads_begin"); + + VEC3 center; + center.x = x + width/2; + center.y = y + height/2; + center.z = 0; + + vertices[num_vertices].pos.x = x; + vertices[num_vertices].pos.y = y; + vertices[num_vertices].tex = texture[0]; + vertices[num_vertices].color = color[0]; + rotate(¢er, &vertices[num_vertices].pos); + + vertices[num_vertices + 1].pos.x = x+width; + vertices[num_vertices + 1].pos.y = y; + vertices[num_vertices + 1].tex = texture[1]; + vertices[num_vertices + 1].color = color[1]; + rotate(¢er, &vertices[num_vertices + 1].pos); + + vertices[num_vertices + 2].pos.x = x + width; + vertices[num_vertices + 2].pos.y = y+height; + vertices[num_vertices + 2].tex = texture[2]; + vertices[num_vertices + 2].color = color[2]; + rotate(¢er, &vertices[num_vertices + 2].pos); + + vertices[num_vertices + 3].pos.x = x; + vertices[num_vertices + 3].pos.y = y+height; + vertices[num_vertices + 3].tex = texture[3]; + vertices[num_vertices + 3].color = color[3]; + rotate(¢er, &vertices[num_vertices + 3].pos); + + draw_quad(0); +} + +void gfx_quads_draw_freeform( + float x0, float y0, + float x1, float y1, + float x2, float y2, + float x3, float y3) +{ + dbg_assert(quads_drawing == 1, "called quads_draw_freeform without quads_begin"); + + vertices[num_vertices].pos.x = x0; + vertices[num_vertices].pos.y = y0; + vertices[num_vertices].tex = texture[0]; + vertices[num_vertices].color = color[0]; + + vertices[num_vertices + 1].pos.x = x1; + vertices[num_vertices + 1].pos.y = y1; + vertices[num_vertices + 1].tex = texture[1]; + vertices[num_vertices + 1].color = color[1]; + + vertices[num_vertices + 2].pos.x = x3; + vertices[num_vertices + 2].pos.y = y3; + vertices[num_vertices + 2].tex = texture[2]; + vertices[num_vertices + 2].color = color[2]; + + vertices[num_vertices + 3].pos.x = x2; + vertices[num_vertices + 3].pos.y = y2; + vertices[num_vertices + 3].tex = texture[3]; + vertices[num_vertices + 3].color = color[3]; + + draw_quad(0); +} + +void gfx_quads_text(float x, float y, float size, const char *text) +{ + gfx_quads_begin(); + float startx = x; + while(*text) + { + char c = *text; + text++; + + if(c == '\n') + { + x = startx; + y += size; + } + else + { + gfx_quads_setsubset( + (c%16)/16.0f, + (c/16)/16.0f, + (c%16)/16.0f+1.0f/16.0f, + (c/16)/16.0f+1.0f/16.0f); + + gfx_quads_drawTL(x,y,size,size); + x += size/2; + } + } + + gfx_quads_end(); +} + +typedef struct +{ + float m_CharStartTable[256]; + float m_CharEndTable[256]; + int font_texture; +} pretty_font; + +pretty_font default_font = +{ +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0.421875, 0.359375, 0.265625, 0.25, 0.1875, 0.25, 0.4375, 0.390625, 0.390625, 0.34375, 0.28125, 0.421875, 0.390625, 0.4375, 0.203125, + 0.265625, 0.28125, 0.28125, 0.265625, 0.25, 0.28125, 0.28125, 0.265625, 0.28125, 0.265625, 0.4375, 0.421875, 0.3125, 0.28125, 0.3125, 0.3125, + 0.25, 0.234375, 0.28125, 0.265625, 0.265625, 0.296875, 0.3125, 0.25, 0.25, 0.421875, 0.28125, 0.265625, 0.328125, 0.171875, 0.234375, 0.25, + 0.28125, 0.234375, 0.265625, 0.265625, 0.28125, 0.265625, 0.234375, 0.09375, 0.234375, 0.234375, 0.265625, 0.390625, 0.203125, 0.390625, 0.296875, 0.28125, + 0.375, 0.3125, 0.3125, 0.3125, 0.296875, 0.3125, 0.359375, 0.296875, 0.3125, 0.4375, 0.390625, 0.328125, 0.4375, 0.203125, 0.3125, 0.296875, + 0.3125, 0.296875, 0.359375, 0.3125, 0.328125, 0.3125, 0.296875, 0.203125, 0.296875, 0.296875, 0.328125, 0.375, 0.421875, 0.375, 0.28125, 0.3125, + 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, + 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, + 0, 0.421875, 0.3125, 0.265625, 0.25, 0.25, 0.421875, 0.265625, 0.375, 0.21875, 0.375, 0.328125, 0.3125, 0, 0.21875, 0.28125, + 0.359375, 0.28125, 0.34375, 0.34375, 0.421875, 0.3125, 0.265625, 0.421875, 0.421875, 0.34375, 0.375, 0.328125, 0.125, 0.125, 0.125, 0.296875, + 0.234375, 0.234375, 0.234375, 0.234375, 0.234375, 0.234375, 0.109375, 0.265625, 0.296875, 0.296875, 0.296875, 0.296875, 0.375, 0.421875, 0.359375, 0.390625, + 0.21875, 0.234375, 0.25, 0.25, 0.25, 0.25, 0.25, 0.296875, 0.21875, 0.265625, 0.265625, 0.265625, 0.265625, 0.234375, 0.28125, 0.3125, + 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.1875, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.375, 0.421875, 0.359375, 0.390625, + 0.3125, 0.3125, 0.296875, 0.296875, 0.296875, 0.296875, 0.296875, 0.28125, 0.28125, 0.3125, 0.3125, 0.3125, 0.3125, 0.296875, 0.3125, 0.296875, +}, +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0.2, 0.5625, 0.625, 0.71875, 0.734375, 0.796875, 0.765625, 0.546875, 0.59375, 0.59375, 0.65625, 0.703125, 0.546875, 0.59375, 0.5625, 0.6875, + 0.71875, 0.609375, 0.703125, 0.703125, 0.71875, 0.703125, 0.703125, 0.6875, 0.703125, 0.703125, 0.5625, 0.546875, 0.671875, 0.703125, 0.671875, 0.671875, + 0.734375, 0.75, 0.734375, 0.734375, 0.734375, 0.6875, 0.6875, 0.734375, 0.71875, 0.5625, 0.65625, 0.765625, 0.703125, 0.8125, 0.75, 0.734375, + 0.734375, 0.765625, 0.71875, 0.71875, 0.703125, 0.71875, 0.75, 0.890625, 0.75, 0.75, 0.71875, 0.59375, 0.6875, 0.59375, 0.6875, 0.703125, + 0.5625, 0.671875, 0.6875, 0.671875, 0.671875, 0.671875, 0.625, 0.671875, 0.671875, 0.5625, 0.546875, 0.703125, 0.5625, 0.78125, 0.671875, 0.671875, + 0.6875, 0.671875, 0.65625, 0.671875, 0.65625, 0.671875, 0.6875, 0.78125, 0.6875, 0.671875, 0.65625, 0.609375, 0.546875, 0.609375, 0.703125, 0.671875, + 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, + 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, + 0, 0.5625, 0.671875, 0.734375, 0.734375, 0.734375, 0.546875, 0.71875, 0.609375, 0.765625, 0.609375, 0.65625, 0.671875, 0, 0.765625, 0.703125, + 0.625, 0.703125, 0.640625, 0.640625, 0.609375, 0.671875, 0.703125, 0.546875, 0.5625, 0.578125, 0.609375, 0.65625, 0.859375, 0.859375, 0.859375, 0.671875, + 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.84375, 0.734375, 0.6875, 0.6875, 0.6875, 0.6875, 0.5625, 0.609375, 0.640625, 0.59375, + 0.734375, 0.75, 0.734375, 0.734375, 0.734375, 0.734375, 0.734375, 0.6875, 0.765625, 0.71875, 0.71875, 0.71875, 0.71875, 0.75, 0.734375, 0.6875, + 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.796875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.5625, 0.609375, 0.625, 0.59375, + 0.6875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.703125, 0.703125, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.6875, 0.671875, +}, +0 +}; + +double extra_kerning[256*256] = {0}; + +pretty_font *current_font = &default_font; + +static int word_length(const char *text) +{ + int s = 1; + while(1) + { + if(*text == 0) + return s-1; + if(*text == '\n' || *text == '\t' || *text == ' ') + return s; + text++; + s++; + } +} + +float gfx_pretty_text_raw(float x, float y, float size, const char *text_, int length) +{ + const unsigned char *text = (unsigned char *)text_; + const float spacing = 0.05f; + gfx_texture_set(current_font->font_texture); + gfx_quads_begin(); + + if(length < 0) + length = strlen(text_); + + while(length) + { + const int c = *text; + text++; + + const float width = current_font->m_CharEndTable[c] - current_font->m_CharStartTable[c]; + + x -= size * current_font->m_CharStartTable[c]; + + gfx_quads_setsubset( + (c%16)/16.0f, // startx + (c/16)/16.0f, // starty + (c%16)/16.0f+1.0f/16.0f, // endx + (c/16)/16.0f+1.0f/16.0f); // endy + + gfx_quads_drawTL(x, y, size, size); + + double x_nudge = 0; + if(length > 1 && text[1]) + x_nudge = extra_kerning[text[0] + text[1] * 256]; + + x += (width + current_font->m_CharStartTable[c] + spacing + x_nudge) * size; + length--; + } + + gfx_quads_end(); + + return x; +} + +void gfx_pretty_text(float x, float y, float size, const char *text, int max_width) +{ + if(max_width == -1) + gfx_pretty_text_raw(x, y, size, text, -1); + else + { + float startx = x; + while(*text) + { + int wlen = word_length(text); + float w = gfx_pretty_text_width(size, text, wlen); + if(x+w-startx > max_width) + { + y += size-2; + x = startx; + } + + x = gfx_pretty_text_raw(x, y, size, text, wlen); + + text += wlen; + } + } +} + +float gfx_pretty_text_width(float size, const char *text_, int length) +{ + const float spacing = 0.05f; + float w = 0.0f; + const unsigned char *text = (unsigned char *)text_; + + const unsigned char *stop; + if (length == -1) + stop = text + strlen((char*)text); + else + stop = text + length; + + while (text < stop) + { + const int c = *text; + const float width = current_font->m_CharEndTable[c] - current_font->m_CharStartTable[c]; + + double x_nudge = 0; + if (text[1]) + x_nudge = extra_kerning[text[0] + text[1] * 256]; + + w += (width + spacing + x_nudge) * size; + + text++; + } + + return w; +} diff --git a/src/engine/client/gfx.cpp b/src/engine/client/gfx.cpp deleted file mode 100644 index 6a096139..00000000 --- a/src/engine/client/gfx.cpp +++ /dev/null @@ -1,910 +0,0 @@ -#include -#include -#include -#include - -#include - -#include "pnglite/pnglite.h" - -#include -#include - -#include - - -using namespace baselib; - -static opengl::context context; - -struct custom_vertex -{ - vec3 pos; - vec2 tex; - vec4 color; -}; - -const int vertex_buffer_size = 32*1024; -static custom_vertex *vertices = 0; -static int num_vertices = 0; -static vec4 color[4]; -static vec2 texture[4]; - -static int do_screenshot = 0; - -static opengl::vertex_buffer vertex_buffer; -static int screen_width = -1; -static int screen_height = -1; -static float rotation = 0; -static int quads_drawing = 0; - -static float screen_x0 = 0; -static float screen_y0 = 0; -static float screen_x1 = 0; -static float screen_y1 = 0; - -struct texture_holder -{ - opengl::texture tex; - int memsize; - int flags; - int next; -}; - -static const int MAX_TEXTURES = 128; - -static texture_holder textures[MAX_TEXTURES]; -static int first_free_texture; -static int memory_usage = 0; - -static const unsigned char null_texture_data[] = { - 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, - 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, - 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, - 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, -}; - -static void draw_quad(bool _bflush = false) -{ - if (!_bflush && ((num_vertices + 4) < vertex_buffer_size)) - { - // Just add - num_vertices += 4; - } - else if (num_vertices) - { - if (!_bflush) - num_vertices += 4; - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - /* - if(GLEW_ARB_vertex_buffer_object) - { - // set the data - vertex_buffer.data(vertices, num_vertices * sizeof(custom_vertex), GL_DYNAMIC_DRAW); - opengl::stream_vertex(&vertex_buffer, 3, GL_FLOAT, sizeof(custom_vertex), 0); - opengl::stream_texcoord(&vertex_buffer, 0, 2, GL_FLOAT, - sizeof(custom_vertex), - sizeof(vec3)); - opengl::stream_color(&vertex_buffer, 4, GL_FLOAT, - sizeof(custom_vertex), - sizeof(vec3)+sizeof(vec2)); - - //glDrawElements(GL_TRIANGLES, num_vertices, GL_UNSIGNED_SHORT, indecies); - opengl::draw_arrays(GL_QUADS, 0, num_vertices); - } - else - {*/ - glVertexPointer(3, GL_FLOAT, - sizeof(custom_vertex), - (char*)vertices); - glTexCoordPointer(2, GL_FLOAT, - sizeof(custom_vertex), - (char*)vertices + sizeof(vec3)); - glColorPointer(4, GL_FLOAT, - sizeof(custom_vertex), - (char*)vertices + sizeof(vec3) + sizeof(vec2)); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - glDrawArrays(GL_QUADS, 0, num_vertices); - - // Reset pointer - num_vertices = 0; - } -} - -bool gfx_init() -{ - screen_width = config.gfx_screen_width; - screen_height = config.gfx_screen_height; - - if(config.stress) - { - screen_width = 320; - screen_height = 240; - } - - if(config.gfx_fullscreen) - { - if(!context.create(screen_width, screen_height, 24, 0, 0, 0, opengl::context::FLAG_FULLSCREEN)) - { - dbg_msg("game", "failed to create gl context"); - return false; - } - } - else - { - if(!context.create(screen_width, screen_height, 0, 0, 0, 0, 0)) - { - dbg_msg("game", "failed to create gl context"); - return false; - } - } - - context.set_title("Teewars"); - - // We don't want to see the window when we run the stress testing - if(config.stress) - context.iconify(); - - // Init vertices - if (vertices) - mem_free(vertices); - vertices = (custom_vertex*)mem_alloc(sizeof(custom_vertex) * vertex_buffer_size, 1); - num_vertices = 0; - - - /* - dbg_msg("gfx", "OpenGL version %d.%d.%d", context.version_major(), - context.version_minor(), - context.version_rev());*/ - - gfx_mapscreen(0,0,config.gfx_screen_width, config.gfx_screen_height); - - // TODO: make wrappers for this - glEnable(GL_BLEND); - - // model - mat4 mat = mat4::identity; - opengl::matrix_modelview(&mat); - - // Set all z to -5.0f - for (int i = 0; i < vertex_buffer_size; i++) - vertices[i].pos.z = -5.0f; - -/* - if(GLEW_ARB_vertex_buffer_object) - { - // set the streams - vertex_buffer.data(vertices, sizeof(vertex_buffer_size), GL_DYNAMIC_DRAW); - opengl::stream_vertex(&vertex_buffer, 3, GL_FLOAT, sizeof(custom_vertex), 0); - opengl::stream_texcoord(&vertex_buffer, 0, 2, GL_FLOAT, - sizeof(custom_vertex), - sizeof(vec3)); - opengl::stream_color(&vertex_buffer, 4, GL_FLOAT, - sizeof(custom_vertex), - sizeof(vec3)+sizeof(vec2)); - }*/ - - // init textures - first_free_texture = 0; - for(int i = 0; i < MAX_TEXTURES; i++) - textures[i].next = i+1; - textures[MAX_TEXTURES-1].next = -1; - - // init indecies - /* - for(int i = 0; i < vertex_buffer_size; i++) - { - indecies[i*6 + 0] = i+0; - indecies[i*6 + 1] = i+1; - indecies[i*6 + 2] = i+2; - - indecies[i*6 + 3] = i+1; - indecies[i*6 + 4] = i+3; - indecies[i*6 + 5] = i+2; - }*/ - - //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - // create null texture, will get id=0 - gfx_load_texture_raw(4,4,IMG_RGBA,null_texture_data); - - // set vsync as needed - gfx_set_vsync(config.gfx_vsync); - - return true; -} - - -int gfx_window_active() -{ - return context.active()?1:0; -} - - -video_mode fakemodes[] = { - {320,240,8,8,8}, {400,300,8,8,8}, {640,480,8,8,8}, - {720,400,8,8,8}, {768,576,8,8,8}, {800,600,8,8,8}, - {1024,600,8,8,8}, {1024,768,8,8,8}, {1152,864,8,8,8}, - {1280,768,8,8,8}, {1280,800,8,8,8}, {1280,960,8,8,8}, - {1280,1024,8,8,8}, {1368,768,8,8,8}, {1400,1050,8,8,8}, - {1440,900,8,8,8}, {1440,1050,8,8,8}, {1600,1000,8,8,8}, - {1600,1200,8,8,8}, {1680,1050,8,8,8}, {1792,1344,8,8,8}, - {1800,1440,8,8,8}, {1856,1392,8,8,8}, {1920,1080,8,8,8}, - {1920,1200,8,8,8}, {1920,1440,8,8,8}, {1920,2400,8,8,8}, - {2048,1536,8,8,8}, - - {320,240,5,6,5}, {400,300,5,6,5}, {640,480,5,6,5}, - {720,400,5,6,5}, {768,576,5,6,5}, {800,600,5,6,5}, - {1024,600,5,6,5}, {1024,768,5,6,5}, {1152,864,5,6,5}, - {1280,768,5,6,5}, {1280,800,5,6,5}, {1280,960,5,6,5}, - {1280,1024,5,6,5}, {1368,768,5,6,5}, {1400,1050,5,6,5}, - {1440,900,5,6,5}, {1440,1050,5,6,5}, {1600,1000,5,6,5}, - {1600,1200,5,6,5}, {1680,1050,5,6,5}, {1792,1344,5,6,5}, - {1800,1440,5,6,5}, {1856,1392,5,6,5}, {1920,1080,5,6,5}, - {1920,1200,5,6,5}, {1920,1440,5,6,5}, {1920,2400,5,6,5}, - {2048,1536,5,6,5} -}; - -int gfx_get_video_modes(video_mode *list, int maxcount) -{ - if(config.gfx_display_all_modes) - { - mem_copy(list, fakemodes, sizeof(fakemodes)); - return min((int)(sizeof(fakemodes)/sizeof(video_mode)), maxcount); - } - - return context.getvideomodes((opengl::videomode *)list, maxcount); -} - -void gfx_set_vsync(int val) -{ - context.set_vsync(val); -} - -int gfx_unload_texture(int index) -{ - textures[index].tex.clear(); - textures[index].next = first_free_texture; - memory_usage -= textures[index].memsize; - first_free_texture = index; - return 0; -} - -void gfx_blend_normal() -{ - // TODO: wrapper for this - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); -} - -void gfx_blend_additive() -{ - // TODO: wrapper for this - glBlendFunc(GL_SRC_ALPHA, GL_ONE); -} - -int gfx_memory_usage() { return memory_usage; } - -static unsigned char sample(int w, int h, const unsigned char *data, int u, int v, int offset) -{ - return (data[(v*w+u)*4+offset]+ - data[(v*w+u+1)*4+offset]+ - data[((v+1)*w+u)*4+offset]+ - data[((v+1)*w+u+1)*4+offset])/4; -} - -int gfx_load_texture_raw(int w, int h, int format, const void *data) -{ - bool mipmap = true; - - // grab texture - int tex = first_free_texture; - first_free_texture = textures[tex].next; - textures[tex].next = -1; - - // resample if needed - unsigned char *texdata = (unsigned char *)data; - unsigned char *tmpdata = 0; - if(config.gfx_texture_quality==0) - { - if(w > 16 && h > 16 && format == IMG_RGBA) - { - w/=2; - h/=2; - unsigned char *tmpdata = (unsigned char *)mem_alloc(w*h*4, 1); - int c = 0; - for(int y = 0; y < h; y++) - for(int x = 0; x < w; x++, c++) - { - tmpdata[c*4] = sample(w*2, h*2, texdata, x*2,y*2, 0); - tmpdata[c*4+1] = sample(w*2, h*2, texdata, x*2,y*2, 1); - tmpdata[c*4+2] = sample(w*2, h*2, texdata, x*2,y*2, 2); - tmpdata[c*4+3] = sample(w*2, h*2, texdata, x*2,y*2, 3); - } - texdata = tmpdata; - } - } - - if(config.debug) - dbg_msg("gfx", "%d = %dx%d", tex, w, h); - - // set data and return - if(config.gfx_texture_compression && GLEW_ARB_texture_compression) - { - if(format == IMG_RGB) - textures[tex].tex.data2d(w, h, GL_COMPRESSED_RGB_ARB, GL_RGB, GL_UNSIGNED_BYTE, texdata); - else if(format == IMG_RGBA) - textures[tex].tex.data2d(w, h, GL_COMPRESSED_RGBA_ARB, GL_RGBA, GL_UNSIGNED_BYTE, texdata); - } - else - { - if(format == IMG_RGB) - textures[tex].tex.data2d(w, h, GL_RGB, GL_RGB, GL_UNSIGNED_BYTE, texdata); - else if(format == IMG_RGBA) - textures[tex].tex.data2d(w, h, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, texdata); - } - - textures[tex].memsize = w*h*4; - if(mipmap) - { - while(w > 2 && h > 2) - { - w>>=1; - h>>=1; - textures[tex].memsize += w*h*4; - } - } - - memory_usage += textures[tex].memsize; - - mem_free(tmpdata); - - return tex; -} -/* -int gfx_load_mip_texture_raw(int w, int h, int format, const void *data) -{ - // grab texture - int tex = first_free_texture; - first_free_texture = textures[tex].next; - textures[tex].next = -1; - - // set data and return - // TODO: should be RGBA, not BGRA - dbg_msg("gfx", "%d = %dx%d", tex, w, h); - dbg_assert(format == IMG_RGBA, "not an RGBA image"); - - unsigned mip_w = w; - unsigned mip_h = h; - unsigned level = 0; - const unsigned char *ptr = (const unsigned char*)data; - while(mip_w > 0 && mip_h > 0) - { - dbg_msg("gfx mip", "%d = %dx%d", level, mip_w, mip_h); - textures[tex].tex.data2d_mip(mip_w, mip_h, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, level, ptr); - level++; - ptr = ptr + mip_w*mip_h*4; - mip_w = mip_w>>1; - mip_h = mip_h>>1; - } - return tex; -} -*/ -// simple uncompressed RGBA loaders -int gfx_load_texture(const char *filename) -{ - int l = strlen(filename); - if(l < 3) - return 0; - image_info img; - if(gfx_load_png(&img, filename)) - { - int id = gfx_load_texture_raw(img.width, img.height, img.format, img.data); - mem_free(img.data); - return id; - } - - return 0; -} - -int gfx_load_png(image_info *img, const char *filename) -{ - // open file for reading - png_init(0,0); - - png_t png; - if(png_open_file(&png, filename) != PNG_NO_ERROR) - { - dbg_msg("game/png", "failed to open file. filename='%s'", filename); - return 0; - } - - if(png.depth != 8 || (png.color_type != PNG_TRUECOLOR && png.color_type != PNG_TRUECOLOR_ALPHA)) - { - dbg_msg("game/png", "invalid format. filename='%s'", filename); - png_close_file(&png); - } - - unsigned char *buffer = (unsigned char *)mem_alloc(png.width * png.height * png.bpp, 1); - png_get_data(&png, buffer); - png_close_file(&png); - - img->width = png.width; - img->height = png.height; - if(png.color_type == PNG_TRUECOLOR) - img->format = IMG_RGB; - else if(png.color_type == PNG_TRUECOLOR_ALPHA) - img->format = IMG_RGBA; - img->data = buffer; - return 1; -} - -void gfx_shutdown() -{ - if (vertices) - mem_free(vertices); - context.destroy(); -} - -void gfx_screenshot() -{ - do_screenshot = 1; -} - -void gfx_swap() -{ - if(do_screenshot) - { - // fetch image data - int w = screen_width; - int h = screen_height; - unsigned char *pixel_data = (unsigned char *)mem_alloc(w*(h+1)*3, 1); - unsigned char *temp_row = pixel_data+w*h*3; - glReadPixels(0,0, w, h, GL_RGB, GL_UNSIGNED_BYTE, pixel_data); - - // flip the pixel because opengl works from bottom left corner - for(int y = 0; y < h/2; y++) - { - mem_copy(temp_row, pixel_data+y*w*3, w*3); - mem_copy(pixel_data+y*w*3, pixel_data+(h-y-1)*w*3, w*3); - mem_copy(pixel_data+(h-y-1)*w*3, temp_row,w*3); - } - - // find filename - char filename[64]; - { - static int index = 1; - for(; index < 1000; index++) - { - sprintf(filename, "screenshot%04d.png", index); - IOHANDLE io = io_open(filename, IOFLAG_READ); - if(io) - io_close(io); - else - break; - } - } - - // save png - png_t png; - png_open_file_write(&png, filename); - png_set_data(&png, w, h, 8, PNG_TRUECOLOR, (unsigned char *)pixel_data); - png_close_file(&png); - - // clean up - mem_free(pixel_data); - do_screenshot = 0; - } - - context.swap(); -} - -int gfx_screenwidth() -{ - return screen_width; -} - -int gfx_screenheight() -{ - return screen_height; -} - -void gfx_texture_set(int slot) -{ - dbg_assert(quads_drawing == 0, "called gfx_texture_set within quads_begin"); - if(slot == -1) - opengl::texture_disable(0); - else - opengl::texture_2d(0, &textures[slot].tex); -} - -void gfx_clear(float r, float g, float b) -{ - glClearColor(r,g,b,1.0f); - opengl::clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); -} - -void gfx_mapscreen(float tl_x, float tl_y, float br_x, float br_y) -{ - screen_x0 = tl_x; - screen_y0 = tl_y; - screen_x1 = br_x; - screen_y1 = br_y; - mat4 mat; - mat.ortho(tl_x, br_x, br_y, tl_y, 1.0f, 10.f); - opengl::matrix_projection(&mat); -} - -void gfx_getscreen(float *tl_x, float *tl_y, float *br_x, float *br_y) -{ - *tl_x = screen_x0; - *tl_y = screen_y0; - *br_x = screen_x1; - *br_y = screen_y1; -} - -void gfx_setoffset(float x, float y) -{ - //const float scale = 0.75f; - const float scale = 1.0f; - mat4 mat = mat4::identity; - mat.m[0] = scale; - mat.m[5] = scale; - mat.m[10] = scale; - mat.m[12] = x*scale; - mat.m[13] = y*scale; - opengl::matrix_modelview(&mat); -} - - - - -void gfx_quads_begin() -{ - dbg_assert(quads_drawing == 0, "called quads_begin twice"); - quads_drawing++; - - gfx_quads_setsubset(0,0,1,1); - gfx_quads_setrotation(0); - gfx_quads_setcolor(1,1,1,1); -} - -void gfx_quads_end() -{ - dbg_assert(quads_drawing == 1, "called quads_end without quads_begin"); - draw_quad(true); - quads_drawing--; -} - - -void gfx_quads_setrotation(float angle) -{ - dbg_assert(quads_drawing == 1, "called gfx_quads_setrotation without quads_begin"); - rotation = angle; -} - -void gfx_quads_setcolorvertex(int i, float r, float g, float b, float a) -{ - dbg_assert(quads_drawing == 1, "called gfx_quads_setcolorvertex without quads_begin"); - color[i].r = r; - color[i].g = g; - color[i].b = b; - color[i].a = a; -} - -void gfx_quads_setcolor(float r, float g, float b, float a) -{ - dbg_assert(quads_drawing == 1, "called gfx_quads_setcolor without quads_begin"); - color[0] = vec4(r,g,b,a); - color[1] = vec4(r,g,b,a); - color[2] = vec4(r,g,b,a); - color[3] = vec4(r,g,b,a); -} - -void gfx_quads_setsubset(float tl_u, float tl_v, float br_u, float br_v) -{ - dbg_assert(quads_drawing == 1, "called gfx_quads_setsubset without quads_begin"); - - texture[0].x = tl_u; - texture[0].y = tl_v; - //g_pVertices[g_iVertexEnd].tex.u = tl_u; - //g_pVertices[g_iVertexEnd].tex.v = tl_v; - - texture[1].x = br_u; - texture[1].y = tl_v; - //g_pVertices[g_iVertexEnd + 2].tex.u = br_u; - //g_pVertices[g_iVertexEnd + 2].tex.v = tl_v; - - texture[2].x = br_u; - texture[2].y = br_v; - //g_pVertices[g_iVertexEnd + 1].tex.u = tl_u; - //g_pVertices[g_iVertexEnd + 1].tex.v = br_v; - - texture[3].x = tl_u; - texture[3].y = br_v; - //g_pVertices[g_iVertexEnd + 3].tex.u = br_u; - //g_pVertices[g_iVertexEnd + 3].tex.v = br_v; -} - -static void rotate(vec3 ¢er, vec3 &point) -{ - vec3 p = point-center; - point.x = p.x * cosf(rotation) - p.y * sinf(rotation) + center.x; - point.y = p.x * sinf(rotation) + p.y * cosf(rotation) + center.y; -} - -void gfx_quads_draw(float x, float y, float w, float h) -{ - gfx_quads_drawTL(x-w/2, y-h/2,w,h); -} - -void gfx_quads_drawTL(float x, float y, float width, float height) -{ - dbg_assert(quads_drawing == 1, "called quads_draw without quads_begin"); - - vec3 center; - center.x = x + width/2; - center.y = y + height/2; - center.z = 0; - - vertices[num_vertices].pos.x = x; - vertices[num_vertices].pos.y = y; - vertices[num_vertices].tex.u = texture[0].x; - vertices[num_vertices].tex.v = texture[0].y; - vertices[num_vertices].color = color[0]; - rotate(center, vertices[num_vertices].pos); - - vertices[num_vertices + 1].pos.x = x+width; - vertices[num_vertices + 1].pos.y = y; - vertices[num_vertices + 1].tex.u = texture[1].x; - vertices[num_vertices + 1].tex.v = texture[1].y; - vertices[num_vertices + 1].color = color[1]; - rotate(center, vertices[num_vertices + 1].pos); - - vertices[num_vertices + 2].pos.x = x + width; - vertices[num_vertices + 2].pos.y = y+height; - vertices[num_vertices + 2].tex.u = texture[2].x; - vertices[num_vertices + 2].tex.v = texture[2].y; - vertices[num_vertices + 2].color = color[2]; - rotate(center, vertices[num_vertices + 2].pos); - - vertices[num_vertices + 3].pos.x = x; - vertices[num_vertices + 3].pos.y = y+height; - vertices[num_vertices + 3].tex.u = texture[3].x; - vertices[num_vertices + 3].tex.v = texture[3].y; - vertices[num_vertices + 3].color = color[3]; - rotate(center, vertices[num_vertices + 3].pos); - - draw_quad(); -} - -void gfx_quads_draw_freeform( - float x0, float y0, - float x1, float y1, - float x2, float y2, - float x3, float y3) -{ - dbg_assert(quads_drawing == 1, "called quads_draw_freeform without quads_begin"); - - vertices[num_vertices].pos.x = x0; - vertices[num_vertices].pos.y = y0; - vertices[num_vertices].tex.u = texture[0].x; - vertices[num_vertices].tex.v = texture[0].y; - vertices[num_vertices].color = color[0]; - - vertices[num_vertices + 1].pos.x = x1; - vertices[num_vertices + 1].pos.y = y1; - vertices[num_vertices + 1].tex.u = texture[1].x; - vertices[num_vertices + 1].tex.v = texture[1].y; - vertices[num_vertices + 1].color = color[1]; - - vertices[num_vertices + 2].pos.x = x3; - vertices[num_vertices + 2].pos.y = y3; - vertices[num_vertices + 2].tex.u = texture[2].x; - vertices[num_vertices + 2].tex.v = texture[2].y; - vertices[num_vertices + 2].color = color[2]; - - vertices[num_vertices + 3].pos.x = x2; - vertices[num_vertices + 3].pos.y = y2; - vertices[num_vertices + 3].tex.u = texture[3].x; - vertices[num_vertices + 3].tex.v = texture[3].y; - vertices[num_vertices + 3].color = color[3]; - - draw_quad(); -} - -void gfx_quads_text(float x, float y, float size, const char *text) -{ - gfx_quads_begin(); - float startx = x; - while(*text) - { - char c = *text; - text++; - - if(c == '\n') - { - x = startx; - y += size; - } - else - { - gfx_quads_setsubset( - (c%16)/16.0f, - (c/16)/16.0f, - (c%16)/16.0f+1.0f/16.0f, - (c/16)/16.0f+1.0f/16.0f); - - gfx_quads_drawTL(x,y,size,size); - x += size/2; - } - } - - gfx_quads_end(); -} - -struct pretty_font -{ - float m_CharStartTable[256]; - float m_CharEndTable[256]; - int font_texture; -}; - -pretty_font default_font = -{ -{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0.421875, 0.359375, 0.265625, 0.25, 0.1875, 0.25, 0.4375, 0.390625, 0.390625, 0.34375, 0.28125, 0.421875, 0.390625, 0.4375, 0.203125, - 0.265625, 0.28125, 0.28125, 0.265625, 0.25, 0.28125, 0.28125, 0.265625, 0.28125, 0.265625, 0.4375, 0.421875, 0.3125, 0.28125, 0.3125, 0.3125, - 0.25, 0.234375, 0.28125, 0.265625, 0.265625, 0.296875, 0.3125, 0.25, 0.25, 0.421875, 0.28125, 0.265625, 0.328125, 0.171875, 0.234375, 0.25, - 0.28125, 0.234375, 0.265625, 0.265625, 0.28125, 0.265625, 0.234375, 0.09375, 0.234375, 0.234375, 0.265625, 0.390625, 0.203125, 0.390625, 0.296875, 0.28125, - 0.375, 0.3125, 0.3125, 0.3125, 0.296875, 0.3125, 0.359375, 0.296875, 0.3125, 0.4375, 0.390625, 0.328125, 0.4375, 0.203125, 0.3125, 0.296875, - 0.3125, 0.296875, 0.359375, 0.3125, 0.328125, 0.3125, 0.296875, 0.203125, 0.296875, 0.296875, 0.328125, 0.375, 0.421875, 0.375, 0.28125, 0.3125, - 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, - 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, - 0, 0.421875, 0.3125, 0.265625, 0.25, 0.25, 0.421875, 0.265625, 0.375, 0.21875, 0.375, 0.328125, 0.3125, 0, 0.21875, 0.28125, - 0.359375, 0.28125, 0.34375, 0.34375, 0.421875, 0.3125, 0.265625, 0.421875, 0.421875, 0.34375, 0.375, 0.328125, 0.125, 0.125, 0.125, 0.296875, - 0.234375, 0.234375, 0.234375, 0.234375, 0.234375, 0.234375, 0.109375, 0.265625, 0.296875, 0.296875, 0.296875, 0.296875, 0.375, 0.421875, 0.359375, 0.390625, - 0.21875, 0.234375, 0.25, 0.25, 0.25, 0.25, 0.25, 0.296875, 0.21875, 0.265625, 0.265625, 0.265625, 0.265625, 0.234375, 0.28125, 0.3125, - 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.1875, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.375, 0.421875, 0.359375, 0.390625, - 0.3125, 0.3125, 0.296875, 0.296875, 0.296875, 0.296875, 0.296875, 0.28125, 0.28125, 0.3125, 0.3125, 0.3125, 0.3125, 0.296875, 0.3125, 0.296875, -}, -{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0.2, 0.5625, 0.625, 0.71875, 0.734375, 0.796875, 0.765625, 0.546875, 0.59375, 0.59375, 0.65625, 0.703125, 0.546875, 0.59375, 0.5625, 0.6875, - 0.71875, 0.609375, 0.703125, 0.703125, 0.71875, 0.703125, 0.703125, 0.6875, 0.703125, 0.703125, 0.5625, 0.546875, 0.671875, 0.703125, 0.671875, 0.671875, - 0.734375, 0.75, 0.734375, 0.734375, 0.734375, 0.6875, 0.6875, 0.734375, 0.71875, 0.5625, 0.65625, 0.765625, 0.703125, 0.8125, 0.75, 0.734375, - 0.734375, 0.765625, 0.71875, 0.71875, 0.703125, 0.71875, 0.75, 0.890625, 0.75, 0.75, 0.71875, 0.59375, 0.6875, 0.59375, 0.6875, 0.703125, - 0.5625, 0.671875, 0.6875, 0.671875, 0.671875, 0.671875, 0.625, 0.671875, 0.671875, 0.5625, 0.546875, 0.703125, 0.5625, 0.78125, 0.671875, 0.671875, - 0.6875, 0.671875, 0.65625, 0.671875, 0.65625, 0.671875, 0.6875, 0.78125, 0.6875, 0.671875, 0.65625, 0.609375, 0.546875, 0.609375, 0.703125, 0.671875, - 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, - 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, - 0, 0.5625, 0.671875, 0.734375, 0.734375, 0.734375, 0.546875, 0.71875, 0.609375, 0.765625, 0.609375, 0.65625, 0.671875, 0, 0.765625, 0.703125, - 0.625, 0.703125, 0.640625, 0.640625, 0.609375, 0.671875, 0.703125, 0.546875, 0.5625, 0.578125, 0.609375, 0.65625, 0.859375, 0.859375, 0.859375, 0.671875, - 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.84375, 0.734375, 0.6875, 0.6875, 0.6875, 0.6875, 0.5625, 0.609375, 0.640625, 0.59375, - 0.734375, 0.75, 0.734375, 0.734375, 0.734375, 0.734375, 0.734375, 0.6875, 0.765625, 0.71875, 0.71875, 0.71875, 0.71875, 0.75, 0.734375, 0.6875, - 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.796875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.5625, 0.609375, 0.625, 0.59375, - 0.6875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.703125, 0.703125, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.6875, 0.671875, -}, -0 -}; - -double extra_kerning[256*256] = {0}; - -pretty_font *current_font = &default_font; - -static int word_length(const char *text) -{ - int s = 1; - while(1) - { - if(*text == 0) - return s-1; - if(*text == '\n' || *text == '\t' || *text == ' ') - return s; - text++; - s++; - } -} - -float gfx_pretty_text_raw(float x, float y, float size, const char *text_, int length) -{ - const unsigned char *text = (unsigned char *)text_; - const float spacing = 0.05f; - gfx_texture_set(current_font->font_texture); - gfx_quads_begin(); - - if(length < 0) - length = strlen(text_); - - while(length) - { - const int c = *text; - text++; - - const float width = current_font->m_CharEndTable[c] - current_font->m_CharStartTable[c]; - - x -= size * current_font->m_CharStartTable[c]; - - gfx_quads_setsubset( - (c%16)/16.0f, // startx - (c/16)/16.0f, // starty - (c%16)/16.0f+1.0f/16.0f, // endx - (c/16)/16.0f+1.0f/16.0f); // endy - - gfx_quads_drawTL(x, y, size, size); - - double x_nudge = 0; - if(length > 1 && text[1]) - x_nudge = extra_kerning[text[0] + text[1] * 256]; - - x += (width + current_font->m_CharStartTable[c] + spacing + x_nudge) * size; - length--; - } - - gfx_quads_end(); - - return x; -} - -void gfx_pretty_text(float x, float y, float size, const char *text, int max_width) -{ - if(max_width == -1) - gfx_pretty_text_raw(x, y, size, text, -1); - else - { - float startx = x; - while(*text) - { - int wlen = word_length(text); - float w = gfx_pretty_text_width(size, text, wlen); - if(x+w-startx > max_width) - { - y += size-2; - x = startx; - } - - x = gfx_pretty_text_raw(x, y, size, text, wlen); - - text += wlen; - } - } -} - -float gfx_pretty_text_width(float size, const char *text_, int length) -{ - const float spacing = 0.05f; - float w = 0.0f; - const unsigned char *text = (unsigned char *)text_; - - const unsigned char *stop; - if (length == -1) - stop = text + strlen((char*)text); - else - stop = text + length; - - while (text < stop) - { - const int c = *text; - const float width = current_font->m_CharEndTable[c] - current_font->m_CharStartTable[c]; - - double x_nudge = 0; - if (text[1]) - x_nudge = extra_kerning[text[0] + text[1] * 256]; - - w += (width + spacing + x_nudge) * size; - - text++; - } - - return w; -} diff --git a/src/engine/client/inp.c b/src/engine/client/inp.c new file mode 100644 index 00000000..6583c53f --- /dev/null +++ b/src/engine/client/inp.c @@ -0,0 +1,95 @@ +#include + +#include +#include + +static int keyboard_state[2][1024]; // TODO: fix this!! +static int keyboard_current = 0; +static int keyboard_first = 1; + +void inp_mouse_relative(int *x, int *y) +{ + static int last_x = 0, last_y = 0; + int nx, ny; + glfwGetMousePos(&nx, &ny); + *x = nx-last_x; + *y = ny-last_y; + last_x = nx; + last_y = ny; +} + +static char last_c = 0; +static int last_k = 0; + +static void char_callback(int character, int action) +{ + if(action == GLFW_PRESS && character < 256) + last_c = (char)character; +} + +static void key_callback(int key, int action) +{ + if(action == GLFW_PRESS) + last_k = key; +} + +void inp_init() +{ + glfwEnable(GLFW_KEY_REPEAT); + glfwSetCharCallback(char_callback); + glfwSetKeyCallback(key_callback); +} + +char inp_last_char() +{ + return last_c; +} + +int inp_last_key() +{ + return last_k; +} + +void inp_clear() +{ + last_k = 0; + last_c = 0; +} + +void inp_mouse_mode_absolute() +{ + glfwEnable(GLFW_MOUSE_CURSOR); +} + +void inp_mouse_mode_relative() +{ + glfwDisable(GLFW_MOUSE_CURSOR); +} + +//int inp_mouse_scroll() { return input::mouse_scroll(); } +int inp_key_pressed(int key) { return keyboard_state[keyboard_current][key]; } +int inp_key_was_pressed(int key) { return keyboard_state[keyboard_current^1][key]; } +int inp_key_down(int key) { return inp_key_pressed(key)&&!inp_key_was_pressed(key); } +int inp_button_pressed(int button) { return keyboard_state[keyboard_current][button]; } + +void inp_update() +{ + if(keyboard_first) + { + // make sure to reset + keyboard_first = 0; + inp_update(); + } + + keyboard_current = keyboard_current^1; + int i, v; + for(i = 0; i < KEY_LAST; i++) + { + if (i >= KEY_MOUSE_FIRST) + v = glfwGetMouseButton(i-KEY_MOUSE_FIRST) == GLFW_PRESS ? 1 : 0; + else + v = glfwGetKey(i) == GLFW_PRESS ? 1 : 0; + keyboard_state[keyboard_current][i] = v; + } + +} diff --git a/src/engine/client/pnglite/pnglite.c b/src/engine/client/pnglite/pnglite.c deleted file mode 100644 index b33f60ba..00000000 --- a/src/engine/client/pnglite/pnglite.c +++ /dev/null @@ -1,877 +0,0 @@ -/* pnglite.c - pnglite library - For conditions of distribution and use, see copyright notice in pnglite.h -*/ -#define DO_CRC_CHECKS 1 -#define USE_ZLIB 1 - -#if USE_ZLIB -#include -#else -#include "zlite.h" -#endif - -#include -#include -#include -#include "pnglite.h" - - - -static png_alloc_t png_alloc; -static png_free_t png_free; - -static size_t file_read(png_t* png, void* out, size_t size, size_t numel) -{ - size_t result; - if(png->read_fun) - { - result = png->read_fun(out, size, numel, png->user_pointer); - } - else - { - if(!out) - { - result = fseek(png->user_pointer, (long)(size*numel), SEEK_CUR); - } - else - { - result = fread(out, size, numel, png->user_pointer); - } - } - - return result; -} - -static size_t file_write(png_t* png, void* p, size_t size, size_t numel) -{ - size_t result; - - if(png->write_fun) - { - result = png->write_fun(p, size, numel, png->user_pointer); - } - else - { - result = fwrite(p, size, numel, png->user_pointer); - } - - return result; -} - -static int file_read_ul(png_t* png, unsigned *out) -{ - unsigned char buf[4]; - - if(file_read(png, buf, 1, 4) != 4) - return PNG_FILE_ERROR; - - *out = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3]; - - return PNG_NO_ERROR; -} - -static int file_write_ul(png_t* png, unsigned in) -{ - unsigned char buf[4]; - - buf[0] = (in>>24) & 0xff; - buf[1] = (in>>16) & 0xff; - buf[2] = (in>>8) & 0xff; - buf[3] = (in) & 0xff; - - if(file_write(png, buf, 1, 4) != 4) - return PNG_FILE_ERROR; - - return PNG_NO_ERROR; -} - - -static unsigned get_ul(unsigned char* buf) -{ - unsigned result; - unsigned char foo[4]; - - memcpy(foo, buf, 4); - - result = (foo[0]<<24) | (foo[1]<<16) | (foo[2]<<8) | foo[3]; - - return result; -} - -static unsigned set_ul(unsigned char* buf, unsigned in) -{ - buf[0] = (in>>24) & 0xff; - buf[1] = (in>>16) & 0xff; - buf[2] = (in>>8) & 0xff; - buf[3] = (in) & 0xff; - - return PNG_NO_ERROR; -} - -int png_init(png_alloc_t pngalloc, png_free_t pngfree) -{ - if(pngalloc) - png_alloc = pngalloc; - else - png_alloc = &malloc; - - if(pngfree) - png_free = pngfree; - else - png_free = &free; - - return PNG_NO_ERROR; -} - -static int png_get_bpp(png_t* png) -{ - int bpp; - - switch(png->color_type) - { - case PNG_GREYSCALE: - bpp = 1; break; - case PNG_TRUECOLOR: - bpp = 3; break; - case PNG_INDEXED: - bpp = 1; break; - case PNG_GREYSCALE_ALPHA: - bpp = 2; break; - case PNG_TRUECOLOR_ALPHA: - bpp = 4; break; - default: - return PNG_FILE_ERROR; - } - - bpp *= png->depth/8; - - return bpp; -} - -static int png_read_ihdr(png_t* png) -{ - unsigned length; -#if DO_CRC_CHECKS - unsigned orig_crc; - unsigned calc_crc; -#endif - unsigned char ihdr[13+4]; /* length should be 13, make room for type (IHDR) */ - - file_read_ul(png, &length); - - if(length != 13) - { - printf("%d\n", length); - return PNG_CRC_ERROR; - } - - if(file_read(png, ihdr, 1, 13+4) != 13+4) - return PNG_EOF_ERROR; -#if DO_CRC_CHECKS - file_read_ul(png, &orig_crc); - - calc_crc = crc32(0L, 0, 0); - calc_crc = crc32(calc_crc, ihdr, 13+4); - - if(orig_crc != calc_crc) - return PNG_CRC_ERROR; -#else - file_read_ul(png); -#endif - - png->width = get_ul(ihdr+4); - png->height = get_ul(ihdr+8); - png->depth = ihdr[12]; - png->color_type = ihdr[13]; - png->compression_method = ihdr[14]; - png->filter_method = ihdr[15]; - png->interlace_method = ihdr[16]; - - if(png->color_type == PNG_INDEXED) - return PNG_NOT_SUPPORTED; - - if(png->depth != 8 && png->depth != 16) - return PNG_NOT_SUPPORTED; - - if(png->interlace_method) - return PNG_NOT_SUPPORTED; - - return PNG_NO_ERROR; -} - -static int png_write_ihdr(png_t* png) -{ - unsigned char ihdr[13+4]; - unsigned char *p = ihdr; - unsigned crc; - - file_write(png, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 1, 8); - - file_write_ul(png, 13); - - *p = 'I'; p++; - *p = 'H'; p++; - *p = 'D'; p++; - *p = 'R'; p++; - set_ul(p, png->width); p+=4; - set_ul(p, png->height); p+=4; - *p = png->depth; p++; - *p = png->color_type; p++; - *p = 0; p++; - *p = 0; p++; - *p = 0; p++; - - file_write(png, ihdr, 1, 13+4); - - crc = crc32(0L, 0, 0); - crc = crc32(crc, ihdr, 13+4); - - file_write_ul(png, crc); - - return PNG_NO_ERROR; -} - -void png_print_info(png_t* png) -{ - printf("PNG INFO:\n"); - printf("\twidth:\t\t%d\n", png->width); - printf("\theight:\t\t%d\n", png->height); - printf("\tdepth:\t\t%d\n", png->depth); - printf("\tcolor:\t\t"); - - switch(png->color_type) - { - case PNG_GREYSCALE: printf("greyscale\n"); break; - case PNG_TRUECOLOR: printf("truecolor\n"); break; - case PNG_INDEXED: printf("palette\n"); break; - case PNG_GREYSCALE_ALPHA: printf("greyscale with alpha\n"); break; - case PNG_TRUECOLOR_ALPHA: printf("truecolor with alpha\n"); break; - default: printf("unknown, this is not good\n"); break; - } - - printf("\tcompression:\t%s\n", png->compression_method?"unknown, this is not good":"inflate/deflate"); - printf("\tfilter:\t\t%s\n", png->filter_method?"unknown, this is not good":"adaptive"); - printf("\tinterlace:\t%s\n", png->interlace_method?"interlace":"no interlace"); -} - -int png_open_read(png_t* png, png_read_callback_t read_fun, void* user_pointer) -{ - char header[8]; - int result; - - png->read_fun = read_fun; - png->write_fun = 0; - png->user_pointer = user_pointer; - - if(!read_fun && !user_pointer) - return PNG_WRONG_ARGUMENTS; - - if(file_read(png, header, 1, 8) != 8) - return PNG_EOF_ERROR; - - if(memcmp(header, "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A", 8) != 0) - return PNG_HEADER_ERROR; - - result = png_read_ihdr(png); - - png->bpp = (unsigned char)png_get_bpp(png); - - return result; -} - -int png_open_write(png_t* png, png_write_callback_t write_fun, void* user_pointer) -{ - png->write_fun = write_fun; - png->read_fun = 0; - png->user_pointer = user_pointer; - - if(!write_fun && !user_pointer) - return PNG_WRONG_ARGUMENTS; - - return PNG_NO_ERROR; -} - -int png_open(png_t* png, png_read_callback_t read_fun, void* user_pointer) -{ - return png_open_read(png, read_fun, user_pointer); -} - -int png_open_file_read(png_t *png, const char* filename) -{ - FILE* fp = fopen(filename, "rb"); - - if(!fp) - return PNG_FILE_ERROR; - - return png_open_read(png, 0, fp); -} - -int png_open_file_write(png_t *png, const char* filename) -{ - FILE* fp = fopen(filename, "wb"); - - if(!fp) - return PNG_FILE_ERROR; - - return png_open_write(png, 0, fp); -} - -int png_open_file(png_t *png, const char* filename) -{ - return png_open_file_read(png, filename); -} - -int png_close_file(png_t* png) -{ - fclose(png->user_pointer); - - return PNG_NO_ERROR; -} - -static int png_init_deflate(png_t* png, unsigned char* data, int datalen) -{ - z_stream *stream; - png->zs = png_alloc(sizeof(z_stream)); - - stream = png->zs; - - if(!stream) - return PNG_MEMORY_ERROR; - - memset(stream, 0, sizeof(z_stream)); - - if(deflateInit(stream, Z_DEFAULT_COMPRESSION) != Z_OK) - return PNG_ZLIB_ERROR; - - stream->next_in = data; - stream->avail_in = datalen; - - return PNG_NO_ERROR; -} - -static int png_init_inflate(png_t* png) -{ -#if USE_ZLIB - z_stream *stream; - png->zs = png_alloc(sizeof(z_stream)); -#else - zl_stream *stream; - png->zs = png_alloc(sizeof(zl_stream)); -#endif - - stream = png->zs; - - if(!stream) - return PNG_MEMORY_ERROR; - - - -#if USE_ZLIB - memset(stream, 0, sizeof(z_stream)); - if(inflateInit(stream) != Z_OK) - return PNG_ZLIB_ERROR; -#else - memset(stream, 0, sizeof(zl_stream)); - if(z_inflateInit(stream) != Z_OK) - return PNG_ZLIB_ERROR; -#endif - - stream->next_out = png->png_data; - stream->avail_out = png->png_datalen; - - return PNG_NO_ERROR; -} - -static int png_end_deflate(png_t* png) -{ - z_stream *stream = png->zs; - - if(!stream) - return PNG_MEMORY_ERROR; - - deflateEnd(stream); - - png_free(png->zs); - - return PNG_NO_ERROR; -} - -static int png_end_inflate(png_t* png) -{ -#if USE_ZLIB - z_stream *stream = png->zs; -#else - zl_stream *stream = png->zs; -#endif - - if(!stream) - return PNG_MEMORY_ERROR; - -#if USE_ZLIB - if(inflateEnd(stream) != Z_OK) -#else - if(z_inflateEnd(stream) != Z_OK) -#endif - { - printf("ZLIB says: %s\n", stream->msg); - return PNG_ZLIB_ERROR; - } - - png_free(png->zs); - - return PNG_NO_ERROR; -} - -static int png_inflate(png_t* png, char* data, int len) -{ - int result; -#if USE_ZLIB - z_stream *stream = png->zs; -#else - zl_stream *stream = png->zs; -#endif - - if(!stream) - return PNG_MEMORY_ERROR; - - stream->next_in = (unsigned char*)data; - stream->avail_in = len; - -#if USE_ZLIB - result = inflate(stream, Z_SYNC_FLUSH); -#else - result = z_inflate(stream); -#endif - - if(result != Z_STREAM_END && result != Z_OK) - { - printf("%s\n", stream->msg); - return PNG_ZLIB_ERROR; - } - - if(stream->avail_in != 0) - return PNG_ZLIB_ERROR; - - return PNG_NO_ERROR; -} - -static int png_deflate(png_t* png, char* outdata, int outlen, int *outwritten) -{ - int result; - - z_stream *stream = png->zs; - - - if(!stream) - return PNG_MEMORY_ERROR; - - stream->next_out = (unsigned char*)outdata; - stream->avail_out = outlen; - - result = deflate(stream, Z_SYNC_FLUSH); - - *outwritten = outlen - stream->avail_out; - - if(result != Z_STREAM_END && result != Z_OK) - { - printf("%s\n", stream->msg); - return PNG_ZLIB_ERROR; - } - - return result; -} - -static int png_write_idats(png_t* png, unsigned char* data) -{ - unsigned char *chunk; - unsigned long written; - unsigned long crc; - unsigned size = png->width * png->height * png->bpp + png->height; - - (void)png_init_deflate; - (void)png_end_deflate; - (void)png_deflate; - - chunk = png_alloc(size); - memcpy(chunk, "IDAT", 4); - - written = size; - compress(chunk+4, &written, data, size); - - crc = crc32(0L, Z_NULL, 0); - crc = crc32(crc, chunk, written+4); - set_ul(chunk+written+4, crc); - file_write_ul(png, written); - file_write(png, chunk, 1, written+8); - png_free(chunk); - - file_write_ul(png, 0); - file_write(png, "IEND", 1, 4); - crc = crc32(0L, (const unsigned char *)"IEND", 4); - file_write_ul(png, crc); - - return PNG_NO_ERROR; -} - -static int png_read_idat(png_t* png, unsigned firstlen) -{ - unsigned type = 0; - char *chunk; - int result; - unsigned length = firstlen; - unsigned old_len = length; - -#if DO_CRC_CHECKS - unsigned orig_crc; - unsigned calc_crc; -#endif - - chunk = png_alloc(firstlen); - - result = png_init_inflate(png); - - if(result != PNG_NO_ERROR) - { - png_end_inflate(png); - png_free(chunk); - return result; - } - - do - { - if(file_read(png, chunk, 1, length) != length) - { - png_end_inflate(png); - png_free(chunk); - return PNG_FILE_ERROR; - } - -#if DO_CRC_CHECKS - calc_crc = crc32(0L, Z_NULL, 0); - calc_crc = crc32(calc_crc, (unsigned char*)"IDAT", 4); - calc_crc = crc32(calc_crc, (unsigned char*)chunk, length); - - file_read_ul(png, &orig_crc); - - if(orig_crc != calc_crc) - { - result = PNG_CRC_ERROR; - break; - } -#else - file_read_ul(png); -#endif - - result = png_inflate(png, chunk, length); - - if(result != PNG_NO_ERROR) break; - - file_read_ul(png, &length); - - if(length > old_len) - { - png_free(chunk); - chunk = png_alloc(length); - old_len = length; - } - - if(file_read(png, &type, 1, 4) != 4) - { - result = PNG_FILE_ERROR; - break; - } - - }while(type == *(unsigned int*)"IDAT"); - - if(type == *(unsigned int*)"IEND") - result = PNG_DONE; - - png_free(chunk); - png_end_inflate(png); - - return result; -} - -static int png_process_chunk(png_t* png) -{ - int result = PNG_NO_ERROR; - unsigned type; - unsigned length; - - file_read_ul(png, &length); - - if(file_read(png, &type, 1, 4) != 4) - return PNG_FILE_ERROR; - - if(type == *(unsigned int*)"IDAT") /* if we found an idat, all other idats should be followed with no other chunks in between */ - { - png->png_datalen = png->width * png->height * png->bpp + png->height; - png->png_data = png_alloc(png->png_datalen); - - if(!png->png_data) - return PNG_MEMORY_ERROR; - - return png_read_idat(png, length); - } - else if(type == *(unsigned int*)"IEND") - { - return PNG_DONE; - } - else - { - file_read(png, 0, 1, length + 4); /* unknown chunk */ - } - - return result; -} - -static void png_filter_sub(int stride, unsigned char* in, unsigned char* out, int len) -{ - int i; - unsigned char a = 0; - - for(i = 0; i < len; i++) - { - if(i >= stride) - a = out[i - stride]; - - out[i] = in[i] + a; - } -} - -static void png_filter_up(int stride, unsigned char* in, unsigned char* out, unsigned char* prev_line, int len) -{ - int i; - - if(prev_line) - { - for(i = 0; i < len; i++) - out[i] = in[i] + prev_line[i]; - } - else - memcpy(out, in, len); -} - -static void png_filter_average(int stride, unsigned char* in, unsigned char* out, unsigned char* prev_line, int len) -{ - int i; - unsigned char a = 0; - unsigned char b = 0; - unsigned int sum = 0; - - for(i = 0; i < len; i++) - { - if(prev_line) - b = prev_line[i]; - - if(i >= stride) - a = out[i - stride]; - - sum = a; - sum += b; - - out[i] = (char)(in[i] + sum/2); - } -} - -static unsigned char png_paeth(unsigned char a, unsigned char b, unsigned char c) -{ - int p = (int)a + b - c; - int pa = abs(p - a); - int pb = abs(p - b); - int pc = abs(p - c); - - int pr; - - if(pa <= pb && pa <= pc) - pr = a; - else if(pb <= pc) - pr = b; - else - pr = c; - - return (char)pr; -} - -static void png_filter_paeth(int stride, unsigned char* in, unsigned char* out, unsigned char* prev_line, int len) -{ - int i; - unsigned char a; - unsigned char b; - unsigned char c; - - for(i = 0; i < len; i++) - { - if(prev_line && i >= stride) - { - a = out[i - stride]; - b = prev_line[i]; - c = prev_line[i - stride]; - } - else - { - if(prev_line) - b = prev_line[i]; - else - b = 0; - - if(i >= stride) - a = out[i - stride]; - else - a = 0; - - c = 0; - } - - out[i] = in[i] + png_paeth(a, b, c); - } -} - -static int png_filter(png_t* png, unsigned char* data) -{ - - - return PNG_NO_ERROR; -} - -static int png_unfilter(png_t* png, unsigned char* data) -{ - unsigned i; - unsigned pos = 0; - unsigned outpos = 0; - unsigned char *filtered = png->png_data; - - int stride = png->bpp; - - while(pos < png->png_datalen) - { - unsigned char filter = filtered[pos]; - - pos++; - - if(png->depth == 16) - { - for(i = 0; i < png->width * stride; i+=2) - { - *(short*)(filtered+pos+i) = (filtered[pos+i] << 8) | filtered[pos+i+1]; - } - } - - switch(filter) - { - case 0: /* none */ - memcpy(data+outpos, filtered+pos, png->width * stride); - break; - case 1: /* sub */ - png_filter_sub(stride, filtered+pos, data+outpos, png->width * stride); - break; - case 2: /* up */ - if(outpos) - png_filter_up(stride, filtered+pos, data+outpos, data + outpos - (png->width*stride), png->width*stride); - else - png_filter_up(stride, filtered+pos, data+outpos, 0, png->width*stride); - break; - case 3: /* average */ - if(outpos) - png_filter_average(stride, filtered+pos, data+outpos, data + outpos - (png->width*stride), png->width*stride); - else - png_filter_average(stride, filtered+pos, data+outpos, 0, png->width*stride); - break; - case 4: /* paeth */ - if(outpos) - png_filter_paeth(stride, filtered+pos, data+outpos, data + outpos - (png->width*stride), png->width*stride); - else - png_filter_paeth(stride, filtered+pos, data+outpos, 0, png->width*stride); - break; - default: - return PNG_UNKNOWN_FILTER; - } - - outpos += png->width * stride; - pos += png->width * stride; - } - - return PNG_NO_ERROR; -} - -int png_get_data(png_t* png, unsigned char* data) -{ - int result = PNG_NO_ERROR; - - while(result == PNG_NO_ERROR) - { - result = png_process_chunk(png); - } - - if(result != PNG_DONE) - { - png_free(png->png_data); - return result; - } - - result = png_unfilter(png, data); - - png_free(png->png_data); - - return result; -} - -int png_set_data(png_t* png, unsigned width, unsigned height, char depth, int color, unsigned char* data) -{ - int i; - unsigned char *filtered; - png->width = width; - png->height = height; - png->depth = depth; - png->color_type = color; - png->bpp = png_get_bpp(png); - - filtered = png_alloc(width * height * png->bpp + height); - - for(i = 0; i < png->height; i++) - { - filtered[i*png->width*png->bpp+i] = 0; - memcpy(&filtered[i*png->width*png->bpp+i+1], data + i * png->width*png->bpp, png->width*png->bpp); - } - - png_filter(png, filtered); - png_write_ihdr(png); - png_write_idats(png, filtered); - - png_free(filtered); - return PNG_NO_ERROR; -} - - -char* png_error_string(int error) -{ - switch(error) - { - case PNG_NO_ERROR: - return "No error"; - case PNG_FILE_ERROR: - return "Unknown file error."; - case PNG_HEADER_ERROR: - return "No PNG header found. Are you sure this is a PNG?"; - case PNG_IO_ERROR: - return "Failure while reading file."; - case PNG_EOF_ERROR: - return "Reached end of file."; - case PNG_CRC_ERROR: - return "CRC or chunk length error."; - case PNG_MEMORY_ERROR: - return "Could not allocate memory."; - case PNG_ZLIB_ERROR: - return "zlib reported an error."; - case PNG_UNKNOWN_FILTER: - return "Unknown filter method used in scanline."; - case PNG_DONE: - return "PNG done"; - case PNG_NOT_SUPPORTED: - return "The PNG is unsupported by pnglite, too bad for you!"; - case PNG_WRONG_ARGUMENTS: - return "Wrong combination of arguments passed to png_open. You must use either a read_function or supply a file pointer to use."; - default: - return "Unknown error."; - }; -} diff --git a/src/engine/client/pnglite/pnglite.h b/src/engine/client/pnglite/pnglite.h deleted file mode 100644 index f464c46b..00000000 --- a/src/engine/client/pnglite/pnglite.h +++ /dev/null @@ -1,227 +0,0 @@ -/* pnglite.h - Interface for pnglite library - Copyright (c) 2007 Daniel Karling - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - - 3. This notice may not be removed or altered from any source - distribution. - - Daniel Karling - daniel.karling@gmail.com - */ - - -#ifndef _PNGLITE_H_ -#define _PNGLITE_H_ - -#ifdef __cplusplus -extern "C"{ -#endif - -/* - Enumerations for pnglite. - Negative numbers are error codes and 0 and up are okay responses. -*/ - -enum -{ - PNG_DONE = 1, - PNG_NO_ERROR = 0, - PNG_FILE_ERROR = -1, - PNG_HEADER_ERROR = -2, - PNG_IO_ERROR = -3, - PNG_EOF_ERROR = -4, - PNG_CRC_ERROR = -5, - PNG_MEMORY_ERROR = -6, - PNG_ZLIB_ERROR = -7, - PNG_UNKNOWN_FILTER = -8, - PNG_NOT_SUPPORTED = -9, - PNG_WRONG_ARGUMENTS = -10 -}; - -/* - The five different kinds of color storage in PNG files. -*/ - -enum -{ - PNG_GREYSCALE = 0, - PNG_TRUECOLOR = 2, - PNG_INDEXED = 3, - PNG_GREYSCALE_ALPHA = 4, - PNG_TRUECOLOR_ALPHA = 6 -}; - -/* - Typedefs for callbacks. -*/ - -typedef unsigned (*png_write_callback_t)(void* input, size_t size, size_t numel, void* user_pointer); -typedef unsigned (*png_read_callback_t)(void* output, size_t size, size_t numel, void* user_pointer); -typedef void (*png_free_t)(void* p); -typedef void * (*png_alloc_t)(size_t s); - -typedef struct -{ - void* zs; /* pointer to z_stream */ - png_read_callback_t read_fun; - png_write_callback_t write_fun; - void* user_pointer; - - unsigned char* png_data; - unsigned png_datalen; - - unsigned width; - unsigned height; - unsigned char depth; - unsigned char color_type; - unsigned char compression_method; - unsigned char filter_method; - unsigned char interlace_method; - unsigned char bpp; -}png_t; - -/* - Function: png_init - - This function initializes pnglite. The parameters can be used to set your own memory allocation routines following these formats: - - > void* (*custom_alloc)(size_t s) - > void (*custom_free)(void* p) - Parameters: - pngalloc - Pointer to custom allocation routine. If 0 is passed, malloc from libc will be used. - pngfree - Pointer to custom free routine. If 0 is passed, free from libc will be used. - - Returns: - Always returns PNG_NO_ERROR. -*/ - -int png_init(png_alloc_t pngalloc, png_free_t pngfree); - -/* - Function: png_open_file - - This function is used to open a png file with the internal file IO system. This function should be used instead of - png_open if no custom read function is used. - - Parameters: - png - Empty png_t struct. - filename - Filename of the file to be opened. - - Returns: - PNG_NO_ERROR on success, otherwise an error code. -*/ - -int png_open_file(png_t *png, const char* filename); - -int png_open_file_read(png_t *png, const char* filename); -int png_open_file_write(png_t *png, const char* filename); - -/* - Function: png_open - - This function reads or writes a png from/to the specified callback. The callbacks should be of the format: - - > size_t (*png_write_callback_t)(void* input, size_t size, size_t numel, void* user_pointer); - > size_t (*png_read_callback_t)(void* output, size_t size, size_t numel, void* user_pointer). - - Only one callback has to be specified. The read callback in case of PNG reading, otherwise the write callback. - - Writing: - The callback will be called like fwrite. - - Reading: - The callback will be called each time pnglite needs more data. The callback should read as much data as requested, - or return 0. This should always be possible if the PNG is sane. If the output-buffer is a null-pointer the callback - should only skip ahead the specified number of elements. If the callback is a null-pointer the user_pointer will be - treated as a file pointer (use png_open_file instead). - - Parameters: - png - png_t struct - read_fun - Callback function for reading. - user_pointer - User pointer to be passed to read_fun. - - Returns: - PNG_NO_ERROR on success, otherwise an error code. -*/ - -int png_open(png_t* png, png_read_callback_t read_fun, void* user_pointer); - -int png_open_read(png_t* png, png_read_callback_t read_fun, void* user_pointer); -int png_open_write(png_t* png, png_write_callback_t write_fun, void* user_pointer); - -/* - Function: png_print_info - - This function prints some info about the opened png file to stdout. - - Parameters: - png - png struct to get info from. -*/ - -void png_print_info(png_t* png); - -/* - Function: png_error_string - - This function translates an error code to a human readable string. - - Parameters: - error - Error code. - - Returns: - Pointer to string. -*/ - -char* png_error_string(int error); - -/* - Function: png_get_data - - This function decodes the opened png file and stores the result in data. data should be big enough to hold the decoded png. Required size will be: - - > width*height*(bytes per pixel) - - Parameters: - data - Where to store result. - - Returns: - PNG_NO_ERROR on success, otherwise an error code. -*/ - -int png_get_data(png_t* png, unsigned char* data); - -int png_set_data(png_t* png, unsigned width, unsigned height, char depth, int color, unsigned char* data); - -/* - Function: png_close_file - - Closes an open png file pointer. Should only be used when the png has been opened with png_open_file. - - Parameters: - png - png to close. - - Returns: - PNG_NO_ERROR -*/ - -int png_close_file(png_t* png); - -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/engine/client/snd.c b/src/engine/client/snd.c new file mode 100644 index 00000000..6b490f72 --- /dev/null +++ b/src/engine/client/snd.c @@ -0,0 +1,634 @@ +#include +#include +#include + +#include +#include + +enum +{ + NUM_FRAMES_STOP = 512, + NUM_FRAMES_LERP = 512, +}; + +static const float NUM_FRAMES_STOP_INV = 1.0f/(float)NUM_FRAMES_STOP; +static const float NUM_FRAMES_LERP_INV = 1.0f/(float)NUM_FRAMES_LERP; + +static const float GLOBAL_VOLUME_SCALE = 0.75f; +static float master_volume = 1.0f; + +static const float GLOBAL_SOUND_DELAY = 0.05f; + +// --- sound --- +typedef struct +{ + /* +public: + sound_data() : + data(0x0), + num_samples(0), + rate(0), + channels(0), + sustain_start(-1), + sustain_end(-1), + last_played(0) + { }*/ + + short *data; + int num_samples; + int rate; + int channels; + int sustain_start; + int sustain_end; + int64 last_played; +} SOUND_DATA; + + +static float clampf(float val, float lower, float upper) +{ + if(val > upper) + return upper; + if(val < lower) + return lower; + return val; +} + + +static int clampi(int val, int lower, int upper) +{ + if(val > upper) + return upper; + if(val < lower) + return lower; + return val; +} +/* +template +inline const T clamp(const T val, const T lower, const T upper) +{ + if(val > upper) + return upper; + if(val < lower) + return lower; + return val; +}*/ + + +typedef struct +{ +/* +public: + channel() + { data = 0; lerp = -1; stop = -1; } +*/ + + SOUND_DATA *data; + int tick; + int loop; + float pan; + float vol; + float old_vol; + float new_vol; + int lerp; + int stop; +} MIXER_CHANNEL; + +enum +{ + MAX_CHANNELS=32, + MAX_FILL_FRAMES=256, +}; + +static MIXER_CHANNEL channels[MAX_CHANNELS]; +static int buffer[MAX_FILL_FRAMES*2]; + +static void mixer_fill_mono(int *out, unsigned long frames, MIXER_CHANNEL *c, float dv) +{ + float pl = clampf(1.0f - c->pan, 0.0f, 1.0f); + float pr = clampf(1.0f + c->pan, 0.0f, 1.0f); + unsigned long i; + + for(i = 0; i < frames; i++) + { + float val = c->vol * master_volume * c->data->data[c->tick]; + + out[i<<1] += (int)(pl*val); + out[(i<<1)+1] += (int)(pr*val); + c->tick++; + c->vol += dv; + if(c->vol < 0.0f) c->vol = 0.0f; + } +} + +static void mixer_fill_stereo(int *out, unsigned long frames, MIXER_CHANNEL *c, float dv) +{ + float pl = clampf(1.0f - c->pan, 0.0f, 1.0f); + float pr = clampf(1.0f + c->pan, 0.0f, 1.0f); + unsigned long i; + + for(i = 0; i < frames; i++) + { + int vl = (int)(pl*c->vol * master_volume * c->data->data[c->tick]); + int vr = (int)(pr*c->vol * master_volume * c->data->data[c->tick + 1]); + out[i<<1] += vl; + out[(i<<1)+1] += vr; + c->tick += 2; + c->vol += dv; + if(c->vol < 0.0f) c->vol = 0.0f; + } +} + +static void mixer_fill(void *output, unsigned long frames) +{ + short *out = (short*)output; + + dbg_assert(frames <= MAX_FILL_FRAMES, "not enough fill frames in buffer"); + unsigned long i; + int c; + + for(i = 0; i < frames; i++) + { + buffer[i<<1] = 0; + buffer[(i<<1)+1] = 0; + } + + for(c = 0; c < MAX_CHANNELS; c++) + { + unsigned long filled = 0; + while(channels[c].data && filled < frames) + { + unsigned long frames_left = (channels[c].data->num_samples - channels[c].tick) >> (channels[c].data->channels-1); + unsigned long to_fill = frames>frames_left?frames_left:frames; + float dv = 0.0f; + + if(channels[c].stop >= 0) + to_fill = (unsigned)channels[c].stop>frames_left?frames:channels[c].stop; + if(channels[c].loop >= 0 && + channels[c].data->sustain_start >= 0) + { + unsigned long tmp = channels[c].data->sustain_end - channels[c].tick; + to_fill = tmp>frames?frames:tmp; + } + + if(channels[c].lerp >= 0) + { + dv = (channels[c].new_vol - channels[c].old_vol) * NUM_FRAMES_LERP_INV; + } + + if(channels[c].data->channels == 1) + mixer_fill_mono(buffer, to_fill, &channels[c], dv); + else + mixer_fill_stereo(buffer, to_fill, &channels[c], dv); + + if(channels[c].loop >= 0 && + channels[c].data->sustain_start >= 0 && + channels[c].tick >= channels[c].data->sustain_end) + channels[c].tick = channels[c].data->sustain_start; + + if(channels[c].stop >= 0) + channels[c].stop -= to_fill; + if(channels[c].tick >= channels[c].data->num_samples || + channels[c].stop == 0) + channels[c].data = 0; + + channels[c].lerp -= to_fill; + if(channels[c].lerp < 0) + channels[c].lerp = -1; + + + filled += to_fill; + } + } + + for(i = 0; i < frames; i++) + { + out[i<<1] = (short)clampi(buffer[i<<1], -0x7fff, 0x7fff); + out[(i<<1)+1] = (short)clampi(buffer[(i<<1)+1], -0x7fff, 0x7fff); + } +} + +int mixer_play(SOUND_DATA *sound, unsigned loop, float vol, float pan) +{ + if(time_get() - sound->last_played < (int64)(time_freq()*GLOBAL_SOUND_DELAY)) + return -1; + + int c; + for(c = 0; c < MAX_CHANNELS; c++) + { + if(channels[c].data == 0) + { + channels[c].data = sound; + channels[c].tick = 0; + channels[c].loop = loop; + channels[c].vol = vol * GLOBAL_VOLUME_SCALE; + channels[c].pan = pan; + channels[c].stop = -1; + channels[c].lerp = -1; + sound->last_played = time_get(); + return c; + } + } + + return -1; +} + +static void mixer_stop(int id) +{ + dbg_assert(id >= 0 && id < MAX_CHANNELS, "id out of bounds"); + channels[id].old_vol = channels[id].vol; + channels[id].stop = NUM_FRAMES_STOP; +} + +static void mixer_set_vol(int id, float vol) +{ + dbg_assert(id >= 0 && id < MAX_CHANNELS, "id out of bounds"); + channels[id].new_vol = vol * GLOBAL_VOLUME_SCALE; + channels[id].old_vol = channels[id].vol; + channels[id].lerp = NUM_FRAMES_LERP; +} + +typedef struct +{ + SOUND_DATA sound; + int next; +} SOUND; + +enum +{ + MAX_SOUNDS = 1024, +}; + +static SOUND sounds[MAX_SOUNDS]; +static int first_free_sound; + +static PaStream *stream = 0; + +static int pacallback(const void *in, void *out, unsigned long frames, const PaStreamCallbackTimeInfo* time, PaStreamCallbackFlags status, void *user) +{ + mixer_fill(out, frames); + return 0; +} + +int snd_init() +{ + int i; + first_free_sound = 0; + for(i = 0; i < MAX_SOUNDS; i++) + sounds[i].next = i+1; + sounds[MAX_SOUNDS-1].next = -1; + + // init PA + PaStreamParameters params; + PaError err = Pa_Initialize(); + if(err != paNoError) + { + dbg_msg("audio_stream", "portaudio error: %s", Pa_GetErrorText(err)); + return 0; + } + + params.device = Pa_GetDefaultOutputDevice(); + if(params.device == -1) + { + dbg_msg("audio_stream", "no default output device"); + return 0; + } + params.channelCount = 2; + params.sampleFormat = paInt16; + params.suggestedLatency = Pa_GetDeviceInfo(params.device)->defaultLowOutputLatency; + params.hostApiSpecificStreamInfo = 0x0; + + err = Pa_OpenStream( + &stream, /* passes back stream pointer */ + 0, /* no input channels */ + ¶ms, /* pointer to parameters */ + 44100, /* sample rate */ + 128, /* frames per buffer */ + paClipOff, /* no clamping */ + pacallback, /* specify our custom callback */ + 0); /* pass our data through to callback */ + + if(err != paNoError) + { + dbg_msg("audio_stream", "portaudio error: %s", Pa_GetErrorText(err)); + return 0; + } + + err = Pa_StartStream(stream); + if(err != paNoError) + { + dbg_msg("audio_stream", "portaudio error: %s", Pa_GetErrorText(err)); + return 0; + } + + return 1; +} + +int snd_shutdown() +{ + Pa_StopStream(stream); + stream = NULL; + Pa_Terminate(); + return 1; +} + +float snd_get_master_volume() +{ + return master_volume; +} + +void snd_set_master_volume(float val) +{ + if(val < 0.0f) + val = 0.0f; + else if(val > 1.0f) + val = 1.0f; + + master_volume = val; +} + +static int snd_alloc_sound() +{ + if(first_free_sound < 0) + return -1; + int id = first_free_sound; + first_free_sound = sounds[id].next; + sounds[id].next = -1; + return id; +} + +static FILE *file = NULL; + +static int read_data(void *buffer, int size) +{ + return fread(buffer, 1, size, file); +} + +int snd_load_wv(const char *filename) +{ + SOUND_DATA snd; + int id = -1; + + char error[100]; + + file = fopen(filename, "rb"); // TODO: use system.h stuff for this + + WavpackContext *context = WavpackOpenFileInput(read_data, error); + if (context) + { + int samples = WavpackGetNumSamples(context); + int bitspersample = WavpackGetBitsPerSample(context); + unsigned int samplerate = WavpackGetSampleRate(context); + int channels = WavpackGetNumChannels(context); + + snd.channels = channels; + snd.rate = samplerate; + + if(snd.channels > 2) + { + dbg_msg("sound/wv", "file is not mono or stereo. filename='%s'", filename); + return -1; + } + + if(snd.rate != 44100) + { + dbg_msg("sound/wv", "file is %d Hz, not 44100 Hz. filename='%s'", snd.rate, filename); + return -1; + } + + if(bitspersample != 16) + { + dbg_msg("sound/wv", "bps is %d, not 16, filname='%s'", bitspersample, filename); + return -1; + } + + int *data = (int *)mem_alloc(4*samples*channels, 1); + WavpackUnpackSamples(context, data, samples); // TODO: check return value + int *src = data; + + snd.data = (short *)mem_alloc(2*samples*channels, 1); + short *dst = snd.data; + + int i; + for (i = 0; i < samples*channels; i++) + *dst++ = (short)*src++; + + mem_free(data); + + snd.num_samples = samples; + snd.sustain_start = -1; + snd.sustain_end = -1; + snd.last_played = 0; + id = snd_alloc_sound(); + sounds[id].sound = snd; + } + else + { + dbg_msg("sound/wv", "failed to open %s: %s", filename, error); + } + + fclose(file); + file = NULL; + + if(id >= 0) + { + if(config.debug) + dbg_msg("sound/wv", "loaded %s", filename); + } + else + { + dbg_msg("sound/wv", "failed to load %s", filename); + } + + return id; +} + +int snd_load_wav(const char *filename) +{ + SOUND_DATA snd; + + // open file for reading + IOHANDLE file; + file = io_open(filename, IOFLAG_READ); + if(!file) + { + dbg_msg("sound/wav", "failed to open file. filename='%s'", filename); + return -1; + } + + int id = -1; + int state = 0; + while(1) + { + // read chunk header + unsigned char head[8]; + if(io_read(file, head, sizeof(head)) != 8) + { + break; + } + + int chunk_size = head[4] | (head[5]<<8) | (head[6]<<16) | (head[7]<<24); + head[4] = 0; + + if(state == 0) + { + // read the riff and wave headers + if(head[0] != 'R' || head[1] != 'I' || head[2] != 'F' || head[3] != 'F') + { + dbg_msg("sound/wav", "not a RIFF file. filename='%s'", filename); + return -1; + } + + unsigned char type[4]; + io_read(file, type, 4); + + if(type[0] != 'W' || type[1] != 'A' || type[2] != 'V' || type[3] != 'E') + { + dbg_msg("sound/wav", "RIFF file is not a WAVE. filename='%s'", filename); + return -1; + } + + state++; + } + else if(state == 1) + { + // read the format chunk + if(head[0] == 'f' && head[1] == 'm' && head[2] == 't' && head[3] == ' ') + { + unsigned char fmt[16]; + if(io_read(file, fmt, sizeof(fmt)) != sizeof(fmt)) + { + dbg_msg("sound/wav", "failed to read format. filename='%s'", filename); + return -1; + } + + // decode format + int compression_code = fmt[0] | (fmt[1]<<8); + snd.channels = fmt[2] | (fmt[3]<<8); + snd.rate = fmt[4] | (fmt[5]<<8) | (fmt[6]<<16) | (fmt[7]<<24); + + if(compression_code != 1) + { + dbg_msg("sound/wav", "file is not uncompressed. filename='%s'", filename); + return -1; + } + + if(snd.channels > 2) + { + dbg_msg("sound/wav", "file is not mono or stereo. filename='%s'", filename); + return -1; + } + + if(snd.rate != 44100) + { + dbg_msg("sound/wav", "file is %d Hz, not 44100 Hz. filename='%s'", snd.rate, filename); + return -1; + } + + int bps = fmt[14] | (fmt[15]<<8); + if(bps != 16) + { + dbg_msg("sound/wav", "bps is %d, not 16, filname='%s'", bps, filename); + return -1; + } + + // next state + state++; + } + else + io_skip(file, chunk_size); + } + else if(state == 2) + { + // read the data + if(head[0] == 'd' && head[1] == 'a' && head[2] == 't' && head[3] == 'a') + { + snd.data = (short*)mem_alloc(chunk_size, 1); + io_read(file, snd.data, chunk_size); + snd.num_samples = chunk_size/(2); +#if defined(CONF_ARCH_ENDIAN_BIG) + for(unsigned i = 0; i < (unsigned)snd.num_samples; i++) + { + unsigned j = i << 1; + snd.data[i] = ((short)((char*)snd.data)[j]) + ((short)((char*)snd.data)[j+1] << 8); + } +#endif + snd.sustain_start = -1; + snd.sustain_end = -1; + snd.last_played = 0; + id = snd_alloc_sound(); + sounds[id].sound = snd; + state++; + } + else + io_skip(file, chunk_size); + } + else if(state == 3) + { + if(head[0] == 's' && head[1] == 'm' && head[2] == 'p' && head[3] == 'l') + { + unsigned char smpl[36]; + unsigned char loop[24]; + if(config.debug) + dbg_msg("sound/wav", "got sustain"); + + io_read(file, smpl, sizeof(smpl)); + unsigned num_loops = (smpl[28] | (smpl[29]<<8) | (smpl[30]<<16) | (smpl[31]<<24)); + unsigned skip = (smpl[32] | (smpl[33]<<8) | (smpl[34]<<16) | (smpl[35]<<24)); + + if(num_loops > 0) + { + io_read(file, loop, sizeof(loop)); + unsigned start = (loop[8] | (loop[9]<<8) | (loop[10]<<16) | (loop[11]<<24)); + unsigned end = (loop[12] | (loop[13]<<8) | (loop[14]<<16) | (loop[15]<<24)); + sounds[id].sound.sustain_start = start * sounds[id].sound.channels; + sounds[id].sound.sustain_end = end * sounds[id].sound.channels; + } + + if(num_loops > 1) + io_skip(file, (num_loops-1) * sizeof(loop)); + + io_skip(file, skip); + state++; + } + else + io_skip(file, chunk_size); + } + else + io_skip(file, chunk_size); + } + + if(id >= 0) + { + if(config.debug) + dbg_msg("sound/wav", "loaded %s", filename); + } + else + dbg_msg("sound/wav", "failed to load %s", filename); + + return id; +} + +int snd_play(int id, int loop, float vol, float pan) +{ + if(id < 0) + { + dbg_msg("snd", "bad sound id"); + return -1; + } + + dbg_assert(sounds[id].sound.data != 0, "null sound"); + dbg_assert(sounds[id].next == -1, "sound isn't allocated"); + return mixer_play(&sounds[id].sound, loop, vol, pan); +} + +void snd_stop(int id) +{ + if(id >= 0) + mixer_stop(id); +} + +void snd_set_vol(int id, float vol) +{ + if(id >= 0) + mixer_set_vol(id, vol); +} diff --git a/src/engine/client/snd.cpp b/src/engine/client/snd.cpp deleted file mode 100644 index 2fefd6f2..00000000 --- a/src/engine/client/snd.cpp +++ /dev/null @@ -1,550 +0,0 @@ -#include -#include -#include - -#include -#include - -extern "C" { -#include "../../wavpack/wavpack.h" -} - -using namespace baselib; - -static const int NUM_FRAMES_STOP = 512; -static const float NUM_FRAMES_STOP_INV = 1.0f/(float)NUM_FRAMES_STOP; -static const int NUM_FRAMES_LERP = 512; -static const float NUM_FRAMES_LERP_INV = 1.0f/(float)NUM_FRAMES_LERP; - -static const float GLOBAL_VOLUME_SCALE = 0.75f; -static float master_volume = 1.0f; - -static const float GLOBAL_SOUND_DELAY = 0.05f; - -// --- sound --- -class sound_data -{ -public: - sound_data() : - data(0x0), - num_samples(0), - rate(0), - channels(0), - sustain_start(-1), - sustain_end(-1), - last_played(0) - { } - - short *data; - int num_samples; - int rate; - int channels; - int sustain_start; - int sustain_end; - int64 last_played; -}; - -template -inline const T clamp(const T val, const T lower, const T upper) -{ - if(val > upper) - return upper; - if(val < lower) - return lower; - return val; -} - -static class mixer : public audio_stream -{ -public: - class channel - { - public: - channel() - { data = 0; lerp = -1; stop = -1; } - - sound_data *data; - int tick; - int loop; - float pan; - float vol; - float old_vol; - float new_vol; - int lerp; - int stop; - }; - - enum - { - MAX_CHANNELS=32, - MAX_FILL_FRAMES=256, - }; - - channel channels[MAX_CHANNELS]; - int buffer[MAX_FILL_FRAMES*2]; - - void fill_mono(int *out, unsigned long frames, channel *c, float dv = 0.0f) - { - float pl = clamp(1.0f - c->pan, 0.0f, 1.0f); - float pr = clamp(1.0f + c->pan, 0.0f, 1.0f); - - for(unsigned long i = 0; i < frames; i++) - { - float val = c->vol * master_volume * c->data->data[c->tick]; - - out[i<<1] += (int)(pl*val); - out[(i<<1)+1] += (int)(pr*val); - c->tick++; - c->vol += dv; - if(c->vol < 0.0f) c->vol = 0.0f; - } - } - - void fill_stereo(int *out, unsigned long frames, channel *c, float dv = 0.0f) - { - float pl = clamp(1.0f - c->pan, 0.0f, 1.0f); - float pr = clamp(1.0f + c->pan, 0.0f, 1.0f); - - for(unsigned long i = 0; i < frames; i++) - { - int vl = (int)(pl*c->vol * master_volume * c->data->data[c->tick]); - int vr = (int)(pr*c->vol * master_volume * c->data->data[c->tick + 1]); - out[i<<1] += vl; - out[(i<<1)+1] += vr; - c->tick += 2; - c->vol += dv; - if(c->vol < 0.0f) c->vol = 0.0f; - } - } - - virtual void fill(void *output, unsigned long frames) - { - short *out = (short*)output; - - dbg_assert(frames <= MAX_FILL_FRAMES, "not enough fill frames in buffer"); - - for(unsigned long i = 0; i < frames; i++) - { - buffer[i<<1] = 0; - buffer[(i<<1)+1] = 0; - } - - for(int c = 0; c < MAX_CHANNELS; c++) - { - unsigned long filled = 0; - while(channels[c].data && filled < frames) - { - unsigned long frames_left = (channels[c].data->num_samples - channels[c].tick) >> (channels[c].data->channels-1); - unsigned long to_fill = frames>frames_left?frames_left:frames; - float dv = 0.0f; - - if(channels[c].stop >= 0) - to_fill = (unsigned)channels[c].stop>frames_left?frames:channels[c].stop; - if(channels[c].loop >= 0 && - channels[c].data->sustain_start >= 0) - { - unsigned long tmp = channels[c].data->sustain_end - channels[c].tick; - to_fill = tmp>frames?frames:tmp; - } - - if(channels[c].lerp >= 0) - { - dv = (channels[c].new_vol - channels[c].old_vol) * NUM_FRAMES_LERP_INV; - } - - if(channels[c].data->channels == 1) - fill_mono(buffer, to_fill, &channels[c], dv); - else - fill_stereo(buffer, to_fill, &channels[c], dv); - - if(channels[c].loop >= 0 && - channels[c].data->sustain_start >= 0 && - channels[c].tick >= channels[c].data->sustain_end) - channels[c].tick = channels[c].data->sustain_start; - - if(channels[c].stop >= 0) - channels[c].stop -= to_fill; - if(channels[c].tick >= channels[c].data->num_samples || - channels[c].stop == 0) - channels[c].data = 0; - - channels[c].lerp -= to_fill; - if(channels[c].lerp < 0) - channels[c].lerp = -1; - - - filled += to_fill; - } - } - - for(unsigned long i = 0; i < frames; i++) - { - out[i<<1] = (short)clamp(buffer[i<<1], -0x7fff, 0x7fff); - out[(i<<1)+1] = (short)clamp(buffer[(i<<1)+1], -0x7fff, 0x7fff); - } - } - - int play(sound_data *sound, unsigned loop, float vol, float pan) - { - if(time_get() - sound->last_played < (int64)(time_freq()*GLOBAL_SOUND_DELAY)) - return -1; - - for(int c = 0; c < MAX_CHANNELS; c++) - { - if(channels[c].data == 0) - { - channels[c].data = sound; - channels[c].tick = 0; - channels[c].loop = loop; - channels[c].vol = vol * GLOBAL_VOLUME_SCALE; - channels[c].pan = pan; - channels[c].stop = -1; - channels[c].lerp = -1; - sound->last_played = time_get(); - return c; - } - } - - return -1; - } - - void stop(int id) - { - dbg_assert(id >= 0 && id < MAX_CHANNELS, "id out of bounds"); - channels[id].old_vol = channels[id].vol; - channels[id].stop = NUM_FRAMES_STOP; - } - - void set_vol(int id, float vol) - { - dbg_assert(id >= 0 && id < MAX_CHANNELS, "id out of bounds"); - channels[id].new_vol = vol * GLOBAL_VOLUME_SCALE; - channels[id].old_vol = channels[id].vol; - channels[id].lerp = NUM_FRAMES_LERP; - } -} mixer; - -struct sound_holder -{ - sound_data sound; - int next; -}; - -static const int MAX_SOUNDS = 1024; -static sound_holder sounds[MAX_SOUNDS]; -static int first_free_sound; - -bool snd_init() -{ - first_free_sound = 0; - for(int i = 0; i < MAX_SOUNDS; i++) - sounds[i].next = i+1; - sounds[MAX_SOUNDS-1].next = -1; - return mixer.create(); -} - -bool snd_shutdown() -{ - mixer.destroy(); - return true; -} - -float snd_get_master_volume() -{ - return master_volume; -} - -void snd_set_master_volume(float val) -{ - if(val < 0.0f) - val = 0.0f; - else if(val > 1.0f) - val = 1.0f; - - master_volume = val; -} - -static int snd_alloc_sound() -{ - if(first_free_sound < 0) - return -1; - int id = first_free_sound; - first_free_sound = sounds[id].next; - sounds[id].next = -1; - return id; -} - -static FILE *file = NULL; - -static int read_data(void *buffer, int size) -{ - return fread(buffer, 1, size, file); -} - -int snd_load_wv(const char *filename) -{ - sound_data snd; - int id = -1; - - char error[100]; - - file = fopen(filename, "rb"); - - WavpackContext *context = WavpackOpenFileInput(read_data, error); - if (context) - { - int samples = WavpackGetNumSamples(context); - int bitspersample = WavpackGetBitsPerSample(context); - unsigned int samplerate = WavpackGetSampleRate(context); - int channels = WavpackGetNumChannels(context); - - snd.channels = channels; - snd.rate = samplerate; - - if(snd.channels > 2) - { - dbg_msg("sound/wv", "file is not mono or stereo. filename='%s'", filename); - return -1; - } - - if(snd.rate != 44100) - { - dbg_msg("sound/wv", "file is %d Hz, not 44100 Hz. filename='%s'", snd.rate, filename); - return -1; - } - - if(bitspersample != 16) - { - dbg_msg("sound/wv", "bps is %d, not 16, filname='%s'", bitspersample, filename); - return -1; - } - - int *data = (int *)mem_alloc(4*samples*channels, 1); - WavpackUnpackSamples(context, data, samples); // TODO: check return value - int *src = data; - - snd.data = (short *)mem_alloc(2*samples*channels, 1); - short *dst = snd.data; - - for (int i = 0; i < samples*channels; i++) - *dst++ = (short)*src++; - - mem_free(data); - - snd.num_samples = samples; - snd.sustain_start = -1; - snd.sustain_end = -1; - snd.last_played = 0; - id = snd_alloc_sound(); - sounds[id].sound = snd; - } - else - { - dbg_msg("sound/wv", "failed to open %s: %s", filename, error); - } - - fclose(file); - file = NULL; - - if(id >= 0) - { - if(config.debug) - dbg_msg("sound/wv", "loaded %s", filename); - } - else - { - dbg_msg("sound/wv", "failed to load %s", filename); - } - - return id; -} - -int snd_load_wav(const char *filename) -{ - sound_data snd; - - // open file for reading - file_stream file; - if(!file.open_r(filename)) - { - dbg_msg("sound/wav", "failed to open file. filename='%s'", filename); - return -1; - } - - int id = -1; - int state = 0; - while(1) - { - // read chunk header - unsigned char head[8]; - if(file.read(head, sizeof(head)) != 8) - { - break; - } - - int chunk_size = head[4] | (head[5]<<8) | (head[6]<<16) | (head[7]<<24); - head[4] = 0; - - if(state == 0) - { - // read the riff and wave headers - if(head[0] != 'R' || head[1] != 'I' || head[2] != 'F' || head[3] != 'F') - { - dbg_msg("sound/wav", "not a RIFF file. filename='%s'", filename); - return -1; - } - - unsigned char type[4]; - file.read(type, 4); - - if(type[0] != 'W' || type[1] != 'A' || type[2] != 'V' || type[3] != 'E') - { - dbg_msg("sound/wav", "RIFF file is not a WAVE. filename='%s'", filename); - return -1; - } - - state++; - } - else if(state == 1) - { - // read the format chunk - if(head[0] == 'f' && head[1] == 'm' && head[2] == 't' && head[3] == ' ') - { - unsigned char fmt[16]; - if(file.read(fmt, sizeof(fmt)) != sizeof(fmt)) - { - dbg_msg("sound/wav", "failed to read format. filename='%s'", filename); - return -1; - } - - // decode format - int compression_code = fmt[0] | (fmt[1]<<8); - snd.channels = fmt[2] | (fmt[3]<<8); - snd.rate = fmt[4] | (fmt[5]<<8) | (fmt[6]<<16) | (fmt[7]<<24); - - if(compression_code != 1) - { - dbg_msg("sound/wav", "file is not uncompressed. filename='%s'", filename); - return -1; - } - - if(snd.channels > 2) - { - dbg_msg("sound/wav", "file is not mono or stereo. filename='%s'", filename); - return -1; - } - - if(snd.rate != 44100) - { - dbg_msg("sound/wav", "file is %d Hz, not 44100 Hz. filename='%s'", snd.rate, filename); - return -1; - } - - int bps = fmt[14] | (fmt[15]<<8); - if(bps != 16) - { - dbg_msg("sound/wav", "bps is %d, not 16, filname='%s'", bps, filename); - return -1; - } - - // next state - state++; - } - else - file.skip(chunk_size); - } - else if(state == 2) - { - // read the data - if(head[0] == 'd' && head[1] == 'a' && head[2] == 't' && head[3] == 'a') - { - snd.data = (short*)mem_alloc(chunk_size, 1); - file.read(snd.data, chunk_size); - snd.num_samples = chunk_size/(2); -#if defined(CONF_ARCH_ENDIAN_BIG) - for(unsigned i = 0; i < (unsigned)snd.num_samples; i++) - { - unsigned j = i << 1; - snd.data[i] = ((short)((char*)snd.data)[j]) + ((short)((char*)snd.data)[j+1] << 8); - } -#endif - snd.sustain_start = -1; - snd.sustain_end = -1; - snd.last_played = 0; - id = snd_alloc_sound(); - sounds[id].sound = snd; - state++; - } - else - file.skip(chunk_size); - } - else if(state == 3) - { - if(head[0] == 's' && head[1] == 'm' && head[2] == 'p' && head[3] == 'l') - { - unsigned char smpl[36]; - unsigned char loop[24]; - if(config.debug) - dbg_msg("sound/wav", "got sustain"); - - file.read(smpl, sizeof(smpl)); - unsigned num_loops = (smpl[28] | (smpl[29]<<8) | (smpl[30]<<16) | (smpl[31]<<24)); - unsigned skip = (smpl[32] | (smpl[33]<<8) | (smpl[34]<<16) | (smpl[35]<<24)); - - if(num_loops > 0) - { - file.read(loop, sizeof(loop)); - unsigned start = (loop[8] | (loop[9]<<8) | (loop[10]<<16) | (loop[11]<<24)); - unsigned end = (loop[12] | (loop[13]<<8) | (loop[14]<<16) | (loop[15]<<24)); - sounds[id].sound.sustain_start = start * sounds[id].sound.channels; - sounds[id].sound.sustain_end = end * sounds[id].sound.channels; - } - - if(num_loops > 1) - file.skip((num_loops-1) * sizeof(loop)); - - file.skip(skip); - state++; - } - else - file.skip(chunk_size); - } - else - file.skip(chunk_size); - } - - if(id >= 0) - { - if(config.debug) - dbg_msg("sound/wav", "loaded %s", filename); - } - else - dbg_msg("sound/wav", "failed to load %s", filename); - - return id; -} - -int snd_play(int id, int loop, float vol, float pan) -{ - if(id < 0) - { - dbg_msg("snd", "bad sound id"); - return -1; - } - - dbg_assert(sounds[id].sound.data != 0, "null sound"); - dbg_assert(sounds[id].next == -1, "sound isn't allocated"); - return mixer.play(&sounds[id].sound, loop, vol, pan); -} - -void snd_stop(int id) -{ - if(id >= 0) - mixer.stop(id); -} - -void snd_set_vol(int id, float vol) -{ - if(id >= 0) - mixer.set_vol(id, vol); -} diff --git a/src/engine/client/ui.c b/src/engine/client/ui.c new file mode 100644 index 00000000..c67b1b13 --- /dev/null +++ b/src/engine/client/ui.c @@ -0,0 +1,117 @@ +#include +#include +#include "ui.h" + +/******************************************************** + UI +*********************************************************/ +//static unsigned mouse_buttons_last = 0; + +struct pretty_font +{ + float m_CharStartTable[256]; + float m_CharEndTable[256]; + int font_texture; +}; + +extern struct pretty_font *current_font; + +static void *hot_item = 0; +static void *active_item = 0; +static void *last_active_item = 0; +static void *becomming_hot_item = 0; +static float mouse_x, mouse_y; // in gui space +static float mouse_wx, mouse_wy; // in world space +static unsigned mouse_buttons = 0; + +float ui_mouse_x() { return mouse_x; } +float ui_mouse_y() { return mouse_y; } +float ui_mouse_world_x() { return mouse_wx; } +float ui_mouse_world_y() { return mouse_wy; } +int ui_mouse_button(int index) { return (mouse_buttons>>index)&1; } + +void ui_set_hot_item(void *id) { becomming_hot_item = id; } +void ui_set_active_item(void *id) { active_item = id; if (id) last_active_item = id; } +void ui_clear_last_active_item() { last_active_item = 0; } +void *ui_hot_item() { return hot_item; } +void *ui_active_item() { return active_item; } +void *ui_last_active_item() { return last_active_item; } + +int ui_update(float mx, float my, float mwx, float mwy, int buttons) +{ + //mouse_buttons_last = mouse_buttons; + mouse_x = mx; + mouse_y = my; + mouse_wx = mwx; + mouse_wy = mwy; + mouse_buttons = buttons; + hot_item = becomming_hot_item; + if(active_item) + hot_item = active_item; + becomming_hot_item = 0; + return 0; +} + +/* +static int ui_mouse_button_released(int index) +{ + return ((mouse_buttons_last>>index)&1) && !(); +}*/ + +int ui_mouse_inside(float x, float y, float w, float h) +{ + if(mouse_x >= x && mouse_x <= x+w && mouse_y >= y && mouse_y <= y+h) + return 1; + return 0; +} + +void ui_do_image(int texture, float x, float y, float w, float h) +{ + gfx_blend_normal(); + gfx_texture_set(texture); + gfx_quads_begin(); + gfx_quads_setcolor(1,1,1,1); + gfx_quads_setsubset( + 0.0f, // startx + 0.0f, // starty + 1.0f, // endx + 1.0f); // endy + gfx_quads_drawTL(x,y,w,h); + gfx_quads_end(); +} + +void ui_do_label(float x, float y, const char *text, float size) +{ + gfx_blend_normal(); + gfx_texture_set(current_font->font_texture); + gfx_pretty_text(x, y, size, text, -1); +} + +int ui_do_button(void *id, const char *text, int checked, float x, float y, float w, float h, draw_button_callback draw_func, void *extra) +{ + // logic + int r = 0; + int inside = ui_mouse_inside(x,y,w,h); + + if(ui_active_item() == id) + { + if(!ui_mouse_button(0)) + { + if(inside) + r = 1; + ui_set_active_item(0); + } + } + else if(ui_hot_item() == id) + { + if(ui_mouse_button(0)) + ui_set_active_item(id); + } + + if(inside) + ui_set_hot_item(id); + + draw_func(id, text, checked, x, y, w, h, extra); + return r; +} + diff --git a/src/engine/client/ui.cpp b/src/engine/client/ui.cpp deleted file mode 100644 index 7b309539..00000000 --- a/src/engine/client/ui.cpp +++ /dev/null @@ -1,122 +0,0 @@ -#include -#include -#include "ui.h" - -/******************************************************** - UI -*********************************************************/ -//static unsigned mouse_buttons_last = 0; - -struct pretty_font -{ - float m_CharStartTable[256]; - float m_CharEndTable[256]; - int font_texture; -}; - -extern pretty_font *current_font; - -static void *hot_item = 0; -static void *active_item = 0; -static void *last_active_item = 0; -static void *becomming_hot_item = 0; -static float mouse_x, mouse_y; // in gui space -static float mouse_wx, mouse_wy; // in world space -static unsigned mouse_buttons = 0; - -float ui_mouse_x() { return mouse_x; } -float ui_mouse_y() { return mouse_y; } -float ui_mouse_world_x() { return mouse_wx; } -float ui_mouse_world_y() { return mouse_wy; } -int ui_mouse_button(int index) { return (mouse_buttons>>index)&1; } - -void ui_set_hot_item(void *id) { becomming_hot_item = id; } -void ui_set_active_item(void *id) { active_item = id; if (id) last_active_item = id; } -void ui_clear_last_active_item() { last_active_item = 0; } -void *ui_hot_item() { return hot_item; } -void *ui_active_item() { return active_item; } -void *ui_last_active_item() { return last_active_item; } - -int ui_update(float mx, float my, float mwx, float mwy, int buttons) -{ - //mouse_buttons_last = mouse_buttons; - mouse_x = mx; - mouse_y = my; - mouse_wx = mwx; - mouse_wy = mwy; - mouse_buttons = buttons; - hot_item = becomming_hot_item; - if(active_item) - hot_item = active_item; - becomming_hot_item = 0; - return 0; -} - -/* -static int ui_mouse_button_released(int index) -{ - return ((mouse_buttons_last>>index)&1) && !(); -}*/ - -int ui_mouse_inside(float x, float y, float w, float h) -{ - if(mouse_x >= x && mouse_x <= x+w && mouse_y >= y && mouse_y <= y+h) - return 1; - return 0; -} - -void ui_do_image(int texture, float x, float y, float w, float h) -{ - gfx_blend_normal(); - gfx_texture_set(texture); - gfx_quads_begin(); - gfx_quads_setcolor(1,1,1,1); - gfx_quads_setsubset( - 0.0f, // startx - 0.0f, // starty - 1.0f, // endx - 1.0f); // endy - gfx_quads_drawTL(x,y,w,h); - gfx_quads_end(); -} - -void ui_do_label(float x, float y, const char *text, float size) -{ - gfx_blend_normal(); - gfx_texture_set(current_font->font_texture); - gfx_pretty_text(x, y, size, text); -} - -int ui_do_button(void *id, const char *text, int checked, float x, float y, float w, float h, draw_button_callback draw_func, void *extra) -{ - // logic - int r = 0; - int inside = ui_mouse_inside(x,y,w,h); - - if(ui_active_item() == id) - { - if(!ui_mouse_button(0)) - { - if(inside) - r = 1; - ui_set_active_item(0); - } - } - else if(ui_hot_item() == id) - { - if(ui_mouse_button(0)) - ui_set_active_item(id); - } - - if(inside) - ui_set_hot_item(id); - - draw_func(id, text, checked, x, y, w, h, extra); - return r; -} - -int ui_do_button(void *id, const char *text, int checked, float x, float y, float w, float h, draw_button_callback draw_func) -{ - return ui_do_button(id, text, checked, x, y, w, h, draw_func, 0x0); -} - diff --git a/src/engine/client/ui.h b/src/engine/client/ui.h index fb9208c1..31757b3b 100644 --- a/src/engine/client/ui.h +++ b/src/engine/client/ui.h @@ -1,12 +1,9 @@ #ifndef _UI_H #define _UI_H -/* -extern void *hot_item; -extern void *active_item; -extern void *becomming_hot_item; -extern float mouse_x, mouse_y; // in gui space -extern float mouse_wx, mouse_wy; // in world space -extern unsigned mouse_buttons;*/ + +#ifdef __cplusplus +extern "C" { +#endif int ui_update(float mx, float my, float mwx, float mwy, int buttons); @@ -30,6 +27,9 @@ typedef void (*draw_button_callback)(void *id, const char *text, int checked, fl void ui_do_image(int texture, float x, float y, float w, float h); void ui_do_label(float x, float y, const char *text, float size); int ui_do_button(void *id, const char *text, int checked, float x, float y, float w, float h, draw_button_callback draw_func, void *extra); -int ui_do_button(void *id, const char *text, int checked, float x, float y, float w, float h, draw_button_callback draw_func); + +#ifdef __cplusplus +} +#endif #endif -- cgit 1.4.1