diff --git a/engine/client/merged.h b/engine/client/merged.h index 0514695ef..e8c5611f6 100644 --- a/engine/client/merged.h +++ b/engine/client/merged.h @@ -185,6 +185,37 @@ typedef struct texid_s texid_tf; #define TEXASSIGNF(d,s) memcpy(&d,&s,sizeof(d)) #define TEXVALID(t) 1 #endif + +//small context for easy vbo creation. +typedef struct +{ + unsigned int maxsize; + unsigned int pos; + int vboid[2]; +} vbobctx_t; + +typedef struct vboarray_s +{ + union + { + void *dummy; +#ifdef GLQUAKE + struct + { + int vbo; + void *addr; + } gl; +#endif +#if defined(D3D9QUAKE) || defined(D3D11QUAKE) + struct + { + void *buff; + unsigned int offs; + } d3d; +#endif + }; +} vboarray_t; + typedef struct texnums_s { texid_t base; texid_t bump; @@ -311,7 +342,10 @@ typedef struct rendererinfo_s { void (*BE_SelectDLight)(struct dlight_s *dl, vec3_t colour); /*check to see if an ent should be drawn for the selected light*/ qboolean (*BE_LightCullModel)(vec3_t org, struct model_s *model); - + void (*BE_VBO_Begin)(vbobctx_t *ctx, unsigned int maxsize); + void (*BE_VBO_Data)(vbobctx_t *ctx, void *data, unsigned int size, vboarray_t *varray); + void (*BE_VBO_Finish)(vbobctx_t *ctx, void *edata, unsigned int esize, vboarray_t *earray); + void (*BE_VBO_Destroy)(vboarray_t *vearray); char *alignment; } rendererinfo_t; @@ -340,3 +374,7 @@ typedef struct rendererinfo_s { #define BE_DrawMesh_Single rf->BE_DrawMesh_Single #define BE_SubmitMeshes rf->BE_SubmitMeshes #define BE_DrawWorld rf->BE_DrawWorld +#define BE_VBO_Begin rf->BE_VBO_Begin +#define BE_VBO_Data rf->BE_VBO_Data +#define BE_VBO_Finish rf->BE_VBO_Finish +#define BE_VBO_Destroy rf->BE_VBO_Destroy diff --git a/engine/client/r_2d.c b/engine/client/r_2d.c index 7d2247454..4ab74092a 100644 --- a/engine/client/r_2d.c +++ b/engine/client/r_2d.c @@ -356,13 +356,7 @@ void R2D_Image(float x, float y, float w, float h, float s1, float t1, float s2, { if (!pic) return; -/* - if (w == 0 && h == 0) - { - w = pic->width; - h = pic->height; - } -*/ + draw_mesh_xyz[0][0] = x; draw_mesh_xyz[0][1] = y; draw_mesh_st[0][0] = s1; diff --git a/engine/client/renderer.c b/engine/client/renderer.c index 8faba8293..ac0c669c4 100644 --- a/engine/client/renderer.c +++ b/engine/client/renderer.c @@ -883,6 +883,10 @@ rendererinfo_t dedicatedrendererinfo = { NULL, NULL, NULL, + NULL, + NULL, + NULL, + NULL, "" }; diff --git a/engine/common/com_mesh.c b/engine/common/com_mesh.c index a2b608e38..1036503be 100644 --- a/engine/common/com_mesh.c +++ b/engine/common/com_mesh.c @@ -1120,6 +1120,9 @@ struct vec3_t *anorm; vec3_t *anorms; vec3_t *anormt; + + vbo_t vbo; + vbo_t *vbop; } meshcache; //#define SSE_INTRINSICS @@ -1451,7 +1454,7 @@ void Alias_Shutdown(void) meshcache.numcoords = 0; } -qboolean Alias_GAliasBuildMesh(mesh_t *mesh, galiasinfo_t *inf, int surfnum, entity_t *e, qboolean usebones) +qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, int surfnum, entity_t *e, qboolean usebones) { extern cvar_t r_nolerp; galiasgroup_t *g1, *g2; @@ -1505,6 +1508,7 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, galiasinfo_t *inf, int surfnum, ent mesh->normals_array = meshcache.anorm; mesh->snormals_array = meshcache.anorms; mesh->tnormals_array = meshcache.anormt; + *vbop = meshcache.vbop; #ifdef SKELETALMODELS if (meshcache.usebonepose) @@ -1535,6 +1539,7 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, galiasinfo_t *inf, int surfnum, ent #ifdef SKELETALMODELS meshcache.usebonepose = NULL; + *vbop = meshcache.vbop = NULL; if (inf->ofs_skel_xyz && !inf->ofs_skel_weight) { //if we have skeletal xyz info, but no skeletal weights, then its a partial model that cannot possibly be animated. @@ -1544,6 +1549,20 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, galiasinfo_t *inf, int surfnum, ent mesh->normals_array = (vec3_t*)((char*)inf + inf->ofs_skel_norm); mesh->snormals_array = (vec3_t*)((char*)inf + inf->ofs_skel_svect); mesh->tnormals_array = (vec3_t*)((char*)inf + inf->ofs_skel_tvect); + + meshcache.vbo.indicies = inf->vboindicies; + meshcache.vbo.indexcount = inf->numindexes; + meshcache.vbo.vertcount = inf->numverts; + meshcache.vbo.texcoord = inf->vbotexcoords; + meshcache.vbo.coord = inf->vbo_skel_verts; + memset(&meshcache.vbo.coord2, 0, sizeof(meshcache.vbo.coord2)); + meshcache.vbo.normals = inf->vbo_skel_normals; + meshcache.vbo.svector = inf->vbo_skel_svector; + meshcache.vbo.tvector = inf->vbo_skel_tvector; + meshcache.vbo.bonenums = inf->vbo_skel_bonenum; + meshcache.vbo.boneweights = inf->vbo_skel_bweight; + if (meshcache.vbo.indicies.dummy) + *vbop = meshcache.vbop = &meshcache.vbo; } else if (inf->numbones) { @@ -1669,18 +1688,33 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, galiasinfo_t *inf, int surfnum, ent mesh->snormals_array = (vec3_t *)((char *)p1 + p1->ofssvector); mesh->tnormals_array = (vec3_t *)((char *)p1 + p1->ofstvector); + meshcache.vbo.indicies = inf->vboindicies; + meshcache.vbo.indexcount = inf->numindexes; + meshcache.vbo.vertcount = inf->numverts; + meshcache.vbo.texcoord = inf->vbotexcoords; + meshcache.vbo.normals = p1->vbonormals; + meshcache.vbo.svector = p1->vbosvector; + meshcache.vbo.tvector = p1->vbotvector; + if (p1 == p2 || r_nolerp.ival) { + meshcache.vbo.coord = p1->vboverts; + memset(&meshcache.vbo.coord2, 0, sizeof(meshcache.vbo.coord2)); mesh->xyz_array = (vecV_t *)((char *)p1 + p1->ofsverts); mesh->xyz2_array = NULL; } else { + meshcache.vbo.coord = p1->vboverts; + meshcache.vbo.coord2 = p2->vboverts; mesh->xyz_blendw[0] = 1-lerp; mesh->xyz_blendw[1] = lerp; mesh->xyz_array = (vecV_t *)((char *)p1 + p1->ofsverts); mesh->xyz2_array = (vecV_t *)((char *)p2 + p2->ofsverts); } + + if (meshcache.vbo.indicies.dummy) + *vbop = meshcache.vbop = &meshcache.vbo; } } @@ -1689,6 +1723,7 @@ qboolean Alias_GAliasBuildMesh(mesh_t *mesh, galiasinfo_t *inf, int surfnum, ent meshcache.anorm = mesh->normals_array; meshcache.anorms = mesh->snormals_array; meshcache.anormt = mesh->tnormals_array; + meshcache.vbop = *vbop; #ifdef SKELETALMODELS if (meshcache.usebonepose) @@ -1969,6 +2004,7 @@ static frameinfo_t *ParseFrameInfo(char *modelname, int *numgroups) return frames; } +//called for non-skeletal model formats. void Mod_BuildTextureVectors(galiasinfo_t *galias) //vec3_t *vc, vec2_t *tc, vec3_t *nv, vec3_t *sv, vec3_t *tv, index_t *idx, int numidx, int numverts) { @@ -1980,10 +2016,22 @@ void Mod_BuildTextureVectors(galiasinfo_t *galias) vec3_t *nv, *sv, *tv; vec2_t *tc; index_t *idx; + int vbospace = 0; + vbobctx_t vboctx; idx = (index_t*)((char*)galias + galias->ofs_indexes); tc = (vec2_t*)((char*)galias + galias->ofs_st_array); group = (galiasgroup_t*)((char*)galias + galias->groupofs); + + //determine the amount of space we need for our vbos. + vbospace += sizeof(*tc) * galias->numverts; + for (i = 0; i < galias->groups; i++) + { + vbospace += group[i].numposes * galias->numverts * (sizeof(vecV_t)+sizeof(vec3_t)*3); + } + BE_VBO_Begin(&vboctx, vbospace); + BE_VBO_Data(&vboctx, tc, sizeof(*tc) * galias->numverts, &galias->vbotexcoords); + for (i = 0; i < galias->groups; i++, group++) { pose = (galiaspose_t*)((char*)group + group->poseofs); @@ -1991,17 +2039,27 @@ void Mod_BuildTextureVectors(galiasinfo_t *galias) { vc = (vecV_t *)((char*)pose + pose->ofsverts); nv = (vec3_t *)((char*)pose + pose->ofsnormals); - if (pose->ofssvector == 0) - continue; - if (pose->ofstvector == 0) - continue; - sv = (vec3_t *)((char*)pose + pose->ofssvector); - tv = (vec3_t *)((char*)pose + pose->ofstvector); + if (pose->ofssvector != 0 && pose->ofstvector != 0) + { + sv = (vec3_t *)((char*)pose + pose->ofssvector); + tv = (vec3_t *)((char*)pose + pose->ofstvector); - Mod_AccumulateTextureVectors(vc, tc, nv, sv, tv, idx, galias->numindexes); - Mod_NormaliseTextureVectors(nv, sv, tv, galias->numverts); + Mod_AccumulateTextureVectors(vc, tc, nv, sv, tv, idx, galias->numindexes); + Mod_NormaliseTextureVectors(nv, sv, tv, galias->numverts); + } + else + { //shouldn't really happen... make error? + sv = NULL; + tv = NULL; + } + + BE_VBO_Data(&vboctx, vc, sizeof(*vc) * galias->numverts, &pose->vboverts); + BE_VBO_Data(&vboctx, nv, sizeof(*nv) * galias->numverts, &pose->vbonormals); + BE_VBO_Data(&vboctx, sv, sizeof(*sv) * galias->numverts, &pose->vbosvector); + BE_VBO_Data(&vboctx, tv, sizeof(*tv) * galias->numverts, &pose->vbotvector); } } + BE_VBO_Finish(&vboctx, idx, sizeof(*idx) * galias->numindexes, &galias->vboindicies); #endif } diff --git a/engine/common/com_mesh.h b/engine/common/com_mesh.h index e2e0ddcc8..cf8fdfd41 100644 --- a/engine/common/com_mesh.h +++ b/engine/common/com_mesh.h @@ -47,6 +47,15 @@ typedef struct { int ofs_skel_tvect; int ofs_skel_idx; int ofs_skel_weight; + + vboarray_t vboindicies; + vboarray_t vbotexcoords; + vboarray_t vbo_skel_verts; + vboarray_t vbo_skel_normals; + vboarray_t vbo_skel_svector; + vboarray_t vbo_skel_tvector; + vboarray_t vbo_skel_bonenum; + vboarray_t vbo_skel_bweight; #endif //these exist only in the root mesh. @@ -56,7 +65,8 @@ typedef struct { } galiasinfo_t; //frame is an index into this -typedef struct { +typedef struct +{ #ifdef SKELETALMODELS qboolean isheirachical; //for models with transforms, states that bones need to be transformed from their parent. //this is actually bad, and can result in bones shortening as they interpolate. @@ -68,12 +78,18 @@ typedef struct { char name[64]; } galiasgroup_t; -typedef struct { +typedef struct +{ int ofsverts; #ifndef SERVERONLY int ofsnormals; int ofstvector; int ofssvector; + + vboarray_t vboverts; + vboarray_t vbonormals; + vboarray_t vbosvector; + vboarray_t vbotvector; #endif vec3_t scale; @@ -82,13 +98,15 @@ typedef struct { typedef struct galiasbone_s galiasbone_t; #ifdef SKELETALMODELS -struct galiasbone_s { +struct galiasbone_s +{ char name[32]; int parent; float inverse[12]; }; -typedef struct { +typedef struct +{ //skeletal poses refer to this. int vertexindex; int boneindex; @@ -128,7 +146,7 @@ float *Alias_GetBonePositions(galiasinfo_t *inf, framestate_t *fstate, float *bu #ifdef SKELETALMODELS void Alias_TransformVerticies(float *bonepose, galisskeletaltransforms_t *weights, int numweights, vecV_t *xyzout, vec3_t *normout); #endif -qboolean Alias_GAliasBuildMesh(mesh_t *mesh, galiasinfo_t *inf, int surfnum, entity_t *e, qboolean allowskel); +qboolean Alias_GAliasBuildMesh(mesh_t *mesh, vbo_t **vbop, galiasinfo_t *inf, int surfnum, entity_t *e, qboolean allowskel); void Alias_FlushCache(void); void Alias_Shutdown(void); diff --git a/engine/common/fs_win32.c b/engine/common/fs_win32.c index 74de94e17..e64db408e 100644 --- a/engine/common/fs_win32.c +++ b/engine/common/fs_win32.c @@ -13,6 +13,7 @@ #define w32filefuncs osfilefuncs typedef struct { + searchpathfuncs_t funcs; HANDLE changenotification; int hashdepth; char rootpath[1]; diff --git a/engine/common/net_wins.c b/engine/common/net_wins.c index d91cb3913..957570db3 100644 --- a/engine/common/net_wins.c +++ b/engine/common/net_wins.c @@ -22,7 +22,6 @@ struct sockaddr; #include "quakedef.h" #include "netinc.h" -#include #ifdef _WIN32 #define USE_GETHOSTNAME_LOCALLISTING @@ -1829,7 +1828,6 @@ qboolean FTENET_NATPMP_GetPacket(struct ftenet_generic_connection_s *con) unsigned int now = Sys_Milliseconds(); if (now - pmp->refreshtime > PMP_POLL_TIME) //weird logic to cope with wrapping { -Con_Printf("nat-pmp refresh (%u - %u > %u)\n", now, pmp->refreshtime, PMP_POLL_TIME); pmp->refreshtime = now; FTENET_NATPMP_Refresh(pmp, pmp->natadr.port, pmp->col); } @@ -2483,13 +2481,16 @@ typedef struct ftenet_tcpconnect_stream_s { enum { - TCPC_UNKNOWN, - TCPC_UNFRAMED, //something else is doing the framing (ie: we're running in emscripten and over some hidden websocket connection) - TCPC_QIZMO, - TCPC_WEBSOCKET + TCPC_UNKNOWN, //waiting to see what they send us. + TCPC_UNFRAMED, //something else is doing the framing (ie: we're running in emscripten and over some hidden websocket connection) + TCPC_HTTPCLIENT, //we're sending a file to this victim. + TCPC_QIZMO, //'qizmo\n' handshake, followed by packets prefixed with a 16bit packet length. + TCPC_WEBSOCKETU, //utf-8 encoded data. + TCPC_WEBSOCKETB, //binary encoded data (subprotocol = 'binary') } clienttype; char inbuffer[3000]; char outbuffer[3000]; + vfsfile_t *file; float timeouttime; netadr_t remoteaddr; struct ftenet_tcpconnect_stream_s *next; @@ -2537,6 +2538,7 @@ void tobase64(unsigned char *out, int outlen, unsigned char *in, int inlen) *out = 0; } +#include "fs.h" int SHA1(char *digest, int maxdigestsize, char *string); qboolean FTENET_TCPConnect_GetPacket(ftenet_generic_connection_t *gcon) { @@ -2574,11 +2576,17 @@ qboolean FTENET_TCPConnect_GetPacket(ftenet_generic_connection_t *gcon) //due to the above checks about invalid sockets, the socket is always open for st below. if (st->timeouttime < timeval) + { + Con_Printf ("tcp peer %s timed out\n", NET_AdrToString (adr, sizeof(adr), st->remoteaddr)); goto closesvstream; + } ret = recv(st->socketnum, st->inbuffer+st->inlen, sizeof(st->inbuffer)-st->inlen, 0); if (ret == 0) + { + Con_Printf ("tcp peer %s closed connection\n", NET_AdrToString (adr, sizeof(adr), st->remoteaddr)); goto closesvstream; + } else if (ret == -1) { err = qerrno; @@ -2748,12 +2756,12 @@ closesvstream: //optionally will be Origin=url, Sec-WebSocket-Protocol=FTEWebSocket, Sec-WebSocket-Extensions //other fields will be ignored. - //FIXME: reply with 426 Upgrade Required if wsversion is not supported - - if (!stricmp(arg[WCATTR_UPGRADE], "websocket") && !stricmp(arg[WCATTR_CONNECTION], "Upgrade")) + if (!stricmp(arg[WCATTR_UPGRADE], "websocket") && (!stricmp(arg[WCATTR_CONNECTION], "Upgrade") || !stricmp(arg[WCATTR_CONNECTION], "keep-alive, Upgrade"))) { if (atoi(arg[WCATTR_WSVER]) != 13) { + Con_Printf("Outdated websocket request from %s. got version %i, expected version 13\n", arg[WCATTR_URL], NET_AdrToString (adr, sizeof(adr), st->remoteaddr), arg[WCATTR_WSVER]); + memmove(st->inbuffer, st->inbuffer+i, st->inlen - (i)); st->inlen -= i; resp = va( "HTTP/1.1 426 Upgrade Required\r\n" @@ -2773,6 +2781,8 @@ closesvstream: tobase64(acceptkey, sizeof(acceptkey), sha1digest, SHA1(sha1digest, sizeof(sha1digest), va("%s258EAFA5-E914-47DA-95CA-C5AB0DC85B11", arg[WCATTR_WSKEY]))); + Con_Printf("Websocket request for %s from %s\n", arg[WCATTR_URL], NET_AdrToString (adr, sizeof(adr), st->remoteaddr)); + resp = va( "HTTP/1.1 101 Switching Protocols\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" @@ -2783,7 +2793,10 @@ closesvstream: send(st->socketnum, resp, strlen(resp), 0); //and the connection is okay - st->clienttype = TCPC_WEBSOCKET; + if (!strcmp(arg[WCATTR_WSPROTO], "binary")) + st->clienttype = TCPC_WEBSOCKETB; //emscripten doesn't give us a choice, but its compact. + else + st->clienttype = TCPC_WEBSOCKETU; //nacl supports only utf-8 encoded data, at least at the time I implemented it. } } else @@ -2814,19 +2827,23 @@ closesvstream: "" ); } - /*else if (!strcmp(arg[WCATTR_URL], "/index.html") || !strcmp(arg[WCATTR_URL], "/")) +/* else if ((!strcmp(arg[WCATTR_URL], "/ftewebgl.html") || !strcmp(arg[WCATTR_URL], "/ftewebgl.html.fmf") || !strcmp(arg[WCATTR_URL], "/pak0.pak")) && ((st->file = VFSOS_Open(va("C:/Incoming/vm%s", arg[WCATTR_URL]), "rb")))) { + Con_Printf("Downloading %s to %s\n", arg[WCATTR_URL], NET_AdrToString (adr, sizeof(adr), st->remoteaddr)); resp = va( "HTTP/1.1 200 Ok\r\n" - "Connection: Close\r\n" "Content-Type: text/html\r\n" - "\r\n" - - "This is a Quake WebSocket server, not an http server.
\r\n" - ""FULLENGINENAME"" + "Content-Length: %i\r\n" + "\r\n", + VFS_GETLEN(st->file) ); - }*/ + send(st->socketnum, resp, strlen(resp), 0); + st->clienttype = TCPC_HTTPCLIENT; + continue; + } +*/ else { + Con_Printf("Invalid download request %s to %s\n", arg[WCATTR_URL], NET_AdrToString (adr, sizeof(adr), st->remoteaddr)); resp = va( "HTTP/1.1 404 Ok\r\n" "Connection: Close\r\n" "Content-Type: text/html\r\n" @@ -2852,6 +2869,31 @@ handshakeerror: } break; + case TCPC_HTTPCLIENT: + if (st->outlen) + { /*try and flush the old data*/ + int done; + done = send(st->socketnum, st->outbuffer, st->outlen, 0); + if (done > 0) + { + memmove(st->outbuffer, st->outbuffer + done, st->outlen - done); + st->outlen -= done; + + st->timeouttime = timeval + 30; + } + } + if (!st->outlen) + { + st->outlen = VFS_READ(st->file, st->outbuffer, sizeof(st->outbuffer)); + if (st->outlen <= 0) + { + VFS_CLOSE(st->file); + st->file = NULL; + st->clienttype = TCPC_UNKNOWN; + Con_Printf ("Outgoing file transfer complete\n"); + } + } + continue; case TCPC_QIZMO: if (st->inlen < 2) continue; @@ -2887,7 +2929,8 @@ handshakeerror: net_message.currentbit = 0; net_from = st->remoteaddr; return true; - case TCPC_WEBSOCKET: + case TCPC_WEBSOCKETU: + case TCPC_WEBSOCKETB: while (st->inlen >= 2) { unsigned short ctrl = ((unsigned char*)st->inbuffer)[0]<<8 | ((unsigned char*)st->inbuffer)[1]; @@ -3008,7 +3051,7 @@ handshakeerror: } break; case 2: /*binary frame*/ - Con_Printf ("websocket binary frame from %s\n", NET_AdrToString (adr, sizeof(adr), st->remoteaddr)); +// Con_Printf ("websocket binary frame from %s\n", NET_AdrToString (adr, sizeof(adr), st->remoteaddr)); net_message.cursize = paylen; if (net_message.cursize >= sizeof(net_message_buffer) ) { @@ -3119,16 +3162,25 @@ qboolean FTENET_TCPConnect_SendPacket(ftenet_generic_connection_t *gcon, int len memcpy(st->outbuffer, data, length); st->outlen = length; break; - case TCPC_WEBSOCKET: + case TCPC_WEBSOCKETU: + case TCPC_WEBSOCKETB: { /*as a server, we don't need the mask stuff*/ - unsigned short ctrl = 0x8100; + unsigned short ctrl = (st->clienttype==TCPC_WEBSOCKETB)?0x8200:0x8100; unsigned int paylen = 0; unsigned int payoffs = 2; int i; - for (i = 0; i < length; i++) + switch((ctrl>>8) & 0xf) { - paylen += (((char*)data)[i] == 0 || ((unsigned char*)data)[i] >= 0x80)?2:1; + case 1: + for (i = 0; i < length; i++) + { + paylen += (((char*)data)[i] == 0 || ((unsigned char*)data)[i] >= 0x80)?2:1; + } + break; + default: + paylen = length; + break; } if (paylen >= 126) { @@ -3145,23 +3197,31 @@ qboolean FTENET_TCPConnect_SendPacket(ftenet_generic_connection_t *gcon, int len st->outbuffer[2] = paylen>>8; st->outbuffer[3] = paylen&0xff; } - /*utf8ify the data*/ - for (i = 0; i < length; i++) + switch((ctrl>>8) & 0xf) { - if (!((unsigned char*)data)[i]) - { /*0 is encoded as 0x100 to avoid safety checks*/ - st->outbuffer[payoffs++] = 0xc0 | (0x100>>6); - st->outbuffer[payoffs++] = 0x80 | (0x100&0x3f); - } - else if (((unsigned char*)data)[i] >= 0x80) - { /*larger bytes require markup*/ - st->outbuffer[payoffs++] = 0xc0 | (((unsigned char*)data)[i]>>6); - st->outbuffer[payoffs++] = 0x80 | (((unsigned char*)data)[i]&0x3f); - } - else - { /*lower 7 bits are as-is*/ - st->outbuffer[payoffs++] = ((char*)data)[i]; + case 1:/*utf8ify the data*/ + for (i = 0; i < length; i++) + { + if (!((unsigned char*)data)[i]) + { /*0 is encoded as 0x100 to avoid safety checks*/ + st->outbuffer[payoffs++] = 0xc0 | (0x100>>6); + st->outbuffer[payoffs++] = 0x80 | (0x100&0x3f); + } + else if (((unsigned char*)data)[i] >= 0x80) + { /*larger bytes require markup*/ + st->outbuffer[payoffs++] = 0xc0 | (((unsigned char*)data)[i]>>6); + st->outbuffer[payoffs++] = 0x80 | (((unsigned char*)data)[i]&0x3f); + } + else + { /*lower 7 bits are as-is*/ + st->outbuffer[payoffs++] = ((char*)data)[i]; + } } + break; + default: //raw data + memcpy(st->outbuffer+payoffs, data, length); + payoffs += length; + break; } st->outlen = payoffs; } diff --git a/engine/common/netinc.h b/engine/common/netinc.h index 02dae0cca..e8e69461e 100644 --- a/engine/common/netinc.h +++ b/engine/common/netinc.h @@ -126,6 +126,7 @@ #include #include #include + #include #include #include diff --git a/engine/d3d/d3d11_backend.c b/engine/d3d/d3d11_backend.c index d765b148e..72487a2f8 100644 --- a/engine/d3d/d3d11_backend.c +++ b/engine/d3d/d3d11_backend.c @@ -2915,5 +2915,17 @@ void D3D11BE_DrawWorld (qboolean drawworld, qbyte *vis) BE_RotateForEntity(&r_worldentity, NULL); } +void D3D11BE_VBO_Begin(vbobctx_t *ctx, unsigned int maxsize) +{ +} +void D3D11BE_VBO_Data(vbobctx_t *ctx, void *data, unsigned int size, vboarray_t *varray) +{ +} +void D3D11BE_VBO_Finish(vbobctx_t *ctx, void *edata, unsigned int esize, vboarray_t *earray) +{ +} +void D3D11BE_VBO_Destroy(vboarray_t *vearray) +{ +} #endif diff --git a/engine/d3d/d3d_backend.c b/engine/d3d/d3d_backend.c index 3b771e833..e8f1c9a58 100644 --- a/engine/d3d/d3d_backend.c +++ b/engine/d3d/d3d_backend.c @@ -3162,6 +3162,17 @@ void D3D9BE_DrawWorld (qboolean drawworld, qbyte *vis) BE_RotateForEntity(&r_worldentity, NULL); } - +void D3D9BE_VBO_Begin(vbobctx_t *ctx, unsigned int maxsize) +{ +} +void D3D9BE_VBO_Data(vbobctx_t *ctx, void *data, unsigned int size, vboarray_t *varray) +{ +} +void D3D9BE_VBO_Finish(vbobctx_t *ctx, void *edata, unsigned int esize, vboarray_t *earray) +{ +} +void D3D9BE_VBO_Destroy(vboarray_t *vearray) +{ +} #endif diff --git a/engine/d3d/vid_d3d.c b/engine/d3d/vid_d3d.c index ff75cbe6f..6747e4af9 100644 --- a/engine/d3d/vid_d3d.c +++ b/engine/d3d/vid_d3d.c @@ -1311,6 +1311,11 @@ rendererinfo_t d3d9rendererinfo = D3D9BE_SelectDLight, D3D9BE_LightCullModel, + D3D9BE_VBO_Begin, + D3D9BE_VBO_Data, + D3D9BE_VBO_Finish, + D3D9BE_VBO_Destroy, + "no more" }; diff --git a/engine/d3d/vid_d3d11.c b/engine/d3d/vid_d3d11.c index 17d635f94..16816e68a 100644 --- a/engine/d3d/vid_d3d11.c +++ b/engine/d3d/vid_d3d11.c @@ -1386,6 +1386,11 @@ rendererinfo_t d3d11rendererinfo = D3D11BE_SelectDLight, D3D11BE_LightCullModel, + D3D11BE_VBO_Begin, + D3D11BE_VBO_Data, + D3D11BE_VBO_Finish, + D3D11BE_VBO_Destroy, + "no more" }; #endif diff --git a/engine/gl/gl_alias.c b/engine/gl/gl_alias.c index 212096bd8..3797ad673 100644 --- a/engine/gl/gl_alias.c +++ b/engine/gl/gl_alias.c @@ -968,7 +968,7 @@ void R_GAlias_DrawBatch(batch_t *batch) { if (batch->surf_first == surfnum) { - needrecolour = Alias_GAliasBuildMesh(&mesh, inf, surfnum, e, batch->shader->prog && batch->shader->prog->permu[PERMUTATION_SKELETAL].handle.glsl); + needrecolour = Alias_GAliasBuildMesh(&mesh, &batch->vbo, inf, surfnum, e, batch->shader->prog && batch->shader->prog->permu[PERMUTATION_SKELETAL].handle.glsl); batch->mesh = &meshl; return; } @@ -1387,7 +1387,7 @@ void R_DrawGAliasShadowVolume(entity_t *e, vec3_t lightpos, float radius) { if (inf->ofs_trineighbours) { - Alias_GAliasBuildMesh(&mesh, inf, surfnum, e, false); + Alias_GAliasBuildMesh(&mesh, NULL, inf, surfnum, e, false); R_CalcFacing(&mesh, lightorg); R_ProjectShadowVolume(&mesh, lightorg); R_DrawShadowVolume(&mesh); diff --git a/engine/gl/gl_backend.c b/engine/gl/gl_backend.c index 137d5f114..beaeb445a 100644 --- a/engine/gl/gl_backend.c +++ b/engine/gl/gl_backend.c @@ -149,8 +149,6 @@ struct { unsigned int streamvbo_offset; unsigned int streamvbo_length; int streamebo; - unsigned int streamebo_offset; - unsigned int streamebo_length; int pendingtexcoordparts[SHADER_TMU_MAX]; int pendingtexcoordvbo[SHADER_TMU_MAX]; @@ -1333,7 +1331,6 @@ void GLBE_Init(void) qglGenBuffersARB(1, &shaderstate.streamvbo); qglGenBuffersARB(1, &shaderstate.streamebo); shaderstate.streamvbo_length = shaderstate.streamvbo_offset = 65536*16 * 64*sizeof(vec_t); - shaderstate.streamebo_length = shaderstate.streamebo_offset = 65536*16 * sizeof(index_t); } #endif } @@ -3830,15 +3827,9 @@ static qboolean BE_GenTempMeshVBO(vbo_t **vbo, mesh_t *m) } //and finally the elements array, which is a much simpler affair - if (shaderstate.streamebo_offset + m->numindexes*sizeof(*m->indexes)) - { - qglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, shaderstate.streamebo_length, NULL, GL_STREAM_DRAW_ARB); - shaderstate.streamebo_offset = 0; - } - qglBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, shaderstate.streamebo_offset, sizeof(*m->indexes) * m->numindexes, m->indexes); - shaderstate.dummyvbo.indicies.gl.addr = (void*)shaderstate.streamebo_offset; + qglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, sizeof(*m->indexes) * m->numindexes, m->indexes, GL_STREAM_DRAW_ARB); + shaderstate.dummyvbo.indicies.gl.addr = (void*)NULL; shaderstate.dummyvbo.indicies.gl.vbo = shaderstate.streamebo; - shaderstate.streamebo_offset += sizeof(*m->indexes) * m->numindexes; } else { @@ -4847,4 +4838,35 @@ void GLBE_DrawWorld (qboolean drawworld, qbyte *vis) TRACE(("GLBE_DrawWorld: drawn everything\n")); } + +void GLBE_VBO_Begin(vbobctx_t *ctx, unsigned int maxsize) +{ + ctx->maxsize = maxsize; + ctx->pos = 0; + qglGenBuffersARB(2, ctx->vboid); + GL_SelectVBO(ctx->vboid[0]); + //WARNING: in emscripten/webgl, we should probably not pass null. + qglBufferDataARB(GL_ARRAY_BUFFER_ARB, ctx->maxsize, NULL, GL_STATIC_DRAW_ARB); +} +void GLBE_VBO_Data(vbobctx_t *ctx, void *data, unsigned int size, vboarray_t *varray) +{ + qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, ctx->pos, size, data); + varray->gl.vbo = ctx->vboid[0]; + varray->gl.addr = (void*)ctx->pos; + ctx->pos += size; +} + +void GLBE_VBO_Finish(vbobctx_t *ctx, void *edata, unsigned int esize, vboarray_t *earray) +{ + if (ctx->pos > ctx->maxsize) + Sys_Error("BE_VBO_Finish: too much data given\n"); + GL_SelectEBO(ctx->vboid[1]); + qglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, esize, edata, GL_STATIC_DRAW_ARB); + earray->gl.vbo = ctx->vboid[1]; + earray->gl.addr = NULL; +} +void GLBE_VBO_Destroy(vboarray_t *vearray) +{ + qglDeleteBuffersARB(1, &vearray->gl.vbo); +} #endif diff --git a/engine/gl/gl_model.h b/engine/gl/gl_model.h index d73c6bf30..c2b970715 100644 --- a/engine/gl/gl_model.h +++ b/engine/gl/gl_model.h @@ -242,30 +242,6 @@ typedef struct mplane_s qbyte pad[2]; } mplane_t; -typedef struct vboarray_s -{ - union - { - void *dummy; - -#ifdef GLQUAKE - struct - { - int vbo; - void *addr; - } gl; -#endif - -#if defined(D3D9QUAKE) || defined(D3D11QUAKE) - struct - { - void *buff; - unsigned int offs; - } d3d; -#endif - }; -} vboarray_t; - typedef struct vbo_s { unsigned int numvisible; diff --git a/engine/gl/gl_shader.c b/engine/gl/gl_shader.c index 12ee42c96..1b160f168 100644 --- a/engine/gl/gl_shader.c +++ b/engine/gl/gl_shader.c @@ -373,7 +373,8 @@ static qboolean Shader_EvaluateCondition(shader_t *shader, char **ptr) Con_Printf("Shader_EvaluateCondition: '%s' is not a cvar\n", token); return conditiontrue; } - token = COM_ParseExt(ptr, false, false); + if (*token) + token = COM_ParseExt(ptr, false, false); cv->flags |= CVAR_SHADERSYSTEM; if (*token) { @@ -403,7 +404,8 @@ static qboolean Shader_EvaluateCondition(shader_t *shader, char **ptr) conditiontrue = conditiontrue == !!cv->value; } } - token = COM_ParseExt(ptr, false, false); + if (*token) + token = COM_ParseExt(ptr, false, false); if (!strcmp(token, "&&")) return Shader_EvaluateCondition(shader, ptr) && conditiontrue; if (!strcmp(token, "||")) diff --git a/engine/gl/gl_vidcommon.c b/engine/gl/gl_vidcommon.c index b1a60addf..7fd71411f 100644 --- a/engine/gl/gl_vidcommon.c +++ b/engine/gl/gl_vidcommon.c @@ -1708,6 +1708,11 @@ rendererinfo_t openglrendererinfo = { GLBE_SelectDLight, GLBE_LightCullModel, + GLBE_VBO_Begin, + GLBE_VBO_Data, + GLBE_VBO_Finish, + GLBE_VBO_Destroy, + "" }; diff --git a/engine/gl/shader.h b/engine/gl/shader.h index 61bb3e993..900d73763 100644 --- a/engine/gl/shader.h +++ b/engine/gl/shader.h @@ -275,8 +275,6 @@ enum{ enum shaderattribs_e { - VATTR_LEG_VERTEX, - VATTR_VERTEX1, VATTR_VERTEX2, VATTR_COLOUR, @@ -291,12 +289,18 @@ enum shaderattribs_e VATTR_LMCOORD3, VATTR_LMCOORD4, + VATTR_LEG_VERTEX, //note: traditionally this is actually index 0. + //however, implementations are allowed to directly alias, or remap, + //so we're never quite sure if 0 is enabled or not when using legacy functions. + //as a result, we use legacy verticies always and never custom attribute 0 if we have any fixed function support. + //we then depend upon gl_Vertex always being supported by the glsl compiler. + //this is likely needed anyway to ensure that ftransform works properly and in all cases for stencil shadows. VATTR_LEG_COLOUR, VATTR_LEG_ELEMENTS, VATTR_LEG_TMU0, - VATTR_LEG_FIRST=VATTR_LEG_COLOUR + VATTR_LEG_FIRST=VATTR_LEG_VERTEX }; typedef struct { @@ -514,6 +518,10 @@ void GLBE_SelectEntity(entity_t *ent); void GLBE_SelectDLight(dlight_t *dl, vec3_t colour); void GLBE_SubmitMeshes (qboolean drawworld, int start, int stop); void GLBE_RenderToTexture(texid_t sourcecol, texid_t sourcedepth, texid_t destcol, texid_t destdepth, qboolean usedepth); +void GLBE_VBO_Begin(vbobctx_t *ctx, unsigned int maxsize); +void GLBE_VBO_Data(vbobctx_t *ctx, void *data, unsigned int size, vboarray_t *varray); +void GLBE_VBO_Finish(vbobctx_t *ctx, void *edata, unsigned int esize, vboarray_t *earray); +void GLBE_VBO_Destroy(vboarray_t *vearray); #endif #ifdef D3D9QUAKE void D3D9BE_Init(void); @@ -530,6 +538,10 @@ void D3D9BE_DrawWorld (qboolean drawworld, qbyte *vis); qboolean D3D9BE_LightCullModel(vec3_t org, model_t *model); void D3D9BE_SelectEntity(entity_t *ent); void D3D9BE_SelectDLight(dlight_t *dl, vec3_t colour); +void D3D9BE_VBO_Begin(vbobctx_t *ctx, unsigned int maxsize); +void D3D9BE_VBO_Data(vbobctx_t *ctx, void *data, unsigned int size, vboarray_t *varray); +void D3D9BE_VBO_Finish(vbobctx_t *ctx, void *edata, unsigned int esize, vboarray_t *earray); +void D3D9BE_VBO_Destroy(vboarray_t *vearray); qboolean D3D9Shader_CreateProgram (program_t *prog, char *sname, int permu, char **precompilerconstants, char *vert, char *frag); int D3D9Shader_FindUniform(union programhandle_u *h, int type, char *name); @@ -558,6 +570,10 @@ void D3D11Shader_Init(void); void D3D11BE_Reset(qboolean before); void D3D11BE_SetupViewCBuffer(void); void D3D11_UploadLightmap(lightmapinfo_t *lm); +void D3D11BE_VBO_Begin(vbobctx_t *ctx, unsigned int maxsize); +void D3D11BE_VBO_Data(vbobctx_t *ctx, void *data, unsigned int size, vboarray_t *varray); +void D3D11BE_VBO_Finish(vbobctx_t *ctx, void *edata, unsigned int esize, vboarray_t *earray); +void D3D11BE_VBO_Destroy(vboarray_t *vearray); #endif //Asks the backend to invoke DrawMeshChain for each surface, and to upload lightmaps as required