From bbd5c0ac8b2e9538ace83533a8a917df0ce9bf3e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 1 Mar 2021 20:48:03 +0100 Subject: [PATCH] - transitioned voxel rendering to use the backend's implementation. --- source/CMakeLists.txt | 2 + source/build/include/mdsprite.h | 12 +- source/build/src/mdsprite.cpp | 4 +- source/build/src/polymost.cpp | 8 +- source/build/src/voxmodel.cpp | 1081 +------------------------ source/common/models/models_md3.cpp | 17 +- source/common/models/models_obj.cpp | 4 +- source/common/models/models_ue1.cpp | 4 +- source/common/models/models_voxel.cpp | 3 + source/games/blood/src/tile.cpp | 3 +- source/glbackend/gl_models.cpp | 118 +++ source/glbackend/gl_models.h | 58 ++ source/glbackend/glbackend.cpp | 22 +- source/glbackend/glbackend.h | 8 + source/glbackend/pm_renderstate.h | 4 + 15 files changed, 274 insertions(+), 1074 deletions(-) create mode 100644 source/glbackend/gl_models.cpp create mode 100644 source/glbackend/gl_models.h diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index fb849c2b1..e587d550f 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -1021,6 +1021,7 @@ set (PCH_SOURCES glbackend/glbackend.cpp glbackend/gl_palmanager.cpp glbackend/gl_texture.cpp + glbackend/gl_models.cpp thirdparty/src/md4.cpp @@ -1509,6 +1510,7 @@ source_group("Core\\2D" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/core/2d source_group("Core\\Console" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/core/console/.+") source_group("Core\\DObject" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/core/dobject/.+") source_group("Core\\Menu" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/core/menu/.+") +source_group("Rendering" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/glbackend/.+") source_group("Platform" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/platform/.+") source_group("Platform\\Win32" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/platform/win32/.+") source_group("Platform\\POSIX" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/platform/posix/.+") diff --git a/source/build/include/mdsprite.h b/source/build/include/mdsprite.h index cd20f6a26..1c4882ab3 100644 --- a/source/build/include/mdsprite.h +++ b/source/build/include/mdsprite.h @@ -4,6 +4,7 @@ #ifdef USE_OPENGL #include "palette.h" #include "gl_hwtexture.h" +#include "model_kvx.h" #if defined(_M_IX86) || defined(_M_AMD64) || defined(__i386) || defined(__x86_64) #define SHIFTMOD32(a) (a) @@ -18,7 +19,8 @@ class FGameTexture; struct mdmodel_t { - int32_t mdnum, shadeoff; + + int32_t mdnum, shadeoff; float scale, bscale, zadd, yoffset; FGameTexture *texture; @@ -188,13 +190,10 @@ typedef struct { vert_t v[4]; } voxrect_t; struct voxmodel_t : public mdmodel_t { - //VOX specific stuff: - voxrect_t *quad; int32_t qcnt, qfacind[7]; - int32_t *mytex, mytexx, mytexy; + FVoxelModel* model = nullptr; vec3_t siz; vec3f_t piv; int32_t is8bit; - TMap *texIds; }; EXTERN mdmodel_t **models; @@ -210,8 +209,7 @@ EXTERN int32_t nextmodelid; EXTERN voxmodel_t *voxmodels[MAXVOXELS]; void voxfree(voxmodel_t *m); -voxmodel_t *voxload(const char *filnam); -voxmodel_t *loadkvxfrombuf(const char *buffer, int32_t length); +voxmodel_t *voxload(int lumpnum); int32_t polymost_voxdraw(voxmodel_t *m, tspriteptr_t const tspr); int md3postload_polymer(md3model_t* m); diff --git a/source/build/src/mdsprite.cpp b/source/build/src/mdsprite.cpp index c7ebdc803..8aba8ec76 100644 --- a/source/build/src/mdsprite.cpp +++ b/source/build/src/mdsprite.cpp @@ -1596,8 +1596,8 @@ static mdmodel_t *mdload(const char *filnam) mdmodel_t *vm; int32_t i; - vm = (mdmodel_t *)voxload(filnam); - if (vm) return vm; + //vm = (mdmodel_t *)voxload(filnam); + //if (vm) return vm; auto fil = fileSystem.OpenFileReader(filnam); diff --git a/source/build/src/polymost.cpp b/source/build/src/polymost.cpp index b512a535b..2bf705d50 100644 --- a/source/build/src/polymost.cpp +++ b/source/build/src/polymost.cpp @@ -3596,8 +3596,12 @@ void PolymostProcessVoxels(void) { if (voxfilenames[i]) { - voxmodels[i] = voxload(voxfilenames[i]); - voxmodels[i]->scale = voxscale[i] * (1.f / 65536.f); + int lumpnum = fileSystem.FindFile(voxfilenames[i]); + if (lumpnum >= 0) + { + voxmodels[i] = voxload(lumpnum); + voxmodels[i]->scale = voxscale[i] * (1.f / 65536.f); + } DO_FREE_AND_NULL(voxfilenames[i]); } } diff --git a/source/build/src/voxmodel.cpp b/source/build/src/voxmodel.cpp index a98c8bef3..79db18c36 100644 --- a/source/build/src/voxmodel.cpp +++ b/source/build/src/voxmodel.cpp @@ -11,1005 +11,48 @@ #include "flatvertices.h" #include "hw_renderstate.h" #include "texturemanager.h" +#include "voxels.h" +#include "glbackend/gl_models.h" #include "palette.h" #include "../../glbackend/glbackend.h" - -//For loading/conversion only -static vec3_t voxsiz; -static int32_t yzsiz, *vbit = 0; //vbit: 1 bit per voxel: 0=air,1=solid -static vec3f_t voxpiv; - -static int32_t *vcolhashead = 0, vcolhashsizm1; -typedef struct { int32_t p, c, n; } voxcol_t; -static voxcol_t *vcol = 0; int32_t vnum = 0, vmax = 0; - -typedef struct { int16_t x, y; } spoint2d; -static spoint2d *shp; -static int32_t *shcntmal, *shcnt = 0, shcntp; - -static int32_t mytexo5, *zbit, gmaxx, gmaxy, garea, pow2m1[33]; -static voxmodel_t *gvox; - -class FStaticImage : public FImageSource -{ - TArray buffer; - -public: - FStaticImage(int w, int h, TArray& srcbuffer) - { - Width = w; - Height = h; - buffer = std::move(srcbuffer); - bUseGamePalette = false; - } - - int CopyPixels(FBitmap* bmp, int conversion) override - { - *bmp = FBitmap(buffer.Data(), 4*Width, Width, Height); - return 0; - } -}; - -//pitch must equal xsiz*4 -static FGameTexture *gloadtex(const int32_t *picbuf, int32_t xsiz, int32_t ysiz, int32_t is8bit, const PalEntry *paldata) -{ - // Correct for GL's RGB order; also apply gamma here: - const coltype *const pic = (const coltype *)picbuf; - TArray buffer(xsiz * ysiz * sizeof(PalEntry)); - PalEntry* pic2 = (PalEntry*)buffer.Data(); - - if (!is8bit) - { - for (int i=xsiz*ysiz-1; i>=0; i--) - { - pic2[i].r = pic[i].r; - pic2[i].g = pic[i].g; - pic2[i].b = pic[i].b; - pic2[i].a = 255; - } - } - else - { - for (int i=xsiz*ysiz-1; i>=0; i--) - { - const int32_t ii = pic[i].a; - - pic2[i].r = paldata[ii].r; - pic2[i].g = paldata[ii].g; - pic2[i].b = paldata[ii].b; - pic2[i].a = 255; - } - } - auto tt = MakeGameTexture(new FImageTexture(new FStaticImage(xsiz, ysiz, buffer)), "", ETextureType::Special); - TexMan.AddGameTexture(tt, false); - return tt; -} - -static int32_t getvox(int32_t x, int32_t y, int32_t z) -{ - z += x*yzsiz + y*voxsiz.z; - - for (x=vcolhashead[(z*214013LL)&vcolhashsizm1]; x>=0; x=vcol[x].n) - if (vcol[x].p == z) - return vcol[x].c; - - return 0x808080; -} - -static void putvox(int32_t x, int32_t y, int32_t z, int32_t col) -{ - if (vnum >= vmax) - { - vmax = max(vmax<<1, 4096); - vcol = (voxcol_t *)Xrealloc(vcol, vmax*sizeof(voxcol_t)); - } - - z += x*yzsiz + y*voxsiz.z; - - vcol[vnum].p = z; z = (z*214013LL)&vcolhashsizm1; - vcol[vnum].c = col; - vcol[vnum].n = vcolhashead[z]; vcolhashead[z] = vnum++; -} - -//Set all bits in vbit from (x,y,z0) to (x,y,z1-1) to 0's -#if 0 -static void setzrange0(int32_t *lptr, int32_t z0, int32_t z1) -{ - if (!((z0^z1)&~31)) { lptr[z0>>5] &= ((~-(1<>5), ze = (z1>>5); - lptr[z] &=~-(1<>5] |= ((~-(1<>5), ze = (z1>>5); - lptr[z] |= -(1<mytexx + x0; - for (dy=0; dy; dy--, i+=gvox->mytexx) - for (x=0; x>5]&(1<>5); - dx += x0-1; - const int32_t c = (dx>>5) - (x0>>5); - - int32_t m = ~pow2m1[x0&31]; - const int32_t m1 = pow2m1[(dx&31)+1]; - - if (!c) - { - for (m &= m1; dy; dy--, i += mytexo5) - if (zbit[i]&m) - return 0; - } - else - { - for (; dy; dy--, i += mytexo5) - { - if (zbit[i]&m) - return 0; - - int32_t x; - for (x=1; xmytexx + x0; - for (y=0; ymytexx) - for (x=0; x>5] |= (1<>5); - dx += x0-1; - const int32_t c = (dx>>5) - (x0>>5); - - int32_t m = ~pow2m1[x0&31]; - const int32_t m1 = pow2m1[(dx&31)+1]; - - if (!c) - { - for (m &= m1; dy; dy--, i += mytexo5) - zbit[i] |= m; - } - else - { - for (; dy; dy--, i += mytexo5) - { - zbit[i] |= m; - - int32_t x; - for (x=1; x gmaxx) gmaxx = x; - if (y > gmaxy) gmaxy = y; - - garea += (x+(VOXBORDWIDTH<<1)) * (y+(VOXBORDWIDTH<<1)); - gvox->qcnt++; -} - -static void addquad(int32_t x0, int32_t y0, int32_t z0, int32_t x1, int32_t y1, int32_t z1, - int32_t x2, int32_t y2, int32_t z2, int32_t face) -{ - int32_t i; - int32_t x = labs(x2-x0), y = labs(y2-y0), z = labs(z2-z0); - - if (x == 0) { x = y; y = z; i = 0; } - else if (y == 0) { y = z; i = 1; } - else i = 2; - - if (x < y) { z = x; x = y; y = z; i += 3; } - - z = shcnt[y*shcntp+x]++; - int32_t *lptr = &gvox->mytex[(shp[z].y+VOXBORDWIDTH)*gvox->mytexx + - (shp[z].x+VOXBORDWIDTH)]; - int32_t nx = 0, ny = 0, nz = 0; - - switch (face) - { - case 0: - ny = y1; x2 = x0; x0 = x1; x1 = x2; break; - case 1: - ny = y0; y0++; y1++; y2++; break; - case 2: - nz = z1; y0 = y2; y2 = y1; y1 = y0; z0++; z1++; z2++; break; - case 3: - nz = z0; break; - case 4: - nx = x1; y2 = y0; y0 = y1; y1 = y2; x0++; x1++; x2++; break; - case 5: - nx = x0; break; - } - - for (bssize_t yy=0; yymytexx) - for (bssize_t xx=0; xxmytex[(shp[z].y+yy)*gvox->mytexx + shp[z].x]; - lptr[xx] = lptr[VOXBORDWIDTH]; - lptr[xx+x+VOXBORDWIDTH] = lptr[x-1+VOXBORDWIDTH]; - } - - //Extend borders vertically - for (bssize_t yy=0; yymytex[(shp[z].y+yy)*gvox->mytexx + shp[z].x], - &gvox->mytex[(shp[z].y+VOXBORDWIDTH)*gvox->mytexx + shp[z].x], - (x+(VOXBORDWIDTH<<1))<<2); - memcpy(&gvox->mytex[(shp[z].y+y+yy+VOXBORDWIDTH)*gvox->mytexx + shp[z].x], - &gvox->mytex[(shp[z].y+y-1+VOXBORDWIDTH)*gvox->mytexx + shp[z].x], - (x+(VOXBORDWIDTH<<1))<<2); - } - - voxrect_t *const qptr = &gvox->quad[gvox->qcnt]; - - qptr->v[0].x = x0; qptr->v[0].y = y0; qptr->v[0].z = z0; - qptr->v[1].x = x1; qptr->v[1].y = y1; qptr->v[1].z = z1; - qptr->v[2].x = x2; qptr->v[2].y = y2; qptr->v[2].z = z2; - - for (bssize_t j=0; j<3; j++) - { - qptr->v[j].u = shp[z].x+VOXBORDWIDTH; - qptr->v[j].v = shp[z].y+VOXBORDWIDTH; - } - - if (i < 3) - qptr->v[1].u += x; - else - qptr->v[1].v += y; - - qptr->v[2].u += x; - qptr->v[2].v += y; - - qptr->v[3].u = qptr->v[0].u - qptr->v[1].u + qptr->v[2].u; - qptr->v[3].v = qptr->v[0].v - qptr->v[1].v + qptr->v[2].v; - qptr->v[3].x = qptr->v[0].x - qptr->v[1].x + qptr->v[2].x; - qptr->v[3].y = qptr->v[0].y - qptr->v[1].y + qptr->v[2].y; - qptr->v[3].z = qptr->v[0].z - qptr->v[1].z + qptr->v[2].z; - - if (gvox->qfacind[face] < 0) - gvox->qfacind[face] = gvox->qcnt; - - gvox->qcnt++; -} - -static inline int32_t isolid(int32_t x, int32_t y, int32_t z) -{ - if ((uint32_t)x >= (uint32_t)voxsiz.x) return 0; - if ((uint32_t)y >= (uint32_t)voxsiz.y) return 0; - if ((uint32_t)z >= (uint32_t)voxsiz.z) return 0; - - z += x*yzsiz + y*voxsiz.z; - - return vbit[z>>5] & (1<>5] & (1<qfacind[i] = -1; - - i = (max(voxsiz.y, voxsiz.z)+1)<<2; - int32_t *const bx0 = (int32_t *)Xmalloc(i<<1); - int32_t *const by0 = (int32_t *)(((intptr_t)bx0)+i); - - int32_t ov, oz=0; - - for (bssize_t cnt=0; cnt<2; cnt++) - { - void (*daquad)(int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t, int32_t) = - cnt == 0 ? cntquad : addquad; - - gvox->qcnt = 0; - - memset(by0, -1, (max(voxsiz.y, voxsiz.z)+1)<<2); - int32_t v = 0; - - for (i=-1; i<=1; i+=2) - for (bssize_t y=0; y= 0) && ((by0[z] != oz) || (v >= ov))) - { - daquad(bx0[z], y, by0[z], x, y, by0[z], x, y, z, i>=0); - by0[z] = -1; - } - - if (v > ov) oz = z; - else if ((v < ov) && (by0[z] != oz)) { bx0[z] = x; by0[z] = oz; } - } - - for (i=-1; i<=1; i+=2) - for (bssize_t z=0; z= 0) && ((by0[y] != oz) || (v >= ov))) - { - daquad(bx0[y], by0[y], z, x, by0[y], z, x, y, z, (i>=0)+2); - by0[y] = -1; - } - - if (v > ov) oz = y; - else if ((v < ov) && (by0[y] != oz)) { bx0[y] = x; by0[y] = oz; } - } - - for (i=-1; i<=1; i+=2) - for (bssize_t x=0; x= 0) && ((by0[z] != oz) || (v >= ov))) - { - daquad(x, bx0[z], by0[z], x, y, by0[z], x, y, z, (i>=0)+4); - by0[z] = -1; - } - - if (v > ov) oz = z; - else if ((v < ov) && (by0[z] != oz)) { bx0[z] = y; by0[z] = oz; } - } - - if (!cnt) - { - shp = (spoint2d *)Xmalloc(gvox->qcnt*sizeof(spoint2d)); - - int32_t sc = 0; - - for (bssize_t y=gmaxy; y; y--) - for (bssize_t x=gmaxx; x>=y; x--) - { - i = shcnt[y*shcntp+x]; shcnt[y*shcntp+x] = sc; //shcnt changes from counter to head index - - for (; i>0; i--) - { - shp[sc].x = x; - shp[sc].y = y; - sc++; - } - } - - for (gvox->mytexx=32; gvox->mytexx<(gmaxx+(VOXBORDWIDTH<<1)); gvox->mytexx<<=1) - /* do nothing */; - - for (gvox->mytexy=32; gvox->mytexy<(gmaxy+(VOXBORDWIDTH<<1)); gvox->mytexy<<=1) - /* do_nothing */; - - while (gvox->mytexx*gvox->mytexy*8 < garea*9) //This should be sufficient to fit most skins... - { -skindidntfit: - if (gvox->mytexx <= gvox->mytexy) - gvox->mytexx <<= 1; - else - gvox->mytexy <<= 1; - } - - mytexo5 = gvox->mytexx>>5; - - i = ((gvox->mytexx*gvox->mytexy+31)>>5)<<2; - zbit = (int32_t *)Xmalloc(i); - memset(zbit, 0, i); - - v = gvox->mytexx*gvox->mytexy; - for (bssize_t z=0; zmytexx, 255)-dx))>>15; - y0 = ((rand()&32767)*(min(gvox->mytexy, 255)-dy))>>15; -#else - x0 = ((rand()&32767)*(gvox->mytexx+1-dx))>>15; - y0 = ((rand()&32767)*(gvox->mytexy+1-dy))>>15; -#endif - i--; - if (i < 0) //Time-out! Very slow if this happens... but at least it still works :P - { - Xfree(zbit); - - //Re-generate shp[].x/y (box sizes) from shcnt (now head indices) for next pass :/ - j = 0; - - for (bssize_t y=gmaxy; y; y--) - for (bssize_t x=gmaxx; x>=y; x--) - { - i = shcnt[y*shcntp+x]; - - for (; jquad = (voxrect_t *)Xmalloc(gvox->qcnt*sizeof(voxrect_t)); - gvox->mytex = (int32_t *)Xmalloc(gvox->mytexx*gvox->mytexy*sizeof(int32_t)); - } - } - - Xfree(shp); Xfree(zbit); Xfree(bx0); - - return gvox; -} - -static void alloc_vcolhashead(void) -{ - vcolhashead = (int32_t *)Xmalloc((vcolhashsizm1+1)*sizeof(int32_t)); - memset(vcolhashead, -1, (vcolhashsizm1+1)*sizeof(int32_t)); -} - -static void alloc_vbit(void) -{ - yzsiz = voxsiz.y*voxsiz.z; - int32_t i = ((voxsiz.x*yzsiz+31)>>3)+1; - - vbit = (int32_t *)Xmalloc(i); - memset(vbit, 0, i); -} - -static void read_pal(FileReader &fil, int32_t pal[256]) -{ - fil.Seek(-768, FileReader::SeekEnd); - - for (bssize_t i=0; i<256; i++) - { - char c[3]; - fil.Read(c, 3); -//#if B_BIG_ENDIAN != 0 - pal[i] = B_LITTLE32(unsigned((c[0]<<18) + (c[1]<<10) + (c[2]<<2) + (i<<24))); -//#endif - } -} - -static int32_t loadvox(const char *filnam) -{ - auto fil = fileSystem.OpenFileReader(filnam); - if (!fil.isOpen()) - return -1; - - fil.Read(&voxsiz, sizeof(vec3_t)); -#if B_BIG_ENDIAN != 0 - voxsiz.x = B_LITTLE32(voxsiz.x); - voxsiz.y = B_LITTLE32(voxsiz.y); - voxsiz.z = B_LITTLE32(voxsiz.z); -#endif - voxpiv.x = (float)voxsiz.x * .5f; - voxpiv.y = (float)voxsiz.y * .5f; - voxpiv.z = (float)voxsiz.z * .5f; - - int32_t pal[256]; - read_pal(fil, pal); - pal[255] = -1; - - vcolhashsizm1 = 8192-1; - alloc_vcolhashead(); - alloc_vbit(); - - char *const tbuf = (char *)Xmalloc(voxsiz.z*sizeof(uint8_t)); - - fil.Seek(12, FileReader::SeekSet); - for (bssize_t x=0; x=0; z--) - if (tbuf[z] != 255) - { - const int32_t i = j+z; - vbit[i>>5] |= (1<=0; i--) - xyoffs[i] = B_LITTLE16(xyoffs[i]); - - int32_t pal[256]; - read_pal(fil, pal); - - alloc_vbit(); - - for (vcolhashsizm1=4096; vcolhashsizm1<(mip1leng>>1); vcolhashsizm1<<=1) - { - /* do nothing */ - } - vcolhashsizm1--; //approx to numvoxs! - alloc_vcolhashead(); - - fil.Seek(28+((voxsiz.x+1)<<2)+((ysizp1*voxsiz.x)<<1), FileReader::SeekSet); - - i = fil.GetLength() - fil.Tell(); - char *const tbuf = (char *)Xmalloc(i); - - fil.Read(tbuf, i); - - char *cptr = tbuf; - - for (bssize_t x=0; x=0; i--) - ylen[i] = B_LITTLE16(ylen[i]); - - fil.Seek(32, FileReader::SeekSet); - - alloc_vbit(); - - for (vcolhashsizm1=4096; vcolhashsizm10; i--) - { - char c[8]; - fil.Read(c, 8); //b,g,r,a,z_lo,z_hi,vis,dir - - const int32_t z0 = B_LITTLE16(B_UNBUF16(&c[4])); - - if (!(c[6]&16)) - setzrange1(vbit, j+z1, j+z0); - - vbit[(j+z0)>>5] |= (1<mytex); - DO_FREE_AND_NULL(m->quad); Xfree(m); } -voxmodel_t *voxload(const char *filnam) +voxmodel_t *voxload(int lumpnum) { - int32_t is8bit, ret; - - const int32_t i = strlen(filnam)-4; - if (i < 0) - return NULL; - - if (!stricmp(&filnam[i], ".vox")) { ret = loadvox(filnam); is8bit = 1; } - else if (!stricmp(&filnam[i], ".kvx")) { ret = loadkvx(filnam); is8bit = 1; } - else if (!stricmp(&filnam[i], ".kv6")) { ret = loadkv6(filnam); is8bit = 0; } - //else if (!stricmp(&filnam[i],".vxl")) { ret = loadvxl(filnam); is8bit = 0; } - else return NULL; - - voxmodel_t *const vm = (ret >= 0) ? vox2poly() : NULL; - - if (vm) + FVoxel* voxel = R_LoadKVX(lumpnum); + if (voxel != nullptr) { + voxmodel_t* vm = (voxmodel_t*)Xmalloc(sizeof(voxmodel_t)); + memset(vm, 0, sizeof(voxmodel_t)); + auto pivot = voxel->Mips[0].Pivot; vm->mdnum = 1; //VOXel model id vm->scale = vm->bscale = 1.f; - vm->siz.x = voxsiz.x; vm->siz.y = voxsiz.y; vm->siz.z = voxsiz.z; - vm->piv.x = voxpiv.x; vm->piv.y = voxpiv.y; vm->piv.z = voxpiv.z; - vm->is8bit = is8bit; - vm->texture = nullptr; + vm->piv.x = float(pivot.X); + vm->piv.y = float(pivot.Y); + vm->piv.z = float(pivot.Z); + vm->siz.x = voxel->Mips[0].SizeX; + vm->siz.y = voxel->Mips[0].SizeY; + vm->siz.z = voxel->Mips[0].SizeZ; + vm->is8bit = true; + voxel->Mips[0].Pivot.Zero(); // Needs to be taken out of the voxel data because it gets baked into the vertex buffer which we cannot use here. + vm->model = new FVoxelModel(voxel, true); + return vm; } - - DO_FREE_AND_NULL(shcntmal); - DO_FREE_AND_NULL(vbit); - DO_FREE_AND_NULL(vcol); - vnum = vmax = 0; - DO_FREE_AND_NULL(vcolhashead); - - return vm; -} - -voxmodel_t *loadkvxfrombuf(const char *kvxbuffer, int32_t length) -{ - int32_t i, mip1leng; - - if (!kvxbuffer) - return NULL; - - char *buffer = (char*)Xmalloc(length); - if (!buffer) - return NULL; - - memcpy(buffer, kvxbuffer, length); - - int32_t *longptr = (int32_t*)buffer; - - mip1leng = *longptr++; mip1leng = B_LITTLE32(mip1leng); - if (mip1leng > length - 4) - { - // Invalid KVX file - Xfree(buffer); - return NULL; - } - memcpy(&voxsiz, longptr, sizeof(vec3_t)); - longptr += 3; -#if B_BIG_ENDIAN != 0 - voxsiz.x = B_LITTLE32(voxsiz.x); - voxsiz.y = B_LITTLE32(voxsiz.y); - voxsiz.z = B_LITTLE32(voxsiz.z); -#endif - i = *longptr++; voxpiv.x = (float)B_LITTLE32(i)*(1.f/256.f); - i = *longptr++; voxpiv.y = (float)B_LITTLE32(i)*(1.f/256.f); - i = *longptr++; voxpiv.z = (float)B_LITTLE32(i)*(1.f/256.f); - longptr += voxsiz.x+1; - - const int32_t ysizp1 = voxsiz.y+1; - i = voxsiz.x*ysizp1*sizeof(int16_t); - uint16_t *xyoffs = (uint16_t*)longptr; - - for (i=i/sizeof(int16_t)-1; i>=0; i--) - xyoffs[i] = B_LITTLE16(xyoffs[i]); - - const char *paloff = buffer+length-768; - int32_t pal[256]; - - for (bssize_t i=0; i<256; i++) - { - const char *c = &paloff[i*3]; -//#if B_BIG_ENDIAN != 0 - pal[i] = B_LITTLE32(int((c[0]<<18) + (c[1]<<10) + (c[2]<<2) + (i<<24))); -//#endif - } - - alloc_vbit(); - - for (vcolhashsizm1=4096; vcolhashsizm1<(mip1leng>>1); vcolhashsizm1<<=1) - { - /* do nothing */ - } - vcolhashsizm1--; //approx to numvoxs! - alloc_vcolhashead(); - - const char *cptr = buffer+28+((voxsiz.x+1)<<2)+((ysizp1*voxsiz.x)<<1); - - for (bssize_t x=0; xmdnum = 1; //VOXel model id - vm->scale = vm->bscale = 1.f; - vm->siz.x = voxsiz.x; vm->siz.y = voxsiz.y; vm->siz.z = voxsiz.z; - vm->piv.x = voxpiv.x; vm->piv.y = voxpiv.y; vm->piv.z = voxpiv.z; - vm->is8bit = 1; - } - - DO_FREE_AND_NULL(shcntmal); - DO_FREE_AND_NULL(vbit); - DO_FREE_AND_NULL(vcol); - vnum = vmax = 0; - DO_FREE_AND_NULL(vcolhashead); - Xfree(buffer); - - return vm; + return nullptr; } //Draw voxel model as perfect cubes // Note: This is a hopeless mess that totally forfeits any chance of using a vertex buffer with its messy coordinate adjustments. :( int32_t polymost_voxdraw(voxmodel_t* m, tspriteptr_t const tspr) { - // float clut[6] = {1.02,1.02,0.94,1.06,0.98,0.98}; float f, g, k0, zoff; if ((intptr_t)m == (intptr_t)(-1)) // hackhackhack @@ -1122,83 +165,25 @@ int32_t polymost_voxdraw(voxmodel_t* m, tspriteptr_t const tspr) for (int i = 0; i < 15; i++) mat[i] *= 1024.f; + // Adjust to backend coordinate system being used by the vertex buffer. + for (int i = 4; i < 8; i++) + { + float f = mat[i]; + mat[i] = -mat[i + 4]; + mat[i + 4] = -f; + } + GLInterface.SetMatrix(Matrix_Model, mat); - const float ru = 1.f/((float)m->mytexx); - const float rv = 1.f/((float)m->mytexy); -#if (VOXBORDWIDTH == 0) - uhack[0] = ru*.125; uhack[1] = -uhack[0]; - vhack[0] = rv*.125; vhack[1] = -vhack[0]; -#endif - const float phack[2] = { 0, 1.f/256.f }; - -#if 1 - int palId = TRANSLATION(Translation_Remap + curbasepal, globalpal); - auto palette = GPalette.TranslationToTable(palId); - if (!m->texIds) m->texIds = new TMap; - auto pTex = m->texIds->CheckKey(palId); - FGameTexture* htex; - if (!pTex) - { - htex = gloadtex(m->mytex, m->mytexx, m->mytexy, m->is8bit, palette->Palette); - m->texIds->Insert(palId, htex); - } - else - { - htex = *pTex; - } - + int palId = TRANSLATION(Translation_Remap + curbasepal, globalpal); GLInterface.SetPalswap(globalpal); GLInterface.SetFade(sector[tspr->sectnum].floorpal); - // The texture here is already translated. - GLInterface.SetTexture(htex, 0/*TRANSLATION(Translation_Remap + curbasepal, globalpal)*/, CLAMP_NOFILTER_XY, true); -#endif - auto data = screen->mVertexData->AllocVertices(m->qcnt * 6); - auto vt = data.first; - - int qstart = data.second; - int qdone = 0; - for (bssize_t i=0, fi=0; iqcnt; i++) - { - if (i == m->qfacind[fi]) - { - f = 1 /*clut[fi++]*/; - if (qdone > 0) - { - GLInterface.Draw(DT_Triangles, qstart, qdone * 6); - qstart += qdone * 6; - qdone = 0; - } - GLInterface.SetColor(pc[0]*f, pc[1]*f, pc[2]*f, pc[3]*f); - } - - const vert_t *const vptr = &m->quad[i].v[0]; - - const int32_t xx = vptr[0].x + vptr[2].x; - const int32_t yy = vptr[0].y + vptr[2].y; - const int32_t zz = vptr[0].z + vptr[2].z; - - for (bssize_t jj=0; jj<6; jj++, vt++) - { - static uint8_t trix[] = { 0, 1, 2, 0, 2, 3 }; - int j = trix[jj]; -#if (VOXBORDWIDTH == 0) - vt->SetTexCoord(((float)vptr[j].u)*ru + uhack[vptr[j].u!=vptr[0].u], - ((float)vptr[j].v)*rv + vhack[vptr[j].v!=vptr[0].v]); -#else - vt->SetTexCoord(((float)vptr[j].u)*ru, ((float)vptr[j].v)*rv); -#endif - vt->SetVertex( - ((float)vptr[j].x) - phack[xx > vptr[j].x * 2] + phack[xx < vptr[j].x * 2], - ((float)vptr[j].y) - phack[yy > vptr[j].y * 2] + phack[yy < vptr[j].y * 2], - ((float)vptr[j].z) - phack[zz > vptr[j].z * 2] + phack[zz < vptr[j].z * 2]); - } - qdone++; - } - - GLInterface.Draw(DT_Triangles, qstart, qdone * 6); - //------------ + auto tex = TexMan.GetGameTexture(m->model->GetPaletteTexture()); + GLInterface.SetTexture(tex, TRANSLATION(Translation_Remap + curbasepal, globalpal), CLAMP_NOFILTER_XY, true); + GLInterface.SetModel(m->model, 0, 0, 0); + GLInterface.Draw(DT_Triangles, 0, 0); + GLInterface.SetModel(nullptr, 0, 0, 0); GLInterface.SetCull(Cull_None); if (shadowHack) diff --git a/source/common/models/models_md3.cpp b/source/common/models/models_md3.cpp index 15224e4fe..ba17eb83a 100644 --- a/source/common/models/models_md3.cpp +++ b/source/common/models/models_md3.cpp @@ -306,7 +306,7 @@ void FMD3Model::AddSkins(uint8_t *hitlist) { for (unsigned i = 0; i < Surfaces.Size(); i++) { - if (curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].isValid()) + if (curSpriteMDLFrame && curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].isValid()) { hitlist[curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].GetIndex()] |= FTextureManager::HIT_Flat; } @@ -357,13 +357,16 @@ void FMD3Model::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int f FGameTexture *surfaceSkin = skin; if (!surfaceSkin) { - if (curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].isValid()) + if (curSpriteMDLFrame) { - surfaceSkin = TexMan.GetGameTexture(curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i], true); - } - else if (surf->numSkins > 0 && surf->Skins[0].isValid()) - { - surfaceSkin = TexMan.GetGameTexture(surf->Skins[0], true); + if (curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].isValid()) + { + surfaceSkin = TexMan.GetGameTexture(curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i], true); + } + else if (surf->numSkins > 0 && surf->Skins[0].isValid()) + { + surfaceSkin = TexMan.GetGameTexture(surf->Skins[0], true); + } } if (!surfaceSkin) diff --git a/source/common/models/models_obj.cpp b/source/common/models/models_obj.cpp index 264ffd6ac..cd0e99536 100644 --- a/source/common/models/models_obj.cpp +++ b/source/common/models/models_obj.cpp @@ -636,7 +636,7 @@ void FOBJModel::RenderFrame(FModelRenderer *renderer, FGameTexture * skin, int f OBJSurface *surf = &surfaces[i]; FGameTexture *userSkin = skin; - if (!userSkin) + if (!userSkin && curSpriteMDLFrame) { if (i < MD3_MAX_SURFACES && curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].isValid()) { @@ -669,7 +669,7 @@ void FOBJModel::AddSkins(uint8_t* hitlist) { for (size_t i = 0; i < surfaces.Size(); i++) { - if (i < MD3_MAX_SURFACES && curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].isValid()) + if (curSpriteMDLFrame && i < MD3_MAX_SURFACES && curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][i].isValid()) { // Precache skins manually reassigned by the user. // On OBJs with lots of skins, such as Doom map OBJs exported from GZDB, diff --git a/source/common/models/models_ue1.cpp b/source/common/models/models_ue1.cpp index 3066b7759..7210a26f2 100644 --- a/source/common/models/models_ue1.cpp +++ b/source/common/models/models_ue1.cpp @@ -242,7 +242,7 @@ void FUE1Model::RenderFrame( FModelRenderer *renderer, FGameTexture *skin, int f FGameTexture *sskin = skin; if ( !sskin ) { - if ( curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][groups[i].texNum].isValid() ) + if (curSpriteMDLFrame && curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][groups[i].texNum].isValid() ) sskin = TexMan.GetGameTexture(curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][groups[i].texNum], true); if ( !sskin ) { @@ -303,7 +303,7 @@ void FUE1Model::BuildVertexBuffer( FModelRenderer *renderer ) void FUE1Model::AddSkins( uint8_t *hitlist ) { for ( int i=0; isurfaceskinIDs[curMDLIndex][groups[i].texNum].isValid() ) + if (curSpriteMDLFrame && curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][groups[i].texNum].isValid() ) hitlist[curSpriteMDLFrame->surfaceskinIDs[curMDLIndex][groups[i].texNum].GetIndex()] |= FTextureManager::HIT_Flat; } diff --git a/source/common/models/models_voxel.cpp b/source/common/models/models_voxel.cpp index cfac12372..2be0521ad 100644 --- a/source/common/models/models_voxel.cpp +++ b/source/common/models/models_voxel.cpp @@ -37,6 +37,7 @@ #include "texturemanager.h" #include "palettecontainer.h" #include "textures.h" +#include "imagehelpers.h" #ifdef _MSC_VER #pragma warning(disable:4244) // warning C4244: conversion from 'double' to 'float', possible loss of data @@ -99,6 +100,7 @@ TArray FVoxelTexture::CreatePalettedPixels(int conversion) pe.b = (pp[2] << 2) | (pp[2] >> 4); // Alphatexture handling is just for completeness, but rather unlikely to be used ever. Pixels[i] = conversion == luminance ? pe.r : ColorMatcher.Pick(pe); + } } else @@ -108,6 +110,7 @@ TArray FVoxelTexture::CreatePalettedPixels(int conversion) Pixels[i] = (uint8_t)i; } } + ImageHelpers::FlipSquareBlock(Pixels.Data(), Width); return Pixels; } diff --git a/source/games/blood/src/tile.cpp b/source/games/blood/src/tile.cpp index b9d35aad6..ac46809d1 100644 --- a/source/games/blood/src/tile.cpp +++ b/source/games/blood/src/tile.cpp @@ -101,8 +101,7 @@ void tileProcessGLVoxels(void) auto index = fileSystem.FindResource(i, "KVX"); if (index >= 0) { - auto data = fileSystem.ReadFile(index); - voxmodels[i] = loadkvxfrombuf((const char*)data.GetMem(), data.GetSize()); + voxmodels[i] = voxload(index); } } } diff --git a/source/glbackend/gl_models.cpp b/source/glbackend/gl_models.cpp new file mode 100644 index 000000000..318ed7d3f --- /dev/null +++ b/source/glbackend/gl_models.cpp @@ -0,0 +1,118 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2005-2016 Christoph Oelckers +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// +/* +** gl_models.cpp +** +** hardware renderer model handling code +** +**/ + +#include "filesystem.h" +#include "i_time.h" +#include "cmdlib.h" +#include "hw_material.h" +#include "hwrenderer/data/buffers.h" +#include "flatvertices.h" +#include "hw_renderstate.h" +#include "gl_models.h" + +//CVAR(Bool, gl_light_models, true, CVAR_ARCHIVE) + +VSMatrix FHWModelRenderer::GetViewToWorldMatrix() +{ + VSMatrix objectToWorldMatrix(0); + //di->VPUniforms.mViewMatrix.inverseMatrix(objectToWorldMatrix); + return objectToWorldMatrix; +} + +void FHWModelRenderer::BeginDrawModel(FRenderStyle style, FSpriteModelFrame *smf, const VSMatrix &objectToWorldMatrix, bool mirrored) +{ + state.SetDepthFunc(DF_LEqual); + state.EnableTexture(true); + state.SetCulling(mirrored ? Cull_CCW : Cull_CW); + + state.mModelMatrix = objectToWorldMatrix; + state.EnableModelMatrix(true); +} + +void FHWModelRenderer::EndDrawModel(FRenderStyle style, FSpriteModelFrame *smf) +{ + state.EnableModelMatrix(false); + state.SetDepthFunc(DF_Less); + state.SetCulling(Cull_None); +} + +void FHWModelRenderer::BeginDrawHUDModel(FRenderStyle style, const VSMatrix &objectToWorldMatrix, bool mirrored) +{ + state.SetDepthFunc(DF_LEqual); + state.SetCulling(mirrored ? Cull_CCW : Cull_CW); + state.mModelMatrix = objectToWorldMatrix; + state.EnableModelMatrix(true); +} + +void FHWModelRenderer::EndDrawHUDModel(FRenderStyle style) +{ + state.EnableModelMatrix(false); + + state.SetDepthFunc(DF_Less); + state.SetCulling(Cull_None); +} + +IModelVertexBuffer *FHWModelRenderer::CreateVertexBuffer(bool needindex, bool singleframe) +{ + return new FModelVertexBuffer(needindex, singleframe); +} + +void FHWModelRenderer::SetInterpolation(double inter) +{ + state.SetInterpolationFactor((float)inter); +} + +void FHWModelRenderer::SetMaterial(FGameTexture *skin, bool clampNoFilter, int translation) +{ + state.SetMaterial(skin, UF_Skin, 0, clampNoFilter ? CLAMP_NOFILTER : CLAMP_NONE, translation, -1); + state.SetLightIndex(modellightindex); +} + +void FHWModelRenderer::DrawArrays(int start, int count) +{ + state.Draw(DT_Triangles, start, count); +} + +void FHWModelRenderer::DrawElements(int numIndices, size_t offset) +{ + state.DrawIndexed(DT_Triangles, int(offset / sizeof(unsigned int)), numIndices); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +void FHWModelRenderer::SetupFrame(FModel *model, unsigned int frame1, unsigned int frame2, unsigned int size) +{ + auto mdbuff = static_cast(model->GetVertexBuffer(GetType())); + state.SetVertexBuffer(mdbuff->vertexBuffer(), frame1, frame2); + if (mdbuff->indexBuffer()) state.SetIndexBuffer(mdbuff->indexBuffer()); +} + diff --git a/source/glbackend/gl_models.h b/source/glbackend/gl_models.h new file mode 100644 index 000000000..c13fc7800 --- /dev/null +++ b/source/glbackend/gl_models.h @@ -0,0 +1,58 @@ +// +//--------------------------------------------------------------------------- +// +// Copyright(C) 2005-2016 Christoph Oelckers +// All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//-------------------------------------------------------------------------- +// + +#pragma once + +#include "tarray.h" +#include "voxels.h" +#include "hwrenderer/data/buffers.h" +#include "hw_modelvertexbuffer.h" +#include "modelrenderer.h" + +class HWSprite; +struct HWDrawInfo; +class FRenderState; + + +class FHWModelRenderer : public FModelRenderer +{ + friend class FModelVertexBuffer; + int modellightindex = -1; + FRenderState &state; +public: + FHWModelRenderer(FRenderState &st, int mli) : modellightindex(mli), state(st) + {} + ModelRendererType GetType() const override { return GLModelRendererType; } + void BeginDrawModel(FRenderStyle style, FSpriteModelFrame *smf, const VSMatrix &objectToWorldMatrix, bool mirrored) override; + void EndDrawModel(FRenderStyle style, FSpriteModelFrame *smf) override; + IModelVertexBuffer *CreateVertexBuffer(bool needindex, bool singleframe) override; + VSMatrix GetViewToWorldMatrix() override; + void BeginDrawHUDModel(FRenderStyle style, const VSMatrix &objectToWorldMatrix, bool mirrored) override; + void EndDrawHUDModel(FRenderStyle style) override; + void SetInterpolation(double interpolation) override; + void SetMaterial(FGameTexture *skin, bool clampNoFilter, int translation) override; + void DrawArrays(int start, int count) override; + void DrawElements(int numIndices, size_t offset) override; + void SetupFrame(FModel *model, unsigned int frame1, unsigned int frame2, unsigned int size) override; + +}; + diff --git a/source/glbackend/glbackend.cpp b/source/glbackend/glbackend.cpp index 6b3dad847..8cc3d006d 100644 --- a/source/glbackend/glbackend.cpp +++ b/source/glbackend/glbackend.cpp @@ -49,6 +49,7 @@ #include "hw_renderstate.h" #include "hw_cvars.h" #include "gamestruct.h" +#include "gl_models.h" CVAR(Bool, gl_texture, true, 0) @@ -111,11 +112,28 @@ void GLInstance::DoDraw() lastState.Flags = ~rendercommands[0].StateFlags; // Force ALL flags to be considered 'changed'. lastState.DepthFunc = INT_MIN; // Something totally invalid. screen->RenderState()->EnableMultisampling(true); + auto& state = *screen->RenderState(); for (auto& rs : rendercommands) { - if (rs.Apply(*screen->RenderState(), lastState)) - screen->RenderState()->Draw(rs.primtype, rs.vindex, rs.vcount); + if (rs.Apply(state, lastState)) + { + if (!rs.model) + { + state.Draw(rs.primtype, rs.vindex, rs.vcount); + } + else + { + FHWModelRenderer mr(*screen->RenderState(), 0); + state.SetDepthFunc(DF_LEqual); + state.EnableTexture(true); + rs.model->BuildVertexBuffer(&mr); + mr.SetupFrame(rs.model, rs.mframes[0], rs.mframes[1], rs.mfactor); + rs.model->RenderFrame(&mr, rs.mMaterial.mTexture, rs.mframes[0], rs.mframes[1], 0.f, rs.mMaterial.mTranslation); + state.SetDepthFunc(DF_Less); + state.SetVertexBuffer(screen->mVertexData); + } + } } renderState.Apply(*screen->RenderState(), lastState); // apply any pending change before returning. rendercommands.Clear(); diff --git a/source/glbackend/glbackend.h b/source/glbackend/glbackend.h index 9fec3bf7d..1a74145e7 100644 --- a/source/glbackend/glbackend.h +++ b/source/glbackend/glbackend.h @@ -329,6 +329,14 @@ public: } bool SetTexture(FGameTexture* tex, int palette, int sampleroverride, bool notindexed = false); + + void SetModel(FModel* model, int frame1, int frame2, float factor) + { + renderState.model = model; + renderState.mframes[0] = frame1; + renderState.mframes[1] = frame2; + renderState.mfactor = factor; + } }; extern GLInstance GLInterface; diff --git a/source/glbackend/pm_renderstate.h b/source/glbackend/pm_renderstate.h index 09d334b1b..7127cbf77 100644 --- a/source/glbackend/pm_renderstate.h +++ b/source/glbackend/pm_renderstate.h @@ -5,6 +5,7 @@ #include "renderstyle.h" struct GLState; class FMaterial; +class FModel; enum EMatrixType { @@ -79,6 +80,9 @@ struct PolymostRenderState short matrixIndex[NUMMATRICES] = { -1 }; FDepthBiasState mBias{ }; PolymostTextureState mMaterial; + FModel* model = nullptr; + int mframes[2] = { 0,0 }; + float mfactor = 0; int StateFlags = STF_COLORMASK|STF_DEPTHMASK; FRenderStyle Style{};