ass2iqe: animlist support + sample anim file
parent
fb2f7ee532
commit
8b6f07e5c5
|
@ -10,36 +10,6 @@
|
|||
|
||||
#include "v4k.h"
|
||||
|
||||
typedef struct anims_t {
|
||||
int inuse; // animation number in use
|
||||
float speed; // x1.00
|
||||
array(anim_t) anims; // [begin,end,flags] frames of every animation in set
|
||||
array(mat44) M; // instanced transforms
|
||||
} anims_t;
|
||||
|
||||
anims_t animations(const char *pathfile, int flags) {
|
||||
anims_t a = {0};
|
||||
char *anim_file = vfs_read(pathfile);
|
||||
for each_substring(anim_file, "\r\n", anim) {
|
||||
int from, to;
|
||||
char anim_name[128] = {0};
|
||||
if( sscanf(anim, "%*s %d-%d %127[^\r\n]", &from, &to, anim_name) != 3) continue;
|
||||
array_push(a.anims, !!strstri(anim_name, "loop") ? loop(from, to, 0, 0) : clip(from, to, 0, 0)); // [from,to,flags]
|
||||
array_back(a.anims)->name = strswap(strswap(strswap(STRDUP(anim_name), "Loop", ""), "loop", ""), "()", "");
|
||||
}
|
||||
array_resize(a.M, 32*32);
|
||||
for(int z = 0, i = 0; z < 32; ++z) {
|
||||
for(int x = 0; x < 32; ++x, ++i) {
|
||||
vec3 p = vec3(-x*3,0,-z*3);
|
||||
vec3 r = vec3(0,0,0);
|
||||
vec3 s = vec3(2,2,2);
|
||||
compose44(a.M[i], p, eulerq(r), s);
|
||||
}
|
||||
}
|
||||
a.speed = 1.0;
|
||||
return a;
|
||||
}
|
||||
|
||||
int main() {
|
||||
bool do_showaabb = 0;
|
||||
bool do_showbones = 0;
|
||||
|
@ -52,8 +22,10 @@ int main() {
|
|||
|
||||
camera_t cam = camera();
|
||||
skybox_t sky = skybox("cubemaps/stardust", 0);
|
||||
model_t mdl = model("kgirls01.fbx", 0);
|
||||
anims_t a = animations("kgirl/animlist.txt", 0);
|
||||
model_t mdl = model("Stan.fbx", 0);
|
||||
anims_t a = animations("Stan.anim", 0);
|
||||
|
||||
|
||||
|
||||
// load all postfx files in all subdirs
|
||||
fx_load("fx**.fs");
|
||||
|
@ -111,7 +83,7 @@ int main() {
|
|||
}
|
||||
|
||||
if( do_showgizmo ) {
|
||||
static vec3 p = {0,0,0}, r = {0,0,0}, s = {2,2,2};
|
||||
static vec3 p = {0,0,0}, r = {0,-90,0}, s = {1,1,1};
|
||||
gizmo(&p, &r, &s);
|
||||
compose44(a.M[0], p, eulerq(r), s);
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -1,716 +0,0 @@
|
|||
// high-level, socket-less networking api. inspired by Quake, MPI and RenderBuckets theories.
|
||||
// - rlyeh, public domain
|
||||
//
|
||||
// Usage:
|
||||
// 1. configure networked memory buffers with flags (world, player1, player2, etc). network_buffer();
|
||||
// 2. then during game loop:
|
||||
// - modify your buffers as much as needed.
|
||||
// - sync buffers at least once per frame. network_sync();
|
||||
// - render your world
|
||||
// 3. optionally, monitor network status & variables. network_get();
|
||||
//
|
||||
// @todo: maybe network_send(msg) + msg *network_recv(); instead of event queue of network_sync() ?
|
||||
|
||||
//enum { NETWORK_HANDSHAKE, NETWORK_ENCRYPT, NETWORK_VERSIONED, NETWORK_CHECKSUM }; // negotiation
|
||||
//enum { NETWORK_TCP, NETWORK_UDP, NETWORK_KCP, NETWORK_ENET, NETWORK_WEBSOCKET }; // transport, where
|
||||
enum { NETWORK_BIND = 2, NETWORK_CONNECT = 4, NETWORK_NOFAIL = 8 };
|
||||
enum { MAX_CLIENTS = 32 };
|
||||
API void network_create(const char *ip, const char *port, unsigned flags); // both ip and port can be null
|
||||
|
||||
//enum { NETWORK_LOSSY, NETWORK_COMPRESS }; // post-processes
|
||||
//enum { NETWORK_UNRELIABLE, NETWORK_UNORDERED, NETWORK_PRIORITY }; // how
|
||||
//enum { NETWORK_PREDICT, NETWORK_RECONCILE, NETWORK_INTERPOLATE, NETWORK_COMPENSATE }; // time authority, when
|
||||
//enum { NETWORK_LAGS, NETWORK_DROPS, NETWORK_THROTTLES, NETWORK_DUPES }; // quality sim, how much
|
||||
//enum { NETWORK_CONST = 1, NETWORK_64,NETWORK_32,NETWORK_16,NETWORK_8, NETWORK_FLT, NETWORK_STR, NETWORK_BLOB }; // type, what
|
||||
enum { NETWORK_SEND = 2, NETWORK_RECV = 4 };
|
||||
API void* network_buffer(void *ptr, unsigned sz, unsigned flags, int64_t rank); // configures a shared/networked buffer
|
||||
API char** network_sync(unsigned timeout_ms); // syncs all buffers & returns null-terminated list of network events
|
||||
|
||||
enum { NETWORK_RANK = 0 }; // [0..N] where 0 is server
|
||||
enum { NETWORK_PING = 1 }; // NETWORK_BANDWIDTH, NETWORK_QUALITY };
|
||||
enum { NETWORK_PORT = 2, NETWORK_IP, NETWORK_LIVE };
|
||||
//enum { NETWORK_USERID, NETWORK_SALT, NETWORK_COUNT/*N users*/ /*...*/,
|
||||
API int64_t network_get(uint64_t key);
|
||||
API int64_t network_put(uint64_t key, int64_t value);
|
||||
|
||||
API void network_rpc(const char *signature, void *function);
|
||||
API void network_rpc_send_to(int64_t rank, unsigned id, const char *cmdline);
|
||||
API void network_rpc_send(unsigned id, const char *cmdline);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// low-level api (sockets based)
|
||||
|
||||
API bool server_bind(int max_clients, int port);
|
||||
API void server_poll();
|
||||
API void server_broadcast_bin(const void *ptr, int len);
|
||||
API void server_broadcast(const char *msg);
|
||||
API void server_terminate();
|
||||
API void server_send(int64_t handle, const char *msg);
|
||||
API void server_send_bin(int64_t handle, const void *ptr, int len);
|
||||
API void server_drop(int64_t handle);
|
||||
|
||||
API int64_t client_join(const char *ip, int port);
|
||||
#define client_send(msg) server_broadcast(msg)
|
||||
#define client_send_bin(ptr,len) server_broadcast_bin(ptr, len)
|
||||
#define client_terminate() server_terminate()
|
||||
|
||||
#define ANYHOST_IPV4 "0.0.0.0"
|
||||
#define ANYHOST_IPV6 "::0"
|
||||
|
||||
#define LOCALHOST_IPV4 "127.0.0.1"
|
||||
#define LOCALHOST_IPV6 "::1"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// implementation
|
||||
|
||||
typedef void* (*rpc_function)();
|
||||
|
||||
typedef struct rpc_call {
|
||||
char *method;
|
||||
rpc_function function;
|
||||
uint64_t function_hash;
|
||||
} rpc_call;
|
||||
|
||||
#define RPC_SIGNATURE_i_iii UINT64_C(0x78409099752fa48a) // printf("%llx\n, HASH_STR("int(int,int,int)"));
|
||||
#define RPC_SIGNATURE_i_ii UINT64_C(0x258290edf43985a5) // printf("%llx\n, HASH_STR("int(int,int)"));
|
||||
#define RPC_SIGNATURE_s_s UINT64_C(0x97deedd17d9afb12) // printf("%llx\n, HASH_STR("char*(char*)"));
|
||||
#define RPC_SIGNATURE_s_v UINT64_C(0x09c16a1242049b80) // printf("%llx\n, HASH_STR("char*(void)"));
|
||||
|
||||
static
|
||||
rpc_call rpc_new_call(const char *signature, rpc_function function) {
|
||||
if( signature && function ) {
|
||||
array(char*)tokens = strsplit(signature, "(,)");
|
||||
if( array_count(tokens) >= 1 ) {
|
||||
char *method = strrchr(tokens[0], ' ')+1;
|
||||
char *rettype = va("%.*s", (int)(method - tokens[0] - 1), tokens[0]);
|
||||
int num_args = array_count(tokens) - 1;
|
||||
char* hash_sig = va("%s(%s)", rettype, num_args ? (array_pop_front(tokens), strjoin(tokens, ",")) : "void");
|
||||
uint64_t hash = hash_str(hash_sig);
|
||||
method = va("%s%d", method, num_args );
|
||||
#if RPC_DEBUG
|
||||
printf("%p %p %s `%s` %s(", function, (void*)hash, rettype, hash_sig, method); for(int i = 0, end = array_count(tokens); i < end; ++i) printf("%s%s", tokens[i], i == (end-1)? "":", "); puts(");");
|
||||
#endif
|
||||
return (rpc_call) { strdup(method), function, hash }; // LEAK
|
||||
}
|
||||
}
|
||||
return (rpc_call) {0};
|
||||
}
|
||||
|
||||
static map(char*, rpc_call) rpc_calls = 0;
|
||||
|
||||
static
|
||||
void rpc_insert(const char *signature, void *function ) {
|
||||
rpc_call call = rpc_new_call(signature, function);
|
||||
if( call.method ) {
|
||||
if( !rpc_calls ) map_init(rpc_calls, less_str, hash_str);
|
||||
if( map_find(rpc_calls, call.method)) {
|
||||
map_erase(rpc_calls, call.method);
|
||||
}
|
||||
map_insert(rpc_calls, call.method, call);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
char *rpc_full(unsigned id, const char* method, unsigned num_args, char *args[]) {
|
||||
#if RPC_DEBUG
|
||||
printf("id:%x method:%s args:", id, method );
|
||||
for( int i = 0; i < num_args; ++i ) printf("%s,", args[i]); puts("");
|
||||
#endif
|
||||
|
||||
method = va("%s%d", method, num_args);
|
||||
rpc_call *found = map_find(rpc_calls, (char*)method);
|
||||
if( found ) {
|
||||
switch(found->function_hash) {
|
||||
case RPC_SIGNATURE_i_iii: return va("%d %d", id, (int)(uintptr_t)found->function(atoi(args[0]), atoi(args[1]), atoi(args[2])) );
|
||||
case RPC_SIGNATURE_i_ii: return va("%d %d", id, (int)(uintptr_t)found->function(atoi(args[0]), atoi(args[1])) );
|
||||
case RPC_SIGNATURE_s_s: return va("%d %s", id, (char*)found->function(args[0]) );
|
||||
case RPC_SIGNATURE_s_v: return va("%d %s", id, (char*)found->function() );
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
return va("%d -1", id);
|
||||
}
|
||||
|
||||
static
|
||||
array(char*) rpc_parse_args( const char *cmdline, bool quote_whitespaces ) { // parse cmdline arguments. must array_free() after use
|
||||
// - supports quotes: "abc" "abc def" "abc \"def\"" "abc \"def\"""ghi" etc.
|
||||
// - #comments removed
|
||||
array(char*) args = 0; // LEAK
|
||||
for( int i = 0; cmdline[i]; ) {
|
||||
char buf[256] = {0}, *ptr = buf;
|
||||
while(cmdline[i] && isspace(cmdline[i])) ++i;
|
||||
bool quoted = cmdline[i] == '\"';
|
||||
if( quoted ) {
|
||||
while(cmdline[++i]) {
|
||||
char ch = cmdline[i];
|
||||
/**/ if (ch == '\\' && cmdline[i + 1] == '\"') *ptr++ = '\"', ++i;
|
||||
else if (ch == '\"' && cmdline[i + 1] == '\"') ++i;
|
||||
else if (ch == '\"' && (!cmdline[i + 1] || isspace(cmdline[i + 1]))) {
|
||||
++i; break;
|
||||
}
|
||||
else *ptr++ = ch;
|
||||
}
|
||||
} else {
|
||||
while(cmdline[i] && !isspace(cmdline[i])) *ptr++ = cmdline[i++];
|
||||
}
|
||||
if (buf[0] && buf[0] != '#') { // exclude empty args + comments
|
||||
if( quote_whitespaces && quoted )
|
||||
array_push(args, va("\"%s\"",buf));
|
||||
else
|
||||
array_push(args, va("%s",buf));
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
static
|
||||
char* rpc(unsigned id, const char* cmdline) {
|
||||
array(char*) args = rpc_parse_args(cmdline, false);
|
||||
int num_args = array_count(args);
|
||||
char *ret = num_args ? rpc_full(id, args[0], num_args - 1, &args[1]) : rpc_full(id, "", 0, NULL);
|
||||
array_free(args);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void enet_quit(void) {
|
||||
do_once {
|
||||
// enet_deinitialize();
|
||||
}
|
||||
}
|
||||
static void enet_init() {
|
||||
do_once {
|
||||
if( enet_initialize() != 0 ) {
|
||||
PANIC("cannot initialize enet");
|
||||
}
|
||||
atexit( enet_quit );
|
||||
}
|
||||
}
|
||||
|
||||
static ENetHost *Server;
|
||||
static map(ENetPeer *, int64_t) clients;
|
||||
static map(int64_t, ENetPeer *) peers;
|
||||
static int64_t next_client_id = 1; // assumes ID 0 is server
|
||||
enum { MSG_INIT, MSG_BUF, MSG_RPC, MSG_RPC_RESP };
|
||||
|
||||
bool server_bind(int max_clients, int port) {
|
||||
map_init(clients, less_64, hash_64);
|
||||
map_init(peers, less_64, hash_64);
|
||||
assert(port == 0 || (port > 1024 && port < 65500));
|
||||
ENetAddress address = {0};
|
||||
address.host = ENET_HOST_ANY;
|
||||
address.port = port;
|
||||
Server = enet_host_create(&address, max_clients, 2 /*channels*/, 0 /*in bandwidth*/, 0 /*out bandwidth*/);
|
||||
return Server != NULL;
|
||||
}
|
||||
|
||||
static
|
||||
void server_drop_client(int64_t handle) {
|
||||
map_erase(clients, *(ENetPeer **)map_find(peers, handle));
|
||||
map_erase(peers, *(int64_t *)handle);
|
||||
}
|
||||
|
||||
static
|
||||
void server_drop_client_peer(ENetPeer *peer) {
|
||||
map_erase(peers, *(int64_t *)map_find(clients, peer));
|
||||
map_erase(clients, peer);
|
||||
}
|
||||
|
||||
void server_poll() {
|
||||
ENetEvent event;
|
||||
while( enet_host_service(Server, &event, 2 /*timeout,ms*/) > 0 ) {
|
||||
switch (event.type) {
|
||||
case ENET_EVENT_TYPE_CONNECT:;
|
||||
char ip[128]; enet_peer_get_ip(event.peer, ip, 128);
|
||||
PRINTF( "A new client connected from ::%s:%u.\n", ip, event.peer->address.port );
|
||||
/* Store any relevant client information here. */
|
||||
event.peer->data = "Client information";
|
||||
|
||||
int64_t client_id = next_client_id++;
|
||||
map_find_or_add(clients, event.peer, client_id);
|
||||
map_find_or_add(peers, client_id, event.peer);
|
||||
break;
|
||||
case ENET_EVENT_TYPE_RECEIVE:
|
||||
PRINTF( "A packet of length %zu containing %s was received from %s on channel %u.\n",
|
||||
event.packet->dataLength,
|
||||
event.packet->data,
|
||||
(char *)event.peer->data,
|
||||
event.channelID );
|
||||
|
||||
char *dbg = (char *)event.peer->data;
|
||||
char *ptr = event.packet->data;
|
||||
unsigned sz = event.packet->dataLength;
|
||||
|
||||
uint32_t mid = *(uint32_t*)ptr;
|
||||
ptr += 4;
|
||||
|
||||
// @todo: propagate event to user
|
||||
switch (mid) {
|
||||
case MSG_INIT: {
|
||||
uint64_t *cid = map_find(clients, event.peer);
|
||||
if (cid) {
|
||||
char init_msg[12];
|
||||
*(uint32_t*)&init_msg[0] = MSG_INIT;
|
||||
*(uint64_t*)&init_msg[4] = *cid;
|
||||
ENetPacket *packet = enet_packet_create(init_msg, 12, ENET_PACKET_FLAG_RELIABLE);
|
||||
enet_peer_send(event.peer, 0, packet);
|
||||
} else {
|
||||
PRINTF("ignoring unk MSG_INIT client packet.\n");
|
||||
}
|
||||
} break;
|
||||
case MSG_RPC:
|
||||
case MSG_RPC_RESP:
|
||||
// @todo: process and send a response back
|
||||
break;
|
||||
default:
|
||||
PRINTF("recving unk %d sz %d from peer %s\n", mid, sz, dbg);
|
||||
}
|
||||
|
||||
/* Clean up the packet now that we're done using it. */
|
||||
enet_packet_destroy( event.packet );
|
||||
break;
|
||||
|
||||
case ENET_EVENT_TYPE_DISCONNECT:
|
||||
PRINTF( "%s disconnected.\n", (char *)event.peer->data );
|
||||
/* Reset the peer's client information. */
|
||||
event.peer->data = NULL;
|
||||
server_drop_client_peer(event.peer);
|
||||
break;
|
||||
|
||||
case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT:
|
||||
PRINTF( "%s timeout.\n", (char *)event.peer->data );
|
||||
event.peer->data = NULL;
|
||||
server_drop_client_peer(event.peer);
|
||||
break;
|
||||
|
||||
case ENET_EVENT_TYPE_NONE: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void client_poll() {
|
||||
ENetEvent event;
|
||||
while( enet_host_service(Server, &event, 2 /*timeout,ms*/) > 0 ) {
|
||||
switch (event.type) {
|
||||
case ENET_EVENT_TYPE_CONNECT:;
|
||||
break;
|
||||
case ENET_EVENT_TYPE_RECEIVE:
|
||||
PRINTF( "A packet of length %zu containing %s was received from %s on channel %u.\n",
|
||||
event.packet->dataLength,
|
||||
event.packet->data,
|
||||
(char *)event.peer->data,
|
||||
event.channelID );
|
||||
|
||||
char *dbg = (char *)event.peer->data;
|
||||
char *ptr = event.packet->data;
|
||||
unsigned sz = event.packet->dataLength;
|
||||
|
||||
uint32_t mid = *(uint32_t*)ptr;
|
||||
ptr += 4;
|
||||
|
||||
// @todo: propagate event to user
|
||||
switch (mid) {
|
||||
case MSG_INIT:
|
||||
/* handled during client_join */
|
||||
break;
|
||||
case MSG_RPC:
|
||||
case MSG_RPC_RESP:
|
||||
// @todo: process and send a response back
|
||||
break;
|
||||
default:
|
||||
PRINTF("recving unk %d sz %d from peer %s\n", mid, sz, dbg);
|
||||
}
|
||||
|
||||
/* Clean up the packet now that we're done using it. */
|
||||
enet_packet_destroy( event.packet );
|
||||
break;
|
||||
|
||||
case ENET_EVENT_TYPE_DISCONNECT:
|
||||
PRINTF( "%s disconnected.\n", (char *)event.peer->data );
|
||||
/* Reset the peer's client information. */
|
||||
event.peer->data = NULL;
|
||||
server_drop_client_peer(event.peer);
|
||||
break;
|
||||
|
||||
case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT:
|
||||
PRINTF( "%s timeout.\n", (char *)event.peer->data );
|
||||
event.peer->data = NULL;
|
||||
server_drop_client_peer(event.peer);
|
||||
break;
|
||||
|
||||
case ENET_EVENT_TYPE_NONE: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void server_broadcast_bin(const void *msg, int len) {
|
||||
ENetPacket *packet = enet_packet_create(msg, len, ENET_PACKET_FLAG_RELIABLE);
|
||||
enet_host_broadcast(Server, 0, packet);
|
||||
//enet_host_flush(Server); // flush if needed
|
||||
}
|
||||
void server_broadcast(const char *msg) {
|
||||
server_broadcast_bin(msg, strlen(msg)+1);
|
||||
}
|
||||
void server_terminate() {
|
||||
enet_host_destroy(Server);
|
||||
Server = 0;
|
||||
}
|
||||
|
||||
volatile int client_join_connected = 0;
|
||||
static int client_join_threaded(void *userdata) {
|
||||
ENetHost *host = (ENetHost *)userdata;
|
||||
|
||||
ENetPacket *packet = enet_packet_create("", 1, ENET_PACKET_FLAG_RELIABLE);
|
||||
enet_host_broadcast(Server, 0, packet);
|
||||
|
||||
/* Wait up to 5 seconds for the connection attempt to succeed. */
|
||||
ENetEvent event;
|
||||
client_join_connected = 0;
|
||||
client_join_connected = enet_host_service(host, &event, 5000) > 0 && event.type == ENET_EVENT_TYPE_CONNECT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int64_t client_join(const char *ip, int port) {
|
||||
assert(port > 1024 && port < 65500);
|
||||
ENetAddress address = {0};
|
||||
// address.host = ENET_HOST_ANY;
|
||||
enet_address_set_host(&address, !strcmp(ip, "localhost") ? "127.0.0.1" : ip);
|
||||
address.port = port;
|
||||
|
||||
ENetHost *host = enet_host_create(NULL, 1 /*outgoing connections*/, 2 /*channels*/, 0 /*in bandwidth*/, 0 /*out bandwidth*/);
|
||||
if(!host) return -1;
|
||||
ENetPeer *peer = enet_host_connect(host, &address, 2, 0);
|
||||
if(!peer) return -1;
|
||||
Server = host;
|
||||
|
||||
#if 1
|
||||
#if 0
|
||||
// sync wait (not working in localhost, unless threaded)
|
||||
thread_ptr_t th = thread_init(client_join_threaded, host, "client_join_threaded()", 0 );
|
||||
thread_join( th );
|
||||
thread_destroy( th );
|
||||
#else
|
||||
ENetEvent event;
|
||||
bool client_join_connected = enet_host_service(host, &event, 5000) > 0 && event.type == ENET_EVENT_TYPE_CONNECT;
|
||||
#endif
|
||||
if(!client_join_connected) { enet_peer_reset(peer); return -1; }
|
||||
#endif
|
||||
|
||||
// ask for server slot
|
||||
char init_msg[4]; *(uint32_t*)init_msg = MSG_INIT;
|
||||
server_broadcast_bin(init_msg, sizeof(init_msg));
|
||||
|
||||
// wait for the response
|
||||
bool msg_received = enet_host_service(host, &event, 5000) > 0 && event.type == ENET_EVENT_TYPE_RECEIVE;
|
||||
if (!msg_received) { enet_peer_reset(peer); return -1; }
|
||||
|
||||
char *ptr = (char *)event.packet->data;
|
||||
int64_t cid = -1;
|
||||
|
||||
// decapsulate incoming packet.
|
||||
uint32_t mid = *(uint32_t*)(ptr + 0);
|
||||
ptr += 4;
|
||||
|
||||
switch (mid) {
|
||||
case MSG_INIT:
|
||||
cid = *(int64_t*)ptr;
|
||||
break;
|
||||
default:
|
||||
enet_peer_reset(peer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Clean up the packet now that we're done using it. */
|
||||
enet_packet_destroy( event.packet );
|
||||
|
||||
return cid;
|
||||
}
|
||||
void server_drop(int64_t handle) {
|
||||
enet_peer_disconnect_now(*(ENetPeer **)map_find(peers, handle), 0);
|
||||
server_drop_client(handle);
|
||||
}
|
||||
|
||||
void server_send_bin(int64_t handle, const void *ptr, int len) {
|
||||
ENetPacket *packet = enet_packet_create(ptr, len, ENET_PACKET_FLAG_RELIABLE);
|
||||
enet_peer_send(*(ENetPeer **)map_find(peers, handle), 0, packet);
|
||||
}
|
||||
|
||||
void server_send(int64_t handle, const char *msg) {
|
||||
server_send_bin(handle, msg, strlen(msg)+1);
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
typedef struct netbuffer_t {
|
||||
int64_t owner;
|
||||
void *ptr;
|
||||
unsigned sz;
|
||||
unsigned flags;
|
||||
} netbuffer_t;
|
||||
|
||||
static array(char*) events; // @todo: make event 128 bytes max?
|
||||
static array(int64_t) values; // @todo: map<key,values> instead?
|
||||
static map( int64_t, array(netbuffer_t) ) buffers; // map<client,array<netbuffer>>
|
||||
|
||||
void network_create(const char *ip, const char *port_, unsigned flags) {
|
||||
if (buffers) map_clear(buffers);
|
||||
do_once {
|
||||
array_resize(values, 128);
|
||||
map_init(buffers, less_64, hash_64);
|
||||
|
||||
enet_init();
|
||||
}
|
||||
|
||||
ip = ip ? ip : "0.0.0.0";
|
||||
int port = atoi(port_ ? port_ : "1234");
|
||||
|
||||
// network_put(NETWORK_IP, 0x7F000001); // 127.0.0.1
|
||||
network_put(NETWORK_PORT, port);
|
||||
network_put(NETWORK_LIVE, -1);
|
||||
|
||||
if( !(flags&NETWORK_CONNECT) || flags&NETWORK_BIND ) {
|
||||
// server, else client
|
||||
PRINTF("Trying to bind server, else we connect as a client...\n");
|
||||
network_put(NETWORK_RANK, 0);
|
||||
if( server_bind(MAX_CLIENTS, port) ) {
|
||||
network_put(NETWORK_LIVE, 1);
|
||||
PRINTF("Server bound\n");
|
||||
} else {
|
||||
network_put(NETWORK_RANK, -1); /* unassigned until we connect successfully */
|
||||
int64_t socket = client_join(ip, port);
|
||||
if( socket >= 0 ) {
|
||||
PRINTF("Client connected, id %lld\n", socket);
|
||||
network_put(NETWORK_LIVE, 1);
|
||||
network_put(NETWORK_RANK, socket);
|
||||
} else {
|
||||
PRINTF("!Client conn failed\n");
|
||||
network_put(NETWORK_LIVE, 0);
|
||||
|
||||
if (!(flags&NETWORK_NOFAIL))
|
||||
PANIC("cannot neither connect to %s:%d, nor create a server", ip, port);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// client only
|
||||
PRINTF("Connecting to server...\n");
|
||||
network_put(NETWORK_RANK, -1); /* unassigned until we connect successfully */
|
||||
int64_t socket = client_join(ip, port);
|
||||
if( socket > 0 ) {
|
||||
PRINTF("Client connected, id %lld\n", socket);
|
||||
network_put(NETWORK_LIVE, 1);
|
||||
network_put(NETWORK_RANK, socket);
|
||||
} else {
|
||||
PRINTF("!Client conn failed\n");
|
||||
network_put(NETWORK_LIVE, 0);
|
||||
if (!(flags&NETWORK_NOFAIL))
|
||||
PANIC("cannot connect to server %s:%d", ip, port);
|
||||
}
|
||||
}
|
||||
|
||||
PRINTF("Network rank:%lld ip:%s port:%lld\n", network_get(NETWORK_RANK), ip, network_get(NETWORK_PORT));
|
||||
}
|
||||
|
||||
int64_t network_put(uint64_t key, int64_t value) {
|
||||
int64_t *found = key < array_count(values) ? &values[key] : NULL;
|
||||
if(found) *found = value;
|
||||
return value;
|
||||
}
|
||||
int64_t network_get(uint64_t key) {
|
||||
int64_t *found = key < array_count(values) ? &values[key] : NULL;
|
||||
return found ? *found : 0;
|
||||
}
|
||||
|
||||
void* network_buffer(void *ptr, unsigned sz, unsigned flags, int64_t rank) {
|
||||
assert(flags);
|
||||
array(netbuffer_t) *found = map_find_or_add(buffers, rank, NULL);
|
||||
|
||||
netbuffer_t nb;
|
||||
nb.owner = rank;
|
||||
nb.ptr = ptr;
|
||||
nb.sz = sz;
|
||||
nb.flags = flags;
|
||||
array_push(*found, nb);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
char** network_sync(unsigned timeout_ms) {
|
||||
array_clear(events);
|
||||
|
||||
int64_t whoami = network_get(NETWORK_RANK);
|
||||
bool is_server = whoami == 0;
|
||||
bool is_client = !is_server;
|
||||
if(timeout_ms < 2) timeout_ms = 2;
|
||||
// sleep_ms(timeout_ms); // @fixme. server only?
|
||||
|
||||
// Split buffers into clients @todo
|
||||
// clients need to do this before network polling; servers should do this after polling.
|
||||
map_foreach(buffers, int64_t, rank, array(netbuffer_t), list) {
|
||||
for(int i = 0, end = array_count(list); i < end; ++i) {
|
||||
netbuffer_t *nb = &list[i];
|
||||
if (!is_server && !(nb->flags & NETWORK_SEND))
|
||||
continue;
|
||||
static array(char) encapsulate;
|
||||
array_resize(encapsulate, nb->sz + 28);
|
||||
uint32_t *mid = (uint32_t*)&encapsulate[0]; *mid = MSG_BUF;
|
||||
uint64_t *st = (uint64_t*)&encapsulate[4]; *st = nb->flags;
|
||||
uint32_t *idx = (uint32_t*)&encapsulate[12]; *idx = i;
|
||||
uint32_t *len = (uint32_t*)&encapsulate[16]; *len = nb->sz;
|
||||
uint64_t *who = (uint64_t*)&encapsulate[20]; *who = nb->owner;
|
||||
// PRINTF("sending %llx %u %lld %u\n", *st, *idx, *who, *len);
|
||||
memcpy(&encapsulate[28], nb->ptr, nb->sz);
|
||||
server_broadcast_bin(&encapsulate[0], nb->sz + 28);
|
||||
}
|
||||
}
|
||||
|
||||
// network poll
|
||||
for( ENetEvent event; Server && enet_host_service(Server, &event, timeout_ms) > 0; ) {
|
||||
char *msg = 0;
|
||||
char ip[128]; enet_peer_get_ip(event.peer, ip, 128);
|
||||
|
||||
switch (event.type) {
|
||||
default: // case ENET_EVENT_TYPE_NONE:
|
||||
break;
|
||||
|
||||
case ENET_EVENT_TYPE_CONNECT:;
|
||||
msg = va( "A new client connected from ::%s:%u", ip, event.peer->address.port );
|
||||
/* Store any relevant client information here. */
|
||||
event.peer->data = "Client information";
|
||||
|
||||
/* ensure we have free slot for client */
|
||||
if (map_count(clients) >= MAX_CLIENTS) {
|
||||
msg = va("%s\n", "Server is at maximum capacity, disconnecting the peer...");
|
||||
enet_peer_disconnect_now(event.peer, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
int64_t client_id = next_client_id++;
|
||||
map_find_or_add(clients, event.peer, client_id);
|
||||
map_find_or_add(peers, client_id, event.peer);
|
||||
break;
|
||||
|
||||
case ENET_EVENT_TYPE_RECEIVE:;
|
||||
/*
|
||||
msg = va( "A packet of length %u containing %s was received from %s on channel %u",
|
||||
(unsigned)event.packet->dataLength,
|
||||
event.packet->data,
|
||||
(char *)event.peer->data,
|
||||
event.channelID );
|
||||
*/
|
||||
char *dbg = (char *)event.peer->data;
|
||||
char *ptr = (char *)event.packet->data;
|
||||
unsigned sz = (unsigned)event.packet->dataLength;
|
||||
unsigned id = (unsigned)event.channelID;
|
||||
|
||||
// debug
|
||||
// puts(dbg);
|
||||
// hexdump(ptr, sz);
|
||||
|
||||
// decapsulate incoming packet.
|
||||
uint32_t mid = *(uint32_t*)(ptr + 0);
|
||||
ptr += 4;
|
||||
|
||||
switch (mid) {
|
||||
case MSG_INIT:
|
||||
if (is_server) {
|
||||
uint64_t *cid = map_find(clients, event.peer);
|
||||
if (cid) {
|
||||
char init_msg[12];
|
||||
*(uint32_t*)&init_msg[0] = MSG_INIT;
|
||||
*(int64_t*)&init_msg[4] = *cid;
|
||||
ENetPacket *packet = enet_packet_create(init_msg, 12, ENET_PACKET_FLAG_RELIABLE);
|
||||
enet_peer_send(event.peer, 0, packet);
|
||||
PRINTF("Client req id %lld for peer ::%s:%u\n", *cid, ip, event.peer->address.port);
|
||||
} else {
|
||||
PRINTF("!Ignoring unk MSG_INIT client packet.\n");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MSG_BUF: {
|
||||
uint64_t *flags = (uint64_t*)(ptr + 0);
|
||||
uint32_t *idx = (uint32_t*)(ptr + 8);
|
||||
uint32_t *len = (uint32_t*)(ptr + 12);
|
||||
uint64_t *who = (uint64_t*)(ptr + 16);
|
||||
// PRINTF("recving %d %llx %u %u %lld\n", mid, *flags, *idx, *len, *who);
|
||||
ptr += 24;
|
||||
|
||||
// validate if peer owns the buffer
|
||||
uint8_t client_valid = 0;
|
||||
|
||||
if (is_server) {
|
||||
int64_t *cid = map_find(clients, event.peer);
|
||||
client_valid = cid ? *cid == *who : 0;
|
||||
}
|
||||
|
||||
// apply incoming packet.
|
||||
if( is_client ? *who != whoami : client_valid ) { // clients merge always foreign packets. servers merge foreign packets.
|
||||
array(netbuffer_t) *list = map_find(buffers, *who);
|
||||
assert( list );
|
||||
assert( *idx < array_count(*list) );
|
||||
netbuffer_t *nb = &(*list)[*idx];
|
||||
assert( *len == nb->sz );
|
||||
memcpy(nb->ptr, ptr, *len);
|
||||
}
|
||||
} break;
|
||||
case MSG_RPC: {
|
||||
unsigned id = *(uint32_t*)ptr; ptr += 4;
|
||||
char *cmdline = ptr;
|
||||
char *resp = rpc(id, cmdline);
|
||||
char *resp_msg = va("%*.s%s", 4, "", resp);
|
||||
*(uint32_t*)&resp_msg[0] = MSG_RPC_RESP;
|
||||
ENetPacket *packet = enet_packet_create(resp_msg, 4 + strlen(resp), ENET_PACKET_FLAG_RELIABLE);
|
||||
enet_peer_send(event.peer, 0, packet);
|
||||
} break;
|
||||
case MSG_RPC_RESP: {
|
||||
// @todo: react on response?
|
||||
msg = ptr;
|
||||
} break;
|
||||
default:
|
||||
// PRINTF("!Receiving unk %d sz %d from peer ::%s:%u\n", mid, sz, ip, event.peer->address.port);
|
||||
break;
|
||||
}
|
||||
/* Clean up the packet now that we're done using it. */
|
||||
enet_packet_destroy( event.packet );
|
||||
break;
|
||||
|
||||
case ENET_EVENT_TYPE_DISCONNECT:
|
||||
msg = va( "%s disconnected", (char *)event.peer->data );
|
||||
/* Reset the peer's client information. */
|
||||
event.peer->data = NULL;
|
||||
if (is_server) server_drop_client_peer(event.peer);
|
||||
else {network_put(NETWORK_RANK, -1); network_put(NETWORK_LIVE, 0);}
|
||||
break;
|
||||
|
||||
case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT:
|
||||
msg = va( "%s timeout", (char *)event.peer->data );
|
||||
event.peer->data = NULL;
|
||||
if (is_server) server_drop_client_peer(event.peer);
|
||||
else {network_put(NETWORK_RANK, -1); network_put(NETWORK_LIVE, 0);}
|
||||
break;
|
||||
}
|
||||
|
||||
if(msg) array_push(events, va("%d %s", event.type, msg));
|
||||
// if(msg) server_broadcast(msg);
|
||||
}
|
||||
|
||||
array_push(events, NULL);
|
||||
return events;
|
||||
}
|
||||
|
||||
void network_rpc(const char *signature, void *function) {
|
||||
rpc_insert(signature, function);
|
||||
}
|
||||
|
||||
void network_rpc_send_to(int64_t rank, unsigned id, const char *cmdline) {
|
||||
assert(network_get(NETWORK_RANK) == 0); /* must be a host */
|
||||
char *msg = va("%*.s%s", 8, "", cmdline);
|
||||
*(uint32_t*)&msg[0] = MSG_RPC;
|
||||
*(uint32_t*)&msg[4] = id;
|
||||
server_send_bin(rank, msg, 8 + strlen(cmdline));
|
||||
}
|
||||
|
||||
void network_rpc_send(unsigned id, const char *cmdline) {
|
||||
char *msg = va("%*.s%s", 8, "", cmdline);
|
||||
*(uint32_t*)&msg[0] = MSG_RPC;
|
||||
*(uint32_t*)&msg[4] = id;
|
||||
server_broadcast_bin(msg, 8 + strlen(cmdline));
|
||||
}
|
|
@ -12,8 +12,9 @@ camera_t cam;
|
|||
|
||||
void game_loop(void *userdata) {
|
||||
// key handler
|
||||
if (input_down(KEY_F11) ) window_fullscreen( window_has_fullscreen()^1 );
|
||||
if (input_down(KEY_ESC) ) window_loop_exit(); // @todo: break -> window_close()
|
||||
// if (input_down(KEY_F11) ) window_fullscreen( window_has_fullscreen()^1 );
|
||||
// if (input_down(KEY_ESC) ) window_loop_exit(); // @todo: break -> window_close()
|
||||
window_resize();
|
||||
|
||||
// animation
|
||||
static float dx = 0, dy = 0;
|
||||
|
|
|
@ -100,6 +100,13 @@
|
|||
},
|
||||
canvas: (function() {
|
||||
var canvas = document.getElementById('canvas');
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
canvas.width = window.innerWidth;
|
||||
canvas.height = window.innerHeight;
|
||||
})
|
||||
return canvas;
|
||||
})(),
|
||||
setStatus: function(text) {
|
||||
|
|
|
@ -2458,6 +2458,13 @@ unsigned num_instances;
|
|||
bool model_get_bone_pose(model_t m, unsigned joint, mat34 *out);
|
||||
void model_destroy(model_t);
|
||||
vec3 pose(bool forward, float curframe, int minframe, int maxframe, bool loop, float *opt_retframe);
|
||||
typedef struct anims_t {
|
||||
int inuse;
|
||||
float speed;
|
||||
anim_t* anims;
|
||||
mat44* M;
|
||||
} anims_t;
|
||||
anims_t animations(const char *pathfile, int flags);
|
||||
typedef struct skybox_t {
|
||||
handle program;
|
||||
mesh_t geometry;
|
||||
|
@ -2803,6 +2810,7 @@ WINDOW_VSYNC_DISABLED =8192,
|
|||
int window_swap();
|
||||
void window_loop(void (*function)(void* loopArg), void* loopArg );
|
||||
void window_loop_exit();
|
||||
void window_resize();
|
||||
void window_title(const char *title);
|
||||
void window_icon(const char *file_icon);
|
||||
void window_color(unsigned color);
|
||||
|
|
|
@ -16619,6 +16619,18 @@ API void model_destroy(model_t);
|
|||
|
||||
API vec3 pose(bool forward, float curframe, int minframe, int maxframe, bool loop, float *opt_retframe);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// model animations
|
||||
|
||||
typedef struct anims_t {
|
||||
int inuse; // animation number in use
|
||||
float speed; // x1.00
|
||||
array(anim_t) anims; // [begin,end,flags] frames of every animation in set
|
||||
array(mat44) M; // instanced transforms
|
||||
} anims_t;
|
||||
|
||||
API anims_t animations(const char *pathfile, int flags);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// skyboxes
|
||||
|
||||
|
@ -17165,6 +17177,7 @@ API int window_swap(); // single function that combines above functions (de
|
|||
|
||||
API void window_loop(void (*function)(void* loopArg), void* loopArg ); // run main loop function continuously (emscripten only)
|
||||
API void window_loop_exit(); // exit from main loop function (emscripten only)
|
||||
API void window_resize(); // resize if canvas size has changed (emscripten only)
|
||||
|
||||
API void window_title(const char *title);
|
||||
API void window_icon(const char *file_icon);
|
||||
|
@ -343986,6 +343999,29 @@ void model_destroy(model_t m) {
|
|||
#undef buf
|
||||
#undef bounds
|
||||
#undef colormaps
|
||||
|
||||
anims_t animations(const char *pathfile, int flags) {
|
||||
anims_t a = {0};
|
||||
char *anim_file = vfs_read(pathfile);
|
||||
for each_substring(anim_file, "\r\n", anim) {
|
||||
int from, to;
|
||||
char anim_name[128] = {0};
|
||||
if( sscanf(anim, "%*s %d-%d %127[^\r\n]", &from, &to, anim_name) != 3) continue;
|
||||
array_push(a.anims, !!strstri(anim_name, "loop") ? loop(from, to, 0, 0) : clip(from, to, 0, 0)); // [from,to,flags]
|
||||
array_back(a.anims)->name = strswap(strswap(strswap(STRDUP(anim_name), "Loop", ""), "loop", ""), "()", "");
|
||||
}
|
||||
array_resize(a.M, 32*32);
|
||||
for(int z = 0, i = 0; z < 32; ++z) {
|
||||
for(int x = 0; x < 32; ++x, ++i) {
|
||||
vec3 p = vec3(-x*3,0,-z*3);
|
||||
vec3 r = vec3(0,0,0);
|
||||
vec3 s = vec3(2,2,2);
|
||||
compose44(a.M[i], p, eulerq(r), s);
|
||||
}
|
||||
}
|
||||
a.speed = 1.0;
|
||||
return a;
|
||||
}
|
||||
#line 0
|
||||
|
||||
#line 1 "v4k_renderdd.c"
|
||||
|
@ -349762,8 +349798,8 @@ void window_loop_exit() {
|
|||
|
||||
vec2 window_canvas() {
|
||||
#if is(ems)
|
||||
int width = EM_ASM_INT_V(return window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth);
|
||||
int height = EM_ASM_INT_V(return window.innerHeight|| document.documentElement.clientHeight|| document.body.clientHeight);
|
||||
int width = EM_ASM_INT_V(return canvas.width || window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth);
|
||||
int height = EM_ASM_INT_V(return canvas.height || window.innerHeight || document.documentElement.clientHeight|| document.body.clientHeight);
|
||||
return vec2(width, height);
|
||||
#else
|
||||
glfw_init();
|
||||
|
@ -349773,6 +349809,23 @@ vec2 window_canvas() {
|
|||
#endif /* __EMSCRIPTEN__ */
|
||||
}
|
||||
|
||||
static vec2 last_canvas_size;
|
||||
|
||||
void window_resize() {
|
||||
#if is(ems)
|
||||
vec2 size = window_canvas();
|
||||
do_once last_canvas_size = size;
|
||||
if (size.x != last_canvas_size.x || size.y != last_canvas_size.y) {
|
||||
w = size.x;
|
||||
h = size.y;
|
||||
g->width = w;
|
||||
g->height = h;
|
||||
glfwSetWindowSize(g->window, w, h);
|
||||
// emscripten_set_canvas_size(w, h);
|
||||
}
|
||||
#endif /* __EMSCRIPTEN__ */
|
||||
}
|
||||
|
||||
int window_width() {
|
||||
return w;
|
||||
}
|
||||
|
|
|
@ -4218,3 +4218,26 @@ void model_destroy(model_t m) {
|
|||
#undef buf
|
||||
#undef bounds
|
||||
#undef colormaps
|
||||
|
||||
anims_t animations(const char *pathfile, int flags) {
|
||||
anims_t a = {0};
|
||||
char *anim_file = vfs_read(pathfile);
|
||||
for each_substring(anim_file, "\r\n", anim) {
|
||||
int from, to;
|
||||
char anim_name[128] = {0};
|
||||
if( sscanf(anim, "%*s %d-%d %127[^\r\n]", &from, &to, anim_name) != 3) continue;
|
||||
array_push(a.anims, !!strstri(anim_name, "loop") ? loop(from, to, 0, 0) : clip(from, to, 0, 0)); // [from,to,flags]
|
||||
array_back(a.anims)->name = strswap(strswap(strswap(STRDUP(anim_name), "Loop", ""), "loop", ""), "()", "");
|
||||
}
|
||||
array_resize(a.M, 32*32);
|
||||
for(int z = 0, i = 0; z < 32; ++z) {
|
||||
for(int x = 0; x < 32; ++x, ++i) {
|
||||
vec3 p = vec3(-x*3,0,-z*3);
|
||||
vec3 r = vec3(0,0,0);
|
||||
vec3 s = vec3(2,2,2);
|
||||
compose44(a.M[i], p, eulerq(r), s);
|
||||
}
|
||||
}
|
||||
a.speed = 1.0;
|
||||
return a;
|
||||
}
|
||||
|
|
|
@ -486,6 +486,18 @@ API void model_destroy(model_t);
|
|||
|
||||
API vec3 pose(bool forward, float curframe, int minframe, int maxframe, bool loop, float *opt_retframe);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// model animations
|
||||
|
||||
typedef struct anims_t {
|
||||
int inuse; // animation number in use
|
||||
float speed; // x1.00
|
||||
array(anim_t) anims; // [begin,end,flags] frames of every animation in set
|
||||
array(mat44) M; // instanced transforms
|
||||
} anims_t;
|
||||
|
||||
API anims_t animations(const char *pathfile, int flags);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// skyboxes
|
||||
|
||||
|
|
|
@ -615,8 +615,8 @@ void window_loop_exit() {
|
|||
|
||||
vec2 window_canvas() {
|
||||
#if is(ems)
|
||||
int width = EM_ASM_INT_V(return window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth);
|
||||
int height = EM_ASM_INT_V(return window.innerHeight|| document.documentElement.clientHeight|| document.body.clientHeight);
|
||||
int width = EM_ASM_INT_V(return canvas.width || window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth);
|
||||
int height = EM_ASM_INT_V(return canvas.height || window.innerHeight || document.documentElement.clientHeight|| document.body.clientHeight);
|
||||
return vec2(width, height);
|
||||
#else
|
||||
glfw_init();
|
||||
|
@ -626,6 +626,23 @@ vec2 window_canvas() {
|
|||
#endif /* __EMSCRIPTEN__ */
|
||||
}
|
||||
|
||||
static vec2 last_canvas_size;
|
||||
|
||||
void window_resize() {
|
||||
#if is(ems)
|
||||
vec2 size = window_canvas();
|
||||
do_once last_canvas_size = size;
|
||||
if (size.x != last_canvas_size.x || size.y != last_canvas_size.y) {
|
||||
w = size.x;
|
||||
h = size.y;
|
||||
g->width = w;
|
||||
g->height = h;
|
||||
glfwSetWindowSize(g->window, w, h);
|
||||
// emscripten_set_canvas_size(w, h);
|
||||
}
|
||||
#endif /* __EMSCRIPTEN__ */
|
||||
}
|
||||
|
||||
int window_width() {
|
||||
return w;
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ API int window_swap(); // single function that combines above functions (de
|
|||
|
||||
API void window_loop(void (*function)(void* loopArg), void* loopArg ); // run main loop function continuously (emscripten only)
|
||||
API void window_loop_exit(); // exit from main loop function (emscripten only)
|
||||
API void window_resize(); // resize if canvas size has changed (emscripten only)
|
||||
|
||||
API void window_title(const char *title);
|
||||
API void window_icon(const char *file_icon);
|
||||
|
|
44
engine/v4k.c
44
engine/v4k.c
|
@ -15056,6 +15056,29 @@ void model_destroy(model_t m) {
|
|||
#undef buf
|
||||
#undef bounds
|
||||
#undef colormaps
|
||||
|
||||
anims_t animations(const char *pathfile, int flags) {
|
||||
anims_t a = {0};
|
||||
char *anim_file = vfs_read(pathfile);
|
||||
for each_substring(anim_file, "\r\n", anim) {
|
||||
int from, to;
|
||||
char anim_name[128] = {0};
|
||||
if( sscanf(anim, "%*s %d-%d %127[^\r\n]", &from, &to, anim_name) != 3) continue;
|
||||
array_push(a.anims, !!strstri(anim_name, "loop") ? loop(from, to, 0, 0) : clip(from, to, 0, 0)); // [from,to,flags]
|
||||
array_back(a.anims)->name = strswap(strswap(strswap(STRDUP(anim_name), "Loop", ""), "loop", ""), "()", "");
|
||||
}
|
||||
array_resize(a.M, 32*32);
|
||||
for(int z = 0, i = 0; z < 32; ++z) {
|
||||
for(int x = 0; x < 32; ++x, ++i) {
|
||||
vec3 p = vec3(-x*3,0,-z*3);
|
||||
vec3 r = vec3(0,0,0);
|
||||
vec3 s = vec3(2,2,2);
|
||||
compose44(a.M[i], p, eulerq(r), s);
|
||||
}
|
||||
}
|
||||
a.speed = 1.0;
|
||||
return a;
|
||||
}
|
||||
#line 0
|
||||
|
||||
#line 1 "v4k_renderdd.c"
|
||||
|
@ -20832,8 +20855,8 @@ void window_loop_exit() {
|
|||
|
||||
vec2 window_canvas() {
|
||||
#if is(ems)
|
||||
int width = EM_ASM_INT_V(return window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth);
|
||||
int height = EM_ASM_INT_V(return window.innerHeight|| document.documentElement.clientHeight|| document.body.clientHeight);
|
||||
int width = EM_ASM_INT_V(return canvas.width || window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth);
|
||||
int height = EM_ASM_INT_V(return canvas.height || window.innerHeight || document.documentElement.clientHeight|| document.body.clientHeight);
|
||||
return vec2(width, height);
|
||||
#else
|
||||
glfw_init();
|
||||
|
@ -20843,6 +20866,23 @@ vec2 window_canvas() {
|
|||
#endif /* __EMSCRIPTEN__ */
|
||||
}
|
||||
|
||||
static vec2 last_canvas_size;
|
||||
|
||||
void window_resize() {
|
||||
#if is(ems)
|
||||
vec2 size = window_canvas();
|
||||
do_once last_canvas_size = size;
|
||||
if (size.x != last_canvas_size.x || size.y != last_canvas_size.y) {
|
||||
w = size.x;
|
||||
h = size.y;
|
||||
g->width = w;
|
||||
g->height = h;
|
||||
glfwSetWindowSize(g->window, w, h);
|
||||
// emscripten_set_canvas_size(w, h);
|
||||
}
|
||||
#endif /* __EMSCRIPTEN__ */
|
||||
}
|
||||
|
||||
int window_width() {
|
||||
return w;
|
||||
}
|
||||
|
|
13
engine/v4k.h
13
engine/v4k.h
|
@ -2702,6 +2702,18 @@ API void model_destroy(model_t);
|
|||
|
||||
API vec3 pose(bool forward, float curframe, int minframe, int maxframe, bool loop, float *opt_retframe);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// model animations
|
||||
|
||||
typedef struct anims_t {
|
||||
int inuse; // animation number in use
|
||||
float speed; // x1.00
|
||||
array(anim_t) anims; // [begin,end,flags] frames of every animation in set
|
||||
array(mat44) M; // instanced transforms
|
||||
} anims_t;
|
||||
|
||||
API anims_t animations(const char *pathfile, int flags);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// skyboxes
|
||||
|
||||
|
@ -3248,6 +3260,7 @@ API int window_swap(); // single function that combines above functions (de
|
|||
|
||||
API void window_loop(void (*function)(void* loopArg), void* loopArg ); // run main loop function continuously (emscripten only)
|
||||
API void window_loop_exit(); // exit from main loop function (emscripten only)
|
||||
API void window_resize(); // resize if canvas size has changed (emscripten only)
|
||||
|
||||
API void window_title(const char *title);
|
||||
API void window_icon(const char *file_icon);
|
||||
|
|
File diff suppressed because one or more lines are too long
Binary file not shown.
|
@ -26,6 +26,7 @@ int doanim = 0; // export animations
|
|||
int dobone = 0; // export skeleton
|
||||
int doflip = 1; // export flipped (quake-style clockwise winding) triangles
|
||||
int doflipUV = 0; // export flipped UVs
|
||||
int doanimlist = 0; // generate list of animations with properties
|
||||
|
||||
int doaxis = 0; // flip bone axis from X to Y to match blender
|
||||
int dounscale = 0; // remove scaling from bind pose
|
||||
|
@ -853,6 +854,23 @@ void export_animations(FILE *out, const struct aiScene *scene)
|
|||
export_static_animation(out, scene);
|
||||
}
|
||||
|
||||
void export_animlist(FILE *out, const struct aiScene *scene)
|
||||
{
|
||||
int i, offset=0, len;
|
||||
|
||||
for (i = 0; i < scene->mNumAnimations; i++) {
|
||||
const struct aiAnimation *anim = scene->mAnimations[i];
|
||||
len = animation_length(anim)-1;
|
||||
fprintf(stderr, "frame: %d-%d %s\n", offset, offset+len, anim->mName.data);
|
||||
fprintf(out, "frame: %d-%d %s\n", offset, offset+len, anim->mName.data);
|
||||
offset += len+1;
|
||||
}
|
||||
|
||||
if (scene->mNumAnimations == 0)
|
||||
fprintf(out, "frame: %s\n", "0-0 Idle");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* For multi-mesh models, sometimes each mesh has its own inv_bind_matrix set
|
||||
* for each bone. To export to IQE we must have only one inv_bind_matrix per
|
||||
|
@ -1248,6 +1266,7 @@ void usage()
|
|||
fprintf(stderr, "\t-U -- flip UVs\n");
|
||||
fprintf(stderr, "\t-n mesh -- export only the named mesh\n");
|
||||
fprintf(stderr, "\t-a -- only export animations\n");
|
||||
fprintf(stderr, "\t-L -- export only animation list\n");
|
||||
fprintf(stderr, "\t-m -- only export mesh\n");
|
||||
fprintf(stderr, "\t-b -- bake mesh to bind pose / initial frame\n");
|
||||
fprintf(stderr, "\t-f -- export counter-clockwise winding triangles\n");
|
||||
|
@ -1301,7 +1320,7 @@ int main(int argc, char **argv)
|
|||
int onlyanim = 0;
|
||||
int onlymesh = 0;
|
||||
|
||||
while ((c = getopt(argc, argv, "AHMPSUabflmn:o:rvxsu:")) != -1) {
|
||||
while ((c = getopt(argc, argv, "AHLMPSUabflmn:o:rvxsu:")) != -1) {
|
||||
switch (c) {
|
||||
case 'A': save_all_bones++; break;
|
||||
case 'H': dohips = 1; break;
|
||||
|
@ -1317,6 +1336,7 @@ case 'U': doflipUV = 1; puts("using flipUV"); break;
|
|||
case 'f': doflip = 0; break;
|
||||
case 'r': dorigid = 1; break;
|
||||
case 'l': dolowprec = 1; break;
|
||||
case 'L': doanimlist = 1; break;
|
||||
case 'v': verbose++; break;
|
||||
case 'x': doaxis = 1; break;
|
||||
case 's': dounscale = 1; break;
|
||||
|
@ -1436,6 +1456,17 @@ flags |= (doflipUV ? aiProcess_FlipUVs : 0);
|
|||
if (onlymesh) { domesh = 1; doanim = 0; }
|
||||
if (onlyanim) { domesh = 0; doanim = 1; }
|
||||
|
||||
if (doanimlist) {
|
||||
fprintf(stderr, "exporting animation list for %s ...\n", basename);
|
||||
file = fopen(output, "w");
|
||||
if (!file) {
|
||||
fprintf(stderr, "cannot open output file: '%s'\n", output);
|
||||
exit(1);
|
||||
}
|
||||
export_animlist(file, scene);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (getenv("DOANIM")) doanim = 1;
|
||||
|
||||
// Convert to Z-UP coordinate system
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -19,7 +19,7 @@ exit
|
|||
@echo off
|
||||
cd "%~dp0"
|
||||
|
||||
git clone https://github.com/assimp/assimp && md assimp\.build && pushd assimp\.build
|
||||
git clone https://github.com/assimp/assimp && pushd assimp && git checkout 05115b07 && popd && md assimp\.build && pushd assimp\.build
|
||||
rem fart -- ..\CMakeLists.txt assimp-${ASSIMP_MSVC_VERSION}-mt.dll assimp.dll
|
||||
rem fart -- ..\CMakeLists.txt assimp-${ASSIMP_MSVC_VERSION}-mt.lib assimp.lib
|
||||
rem cmake .. -DCMAKE_BUILD_TYPE=Release -G "Visual Studio 14 2015" && msbuild assimp.sln -m -p:Configuration=Release -p:PlatformToolset=140
|
||||
|
@ -30,7 +30,7 @@ xcopy /y assimp\.build\bin\release\*.dll
|
|||
xcopy /y assimp\.build\lib\release\*.lib
|
||||
|
||||
copy /y assimp\include\assimp\config.h.in assimp\config.h && fart -- assimp\config.h "cmakedefine" "//#define"
|
||||
cl ass2iqe.c -I . -I assimp\include assimp-vc142-mt.lib /O2 /Oy /MT /DNDEBUG
|
||||
cl ass2iqe.c -I . -I assimp\include assimp-vc143-mt.lib /O2 /Oy /MT /DNDEBUG
|
||||
|
||||
rem xcopy assimp\.build\bin\release\*.dll x64\ /s
|
||||
rem xcopy assimp\.build\lib\release\*.lib x64\ /s
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -39,6 +39,7 @@ icons=ico
|
|||
image=jpg,png,bmp,psd,pic,pnm,hdr
|
||||
texture=pvr,ktx,ktx2,dds,astc,basis,tga
|
||||
model=iqm,iqe,gltf,gltf2,glb,fbx,obj,dae,blend,md3,md5,ms3d,smd,x,3ds,bvh,dxf,lwo
|
||||
anims=anim
|
||||
audio=wav,flac,ogg,mp1,mp3,mid,sfxr ; ,mod,xm
|
||||
audio-modules=mod,xm,s3m,it
|
||||
audio-furnace=fur
|
||||
|
@ -67,7 +68,7 @@ TOOLS/ffmpeg.EXE -hide_banner -nostdin -loglevel fatal -y -i INPUT -f ogg -b:a 3
|
|||
; cook midis as wavs here
|
||||
|
||||
[cook audio && mid]
|
||||
SOUNDBANK=AweROMGM.sf3
|
||||
SOUNDBANK=AweROMGM.sf3 ; note: GeneralUser_GS_v1_471.sf3 or FluidR3.sf3 likely to produce better results
|
||||
TOOLS/mid2wav.EXE INPUT OUTPUT TOOLS/SOUNDBANK -> wav
|
||||
|
||||
; and furs as wavs...
|
||||
|
@ -163,12 +164,16 @@ TOOLS/cuttlefish.EXE -q -m -i INPUT -o OUTPUT -f BC1_RGBA -> ktx
|
|||
|
||||
; ------------------------------------------------------------------------------
|
||||
; finally, let's cook all models. the logic here is:
|
||||
; 1. cook all models into iqe (ass2iqe), then into iqm (iqe2iqm): any -> iqe -> iqm
|
||||
; 2. unless input is iqe. these models will run iqe2iqm only (no ass2iqe): iqe -> iqm.
|
||||
; 3. unless input is iqm. these models will not run any conversion at all: iqm.
|
||||
; 4. also, dae models need to flip their UVs coordinates (see -U flag below).
|
||||
; 1. export animation list from models using ass2iqe -L
|
||||
; 2. cook all models into iqe (ass2iqe), then into iqm (iqe2iqm): any -> iqe -> iqm
|
||||
; 3. unless input is iqe. these models will run iqe2iqm only (no ass2iqe): iqe -> iqm.
|
||||
; 4. unless input is iqm. these models will not run any conversion at all: iqm.
|
||||
; 5. also, dae models need to flip their UVs coordinates (see -U flag below).
|
||||
|
||||
[cook model && dae] ; pass dae, reject iqm,iqe or any other model
|
||||
[cook anims] ; process all models to extract animlist
|
||||
TOOLS/ass2iqe.EXE -L -o OUTPUT INPUT 2> NUL
|
||||
|
||||
[cook model && dae &&] ; pass dae, reject iqm,iqe or any other model
|
||||
FLAGS=
|
||||
TOOLS/ass2iqe.EXE FLAGS -o OUTPUT INPUT -U 2> NUL -> iqe
|
||||
|
||||
|
@ -199,5 +204,5 @@ TOOLS/iqe2iqm.EXE OUTPUT INPUT > NUL -> iqm
|
|||
; hint: use plain `0` to exclude those files we would like to directly stream within the final zipfile (flac,mp3,adpcm wav,...)
|
||||
|
||||
[compress]
|
||||
0|ULZ=texture,image,model,audio,font,text,shader,script
|
||||
0=video,flac,ogg,wav,mp1,mp3,jpg,png
|
||||
0|ULZ=texture,image,model,audio,font,text,shader,script,animlist
|
||||
0=video,flac,ogg,wav,mp1,mp3,jpg,png
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -3,7 +3,7 @@
|
|||
[
|
||||
{
|
||||
"path": ".",
|
||||
"file_exclude_patterns": ["*.zip"],
|
||||
"file_exclude_patterns": ["*.zip", "*.ilk", "*.exp"],
|
||||
"index_exclude_patterns": ["engine/joint/v4k.h", "engine/v4k.h", "engine/v4k.c"]
|
||||
}
|
||||
],
|
||||
|
|
Loading…
Reference in New Issue