diff --git a/src/swrenderer/scene/r_bsp.cpp b/src/swrenderer/scene/r_bsp.cpp index 3a910f01b1..bd18ab41ef 100644 --- a/src/swrenderer/scene/r_bsp.cpp +++ b/src/swrenderer/scene/r_bsp.cpp @@ -35,6 +35,7 @@ #include "swrenderer/r_main.h" #include "swrenderer/drawers/r_draw.h" #include "swrenderer/plane/r_visibleplane.h" +#include "swrenderer/things/r_sprite.h" #include "swrenderer/things/r_particle.h" #include "swrenderer/segments/r_clipsegment.h" #include "swrenderer/line/r_wallsetup.h" @@ -76,12 +77,12 @@ namespace swrenderer // Similar for ceiling, only reflected. // [RH] allow per-plane lighting - if (floorlightlevel != NULL) + if (floorlightlevel != nullptr) { *floorlightlevel = sec->GetFloorLight(); } - if (ceilinglightlevel != NULL) + if (ceilinglightlevel != nullptr) { *ceilinglightlevel = sec->GetCeilingLight(); } @@ -89,7 +90,7 @@ namespace swrenderer FakeSide = WaterFakeSide::Center; const sector_t *s = sec->GetHeightSec(); - if (s != NULL) + if (s != nullptr) { sector_t *heightsec = viewsector->heightsec; bool underwater = r_fakingunderwater || @@ -116,12 +117,12 @@ namespace swrenderer { tempsec->lightlevel = s->lightlevel; - if (floorlightlevel != NULL) + if (floorlightlevel != nullptr) { *floorlightlevel = s->GetFloorLight(); } - if (ceilinglightlevel != NULL) + if (ceilinglightlevel != nullptr) { *ceilinglightlevel = s->GetCeilingLight(); } @@ -160,7 +161,7 @@ namespace swrenderer // are underwater but not in a water sector themselves. // Only works if you cannot see the top surface of any deep water // sectors at the same time. - if (backline && !r_fakingunderwater && backline->frontsector->heightsec == NULL) + if (backline && !r_fakingunderwater && backline->frontsector->heightsec == nullptr) { if (frontcz1 <= s->floorplane.ZatPoint(backline->v1) && frontcz2 <= s->floorplane.ZatPoint(backline->v2)) @@ -215,12 +216,12 @@ namespace swrenderer { tempsec->lightlevel = s->lightlevel; - if (floorlightlevel != NULL) + if (floorlightlevel != nullptr) { *floorlightlevel = s->GetFloorLight(); } - if (ceilinglightlevel != NULL) + if (ceilinglightlevel != nullptr) { *ceilinglightlevel = s->GetCeilingLight(); } @@ -252,12 +253,12 @@ namespace swrenderer { tempsec->lightlevel = s->lightlevel; - if (floorlightlevel != NULL) + if (floorlightlevel != nullptr) { *floorlightlevel = s->GetFloorLight(); } - if (ceilinglightlevel != NULL) + if (ceilinglightlevel != nullptr) { *ceilinglightlevel = s->GetCeilingLight(); } @@ -376,7 +377,7 @@ namespace swrenderer void RenderBSP::AddPolyobjs(subsector_t *sub) { - if (sub->BSP == NULL || sub->BSP->bDirty) + if (sub->BSP == nullptr || sub->BSP->bDirty) { sub->BuildPolyBSP(); } @@ -430,8 +431,8 @@ namespace swrenderer //secplane_t templane; lightlist_t *light; - if (InSubsector != NULL) - { // InSubsector is not NULL. This means we are rendering from a mini-BSP. + if (InSubsector != nullptr) + { // InSubsector is not nullptr. This means we are rendering from a mini-BSP. outersubsector = false; } else @@ -445,14 +446,14 @@ namespace swrenderer I_Error("RenderSubsector: ss %ti with numss = %i", sub - subsectors, numsubsectors); #endif - assert(sub->sector != NULL); + assert(sub->sector != nullptr); if (sub->polys) { // Render the polyobjs in the subsector first AddPolyobjs(sub); if (outersubsector) { - InSubsector = NULL; + InSubsector = nullptr; } return; } @@ -493,7 +494,7 @@ namespace swrenderer visplane_t *ceilingplane = frontsector->ceilingplane.PointOnSide(ViewPos) > 0 || frontsector->GetTexture(sector_t::ceiling) == skyflatnum || - portal != NULL || + portal != nullptr || (frontsector->heightsec && !(frontsector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC) && frontsector->heightsec->GetTexture(sector_t::floor) == skyflatnum) ? @@ -505,7 +506,7 @@ namespace swrenderer frontsector->planes[sector_t::ceiling].xform, frontsector->sky, portal - ) : NULL; + ) : nullptr; if (ceilingplane) R_AddPlaneLights(ceilingplane, frontsector->lighthead); @@ -533,7 +534,7 @@ namespace swrenderer visplane_t *floorplane = frontsector->floorplane.PointOnSide(ViewPos) > 0 || // killough 3/7/98 frontsector->GetTexture(sector_t::floor) == skyflatnum || - portal != NULL || + portal != nullptr || (frontsector->heightsec && !(frontsector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC) && frontsector->heightsec->GetTexture(sector_t::ceiling) == skyflatnum) ? @@ -545,7 +546,7 @@ namespace swrenderer frontsector->planes[sector_t::floor].xform, frontsector->sky, portal - ) : NULL; + ) : nullptr; if (floorplane) R_AddPlaneLights(floorplane, frontsector->lighthead); @@ -601,7 +602,7 @@ namespace swrenderer floorlightlevel = *light->p_lightlevel; } - ceilingplane = NULL; + ceilingplane = nullptr; floorplane = R_FindPlane(frontsector->floorplane, frontsector->GetTexture(sector_t::floor), floorlightlevel + r_actualextralight, // killough 3/16/98 @@ -609,7 +610,7 @@ namespace swrenderer !!(clip3d->fakeFloor->flags & FF_ADDITIVETRANS), frontsector->planes[position].xform, frontsector->sky, - NULL); + nullptr); if (floorplane) R_AddPlaneLights(floorplane, frontsector->lighthead); @@ -666,7 +667,7 @@ namespace swrenderer } tempsec.ceilingplane.ChangeHeight(1 / 65536.); - floorplane = NULL; + floorplane = nullptr; ceilingplane = R_FindPlane(frontsector->ceilingplane, // killough 3/8/98 frontsector->GetTexture(sector_t::ceiling), ceilinglightlevel + r_actualextralight, // killough 4/11/98 @@ -674,7 +675,7 @@ namespace swrenderer !!(clip3d->fakeFloor->flags & FF_ADDITIVETRANS), frontsector->planes[position].xform, frontsector->sky, - NULL); + nullptr); if (ceilingplane) R_AddPlaneLights(ceilingplane, frontsector->lighthead); @@ -684,7 +685,7 @@ namespace swrenderer frontsector = sub->sector; } } - clip3d->fakeFloor = NULL; + clip3d->fakeFloor = nullptr; floorplane = backupfp; ceilingplane = backupcp; } @@ -698,7 +699,7 @@ namespace swrenderer // lightlevels on floor & ceiling lightlevels in the surrounding area. // [RH] Handle sprite lighting like Duke 3D: If the ceiling is a sky, sprites are lit by // it, otherwise they are lit by the floor. - R_AddSprites(sub->sector, frontsector->GetTexture(sector_t::ceiling) == skyflatnum ? ceilinglightlevel : floorlightlevel, FakeSide); + AddSprites(sub->sector, frontsector->GetTexture(sector_t::ceiling) == skyflatnum ? ceilinglightlevel : floorlightlevel, FakeSide); // [RH] Add particles if ((unsigned int)(sub - subsectors) < (unsigned int)numsubsectors) @@ -715,15 +716,15 @@ namespace swrenderer while (count--) { - if (!outersubsector || line->sidedef == NULL || !(line->sidedef->Flags & WALLF_POLYOBJ)) + if (!outersubsector || line->sidedef == nullptr || !(line->sidedef->Flags & WALLF_POLYOBJ)) { // kg3D - fake planes bounding calculation if (r_3dfloors && line->backsector && frontsector->e && line->backsector->e->XFloor.ffloors.Size()) { backupfp = floorplane; backupcp = ceilingplane; - floorplane = NULL; - ceilingplane = NULL; + floorplane = nullptr; + ceilingplane = nullptr; Clip3DFloors *clip3d = Clip3DFloors::Instance(); for (unsigned int i = 0; i < line->backsector->e->XFloor.ffloors.Size(); i++) { @@ -742,7 +743,7 @@ namespace swrenderer } renderline.Render(line, InSubsector, frontsector, &tempsec, floorplane, ceilingplane); // fake } - clip3d->fakeFloor = NULL; + clip3d->fakeFloor = nullptr; clip3d->fake3D = 0; floorplane = backupfp; ceilingplane = backupcp; @@ -753,7 +754,7 @@ namespace swrenderer } if (outersubsector) { - InSubsector = NULL; + InSubsector = nullptr; } } @@ -802,4 +803,62 @@ namespace swrenderer fillshort(floorclip, viewwidth, viewheight); fillshort(ceilingclip, viewwidth, !screen->Accel2D && ConBottom > viewwindowy && !bRenderingToCanvas ? (ConBottom - viewwindowy) : 0); } + + void RenderBSP::AddSprites(sector_t *sec, int lightlevel, WaterFakeSide fakeside) + { + F3DFloor *fakeceiling = nullptr; + F3DFloor *fakefloor = nullptr; + + // BSP is traversed by subsector. + // A sector might have been split into several + // subsectors during BSP building. + // Thus we check whether it was already added. + if (sec->touching_renderthings == nullptr || sec->validcount == validcount) + return; + + // Well, now it will be done. + sec->validcount = validcount; + + int spriteshade = LIGHT2SHADE(lightlevel + r_actualextralight); + + // Handle all things in sector. + for (auto p = sec->touching_renderthings; p != nullptr; p = p->m_snext) + { + auto thing = p->m_thing; + if (thing->validcount == validcount) continue; + thing->validcount = validcount; + + FIntCVar *cvar = thing->GetClass()->distancecheck; + if (cvar != nullptr && *cvar >= 0) + { + double dist = (thing->Pos() - ViewPos).LengthSquared(); + double check = (double)**cvar; + if (dist >= check * check) + { + continue; + } + } + + // find fake level + for (auto rover : thing->Sector->e->XFloor.ffloors) + { + if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_RENDERPLANES)) continue; + if (!(rover->flags & FF_SOLID) || rover->alpha != 255) continue; + if (!fakefloor) + { + if (!rover->top.plane->isSlope()) + { + if (rover->top.plane->ZatPoint(0., 0.) <= thing->Z()) fakefloor = rover; + } + } + if (!rover->bottom.plane->isSlope()) + { + if (rover->bottom.plane->ZatPoint(0., 0.) >= thing->Top()) fakeceiling = rover; + } + } + R_ProjectSprite(thing, fakeside, fakefloor, fakeceiling, sec, spriteshade); + fakeceiling = nullptr; + fakefloor = nullptr; + } + } } diff --git a/src/swrenderer/scene/r_bsp.h b/src/swrenderer/scene/r_bsp.h index 4a47da5935..e673baff6b 100644 --- a/src/swrenderer/scene/r_bsp.h +++ b/src/swrenderer/scene/r_bsp.h @@ -57,6 +57,8 @@ namespace swrenderer void AddPolyobjs(subsector_t *sub); void FakeDrawLoop(subsector_t *sub, visplane_t *floorplane, visplane_t *ceilingplane); + void AddSprites(sector_t *sec, int lightlevel, WaterFakeSide fakeside); + subsector_t *InSubsector = nullptr; sector_t *frontsector = nullptr; WaterFakeSide FakeSide = WaterFakeSide::Center; diff --git a/src/swrenderer/scene/r_things.cpp b/src/swrenderer/scene/r_things.cpp index af503244dc..84dfc44ec7 100644 --- a/src/swrenderer/scene/r_things.cpp +++ b/src/swrenderer/scene/r_things.cpp @@ -66,6 +66,7 @@ #include "swrenderer/things/r_playersprite.h" #include "swrenderer/things/r_wallsprite.h" #include "swrenderer/things/r_sprite.h" +#include "swrenderer/plane/r_visibleplane.h" #include "swrenderer/r_memory.h" #include "g_levellocals.h" @@ -172,503 +173,6 @@ bool R_ClipSpriteColumnWithPortals(int x, vissprite_t* spr) return false; } -// -// R_ProjectSprite -// Generates a vissprite for a thing if it might be visible. -// -void R_ProjectSprite (AActor *thing, WaterFakeSide fakeside, F3DFloor *fakefloor, F3DFloor *fakeceiling, sector_t *current_sector, int spriteshade) -{ - double tr_x; - double tr_y; - - double gzt; // killough 3/27/98 - double gzb; // [RH] use bottom of sprite, not actor - double tx;// , tx2; - double tz; - - double xscale = 1, yscale = 1; - - int x1; - int x2; - - FTextureID picnum; - FTexture *tex; - FVoxelDef *voxel; - - vissprite_t* vis; - - fixed_t iscale; - - sector_t* heightsec; // killough 3/27/98 - - // Don't waste time projecting sprites that are definitely not visible. - if (thing == NULL || - (thing->renderflags & RF_INVISIBLE) || - !thing->RenderStyle.IsVisible(thing->Alpha) || - !thing->IsVisibleToPlayer() || - !thing->IsInsideVisibleAngles()) - { - return; - } - - RenderPortal *renderportal = RenderPortal::Instance(); - - // [ZZ] Or less definitely not visible (hue) - // [ZZ] 10.01.2016: don't try to clip stuff inside a skybox against the current portal. - if (!renderportal->CurrentPortalInSkybox && renderportal->CurrentPortal && !!P_PointOnLineSidePrecise(thing->Pos(), renderportal->CurrentPortal->dst)) - return; - - // [RH] Interpolate the sprite's position to make it look smooth - DVector3 pos = thing->InterpolatedPosition(r_TicFracF); - pos.Z += thing->GetBobOffset(r_TicFracF); - - tex = NULL; - voxel = NULL; - - int spritenum = thing->sprite; - DVector2 spriteScale = thing->Scale; - int renderflags = thing->renderflags; - if (spriteScale.Y < 0) - { - spriteScale.Y = -spriteScale.Y; - renderflags ^= RF_YFLIP; - } - if (thing->player != NULL) - { - P_CheckPlayerSprite(thing, spritenum, spriteScale); - } - - if (thing->picnum.isValid()) - { - picnum = thing->picnum; - - tex = TexMan(picnum); - if (tex->UseType == FTexture::TEX_Null) - { - return; - } - - if (tex->Rotations != 0xFFFF) - { - // choose a different rotation based on player view - spriteframe_t *sprframe = &SpriteFrames[tex->Rotations]; - DAngle ang = (pos - ViewPos).Angle(); - angle_t rot; - if (sprframe->Texture[0] == sprframe->Texture[1]) - { - if (thing->flags7 & MF7_SPRITEANGLE) - rot = (thing->SpriteAngle + 45.0 / 2 * 9).BAMs() >> 28; - else - rot = (ang - (thing->Angles.Yaw + thing->SpriteRotation) + 45.0 / 2 * 9).BAMs() >> 28; - } - else - { - if (thing->flags7 & MF7_SPRITEANGLE) - rot = (thing->SpriteAngle + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; - else - rot = (ang - (thing->Angles.Yaw + thing->SpriteRotation) + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; - } - picnum = sprframe->Texture[rot]; - if (sprframe->Flip & (1 << rot)) - { - renderflags ^= RF_XFLIP; - } - tex = TexMan[picnum]; // Do not animate the rotation - } - } - else - { - // decide which texture to use for the sprite - if ((unsigned)spritenum >= sprites.Size ()) - { - DPrintf (DMSG_ERROR, "R_ProjectSprite: invalid sprite number %u\n", spritenum); - return; - } - spritedef_t *sprdef = &sprites[spritenum]; - if (thing->frame >= sprdef->numframes) - { - // If there are no frames at all for this sprite, don't draw it. - return; - } - else - { - //picnum = SpriteFrames[sprdef->spriteframes + thing->frame].Texture[0]; - // choose a different rotation based on player view - spriteframe_t *sprframe = &SpriteFrames[sprdef->spriteframes + thing->frame]; - DAngle ang = (pos - ViewPos).Angle(); - angle_t rot; - if (sprframe->Texture[0] == sprframe->Texture[1]) - { - if (thing->flags7 & MF7_SPRITEANGLE) - rot = (thing->SpriteAngle + 45.0 / 2 * 9).BAMs() >> 28; - else - rot = (ang - (thing->Angles.Yaw + thing->SpriteRotation) + 45.0 / 2 * 9).BAMs() >> 28; - } - else - { - if (thing->flags7 & MF7_SPRITEANGLE) - rot = (thing->SpriteAngle + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; - else - rot = (ang - (thing->Angles.Yaw + thing->SpriteRotation) + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; - } - picnum = sprframe->Texture[rot]; - if (sprframe->Flip & (1 << rot)) - { - renderflags ^= RF_XFLIP; - } - tex = TexMan[picnum]; // Do not animate the rotation - if (r_drawvoxels) - { - voxel = sprframe->Voxel; - } - } - } - if (spriteScale.X < 0) - { - spriteScale.X = -spriteScale.X; - renderflags ^= RF_XFLIP; - } - if (voxel == NULL && (tex == NULL || tex->UseType == FTexture::TEX_Null)) - { - return; - } - - if ((renderflags & RF_SPRITETYPEMASK) == RF_WALLSPRITE) - { - R_ProjectWallSprite(thing, pos, picnum, spriteScale, renderflags, spriteshade); - return; - } - - // transform the origin point - tr_x = pos.X - ViewPos.X; - tr_y = pos.Y - ViewPos.Y; - - tz = tr_x * ViewTanCos + tr_y * ViewTanSin; - - // thing is behind view plane? - if (voxel == NULL && tz < MINZ) - return; - - tx = tr_x * ViewSin - tr_y * ViewCos; - - // [RH] Flip for mirrors - if (renderportal->MirrorFlags & RF_XFLIP) - { - tx = -tx; - } - //tx2 = tx >> 4; - - // too far off the side? - // if it's a voxel, it can be further off the side - if ((voxel == NULL && (fabs(tx / 64) > fabs(tz))) || - (voxel != NULL && (fabs(tx / 128) > fabs(tz)))) - { - return; - } - - if (voxel == NULL) - { - // [RH] Added scaling - int scaled_to = tex->GetScaledTopOffset(); - int scaled_bo = scaled_to - tex->GetScaledHeight(); - gzt = pos.Z + spriteScale.Y * scaled_to; - gzb = pos.Z + spriteScale.Y * scaled_bo; - } - else - { - xscale = spriteScale.X * voxel->Scale; - yscale = spriteScale.Y * voxel->Scale; - double piv = voxel->Voxel->Mips[0].Pivot.Z; - gzt = pos.Z + yscale * piv - thing->Floorclip; - gzb = pos.Z + yscale * (piv - voxel->Voxel->Mips[0].SizeZ); - if (gzt <= gzb) - return; - } - - // killough 3/27/98: exclude things totally separated - // from the viewer, by either water or fake ceilings - // killough 4/11/98: improve sprite clipping for underwater/fake ceilings - - heightsec = thing->Sector->GetHeightSec(); - - if (heightsec != NULL) // only clip things which are in special sectors - { - if (fakeside == WaterFakeSide::AboveCeiling) - { - if (gzt < heightsec->ceilingplane.ZatPoint(pos)) - return; - } - else if (fakeside == WaterFakeSide::BelowFloor) - { - if (gzb >= heightsec->floorplane.ZatPoint(pos)) - return; - } - else - { - if (gzt < heightsec->floorplane.ZatPoint(pos)) - return; - if (!(heightsec->MoreFlags & SECF_FAKEFLOORONLY) && gzb >= heightsec->ceilingplane.ZatPoint(pos)) - return; - } - } - - if (voxel == NULL) - { - xscale = CenterX / tz; - - // [RH] Reject sprites that are off the top or bottom of the screen - if (globaluclip * tz > ViewPos.Z - gzb || globaldclip * tz < ViewPos.Z - gzt) - { - return; - } - - // [RH] Flip for mirrors - renderflags ^= renderportal->MirrorFlags & RF_XFLIP; - - // calculate edges of the shape - const double thingxscalemul = spriteScale.X / tex->Scale.X; - - tx -= ((renderflags & RF_XFLIP) ? (tex->GetWidth() - tex->LeftOffset - 1) : tex->LeftOffset) * thingxscalemul; - double dtx1 = tx * xscale; - x1 = centerx + xs_RoundToInt(dtx1); - - // off the right side? - if (x1 >= renderportal->WindowRight) - return; - - tx += tex->GetWidth() * thingxscalemul; - x2 = centerx + xs_RoundToInt(tx * xscale); - - // off the left side or too small? - if ((x2 < renderportal->WindowLeft || x2 <= x1)) - return; - - xscale = spriteScale.X * xscale / tex->Scale.X; - iscale = (fixed_t)(FRACUNIT / xscale); // Round towards zero to avoid wrapping in edge cases - - double yscale = spriteScale.Y / tex->Scale.Y; - - // store information in a vissprite - vis = R_NewVisSprite(); - - vis->CurrentPortalUniq = renderportal->CurrentPortalUniq; - vis->xscale = FLOAT2FIXED(xscale); - vis->yscale = float(InvZtoScale * yscale / tz); - vis->idepth = float(1 / tz); - vis->floorclip = thing->Floorclip / yscale; - vis->texturemid = tex->TopOffset - (ViewPos.Z - pos.Z + thing->Floorclip) / yscale; - vis->x1 = x1 < renderportal->WindowLeft ? renderportal->WindowLeft : x1; - vis->x2 = x2 > renderportal->WindowRight ? renderportal->WindowRight : x2; - vis->Angle = thing->Angles.Yaw; - - if (renderflags & RF_XFLIP) - { - vis->startfrac = (tex->GetWidth() << FRACBITS) - 1; - vis->xiscale = -iscale; - } - else - { - vis->startfrac = 0; - vis->xiscale = iscale; - } - - vis->startfrac += (fixed_t)(vis->xiscale * (vis->x1 - centerx + 0.5 - dtx1)); - } - else - { - vis = R_NewVisSprite(); - - vis->CurrentPortalUniq = renderportal->CurrentPortalUniq; - vis->xscale = FLOAT2FIXED(xscale); - vis->yscale = (float)yscale; - vis->x1 = renderportal->WindowLeft; - vis->x2 = renderportal->WindowRight; - vis->idepth = 1 / MINZ; - vis->floorclip = thing->Floorclip; - - pos.Z -= thing->Floorclip; - - vis->Angle = thing->Angles.Yaw + voxel->AngleOffset; - - int voxelspin = (thing->flags & MF_DROPPED) ? voxel->DroppedSpin : voxel->PlacedSpin; - if (voxelspin != 0) - { - DAngle ang = double(I_FPSTime()) * voxelspin / 1000; - vis->Angle -= ang; - } - - vis->pa.vpos = { (float)ViewPos.X, (float)ViewPos.Y, (float)ViewPos.Z }; - vis->pa.vang = FAngle((float)ViewAngle.Degrees); - } - - // killough 3/27/98: save sector for special clipping later - vis->heightsec = heightsec; - vis->sector = thing->Sector; - - vis->depth = (float)tz; - vis->gpos = { (float)pos.X, (float)pos.Y, (float)pos.Z }; - vis->gzb = (float)gzb; // [RH] use gzb, not thing->z - vis->gzt = (float)gzt; // killough 3/27/98 - vis->deltax = float(pos.X - ViewPos.X); - vis->deltay = float(pos.Y - ViewPos.Y); - vis->renderflags = renderflags; - if(thing->flags5 & MF5_BRIGHT) - vis->renderflags |= RF_FULLBRIGHT; // kg3D - vis->Style.RenderStyle = thing->RenderStyle; - vis->FillColor = thing->fillcolor; - vis->Translation = thing->Translation; // [RH] thing translation table - vis->FakeFlatStat = fakeside; - vis->Style.Alpha = float(thing->Alpha); - vis->fakefloor = fakefloor; - vis->fakeceiling = fakeceiling; - vis->Style.ColormapNum = 0; - vis->bInMirror = renderportal->MirrorFlags & RF_XFLIP; - vis->bSplitSprite = false; - - if (voxel != NULL) - { - vis->voxel = voxel->Voxel; - vis->bIsVoxel = true; - vis->bWallSprite = false; - DrewAVoxel = true; - } - else - { - vis->pic = tex; - vis->bIsVoxel = false; - vis->bWallSprite = false; - } - - // The software renderer cannot invert the source without inverting the overlay - // too. That means if the source is inverted, we need to do the reverse of what - // the invert overlay flag says to do. - INTBOOL invertcolormap = (vis->Style.RenderStyle.Flags & STYLEF_InvertOverlay); - - if (vis->Style.RenderStyle.Flags & STYLEF_InvertSource) - { - invertcolormap = !invertcolormap; - } - - FDynamicColormap *mybasecolormap = basecolormap; - if (current_sector->sectornum != thing->Sector->sectornum) // compare sectornums to account for R_FakeFlat copies. - { - // Todo: The actor is from a different sector so we have to retrieve the proper basecolormap for that sector. - } - - // Sprites that are added to the scene must fade to black. - if (vis->Style.RenderStyle == LegacyRenderStyles[STYLE_Add] && mybasecolormap->Fade != 0) - { - mybasecolormap = GetSpecialLights(mybasecolormap->Color, 0, mybasecolormap->Desaturate); - } - - if (vis->Style.RenderStyle.Flags & STYLEF_FadeToBlack) - { - if (invertcolormap) - { // Fade to white - mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(255,255,255), mybasecolormap->Desaturate); - invertcolormap = false; - } - else - { // Fade to black - mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(0,0,0), mybasecolormap->Desaturate); - } - } - - // get light level - if (fixedcolormap != NULL) - { // fixed map - vis->Style.BaseColormap = fixedcolormap; - vis->Style.ColormapNum = 0; - } - else - { - if (invertcolormap) - { - mybasecolormap = GetSpecialLights(mybasecolormap->Color, mybasecolormap->Fade.InverseColor(), mybasecolormap->Desaturate); - } - if (fixedlightlev >= 0) - { - vis->Style.BaseColormap = mybasecolormap; - vis->Style.ColormapNum = fixedlightlev >> COLORMAPSHIFT; - } - else if (!foggy && ((renderflags & RF_FULLBRIGHT) || (thing->flags5 & MF5_BRIGHT))) - { // full bright - vis->Style.BaseColormap = (r_fullbrightignoresectorcolor) ? &FullNormalLight : mybasecolormap; - vis->Style.ColormapNum = 0; - } - else - { // diminished light - vis->Style.ColormapNum = GETPALOOKUP( - r_SpriteVisibility / MAX(tz, MINZ), spriteshade); - vis->Style.BaseColormap = mybasecolormap; - } - } -} - -// -// R_AddSprites -// During BSP traversal, this adds sprites by sector. -// -// killough 9/18/98: add lightlevel as parameter, fixing underwater lighting -// [RH] Save which side of heightsec sprite is on here. -void R_AddSprites (sector_t *sec, int lightlevel, WaterFakeSide fakeside) -{ - F3DFloor *fakeceiling = NULL; - F3DFloor *fakefloor = NULL; - - // BSP is traversed by subsector. - // A sector might have been split into several - // subsectors during BSP building. - // Thus we check whether it was already added. - if (sec->touching_renderthings == nullptr || sec->validcount == validcount) - return; - - // Well, now it will be done. - sec->validcount = validcount; - - int spriteshade = LIGHT2SHADE(lightlevel + r_actualextralight); - - // Handle all things in sector. - for(auto p = sec->touching_renderthings; p != nullptr; p = p->m_snext) - { - auto thing = p->m_thing; - if (thing->validcount == validcount) continue; - thing->validcount = validcount; - - FIntCVar *cvar = thing->GetClass()->distancecheck; - if (cvar != NULL && *cvar >= 0) - { - double dist = (thing->Pos() - ViewPos).LengthSquared(); - double check = (double)**cvar; - if (dist >= check * check) - { - continue; - } - } - - // find fake level - for(auto rover : thing->Sector->e->XFloor.ffloors) - { - if(!(rover->flags & FF_EXISTS) || !(rover->flags & FF_RENDERPLANES)) continue; - if(!(rover->flags & FF_SOLID) || rover->alpha != 255) continue; - if(!fakefloor) - { - if(!rover->top.plane->isSlope()) - { - if(rover->top.plane->ZatPoint(0., 0.) <= thing->Z()) fakefloor = rover; - } - } - if(!rover->bottom.plane->isSlope()) - { - if(rover->bottom.plane->ZatPoint(0., 0.) >= thing->Top()) fakeceiling = rover; - } - } - R_ProjectSprite (thing, fakeside, fakefloor, fakeceiling, sec, spriteshade); - fakeceiling = NULL; - fakefloor = NULL; - } -} - // // R_SortVisSprites @@ -1196,8 +700,6 @@ void R_DrawMaskedSingle (bool renew) } } -void R_DrawHeightPlanes(double height); // kg3D - fake planes - void R_DrawMasked () { R_CollectPortals(); @@ -1255,11 +757,4 @@ void R_DrawMasked () R_DrawPlayerSprites(); } -extern double BaseYaspectMul;; - -inline int sgn(int v) -{ - return v < 0 ? -1 : v > 0 ? 1 : 0; -} - } diff --git a/src/swrenderer/scene/r_things.h b/src/swrenderer/scene/r_things.h index 04685d8bbf..e972f62fd0 100644 --- a/src/swrenderer/scene/r_things.h +++ b/src/swrenderer/scene/r_things.h @@ -43,7 +43,6 @@ bool R_ClipSpriteColumnWithPortals(int x, vissprite_t* spr); void R_CacheSprite (spritedef_t *sprite); void R_SortVisSprites (int (*compare)(const void *, const void *), size_t first); -void R_AddSprites (sector_t *sec, int lightlevel, WaterFakeSide fakeside); void R_DrawSprites (); void R_ClearSprites (); void R_DrawMasked (); @@ -52,6 +51,7 @@ enum { DVF_OFFSCREEN = 1, DVF_SPANSONLY = 2, DVF_MIRRORED = 4 }; void R_ClipVisSprite (vissprite_t *vis, int xl, int xh); +extern bool DrewAVoxel; } diff --git a/src/swrenderer/things/r_sprite.cpp b/src/swrenderer/things/r_sprite.cpp index c998b35755..8c1553f6de 100644 --- a/src/swrenderer/things/r_sprite.cpp +++ b/src/swrenderer/things/r_sprite.cpp @@ -55,8 +55,439 @@ #include "swrenderer/things/r_sprite.h" #include "swrenderer/r_memory.h" +EXTERN_CVAR(Bool, r_drawvoxels) + namespace swrenderer { + void R_ProjectSprite(AActor *thing, WaterFakeSide fakeside, F3DFloor *fakefloor, F3DFloor *fakeceiling, sector_t *current_sector, int spriteshade) + { + double tr_x; + double tr_y; + + double gzt; // killough 3/27/98 + double gzb; // [RH] use bottom of sprite, not actor + double tx;// , tx2; + double tz; + + double xscale = 1, yscale = 1; + + int x1; + int x2; + + FTextureID picnum; + FTexture *tex; + FVoxelDef *voxel; + + vissprite_t* vis; + + fixed_t iscale; + + sector_t* heightsec; // killough 3/27/98 + + // Don't waste time projecting sprites that are definitely not visible. + if (thing == nullptr || + (thing->renderflags & RF_INVISIBLE) || + !thing->RenderStyle.IsVisible(thing->Alpha) || + !thing->IsVisibleToPlayer() || + !thing->IsInsideVisibleAngles()) + { + return; + } + + RenderPortal *renderportal = RenderPortal::Instance(); + + // [ZZ] Or less definitely not visible (hue) + // [ZZ] 10.01.2016: don't try to clip stuff inside a skybox against the current portal. + if (!renderportal->CurrentPortalInSkybox && renderportal->CurrentPortal && !!P_PointOnLineSidePrecise(thing->Pos(), renderportal->CurrentPortal->dst)) + return; + + // [RH] Interpolate the sprite's position to make it look smooth + DVector3 pos = thing->InterpolatedPosition(r_TicFracF); + pos.Z += thing->GetBobOffset(r_TicFracF); + + tex = nullptr; + voxel = nullptr; + + int spritenum = thing->sprite; + DVector2 spriteScale = thing->Scale; + int renderflags = thing->renderflags; + if (spriteScale.Y < 0) + { + spriteScale.Y = -spriteScale.Y; + renderflags ^= RF_YFLIP; + } + if (thing->player != nullptr) + { + P_CheckPlayerSprite(thing, spritenum, spriteScale); + } + + if (thing->picnum.isValid()) + { + picnum = thing->picnum; + + tex = TexMan(picnum); + if (tex->UseType == FTexture::TEX_Null) + { + return; + } + + if (tex->Rotations != 0xFFFF) + { + // choose a different rotation based on player view + spriteframe_t *sprframe = &SpriteFrames[tex->Rotations]; + DAngle ang = (pos - ViewPos).Angle(); + angle_t rot; + if (sprframe->Texture[0] == sprframe->Texture[1]) + { + if (thing->flags7 & MF7_SPRITEANGLE) + rot = (thing->SpriteAngle + 45.0 / 2 * 9).BAMs() >> 28; + else + rot = (ang - (thing->Angles.Yaw + thing->SpriteRotation) + 45.0 / 2 * 9).BAMs() >> 28; + } + else + { + if (thing->flags7 & MF7_SPRITEANGLE) + rot = (thing->SpriteAngle + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; + else + rot = (ang - (thing->Angles.Yaw + thing->SpriteRotation) + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; + } + picnum = sprframe->Texture[rot]; + if (sprframe->Flip & (1 << rot)) + { + renderflags ^= RF_XFLIP; + } + tex = TexMan[picnum]; // Do not animate the rotation + } + } + else + { + // decide which texture to use for the sprite + if ((unsigned)spritenum >= sprites.Size()) + { + DPrintf(DMSG_ERROR, "R_ProjectSprite: invalid sprite number %u\n", spritenum); + return; + } + spritedef_t *sprdef = &sprites[spritenum]; + if (thing->frame >= sprdef->numframes) + { + // If there are no frames at all for this sprite, don't draw it. + return; + } + else + { + //picnum = SpriteFrames[sprdef->spriteframes + thing->frame].Texture[0]; + // choose a different rotation based on player view + spriteframe_t *sprframe = &SpriteFrames[sprdef->spriteframes + thing->frame]; + DAngle ang = (pos - ViewPos).Angle(); + angle_t rot; + if (sprframe->Texture[0] == sprframe->Texture[1]) + { + if (thing->flags7 & MF7_SPRITEANGLE) + rot = (thing->SpriteAngle + 45.0 / 2 * 9).BAMs() >> 28; + else + rot = (ang - (thing->Angles.Yaw + thing->SpriteRotation) + 45.0 / 2 * 9).BAMs() >> 28; + } + else + { + if (thing->flags7 & MF7_SPRITEANGLE) + rot = (thing->SpriteAngle + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; + else + rot = (ang - (thing->Angles.Yaw + thing->SpriteRotation) + (45.0 / 2 * 9 - 180.0 / 16)).BAMs() >> 28; + } + picnum = sprframe->Texture[rot]; + if (sprframe->Flip & (1 << rot)) + { + renderflags ^= RF_XFLIP; + } + tex = TexMan[picnum]; // Do not animate the rotation + if (r_drawvoxels) + { + voxel = sprframe->Voxel; + } + } + } + if (spriteScale.X < 0) + { + spriteScale.X = -spriteScale.X; + renderflags ^= RF_XFLIP; + } + if (voxel == nullptr && (tex == nullptr || tex->UseType == FTexture::TEX_Null)) + { + return; + } + + if ((renderflags & RF_SPRITETYPEMASK) == RF_WALLSPRITE) + { + R_ProjectWallSprite(thing, pos, picnum, spriteScale, renderflags, spriteshade); + return; + } + + // transform the origin point + tr_x = pos.X - ViewPos.X; + tr_y = pos.Y - ViewPos.Y; + + tz = tr_x * ViewTanCos + tr_y * ViewTanSin; + + // thing is behind view plane? + if (voxel == nullptr && tz < MINZ) + return; + + tx = tr_x * ViewSin - tr_y * ViewCos; + + // [RH] Flip for mirrors + if (renderportal->MirrorFlags & RF_XFLIP) + { + tx = -tx; + } + //tx2 = tx >> 4; + + // too far off the side? + // if it's a voxel, it can be further off the side + if ((voxel == nullptr && (fabs(tx / 64) > fabs(tz))) || + (voxel != nullptr && (fabs(tx / 128) > fabs(tz)))) + { + return; + } + + if (voxel == nullptr) + { + // [RH] Added scaling + int scaled_to = tex->GetScaledTopOffset(); + int scaled_bo = scaled_to - tex->GetScaledHeight(); + gzt = pos.Z + spriteScale.Y * scaled_to; + gzb = pos.Z + spriteScale.Y * scaled_bo; + } + else + { + xscale = spriteScale.X * voxel->Scale; + yscale = spriteScale.Y * voxel->Scale; + double piv = voxel->Voxel->Mips[0].Pivot.Z; + gzt = pos.Z + yscale * piv - thing->Floorclip; + gzb = pos.Z + yscale * (piv - voxel->Voxel->Mips[0].SizeZ); + if (gzt <= gzb) + return; + } + + // killough 3/27/98: exclude things totally separated + // from the viewer, by either water or fake ceilings + // killough 4/11/98: improve sprite clipping for underwater/fake ceilings + + heightsec = thing->Sector->GetHeightSec(); + + if (heightsec != nullptr) // only clip things which are in special sectors + { + if (fakeside == WaterFakeSide::AboveCeiling) + { + if (gzt < heightsec->ceilingplane.ZatPoint(pos)) + return; + } + else if (fakeside == WaterFakeSide::BelowFloor) + { + if (gzb >= heightsec->floorplane.ZatPoint(pos)) + return; + } + else + { + if (gzt < heightsec->floorplane.ZatPoint(pos)) + return; + if (!(heightsec->MoreFlags & SECF_FAKEFLOORONLY) && gzb >= heightsec->ceilingplane.ZatPoint(pos)) + return; + } + } + + if (voxel == nullptr) + { + xscale = CenterX / tz; + + // [RH] Reject sprites that are off the top or bottom of the screen + if (globaluclip * tz > ViewPos.Z - gzb || globaldclip * tz < ViewPos.Z - gzt) + { + return; + } + + // [RH] Flip for mirrors + renderflags ^= renderportal->MirrorFlags & RF_XFLIP; + + // calculate edges of the shape + const double thingxscalemul = spriteScale.X / tex->Scale.X; + + tx -= ((renderflags & RF_XFLIP) ? (tex->GetWidth() - tex->LeftOffset - 1) : tex->LeftOffset) * thingxscalemul; + double dtx1 = tx * xscale; + x1 = centerx + xs_RoundToInt(dtx1); + + // off the right side? + if (x1 >= renderportal->WindowRight) + return; + + tx += tex->GetWidth() * thingxscalemul; + x2 = centerx + xs_RoundToInt(tx * xscale); + + // off the left side or too small? + if ((x2 < renderportal->WindowLeft || x2 <= x1)) + return; + + xscale = spriteScale.X * xscale / tex->Scale.X; + iscale = (fixed_t)(FRACUNIT / xscale); // Round towards zero to avoid wrapping in edge cases + + double yscale = spriteScale.Y / tex->Scale.Y; + + // store information in a vissprite + vis = R_NewVisSprite(); + + vis->CurrentPortalUniq = renderportal->CurrentPortalUniq; + vis->xscale = FLOAT2FIXED(xscale); + vis->yscale = float(InvZtoScale * yscale / tz); + vis->idepth = float(1 / tz); + vis->floorclip = thing->Floorclip / yscale; + vis->texturemid = tex->TopOffset - (ViewPos.Z - pos.Z + thing->Floorclip) / yscale; + vis->x1 = x1 < renderportal->WindowLeft ? renderportal->WindowLeft : x1; + vis->x2 = x2 > renderportal->WindowRight ? renderportal->WindowRight : x2; + vis->Angle = thing->Angles.Yaw; + + if (renderflags & RF_XFLIP) + { + vis->startfrac = (tex->GetWidth() << FRACBITS) - 1; + vis->xiscale = -iscale; + } + else + { + vis->startfrac = 0; + vis->xiscale = iscale; + } + + vis->startfrac += (fixed_t)(vis->xiscale * (vis->x1 - centerx + 0.5 - dtx1)); + } + else + { + vis = R_NewVisSprite(); + + vis->CurrentPortalUniq = renderportal->CurrentPortalUniq; + vis->xscale = FLOAT2FIXED(xscale); + vis->yscale = (float)yscale; + vis->x1 = renderportal->WindowLeft; + vis->x2 = renderportal->WindowRight; + vis->idepth = 1 / MINZ; + vis->floorclip = thing->Floorclip; + + pos.Z -= thing->Floorclip; + + vis->Angle = thing->Angles.Yaw + voxel->AngleOffset; + + int voxelspin = (thing->flags & MF_DROPPED) ? voxel->DroppedSpin : voxel->PlacedSpin; + if (voxelspin != 0) + { + DAngle ang = double(I_FPSTime()) * voxelspin / 1000; + vis->Angle -= ang; + } + + vis->pa.vpos = { (float)ViewPos.X, (float)ViewPos.Y, (float)ViewPos.Z }; + vis->pa.vang = FAngle((float)ViewAngle.Degrees); + } + + // killough 3/27/98: save sector for special clipping later + vis->heightsec = heightsec; + vis->sector = thing->Sector; + + vis->depth = (float)tz; + vis->gpos = { (float)pos.X, (float)pos.Y, (float)pos.Z }; + vis->gzb = (float)gzb; // [RH] use gzb, not thing->z + vis->gzt = (float)gzt; // killough 3/27/98 + vis->deltax = float(pos.X - ViewPos.X); + vis->deltay = float(pos.Y - ViewPos.Y); + vis->renderflags = renderflags; + if (thing->flags5 & MF5_BRIGHT) + vis->renderflags |= RF_FULLBRIGHT; // kg3D + vis->Style.RenderStyle = thing->RenderStyle; + vis->FillColor = thing->fillcolor; + vis->Translation = thing->Translation; // [RH] thing translation table + vis->FakeFlatStat = fakeside; + vis->Style.Alpha = float(thing->Alpha); + vis->fakefloor = fakefloor; + vis->fakeceiling = fakeceiling; + vis->Style.ColormapNum = 0; + vis->bInMirror = renderportal->MirrorFlags & RF_XFLIP; + vis->bSplitSprite = false; + + if (voxel != nullptr) + { + vis->voxel = voxel->Voxel; + vis->bIsVoxel = true; + vis->bWallSprite = false; + DrewAVoxel = true; + } + else + { + vis->pic = tex; + vis->bIsVoxel = false; + vis->bWallSprite = false; + } + + // The software renderer cannot invert the source without inverting the overlay + // too. That means if the source is inverted, we need to do the reverse of what + // the invert overlay flag says to do. + INTBOOL invertcolormap = (vis->Style.RenderStyle.Flags & STYLEF_InvertOverlay); + + if (vis->Style.RenderStyle.Flags & STYLEF_InvertSource) + { + invertcolormap = !invertcolormap; + } + + FDynamicColormap *mybasecolormap = basecolormap; + if (current_sector->sectornum != thing->Sector->sectornum) // compare sectornums to account for R_FakeFlat copies. + { + // Todo: The actor is from a different sector so we have to retrieve the proper basecolormap for that sector. + } + + // Sprites that are added to the scene must fade to black. + if (vis->Style.RenderStyle == LegacyRenderStyles[STYLE_Add] && mybasecolormap->Fade != 0) + { + mybasecolormap = GetSpecialLights(mybasecolormap->Color, 0, mybasecolormap->Desaturate); + } + + if (vis->Style.RenderStyle.Flags & STYLEF_FadeToBlack) + { + if (invertcolormap) + { // Fade to white + mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(255, 255, 255), mybasecolormap->Desaturate); + invertcolormap = false; + } + else + { // Fade to black + mybasecolormap = GetSpecialLights(mybasecolormap->Color, MAKERGB(0, 0, 0), mybasecolormap->Desaturate); + } + } + + // get light level + if (fixedcolormap != nullptr) + { // fixed map + vis->Style.BaseColormap = fixedcolormap; + vis->Style.ColormapNum = 0; + } + else + { + if (invertcolormap) + { + mybasecolormap = GetSpecialLights(mybasecolormap->Color, mybasecolormap->Fade.InverseColor(), mybasecolormap->Desaturate); + } + if (fixedlightlev >= 0) + { + vis->Style.BaseColormap = mybasecolormap; + vis->Style.ColormapNum = fixedlightlev >> COLORMAPSHIFT; + } + else if (!foggy && ((renderflags & RF_FULLBRIGHT) || (thing->flags5 & MF5_BRIGHT))) + { // full bright + vis->Style.BaseColormap = (r_fullbrightignoresectorcolor) ? &FullNormalLight : mybasecolormap; + vis->Style.ColormapNum = 0; + } + else + { // diminished light + vis->Style.ColormapNum = GETPALOOKUP( + r_SpriteVisibility / MAX(tz, MINZ), spriteshade); + vis->Style.BaseColormap = mybasecolormap; + } + } + } + void R_DrawVisSprite(vissprite_t *vis, const short *mfloorclip, const short *mceilingclip) { fixed_t frac; diff --git a/src/swrenderer/things/r_sprite.h b/src/swrenderer/things/r_sprite.h index 5d8f898f6b..fd31ec759d 100644 --- a/src/swrenderer/things/r_sprite.h +++ b/src/swrenderer/things/r_sprite.h @@ -17,5 +17,6 @@ namespace swrenderer { + void R_ProjectSprite(AActor *thing, WaterFakeSide fakeside, F3DFloor *fakefloor, F3DFloor *fakeceiling, sector_t *current_sector, int spriteshade); void R_DrawVisSprite(vissprite_t *vis, const short *mfloorclip, const short *mceilingclip); }