From 2c55dcca27155882221f68f8f101dd0a1d652c1b Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 1 Feb 2016 18:14:00 +0100 Subject: [PATCH] - use hardware clipping to apply 3D lights to sprites. This solves all the problems with properly lighting models and billboarded sprites. - add some restrictions for the checks that exclude sprites which are too close to the camera. Most importantly, for clipping the first frame of a projectile, do not solely clip against the projectile's speed but use the player's radius as a limiting factor to avoid too large distances. --- src/gl/scene/gl_sprite.cpp | 298 +++++++++++++++++-------------------- src/gl/scene/gl_wall.h | 3 +- 2 files changed, 139 insertions(+), 162 deletions(-) diff --git a/src/gl/scene/gl_sprite.cpp b/src/gl/scene/gl_sprite.cpp index 2abfd47f7..bfd2a9deb 100644 --- a/src/gl/scene/gl_sprite.cpp +++ b/src/gl/scene/gl_sprite.cpp @@ -217,75 +217,120 @@ void GLSprite::Draw(int pass) if (gltexture) gl_RenderState.SetMaterial(gltexture, CLAMP_XY, translation, OverrideShader, !!(RenderStyle.Flags & STYLEF_RedIsAlpha)); else if (!modelframe) gl_RenderState.EnableTexture(false); - if (!modelframe) + //gl_SetColor(lightlevel, rel, Colormap, trans); + + unsigned int iter; + + if (lightlist) { - // [BB] Billboard stuff - const bool drawWithXYBillboard = ( (particle && gl_billboard_particles) || (!(actor && actor->renderflags & RF_FORCEYBILLBOARD) - //&& GLRenderer->mViewActor != NULL - && (gl_billboard_mode == 1 || (actor && actor->renderflags & RF_FORCEXYBILLBOARD ))) ); - gl_RenderState.Apply(); - - Vector v1; - Vector v2; - Vector v3; - Vector v4; - - if (drawWithXYBillboard) - { - // Rotate the sprite about the vector starting at the center of the sprite - // triangle strip and with direction orthogonal to where the player is looking - // in the x/y plane. - float xcenter = (x1 + x2)*0.5; - float ycenter = (y1 + y2)*0.5; - float zcenter = (z1 + z2)*0.5; - float angleRad = DEG2RAD(270. - float(GLRenderer->mAngles.Yaw)); - - Matrix3x4 mat; - mat.MakeIdentity(); - mat.Translate(xcenter, zcenter, ycenter); - mat.Rotate(-sin(angleRad), 0, cos(angleRad), -GLRenderer->mAngles.Pitch); - mat.Translate(-xcenter, -zcenter, -ycenter); - v1 = mat * Vector(x1, z1, y1); - v2 = mat * Vector(x2, z1, y2); - v3 = mat * Vector(x1, z2, y1); - v4 = mat * Vector(x2, z2, y2); - } - else - { - v1 = Vector(x1, z1, y1); - v2 = Vector(x2, z1, y2); - v3 = Vector(x1, z2, y1); - v4 = Vector(x2, z2, y2); - } - - FFlatVertex *ptr; - unsigned int offset, count; - ptr = GLRenderer->mVBO->GetBuffer(); - ptr->Set(v1[0], v1[1], v1[2], ul, vt); - ptr++; - ptr->Set(v2[0], v2[1], v2[2], ur, vt); - ptr++; - ptr->Set(v3[0], v3[1], v3[2], ul, vb); - ptr++; - ptr->Set(v4[0], v4[1], v4[2], ur, vb); - ptr++; - GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP, &offset, &count); - - if (foglayer) - { - // If we get here we know that we have colored fog and no fixed colormap. - gl_SetFog(foglevel, rel, &Colormap, additivefog); - gl_RenderState.SetFixedColormap(CM_FOGLAYER); - gl_RenderState.BlendEquation(GL_FUNC_ADD); - gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - gl_RenderState.Apply(); - GLRenderer->mVBO->RenderArray(GL_TRIANGLE_STRIP, offset, count); - gl_RenderState.SetFixedColormap(CM_DEFAULT); - } + iter = lightlist->Size(); + gl_RenderState.EnableSplit(true); + glEnable(GL_CLIP_DISTANCE3); + glEnable(GL_CLIP_DISTANCE4); } else { - gl_RenderModel(this); + iter = 1; + } + + for (unsigned i = 0; i < iter; i++) + { + if (lightlist) + { + // set up the light slice + static secplane_t bottommost = { 0, 0, FRACUNIT, 32767<renderflags & RF_FORCEYBILLBOARD) + //&& GLRenderer->mViewActor != NULL + && (gl_billboard_mode == 1 || (actor && actor->renderflags & RF_FORCEXYBILLBOARD)))); + + gl_RenderState.Apply(); + + Vector v1; + Vector v2; + Vector v3; + Vector v4; + + if (drawWithXYBillboard) + { + // Rotate the sprite about the vector starting at the center of the sprite + // triangle strip and with direction orthogonal to where the player is looking + // in the x/y plane. + float xcenter = (x1 + x2)*0.5; + float ycenter = (y1 + y2)*0.5; + float zcenter = (z1 + z2)*0.5; + float angleRad = DEG2RAD(270. - float(GLRenderer->mAngles.Yaw)); + + Matrix3x4 mat; + mat.MakeIdentity(); + mat.Translate(xcenter, zcenter, ycenter); + mat.Rotate(-sin(angleRad), 0, cos(angleRad), -GLRenderer->mAngles.Pitch); + mat.Translate(-xcenter, -zcenter, -ycenter); + v1 = mat * Vector(x1, z1, y1); + v2 = mat * Vector(x2, z1, y2); + v3 = mat * Vector(x1, z2, y1); + v4 = mat * Vector(x2, z2, y2); + } + else + { + + v1 = Vector(x1, z1, y1); + v2 = Vector(x2, z1, y2); + v3 = Vector(x1, z2, y1); + v4 = Vector(x2, z2, y2); + } + + FFlatVertex *ptr; + unsigned int offset, count; + ptr = GLRenderer->mVBO->GetBuffer(); + ptr->Set(v1[0], v1[1], v1[2], ul, vt); + ptr++; + ptr->Set(v2[0], v2[1], v2[2], ur, vt); + ptr++; + ptr->Set(v3[0], v3[1], v3[2], ul, vb); + ptr++; + ptr->Set(v4[0], v4[1], v4[2], ur, vb); + ptr++; + GLRenderer->mVBO->RenderCurrent(ptr, GL_TRIANGLE_STRIP, &offset, &count); + + if (foglayer) + { + // If we get here we know that we have colored fog and no fixed colormap. + gl_SetFog(foglevel, rel, &Colormap, additivefog); + gl_RenderState.SetFixedColormap(CM_FOGLAYER); + gl_RenderState.BlendEquation(GL_FUNC_ADD); + gl_RenderState.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl_RenderState.Apply(); + GLRenderer->mVBO->RenderArray(GL_TRIANGLE_STRIP, offset, count); + gl_RenderState.SetFixedColormap(CM_DEFAULT); + } + } + else + { + gl_RenderModel(this); + } + } + + if (lightlist) + { + glDisable(GL_CLIP_DISTANCE3); + glDisable(GL_CLIP_DISTANCE4); + gl_RenderState.EnableSplit(false); } if (pass==GLPASS_TRANSLUCENT) @@ -322,87 +367,6 @@ inline void GLSprite::PutSprite(bool translucent) gl_drawinfo->drawlists[list].AddSprite(this); } -//========================================================================== -// -// -// -//========================================================================== -void GLSprite::SplitSprite(sector_t * frontsector, bool translucent) -{ - GLSprite copySprite; - fixed_t lightbottom; - float maplightbottom; - unsigned int i; - bool put=false; - TArray & lightlist=frontsector->e->XFloor.lightlist; - - for(i=0;ifloorplane.ZatPoint(actor); - - maplightbottom=FIXED2FLOAT(lightbottom); - if (maplightbottom & lightlist=actor->Sector->e->XFloor.lightlist; - - for(i=0;ifloorplane.ZatPoint(actor); - - //maplighttop=FIXED2FLOAT(lightlist[i].height); - maplightbottom=FIXED2FLOAT(lightbottom); - if (maplightbottomvelx==0 && thing->vely==0 && thing->velz!=0)) + if (viewz >= thingpos.z - 2 * FRACUNIT && viewz <= thingpos.z + thing->height + 2 * FRACUNIT) { - if (!gl_FindModelFrame(RUNTIME_TYPE(thing), spritenum, thing->frame, false)) + // exclude vertically moving objects from this check. + if (!(thing->velx == 0 && thing->vely == 0 && thing->velz != 0)) { - return; + if (!gl_FindModelFrame(RUNTIME_TYPE(thing), spritenum, thing->frame, false)) + { + return; + } } } } // don't draw first frame of a player missile - if (thing->flags&MF_MISSILE && thing->target==GLRenderer->mViewActor && GLRenderer->mViewActor != NULL) + if (thing->flags&MF_MISSILE && !(thing->flags7 & MF7_FLYCHEAT) && thing->target==GLRenderer->mViewActor && GLRenderer->mViewActor != NULL) { - if (P_AproxDistance(thingpos.x-viewx, thingpos.y-viewy) < thing->Speed ) return; + fixed_t clipdist = clamp(thing->Speed, thing->target->radius, thing->target->radius*2); + if (P_AproxDistance(thingpos.x-viewx, thingpos.y-viewy) < clipdist) return; } + thing->flags7 |= MF7_FLYCHEAT; // do this only once for the very first frame, but not if it gets into range again. if (GLRenderer->mCurrentPortal) { @@ -831,19 +800,23 @@ void GLSprite::Process(AActor* thing,sector_t * sector) && (gl_billboard_mode == 1 || actor->renderflags & RF_FORCEXYBILLBOARD ) ); - if (drawWithXYBillboard || modelframe) + // no light splitting when: + // 1. no lightlist + // 2. any fixed colormap + // 3. any bright object + // 4. any with render style shadow (which doesn't use the sector light) + // 5. anything with render style reverse subtract (light effect is not what would be desired here) + if (thing->Sector->e->XFloor.lightlist.Size() != 0 && gl_fixedcolormap == CM_DEFAULT && !fullbright && + RenderStyle.BlendOp != STYLEOP_Shadow && RenderStyle.BlendOp != STYLEOP_RevSub) { - if (!gl_fixedcolormap && !fullbright) SetSpriteColor(actor->Sector, thingpos.y + (actor->height>>1)); - PutSprite(hw_styleflags != STYLEHW_Solid); - } - else if (thing->Sector->e->XFloor.lightlist.Size()==0 || gl_fixedcolormap || fullbright) - { - PutSprite(hw_styleflags != STYLEHW_Solid); + lightlist = &thing->Sector->e->XFloor.lightlist; } else { - SplitSprite(thing->Sector, hw_styleflags != STYLEHW_Solid); + lightlist = NULL; } + + PutSprite(hw_styleflags != STYLEHW_Solid); rendered_sprites++; } @@ -965,6 +938,11 @@ void GLSprite::ProcessParticle (particle_t *particle, sector_t *sector)//, int s if (gl_particles_style != 2 && trans>=1.0f-FLT_EPSILON) hw_styleflags = STYLEHW_Solid; else hw_styleflags = STYLEHW_NoAlphaTest; + if (sector->e->XFloor.lightlist.Size() != 0 && gl_fixedcolormap == CM_DEFAULT && !fullbright) + lightlist = §or->e->XFloor.lightlist; + else + lightlist = NULL; + PutSprite(hw_styleflags != STYLEHW_Solid); rendered_sprites++; } diff --git a/src/gl/scene/gl_wall.h b/src/gl/scene/gl_wall.h index d9ef91232..67d75dc3d 100644 --- a/src/gl/scene/gl_wall.h +++ b/src/gl/scene/gl_wall.h @@ -333,8 +333,8 @@ public: float trans; AActor * actor; particle_t * particle; + TArray *lightlist; - void SplitSprite(sector_t * frontsector, bool translucent); void SetLowerParam(); void PerformSpriteClipAdjustment(AActor *thing, fixed_t thingx, fixed_t thingy, float spriteheight); @@ -345,7 +345,6 @@ public: void Process(AActor* thing,sector_t * sector); void ProcessParticle (particle_t *particle, sector_t *sector);//, int shade, int fakeside) void SetThingColor(PalEntry); - void SetSpriteColor(sector_t *sector, fixed_t y); // Lines start-end and fdiv must intersect. double CalcIntersectionVertex(GLWall * w2);