From 9002a68e646da88a58cfa8781d611072a181043f Mon Sep 17 00:00:00 2001 From: Plagman Date: Sat, 14 Oct 2006 23:33:10 +0000 Subject: [PATCH] Polymer cliplanes and FOV. git-svn-id: https://svn.eduke32.com/eduke32@310 1a8010ca-5511-0410-912e-c29ae57300e0 --- polymer/build/include/build.h | 10 ++ polymer/build/include/polymer.h | 15 +- polymer/build/src/engine.c | 23 ++- polymer/build/src/polymer.c | 245 ++++++++++++++++++------------- polymer/eduke32/source/osdcmds.c | 1 + polymer/polymer/polymer.suo | Bin 71680 -> 79360 bytes 6 files changed, 176 insertions(+), 118 deletions(-) diff --git a/polymer/build/include/build.h b/polymer/build/include/build.h index 6f6a551c1..dbfc4dc23 100644 --- a/polymer/build/include/build.h +++ b/polymer/build/include/build.h @@ -433,6 +433,16 @@ long setsprite(short spritenum, long newx, long newy, long newz); long screencapture(char *filename, char inverseit); +// PLAG: line utility functions +typedef struct s_equation { + float a, b, c; +} _equation; +typedef struct s_point2d { + long x, y; +} _point2d; +_equation equation(long x1, long y1, long x2, long y2); +int sameside(_equation* eq, _point2d* p1, _point2d* p2); + #define STATUS2DSIZ 144 void qsetmode640350(void); void qsetmode640480(void); diff --git a/polymer/build/include/polymer.h b/polymer/build/include/polymer.h index 152545d1f..77e317cd4 100644 --- a/polymer/build/include/polymer.h +++ b/polymer/build/include/polymer.h @@ -17,6 +17,7 @@ # include "pragmas.h" // CVARS +extern unsigned int pr_fov; extern char pr_verbosity; extern char pr_wireframe; @@ -37,6 +38,7 @@ typedef struct s_prsector { short wallcount; char invalidate; + char drawingstate; // 0: fcuk, 1: in queue, 2: todraw, 3: drawn } _prsector; typedef struct s_prwall { @@ -51,6 +53,12 @@ typedef struct s_prwall { char invalidate; } _prwall; +typedef struct s_cliplane { + _equation left, right, clip; + _point2d ref; + char clipsign; +} _cliplane; + extern _prsector* prsectors[MAXSECTORS]; extern _prwall* prwalls[MAXWALLS]; @@ -59,6 +67,9 @@ int polymer_init(void); void polymer_glinit(void); void polymer_loadboard(void); void polymer_drawrooms(long daposx, long daposy, long daposz, short daang, long dahoriz, short dacursectnum, int root); +void polymer_rotatesprite(long sx, long sy, long z, short a, short picnum, signed char dashade, char dapalnum, char dastat, long cx1, long cy1, long cx2, long cy2); +void polymer_drawmaskwall(long damaskwallcnt); +void polymer_drawsprite(long snum); // SECTOR MANAGEMENT int polymer_initsector(short sectnum); int polymer_updatesector(short sectnum); @@ -67,10 +78,12 @@ void PR_CALLBACK polymer_tesserror(GLenum error); void PR_CALLBACK polymer_tessedgeflag(GLenum error); void PR_CALLBACK polymer_tessvertex(void* vertex, void* sector); int polymer_buildfloor(short sectnum); -void polymer_drawsector(long daposx, long daposy, long daposz, short daang, long dahoriz, short sectnum, int root); +void polymer_drawsector(short sectnum); // WALL MANAGEMENT int polymer_initwall(short wallnum); void polymer_updatewall(short wallnum); void polymer_drawwall(short wallnum); +// HSR +int wallincliplane(short wallnum, _cliplane* cliplane); #endif // !_polymer_h_ diff --git a/polymer/build/src/engine.c b/polymer/build/src/engine.c index decc9e428..d746bc23d 100644 --- a/polymer/build/src/engine.c +++ b/polymer/build/src/engine.c @@ -3123,13 +3123,14 @@ static void drawsprite(long snum) //============================================================================= //POLYMOST BEGINS #ifdef POLYMOST - if (rendmode) { + if (rendmode == 3) { polymost_drawsprite(snum); #ifdef USE_OPENGL bglDepthMask(1); #endif return; } + if (rendmode == 4) { polymer_drawsprite(snum); return; } #endif //============================================================================= //POLYMOST ENDS @@ -4045,7 +4046,8 @@ static void drawmaskwall(short damaskwallcnt) //============================================================================= //POLYMOST BEGINS #ifdef POLYMOST - if (rendmode) { polymost_drawmaskwall(damaskwallcnt); return; } + if (rendmode == 3) { polymost_drawmaskwall(damaskwallcnt); return; } + if (rendmode == 4) { polymer_drawmaskwall(damaskwallcnt); return; } #endif //============================================================================= //POLYMOST ENDS @@ -4523,7 +4525,8 @@ static void dorotatesprite(long sx, long sy, long z, short a, short picnum, sign //============================================================================= //POLYMOST BEGINS #ifdef POLYMOST - if (rendmode) { polymost_dorotatesprite(sx,sy,z,a,picnum,dashade,dapalnum,dastat,cx1,cy1,cx2,cy2,uniqid); return; } + if (rendmode >= 3) { polymost_dorotatesprite(sx,sy,z,a,picnum,dashade,dapalnum,dastat,cx1,cy1,cx2,cy2,uniqid); return; } + if (rendmode == 4) { polymer_rotatesprite(sx,sy,z,a,picnum,dashade,dapalnum,dastat,cx1,cy1,cx2,cy2); return; } #endif //============================================================================= //POLYMOST ENDS @@ -5788,14 +5791,6 @@ void drawrooms(long daposx, long daposy, long daposz, } // UTILITY TYPES AND FUNCTIONS FOR DRAWMASKS OCCLUSION TREE -typedef struct s_point2d { - long x, y; -} _point2d; - -typedef struct s_equation { - float a, b, c; -} _equation; - typedef struct s_maskleaf { long index; _point2d p1, p2; @@ -7403,10 +7398,12 @@ if (searchx < 0) { searchx = halfxdimen; searchy = (ydimen>>1); } #if defined(POLYMOST) && defined(USE_OPENGL) if (rendmode == 3) + { polymost_glinit(); - else if (rendmode == 4) + polymost_glreset(); + } + if (rendmode == 4) polymer_glinit(); - polymost_glreset(); #endif qsetmode = 200; return(0); diff --git a/polymer/build/src/polymer.c b/polymer/build/src/polymer.c index 4fa2e4470..673217aab 100644 --- a/polymer/build/src/polymer.c +++ b/polymer/build/src/polymer.c @@ -3,15 +3,15 @@ #include "polymer.h" // CVARS -char pr_verbosity = 1; // 0: silent, 1: errors and one-times, 2: multiple-times, 3: flood -char pr_wireframe = 0; +unsigned int pr_fov = 340; // 60 degrees, appears to be the classic setting. +char pr_verbosity = 1; // 0: silent, 1: errors and one-times, 2: multiple-times, 3: flood +char pr_wireframe = 0; // DATA _prsector *prsectors[MAXSECTORS]; _prwall *prwalls[MAXWALLS]; -float polymostprojmatrix[16]; -float polymostmodelmatrix[16]; +_cliplane cliplane; GLUtesselator* prtess; int tempverticescount; @@ -53,30 +53,15 @@ int polymer_init(void) void polymer_glinit(void) { - GLfloat params[4]; - - bglGetFloatv(GL_PROJECTION_MATRIX, polymostprojmatrix); - bglGetFloatv(GL_MODELVIEW_MATRIX, polymostmodelmatrix); bglClearColor(0.0f, 0.0f, 0.0f, 1.0f); bglClearStencil(0); bglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); - bglViewport(0, 0, 1024, 768); + bglViewport(0, 0, xdim, ydim); // texturing bglEnable(GL_TEXTURE_2D); bglTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT); bglTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT); - /*bglEnable(GL_TEXTURE_GEN_S); - bglEnable(GL_TEXTURE_GEN_T); - params[0] = GL_OBJECT_LINEAR; - bglTexGenfv(GL_S, GL_TEXTURE_GEN_MODE, params); - bglTexGenfv(GL_T, GL_TEXTURE_GEN_MODE, params); - params[0] = 1.0 / 10000.0; - params[1] = 1.0 / 10000.0; - params[2] = 1.0 / 10000.0; - params[3] = 1.0 / 10000.0; - bglTexGenfv(GL_S, GL_OBJECT_PLANE, params); - bglTexGenfv(GL_T, GL_OBJECT_PLANE, params);*/ bglDisable(GL_FOG); bglEnable(GL_DEPTH_TEST); @@ -87,7 +72,8 @@ void polymer_glinit(void) bglMatrixMode(GL_PROJECTION); bglLoadIdentity(); - bglFrustum(-1.0f, 1.0f, -0.75f, 0.75, 1.0f, 1000000.0f); + gluPerspective((float)(pr_fov) / (2048.0f / 360.0f), (float)xdim / (float)ydim, 1.0f, 1000000.0f); + bglMatrixMode(GL_MODELVIEW); bglLoadIdentity(); @@ -123,25 +109,120 @@ void polymer_loadboard(void) } void polymer_drawrooms(long daposx, long daposy, long daposz, short daang, long dahoriz, short dacursectnum, int root) { - int i; + int i, j; + float ang, horizang, tiltang; + double pos[3]; + _point2d ref; + sectortype *sec; + walltype *wal; + short drawnsectors, fov; if (pr_verbosity >= 3) OSD_Printf("PR : Drawing rooms...\n"); + OSD_Printf("PR : %d\n", dahoriz); + + ang = (float)(daang) / (2048.0f / 360.0f); + horizang = (float)(100 - dahoriz) / (512.0f / 180.0f); + tiltang = (gtang * 90.0f); + fov = (pr_fov * (float)xdim / (float)ydim * 1) / 2; + + // FOV cliplane + rotatepoint(daposx, daposy, daposx, daposy - 1000, daang, &ref.x, &ref.y); + cliplane.clip = equation(daposx, daposy, ref.x, ref.y); + rotatepoint(daposx, daposy, daposx, daposy - 1000, (daang + 512 - fov) & 2047, &ref.x, &ref.y); + cliplane.left = equation(daposx, daposy, ref.x, ref.y); + rotatepoint(daposx, daposy, daposx, daposy - 1000, (daang + 512 + fov) & 2047, &ref.x, &ref.y); + cliplane.right = equation(daposx, daposy, ref.x, ref.y); + rotatepoint(daposx, daposy, daposx, daposy - 1000, (daang + 512) & 2047, &ref.x, &ref.y); + cliplane.ref = ref; + cliplane.clipsign = 1; + + pos[0] = -daposy; + pos[1] = daposz; + pos[2] = daposx; + + bglMatrixMode(GL_MODELVIEW); + bglLoadIdentity(); + + bglRotatef(horizang, 1.0f, 0.0f, 0.0f); + bglRotatef(ang, 0.0f, 1.0f, 0.0f); + bglRotatef(tiltang, 0.0f, 0.0f, -1.0f); + + bglScalef(1.0f, 1.0f / 16.0f, 1.0f); + bglTranslatef(pos[0], pos[1], pos[2]); + i = 0; while (i < numsectors) { - polymer_drawsector(daposx, daposy, daposz, daang, dahoriz, i, 0); + if (prsectors[i]) + { + if (i == dacursectnum) + prsectors[i]->drawingstate = 2; + else + prsectors[i]->drawingstate = 0; + } i++; } - bglMatrixMode(GL_PROJECTION); - bglLoadMatrixf(polymostprojmatrix); - bglMatrixMode(GL_MODELVIEW); - bglLoadMatrixf(polymostmodelmatrix); + drawnsectors = 1; + while (drawnsectors > 0) + { + drawnsectors = 0; + + i = 0; + while (i < numsectors) + { + if (prsectors[i] && prsectors[i]->drawingstate == 2) + { + polymer_drawsector(i); + + sec = §or[i]; + wal = &wall[sec->wallptr]; + + j = 0; + while (j < sec->wallnum) + { + if (wallincliplane(sec->wallptr + j, &cliplane)) + { + polymer_drawwall(sec->wallptr + j); + if ((wal->nextsector != -1) && (prsectors[wal->nextsector]->drawingstate == 0)) + prsectors[wal->nextsector]->drawingstate = 1; + } + + j++; + wal = &wall[sec->wallptr + j]; + } + + prsectors[i]->drawingstate = 3; + drawnsectors++; + } + i++; + } + + i = 0; + while (i < numsectors) + { + if (prsectors[i] && prsectors[i]->drawingstate == 1) + prsectors[i]->drawingstate = 2; + i++; + } + } if (pr_verbosity >= 3) OSD_Printf("PR : Rooms drawn.\n"); } +void polymer_rotatesprite(long sx, long sy, long z, short a, short picnum, signed char dashade, char dapalnum, char dastat, long cx1, long cy1, long cx2, long cy2) +{ +} + +void polymer_drawmaskwall(long damaskwallcnt) +{ +} + +void polymer_drawsprite(long snum) +{ +} + // SECTOR MANAGEMENT int polymer_initsector(short sectnum) { @@ -414,13 +495,11 @@ int polymer_buildfloor(short sectnum) return (1); } -void polymer_drawsector(long daposx, long daposy, long daposz, short daang, long dahoriz, short sectnum, int root) +void polymer_drawsector(short sectnum) { sectortype *sec, *nextsec; walltype *wal; _prsector* s; - float ang, horizang, tiltang; - double pos[3]; int i; long zdiff; @@ -436,7 +515,7 @@ void polymer_drawsector(long daposx, long daposy, long daposz, sh polymer_updatesector(sectnum); polymer_buildfloor(sectnum); } - else if (prsectors[sectnum]->invalidate || 0) + else if (prsectors[sectnum]->invalidate || 1) { if (pr_verbosity >= 2) OSD_Printf("PR : Sector %i invalidated. Tesselating...\n", sectnum); polymer_updatesector(sectnum); @@ -450,72 +529,14 @@ void polymer_drawsector(long daposx, long daposy, long daposz, sh prsectors[sectnum]->invalidate = 0; } - - ang = (float)(daang) / (2048.0f / 360.0f); - horizang = (float)(100 - dahoriz) / (256.0f / 90.0f); - tiltang = (gtang * 90.0f); - - pos[0] = -daposy; - pos[1] = daposz; - pos[2] = daposx; - - bglMatrixMode(GL_MODELVIEW); - bglLoadIdentity(); - - bglRotatef(horizang, 1.0f, 0.0f, 0.0f); - bglRotatef(ang, 0.0f, 1.0f, 0.0f); - bglRotatef(tiltang, 0.0f, 0.0f, -1.0f); - - bglScalef(1.0f, 1.0f / 16.0f, 1.0f); - bglTranslatef(pos[0], pos[1], pos[2]); - - bglEnable(GL_TEXTURE_2D); - // floor if (!(sec->floorstat & 1)) { - if (root) - { - bglDisable(GL_TEXTURE_2D); - bglColorMask(0, 0, 0, 0); - bglEnable(GL_STENCIL_TEST); - - bglStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); - bglStencilFunc(GL_ALWAYS, 1, 1); - - bglVertexPointer(3, GL_FLOAT, 5 * sizeof(GLfloat), s->floorbuffer); - bglTexCoordPointer(2, GL_FLOAT, 5 * sizeof(GLfloat), &s->floorbuffer[3]); - bglDrawElements(GL_TRIANGLES, s->indicescount, GL_UNSIGNED_SHORT, s->floorindices); - - bglStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); - bglStencilFunc(GL_EQUAL, 1, 1); - bglColorMask(1, 1, 1, 1); - bglEnable(GL_TEXTURE_2D); - - //bglDepthMask(0); - polymer_drawrooms(daposx, daposy, daposz - ((daposz - sec->floorz) * 2), daang, dahoriz, sectnum, 0); - //bglDepthMask(1); - - bglDisable(GL_STENCIL_TEST); - bglEnable(GL_BLEND); - bglBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); - - bglBindTexture(GL_TEXTURE_2D, s->floorglpic); - bglColor4f(s->floorcolor[0], s->floorcolor[1], s->floorcolor[2], 0.5f); - bglVertexPointer(3, GL_FLOAT, 5 * sizeof(GLfloat), s->floorbuffer); - bglTexCoordPointer(2, GL_FLOAT, 5 * sizeof(GLfloat), &s->floorbuffer[3]); - bglDrawElements(GL_TRIANGLES, s->indicescount, GL_UNSIGNED_SHORT, s->floorindices); - - bglDisable(GL_BLEND); - } - else - { - bglBindTexture(GL_TEXTURE_2D, s->floorglpic); - bglColor4f(s->floorcolor[0], s->floorcolor[1], s->floorcolor[2], s->floorcolor[3]); - bglVertexPointer(3, GL_FLOAT, 5 * sizeof(GLfloat), s->floorbuffer); - bglTexCoordPointer(2, GL_FLOAT, 5 * sizeof(GLfloat), &s->floorbuffer[3]); - bglDrawElements(GL_TRIANGLES, s->indicescount, GL_UNSIGNED_SHORT, s->floorindices); - } + bglBindTexture(GL_TEXTURE_2D, s->floorglpic); + bglColor4f(s->floorcolor[0], s->floorcolor[1], s->floorcolor[2], s->floorcolor[3]); + bglVertexPointer(3, GL_FLOAT, 5 * sizeof(GLfloat), s->floorbuffer); + bglTexCoordPointer(2, GL_FLOAT, 5 * sizeof(GLfloat), &s->floorbuffer[3]); + bglDrawElements(GL_TRIANGLES, s->indicescount, GL_UNSIGNED_SHORT, s->floorindices); } // ceiling @@ -528,16 +549,6 @@ void polymer_drawsector(long daposx, long daposy, long daposz, sh bglDrawElements(GL_TRIANGLES, s->indicescount, GL_UNSIGNED_SHORT, s->ceilindices); } - // walls - i = 0; - while (i < sec->wallnum) - { - polymer_drawwall(sec->wallptr + i); - - i++; - wal = &wall[sec->wallptr + i]; - } - if (pr_verbosity >= 3) OSD_Printf("PR : Finished drawing sector %i...\n", sectnum); } @@ -771,3 +782,29 @@ void polymer_drawwall(short wallnum) if (pr_verbosity >= 3) OSD_Printf("PR : Finished drawing wall %i...\n", wallnum); } + +// HSR +int wallincliplane(short wallnum, _cliplane* cliplane) +{ + walltype *wal; + _point2d p1, p2; + + wal = &wall[wallnum]; + + p1.x = wal->x; + p1.y = wal->y; + + p2.x = wall[wal->point2].x; + p2.y = wall[wal->point2].y; + + if ((sameside(&cliplane->clip, &cliplane->ref, &p1) != cliplane->clipsign) && (sameside(&cliplane->clip, &cliplane->ref, &p2) != cliplane->clipsign)) + return (0); + + /*if ((sameside(&cliplane->left, &cliplane->ref, &p1) == 0) && (sameside(&cliplane->left, &cliplane->ref, &p2) == 0)) + return (0); + + if ((sameside(&cliplane->right, &cliplane->ref, &p1) == 0) && (sameside(&cliplane->right, &cliplane->ref, &p2) == 0)) + return (0);*/ + + return (1); +} diff --git a/polymer/eduke32/source/osdcmds.c b/polymer/eduke32/source/osdcmds.c index 77e23811f..49386938f 100644 --- a/polymer/eduke32/source/osdcmds.c +++ b/polymer/eduke32/source/osdcmds.c @@ -472,6 +472,7 @@ struct cvarmappings { { "r_anamorphic", "r_anamorphic: enable/disable widescreen mode", (void*)&glwidescreen, CVAR_BOOL, 0, 0, 1 }, { "r_projectionhack", "r_projectionhack: enable/disable projection hack", (void*)&glprojectionhacks, CVAR_BOOL, 0, 0, 1 }, // polymer cvars + { "pr_fov", "pr_fov: sets the field of vision in build angle", (void*)&pr_fov, CVAR_UNSIGNEDINT, 0, 0, 1024}, { "pr_verbosity", "pr_verbosity: verbosity level of the polymer renderer", (void*)&pr_verbosity, CVAR_INT, 0, 0, 3 }, { "pr_wireframe", "pr_wireframe: toggles wireframe mode", (void*)&pr_wireframe, CVAR_BOOL, 0, 0, 1 }, #endif diff --git a/polymer/polymer/polymer.suo b/polymer/polymer/polymer.suo index e1e9d0f2732e07c92a999da6cd5cb3288d0a845e..a356a588d9962a09404a69518c13832b9dcdafcc 100644 GIT binary patch delta 20104 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 S%;-b^&u;?H#q$-@%l;SJvPrK1 delta 14957 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 ClJFe>