diff --git a/src/actor.h b/src/actor.h index 552e7db77..6fcd8b1c0 100644 --- a/src/actor.h +++ b/src/actor.h @@ -381,6 +381,7 @@ enum ActorFlag7 MF7_FORCEDECAL = 0x00080000, // [ZK] Forces puff's decal to override the weapon's. MF7_LAXTELEFRAGDMG = 0x00100000, // [MC] Telefrag damage can be reduced. MF7_ICESHATTER = 0x00200000, // [MC] Shatters ice corpses regardless of damagetype. + MF7_ALLOWTHRUFLAGS = 0x00400000, // [MC] Allow THRUACTORS and the likes on puffs to prevent mod breakage. }; // --- mobj.renderflags --- diff --git a/src/g_shared/a_quake.cpp b/src/g_shared/a_quake.cpp index a970f8969..f2f2e9b57 100644 --- a/src/g_shared/a_quake.cpp +++ b/src/g_shared/a_quake.cpp @@ -37,7 +37,8 @@ DEarthquake::DEarthquake() DEarthquake::DEarthquake(AActor *center, int intensityX, int intensityY, int intensityZ, int duration, int damrad, int tremrad, FSoundID quakesound, int flags, - double waveSpeedX, double waveSpeedY, double waveSpeedZ, int falloff, int highpoint) + double waveSpeedX, double waveSpeedY, double waveSpeedZ, int falloff, int highpoint, int rollIntensity, + double rollWave) : DThinker(STAT_EARTHQUAKE) { m_QuakeSFX = quakesound; @@ -53,6 +54,8 @@ DEarthquake::DEarthquake(AActor *center, int intensityX, int intensityY, int int m_Falloff = falloff; m_Highpoint = highpoint; m_MiniCount = highpoint; + m_RollIntensity = rollIntensity; + m_RollWave = rollWave; } //========================================================================== @@ -69,6 +72,10 @@ void DEarthquake::Serialize (FArchive &arc) << m_QuakeSFX << m_Flags << m_CountdownStart << m_WaveSpeed << m_Falloff << m_Highpoint << m_MiniCount; + if (SaveVersion >= 4544) + { + arc << m_RollIntensity << m_RollWave; + } } //========================================================================== @@ -281,11 +288,12 @@ int DEarthquake::StaticGetQuakeIntensities(AActor *victim, FQuakeJiggers &jigger double x = quake->GetModIntensity(quake->m_Intensity.X); double y = quake->GetModIntensity(quake->m_Intensity.Y); double z = quake->GetModIntensity(quake->m_Intensity.Z); - + double r = quake->GetModIntensity(quake->m_RollIntensity); if (!(quake->m_Flags & QF_WAVE)) { jiggers.Falloff = MAX(falloff, jiggers.Falloff); + jiggers.RollIntensity = MAX(r, jiggers.RollIntensity); if (quake->m_Flags & QF_RELATIVE) { jiggers.RelIntensity.X = MAX(x, jiggers.RelIntensity.X); @@ -302,9 +310,11 @@ int DEarthquake::StaticGetQuakeIntensities(AActor *victim, FQuakeJiggers &jigger else { jiggers.WFalloff = MAX(falloff, jiggers.WFalloff); + double mr = r * quake->GetModWave(quake->m_RollWave); double mx = x * quake->GetModWave(quake->m_WaveSpeed.X); double my = y * quake->GetModWave(quake->m_WaveSpeed.Y); double mz = z * quake->GetModWave(quake->m_WaveSpeed.Z); + jiggers.RollWave = r * quake->GetModWave(quake->m_RollWave); // [RH] This only gives effect to the last sine quake. I would // prefer if some way was found to make multiples coexist @@ -338,7 +348,8 @@ int DEarthquake::StaticGetQuakeIntensities(AActor *victim, FQuakeJiggers &jigger bool P_StartQuakeXYZ(AActor *activator, int tid, int intensityX, int intensityY, int intensityZ, int duration, int damrad, int tremrad, FSoundID quakesfx, int flags, - double waveSpeedX, double waveSpeedY, double waveSpeedZ, int falloff, int highpoint) + double waveSpeedX, double waveSpeedY, double waveSpeedZ, int falloff, int highpoint, + int rollIntensity, double rollWave) { AActor *center; bool res = false; @@ -352,7 +363,7 @@ bool P_StartQuakeXYZ(AActor *activator, int tid, int intensityX, int intensityY, if (activator != NULL) { new DEarthquake(activator, intensityX, intensityY, intensityZ, duration, damrad, tremrad, - quakesfx, flags, waveSpeedX, waveSpeedY, waveSpeedZ, falloff, highpoint); + quakesfx, flags, waveSpeedX, waveSpeedY, waveSpeedZ, falloff, highpoint, rollIntensity, rollWave); return true; } } @@ -363,7 +374,7 @@ bool P_StartQuakeXYZ(AActor *activator, int tid, int intensityX, int intensityY, { res = true; new DEarthquake(center, intensityX, intensityY, intensityZ, duration, damrad, tremrad, - quakesfx, flags, waveSpeedX, waveSpeedY, waveSpeedZ, falloff, highpoint); + quakesfx, flags, waveSpeedX, waveSpeedY, waveSpeedZ, falloff, highpoint, rollIntensity, rollWave); } } @@ -372,5 +383,5 @@ bool P_StartQuakeXYZ(AActor *activator, int tid, int intensityX, int intensityY, bool P_StartQuake(AActor *activator, int tid, int intensity, int duration, int damrad, int tremrad, FSoundID quakesfx) { //Maintains original behavior by passing 0 to intensityZ, flags, and everything else after QSFX. - return P_StartQuakeXYZ(activator, tid, intensity, intensity, 0, duration, damrad, tremrad, quakesfx, 0, 0, 0, 0, 0, 0); + return P_StartQuakeXYZ(activator, tid, intensity, intensity, 0, duration, damrad, tremrad, quakesfx, 0, 0, 0, 0, 0, 0, 0, 0); } diff --git a/src/g_shared/a_sharedglobal.h b/src/g_shared/a_sharedglobal.h index f746375d3..e3a30fc67 100644 --- a/src/g_shared/a_sharedglobal.h +++ b/src/g_shared/a_sharedglobal.h @@ -153,8 +153,8 @@ struct FQuakeJiggers DVector3 RelIntensity; DVector3 Offset; DVector3 RelOffset; - double Falloff; - double WFalloff; + double Falloff, WFalloff; + double RollIntensity, RollWave; }; class DEarthquake : public DThinker @@ -164,7 +164,7 @@ class DEarthquake : public DThinker public: DEarthquake(AActor *center, int intensityX, int intensityY, int intensityZ, int duration, int damrad, int tremrad, FSoundID quakesfx, int flags, - double waveSpeedX, double waveSpeedY, double waveSpeedZ, int falloff, int highpoint); + double waveSpeedX, double waveSpeedY, double waveSpeedZ, int falloff, int highpoint, int rollIntensity, double rollWave); void Serialize (FArchive &arc); void Tick (); @@ -178,6 +178,8 @@ public: DVector3 m_WaveSpeed; double m_Falloff; int m_Highpoint, m_MiniCount; + double m_RollIntensity, m_RollWave; + double GetModIntensity(double intensity) const; double GetModWave(double waveMultiplier) const; diff --git a/src/g_shared/sbar.h b/src/g_shared/sbar.h index 560a7500a..03901f66f 100644 --- a/src/g_shared/sbar.h +++ b/src/g_shared/sbar.h @@ -264,6 +264,7 @@ class FMugShot DISABLEOUCH = 0x8, DISABLEPAIN = 0x10, DISABLERAMPAGE = 0x20, + CUSTOM = 0x40, }; FMugShot(); diff --git a/src/g_shared/sbar_mugshot.cpp b/src/g_shared/sbar_mugshot.cpp index b26541c99..148b7cb32 100644 --- a/src/g_shared/sbar_mugshot.cpp +++ b/src/g_shared/sbar_mugshot.cpp @@ -489,7 +489,7 @@ FTexture *FMugShot::GetFace(player_t *player, const char *default_face, int accu if (CurrentState != NULL) { int skin = player->userinfo.GetSkin(); - const char *skin_face = player->morphTics ? player->MorphedPlayerClass->Face.GetChars() : skins[skin].face; + const char *skin_face = (stateflags & FMugShot::CUSTOM) ? nullptr : (player->morphTics ? player->MorphedPlayerClass->Face.GetChars() : skins[skin].face); return CurrentState->GetCurrentFrameTexture(default_face, skin_face, level, angle); } return NULL; diff --git a/src/g_shared/sbarinfo_commands.cpp b/src/g_shared/sbarinfo_commands.cpp index 26b4c9510..854cb4106 100644 --- a/src/g_shared/sbarinfo_commands.cpp +++ b/src/g_shared/sbarinfo_commands.cpp @@ -1578,23 +1578,25 @@ class CommandDrawMugShot : public SBarInfoCommand sc.ScriptError("Expected a number between 1 and 9, got %d instead.", sc.Number); accuracy = sc.Number; sc.MustGetToken(','); - while(sc.CheckToken(TK_Identifier)) + while (sc.CheckToken(TK_Identifier)) { - if(sc.Compare("xdeathface")) - stateFlags = static_cast (stateFlags|FMugShot::XDEATHFACE); - else if(sc.Compare("animatedgodmode")) - stateFlags = static_cast (stateFlags|FMugShot::ANIMATEDGODMODE); - else if(sc.Compare("disablegrin")) - stateFlags = static_cast (stateFlags|FMugShot::DISABLEGRIN); - else if(sc.Compare("disableouch")) - stateFlags = static_cast (stateFlags|FMugShot::DISABLEOUCH); - else if(sc.Compare("disablepain")) - stateFlags = static_cast (stateFlags|FMugShot::DISABLEPAIN); - else if(sc.Compare("disablerampage")) - stateFlags = static_cast (stateFlags|FMugShot::DISABLERAMPAGE); + if (sc.Compare("xdeathface")) + stateFlags = static_cast (stateFlags | FMugShot::XDEATHFACE); + else if (sc.Compare("animatedgodmode")) + stateFlags = static_cast (stateFlags | FMugShot::ANIMATEDGODMODE); + else if (sc.Compare("disablegrin")) + stateFlags = static_cast (stateFlags | FMugShot::DISABLEGRIN); + else if (sc.Compare("disableouch")) + stateFlags = static_cast (stateFlags | FMugShot::DISABLEOUCH); + else if (sc.Compare("disablepain")) + stateFlags = static_cast (stateFlags | FMugShot::DISABLEPAIN); + else if (sc.Compare("disablerampage")) + stateFlags = static_cast (stateFlags | FMugShot::DISABLERAMPAGE); + else if (sc.Compare("custom")) + stateFlags = static_cast (stateFlags | FMugShot::CUSTOM); else sc.ScriptError("Unknown flag '%s'.", sc.String); - if(!sc.CheckToken('|')) + if (!sc.CheckToken('|')) sc.MustGetToken(','); } diff --git a/src/gl/scene/gl_scene.cpp b/src/gl/scene/gl_scene.cpp index a4d440abb..658a816a5 100644 --- a/src/gl/scene/gl_scene.cpp +++ b/src/gl/scene/gl_scene.cpp @@ -799,7 +799,7 @@ sector_t * FGLRenderer::RenderViewpoint (AActor * camera, GL_IRECT * bounds, flo double alen = sqrt(angx*angx + angy*angy); mAngles.Pitch = (float)RAD2DEG(asin(angy / alen)); - mAngles.Roll.Degrees = camera->Angles.Roll.Degrees; + mAngles.Roll.Degrees = ViewRoll.Degrees; // Scroll the sky mSky1Pos = (float)fmod(gl_frameMS * level.skyspeed1, 1024.f) * 90.f/256.f; diff --git a/src/m_specialpaths.cpp b/src/m_specialpaths.cpp index 60ccf08a8..abfb5db8f 100644 --- a/src/m_specialpaths.cpp +++ b/src/m_specialpaths.cpp @@ -198,6 +198,13 @@ FString M_GetConfigPath(bool for_reading) FString path; HRESULT hr; + path.Format("%s" GAMENAME "_portable.ini", progdir.GetChars()); + if (FileExists(path)) + { + return path; + } + path = ""; + // Construct a user-specific config name if (UseKnownFolders() && GetKnownFolder(CSIDL_APPDATA, FOLDERID_RoamingAppData, true, path)) { diff --git a/src/p_3dfloors.cpp b/src/p_3dfloors.cpp index 384d24c8c..125e5dc14 100644 --- a/src/p_3dfloors.cpp +++ b/src/p_3dfloors.cpp @@ -790,7 +790,7 @@ void P_LineOpening_XFloors (FLineOpening &open, AActor * thing, const line_t *li lowestceilingsec = j == 0 ? linedef->frontsector : linedef->backsector; } - if(ff_top > highestfloor && delta1 < delta2 && (!restrict || thing->Z() >= ff_top)) + if(ff_top > highestfloor && delta1 <= delta2 && (!restrict || thing->Z() >= ff_top)) { highestfloor = ff_top; highestfloorpic = *rover->top.texture; diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 266ff0bdc..088612e2a 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -5768,7 +5768,9 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) argCount > 10 ? ACSToDouble(args[10]) : 1.0, argCount > 11 ? ACSToDouble(args[11]) : 1.0, argCount > 12 ? args[12] : 0, - argCount > 13 ? args[13] : 0); + argCount > 13 ? args[13] : 0, + argCount > 14 ? args[14] : 0, + argCount > 15 ? args[15] : 0); } case ACSF_SetLineActivation: diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index 8aeba4c8b..73f18be36 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -1068,11 +1068,11 @@ FUNC(LS_Teleport_NoFog) break; case 2: - flags |= TELF_KEEPORIENTATION | TELF_ROTATEBOOM; // adjust to exit thing like Boom (i.e. with incorrect reversed angle) + if (ln != NULL) flags |= TELF_KEEPORIENTATION | TELF_ROTATEBOOM; // adjust to exit thing like Boom (i.e. with incorrect reversed angle) break; case 3: - flags |= TELF_KEEPORIENTATION | TELF_ROTATEBOOMINVERSE; // adjust to exit thing correctly + if (ln != NULL) flags |= TELF_KEEPORIENTATION | TELF_ROTATEBOOMINVERSE; // adjust to exit thing correctly break; } diff --git a/src/p_map.cpp b/src/p_map.cpp index 70786045e..c88c8e1fd 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -46,6 +46,7 @@ #include "p_checkposition.h" #include "r_utility.h" #include "p_blockmap.h" +#include "p_3dmidtex.h" #include "s_sound.h" #include "decallib.h" @@ -1515,7 +1516,47 @@ bool P_CheckPosition(AActor *thing, const DVector2 &pos, FCheckPosition &tm, boo // Any contacted lines the step closer together will adjust them. if (!thing->IsNoClip2()) { - P_GetFloorCeilingZ(tm, FFCF_SAMESECTOR); + if (!newsec->PortalBlocksMovement(sector_t::ceiling) || !newsec->PortalBlocksMovement(sector_t::floor)) + { + // Use P_GetFloorCeilingZ only if there's portals to consider. Its logic is subtly different than what is needed here for 3D floors. + P_GetFloorCeilingZ(tm, FFCF_SAMESECTOR); + } + else + { + tm.floorz = tm.dropoffz = newsec->floorplane.ZatPoint(pos); + tm.floorpic = newsec->GetTexture(sector_t::floor); + tm.ceilingz = newsec->ceilingplane.ZatPoint(pos); + tm.ceilingpic = newsec->GetTexture(sector_t::ceiling); + tm.floorsector = tm.ceilingsector = newsec; + tm.floorterrain = newsec->GetTerrain(sector_t::floor); + } + + F3DFloor* rover; + double thingtop = thing->Height > 0 ? thing->Top() : thing->Z() + 1; + + for (unsigned i = 0; ie->XFloor.ffloors.Size(); i++) + { + rover = newsec->e->XFloor.ffloors[i]; + if (!(rover->flags & FF_SOLID) || !(rover->flags & FF_EXISTS)) continue; + + double ff_bottom = rover->bottom.plane->ZatPoint(pos); + double ff_top = rover->top.plane->ZatPoint(pos); + + double delta1 = thing->Z() - (ff_bottom + ((ff_top - ff_bottom) / 2)); + double delta2 = thingtop - (ff_bottom + ((ff_top - ff_bottom) / 2)); + + if (ff_top > tm.floorz && fabs(delta1) < fabs(delta2)) + { + tm.floorz = tm.dropoffz = ff_top; + tm.floorpic = *rover->top.texture; + tm.floorterrain = rover->model->GetTerrain(rover->top.isceiling); + } + if (ff_bottom < tm.ceilingz && abs(delta1) >= abs(delta2)) + { + tm.ceilingz = ff_bottom; + tm.ceilingpic = *rover->bottom.texture; + } + } } else { @@ -1866,6 +1907,15 @@ static void CheckForPushSpecial(line_t *line, int side, AActor *mobj, DVector2 * if (fzt >= mobj->Top() && bzt >= mobj->Top() && fzb <= mobj->Z() && bzb <= mobj->Z()) { + if (line->flags & ML_3DMIDTEX) + { + double top, bot; + P_GetMidTexturePosition(line, side, &top, &bot); + if (bot < mobj->Top() && top > mobj->Z()) + { + goto isblocking; + } + } // we must also check if some 3D floor in the backsector may be blocking for (auto rover : line->backsector->e->XFloor.ffloors) { @@ -3974,8 +4024,11 @@ DAngle P_AimLineAttack(AActor *t1, DAngle angle, double distance, FTranslatedLin struct Origin { AActor *Caller; + FNameNoInit PuffSpecies; bool hitGhosts; - bool hitSameSpecies; + bool MThruSpecies; + bool ThruSpecies; + bool ThruActors; }; static ETraceStatus CheckForActor(FTraceResults &res, void *userdata) @@ -3987,17 +4040,16 @@ static ETraceStatus CheckForActor(FTraceResults &res, void *userdata) Origin *data = (Origin *)userdata; - // check for physical attacks on spectrals - if (res.Actor->flags4 & MF4_SPECTRAL) - { - return TRACE_Skip; - } + // Skip actors if the puff has: + // 1. THRUACTORS or SPECTRAL + // 2. MTHRUSPECIES on puff and the shooter has same species as the hit actor + // 3. THRUSPECIES on puff and the puff has same species as the hit actor + // 4. THRUGHOST on puff and the GHOST flag on the hit actor - if (data->hitSameSpecies && res.Actor->GetSpecies() == data->Caller->GetSpecies()) - { - return TRACE_Skip; - } - if (data->hitGhosts && res.Actor->flags3 & MF3_GHOST) + if ((data->ThruActors) || (res.Actor->flags4 & MF4_SPECTRAL) || + (data->MThruSpecies && res.Actor->GetSpecies() == data->Caller->GetSpecies()) || + (data->ThruSpecies && res.Actor->GetSpecies() == data->PuffSpecies) || + (data->hitGhosts && res.Actor->flags3 & MF3_GHOST)) { return TRACE_Skip; } @@ -4059,14 +4111,41 @@ AActor *P_LineAttack(AActor *t1, DAngle angle, double distance, // We need to check the defaults of the replacement here AActor *puffDefaults = GetDefaultByType(pufftype->GetReplacement()); - + TData.hitGhosts = (t1->player != NULL && t1->player->ReadyWeapon != NULL && (t1->player->ReadyWeapon->flags2 & MF2_THRUGHOST)) || (puffDefaults && (puffDefaults->flags2 & MF2_THRUGHOST)); - TData.hitSameSpecies = (puffDefaults && (puffDefaults->flags6 & MF6_MTHRUSPECIES)); + TData.MThruSpecies = (puffDefaults && (puffDefaults->flags6 & MF6_MTHRUSPECIES)); + TData.PuffSpecies = NAME_None; + // [MC] To prevent possible mod breakage, this flag is pretty much necessary. + // Somewhere, someone is relying on these to spawn on actors and move through them. + + if ((puffDefaults->flags7 & MF7_ALLOWTHRUFLAGS)) + { + TData.ThruSpecies = (puffDefaults && (puffDefaults->flags6 & MF6_THRUSPECIES)); + TData.ThruActors = (puffDefaults && (puffDefaults->flags2 & MF2_THRUACTORS)); + + // [MC] Because this is a one-hit trace event, we need to spawn the puff, get the species + // and destroy it. Assume there is no species unless tempuff isn't NULL. We cannot get + // a proper species the same way as puffDefaults flags it appears... + + AActor *tempuff = NULL; + if (pufftype != NULL) + tempuff = Spawn(pufftype, t1->Pos(), ALLOW_REPLACE); + if (tempuff != NULL) + { + TData.PuffSpecies = tempuff->GetSpecies(); + tempuff->Destroy(); + } + } + else + { + TData.ThruSpecies = false; + TData.ThruActors = false; + } // if the puff uses a non-standard damage type, this will override default, hitscan and melee damage type. // All other explicitly passed damage types (currenty only MDK) will be preserved. if ((damageType == NAME_None || damageType == NAME_Melee || damageType == NAME_Hitscan) && @@ -4494,9 +4573,13 @@ struct RailData AActor *Caller; TArray RailHits; TArray PortalHits; + FNameNoInit PuffSpecies; bool StopAtOne; bool StopAtInvul; + bool ThruGhosts; bool ThruSpecies; + bool MThruSpecies; + bool ThruActors; }; static ETraceStatus ProcessRailHit(FTraceResults &res, void *userdata) @@ -4523,8 +4606,16 @@ static ETraceStatus ProcessRailHit(FTraceResults &res, void *userdata) return TRACE_Stop; } - // Skip actors with the same species if the puff has MTHRUSPECIES. - if (data->ThruSpecies && res.Actor->GetSpecies() == data->Caller->GetSpecies()) + // Skip actors if the puff has: + // 1. THRUACTORS (This one did NOT include a check for spectral) + // 2. MTHRUSPECIES on puff and the shooter has same species as the hit actor + // 3. THRUSPECIES on puff and the puff has same species as the hit actor + // 4. THRUGHOST on puff and the GHOST flag on the hit actor + + if ((data->ThruActors) || + (data->MThruSpecies && res.Actor->GetSpecies() == data->Caller->GetSpecies()) || + (data->ThruSpecies && res.Actor->GetSpecies() == data->PuffSpecies) || + (data->ThruGhosts && res.Actor->flags3 & MF3_GHOST)) { return TRACE_Skip; } @@ -4599,7 +4690,27 @@ void P_RailAttack(FRailParams *p) // disabled because not complete yet. flags = (puffDefaults->flags6 & MF6_NOTRIGGER) ? TRACE_ReportPortals : TRACE_PCross | TRACE_Impact | TRACE_ReportPortals; rail_data.StopAtInvul = (puffDefaults->flags3 & MF3_FOILINVUL) ? false : true; - rail_data.ThruSpecies = (puffDefaults->flags6 & MF6_MTHRUSPECIES) ? true : false; + rail_data.MThruSpecies = ((puffDefaults->flags6 & MF6_MTHRUSPECIES)) ? true : false; + + // Prevent mod breakage as somewhere, someone is relying on these to spawn on an actor + // and move through them... + if ((puffDefaults->flags7 & MF7_ALLOWTHRUFLAGS)) + { + rail_data.ThruGhosts = !!(puffDefaults->flags2 & MF2_THRUGHOST); + rail_data.ThruSpecies = !!(puffDefaults->flags6 & MF6_THRUSPECIES); + rail_data.ThruActors = !!(puffDefaults->flags2 & MF2_THRUACTORS); + } + else + { + rail_data.ThruGhosts = false; + rail_data.MThruSpecies = false; + rail_data.ThruActors = false; + } + // used as damage inflictor + AActor *thepuff = NULL; + + if (puffclass != NULL) thepuff = Spawn(puffclass, source->Pos(), ALLOW_REPLACE); + rail_data.PuffSpecies = (thepuff != NULL) ? thepuff->GetSpecies() : NAME_None; Trace(start, source->Sector, vec, p->distance, MF_SHOOTABLE, ML_BLOCKEVERYTHING, source, trace, flags, ProcessRailHit, &rail_data); @@ -4607,11 +4718,6 @@ void P_RailAttack(FRailParams *p) unsigned int i; FName damagetype = (puffDefaults == NULL || puffDefaults->DamageType == NAME_None) ? FName(NAME_Railgun) : puffDefaults->DamageType; - // used as damage inflictor - AActor *thepuff = NULL; - - if (puffclass != NULL) thepuff = Spawn(puffclass, source->Pos(), ALLOW_REPLACE); - for (i = 0; i < rail_data.RailHits.Size(); i++) { bool spawnpuff; diff --git a/src/p_spec.h b/src/p_spec.h index 3cecbe866..fd27cbac4 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -699,7 +699,7 @@ void P_DoDeferedScripts (void); // // [RH] p_quake.c // -bool P_StartQuakeXYZ(AActor *activator, int tid, int intensityX, int intensityY, int intensityZ, int duration, int damrad, int tremrad, FSoundID quakesfx, int flags, double waveSpeedX, double waveSpeedY, double waveSpeedZ, int falloff, int highpoint); +bool P_StartQuakeXYZ(AActor *activator, int tid, int intensityX, int intensityY, int intensityZ, int duration, int damrad, int tremrad, FSoundID quakesfx, int flags, double waveSpeedX, double waveSpeedY, double waveSpeedZ, int falloff, int highpoint, int rollIntensity, double rollWave); bool P_StartQuake(AActor *activator, int tid, int intensity, int duration, int damrad, int tremrad, FSoundID quakesfx); #endif diff --git a/src/r_3dfloors.cpp b/src/r_3dfloors.cpp new file mode 100644 index 000000000..61a23187d --- /dev/null +++ b/src/r_3dfloors.cpp @@ -0,0 +1,162 @@ +/* +** r_3dfloors.cpp +** software 3D floors addon +** +** by kgsws +*/ + +#include "templates.h" +#include "doomdef.h" +#include "p_local.h" +#include "c_dispatch.h" +#include "r_local.h" +#include "r_bsp.h" +#include "r_plane.h" +#include "c_cvars.h" +#include "r_3dfloors.h" + +// external variables +int fake3D; +F3DFloor *fakeFloor; +fixed_t fakeHeight; +fixed_t fakeAlpha; +int fakeActive = 0; +double sclipBottom; +double sclipTop; +HeightLevel *height_top = NULL; +HeightLevel *height_cur = NULL; +int CurrentMirror = 0; +int CurrentSkybox = 0; + +CVAR(Int, r_3dfloors, true, 0); + +// private variables +int height_max = -1; +TArray toplist; +ClipStack *clip_top = NULL; +ClipStack *clip_cur = NULL; + +void R_3D_DeleteHeights() +{ + height_cur = height_top; + while(height_cur) { + height_top = height_cur; + height_cur = height_cur->next; + M_Free(height_top); + } + height_max = -1; + height_top = height_cur = NULL; +} + +void R_3D_AddHeight(secplane_t *add, sector_t *sec) +{ + HeightLevel *near; + HeightLevel *curr; + + double height = add->ZatPoint(ViewPos); + if(height >= sec->CenterCeiling()) return; + if(height <= sec->CenterFloor()) return; + + fakeActive = 1; + + if(height_max >= 0) { + near = height_top; + while(near && near->height < height) near = near->next; + if(near) { + if(near->height == height) return; + curr = (HeightLevel*)M_Malloc(sizeof(HeightLevel)); + curr->height = height; + curr->prev = near->prev; + curr->next = near; + if(near->prev) near->prev->next = curr; + else height_top = curr; + near->prev = curr; + } else { + curr = (HeightLevel*)M_Malloc(sizeof(HeightLevel)); + curr->height = height; + curr->prev = height_cur; + curr->next = NULL; + height_cur->next = curr; + height_cur = curr; + } + } else { + height_top = height_cur = (HeightLevel*)M_Malloc(sizeof(HeightLevel)); + height_top->height = height; + height_top->prev = NULL; + height_top->next = NULL; + } + height_max++; +} + +void R_3D_NewClip() +{ + ClipStack *curr; +// extern short floorclip[MAXWIDTH]; +// extern short ceilingclip[MAXWIDTH]; + + curr = (ClipStack*)M_Malloc(sizeof(ClipStack)); + curr->next = 0; + memcpy(curr->floorclip, floorclip, sizeof(short) * MAXWIDTH); + memcpy(curr->ceilingclip, ceilingclip, sizeof(short) * MAXWIDTH); + curr->ffloor = fakeFloor; + assert(fakeFloor->floorclip == NULL); + assert(fakeFloor->ceilingclip == NULL); + fakeFloor->floorclip = curr->floorclip; + fakeFloor->ceilingclip = curr->ceilingclip; + if(clip_top) { + clip_cur->next = curr; + clip_cur = curr; + } else { + clip_top = clip_cur = curr; + } +} + +void R_3D_ResetClip() +{ + clip_cur = clip_top; + while(clip_cur) + { + assert(clip_cur->ffloor->floorclip != NULL); + assert(clip_cur->ffloor->ceilingclip != NULL); + clip_cur->ffloor->ceilingclip = clip_cur->ffloor->floorclip = NULL; + clip_top = clip_cur; + clip_cur = clip_cur->next; + M_Free(clip_top); + } + clip_cur = clip_top = NULL; +} + +void R_3D_EnterSkybox() +{ + HeightStack current; + + current.height_top = height_top; + current.height_cur = height_cur; + current.height_max = height_max; + + toplist.Push(current); + + height_top = NULL; + height_cur = NULL; + height_max = -1; + + CurrentSkybox++; +} + +void R_3D_LeaveSkybox() +{ + HeightStack current; + + current.height_top = NULL; + current.height_cur = NULL; + current.height_max = -1; + + toplist.Pop(current); + + height_top = current.height_top; + height_cur = current.height_cur; + height_max = current.height_max; + + CurrentSkybox--; +} + diff --git a/src/r_plane.cpp b/src/r_plane.cpp new file mode 100644 index 000000000..a3877010f --- /dev/null +++ b/src/r_plane.cpp @@ -0,0 +1,1836 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// $Log:$ +// +// DESCRIPTION: +// Here is a core component: drawing the floors and ceilings, +// while maintaining a per column clipping list only. +// Moreover, the sky areas have to be determined. +// +// MAXVISPLANES is no longer a limit on the number of visplanes, +// but a limit on the number of hash slots; larger numbers mean +// better performance usually but after a point they are wasted, +// and memory and time overheads creep in. +// +// Lee Killough +// +// [RH] Further modified to significantly increase accuracy and add slopes. +// +//----------------------------------------------------------------------------- + +#include +#include + +#include "templates.h" +#include "i_system.h" +#include "w_wad.h" + +#include "doomdef.h" +#include "doomstat.h" + +#include "r_local.h" +#include "r_sky.h" +#include "stats.h" + +#include "v_video.h" +#include "a_sharedglobal.h" +#include "c_console.h" +#include "cmdlib.h" +#include "d_net.h" +#include "g_level.h" +#include "r_bsp.h" +#include "r_plane.h" +#include "r_segs.h" +#include "r_3dfloors.h" +#include "v_palette.h" +#include "r_data/colormaps.h" + +#ifdef _MSC_VER +#pragma warning(disable:4244) +#endif + +//EXTERN_CVAR (Int, tx) +//EXTERN_CVAR (Int, ty) + +extern subsector_t *InSubsector; + +static void R_DrawSkyStriped (visplane_t *pl); + +planefunction_t floorfunc; +planefunction_t ceilingfunc; + +// Here comes the obnoxious "visplane". +#define MAXVISPLANES 128 /* must be a power of 2 */ + +// Avoid infinite recursion with stacked sectors by limiting them. +#define MAX_SKYBOX_PLANES 1000 + +// [RH] Allocate one extra for sky box planes. +static visplane_t *visplanes[MAXVISPLANES+1]; // killough +static visplane_t *freetail; // killough +static visplane_t **freehead = &freetail; // killough + +visplane_t *floorplane; +visplane_t *ceilingplane; + +// killough -- hash function for visplanes +// Empirically verified to be fairly uniform: + +#define visplane_hash(picnum,lightlevel,height) \ + ((unsigned)((picnum)*3+(lightlevel)+(FLOAT2FIXED((height).fD()))*7) & (MAXVISPLANES-1)) + +// These are copies of the main parameters used when drawing stacked sectors. +// When you change the main parameters, you should copy them here too *unless* +// you are changing them to draw a stacked sector. Otherwise, stacked sectors +// won't draw in skyboxes properly. +int stacked_extralight; +double stacked_visibility; +DVector3 stacked_viewpos; +DAngle stacked_angle; + + +// +// opening +// + +size_t maxopenings; +short *openings; +ptrdiff_t lastopening; + +// +// Clip values are the solid pixel bounding the range. +// floorclip starts out SCREENHEIGHT and is just outside the range +// ceilingclip starts out 0 and is just inside the range +// +short floorclip[MAXWIDTH]; +short ceilingclip[MAXWIDTH]; + +// +// texture mapping +// + +static double planeheight; + +extern "C" { +// +// spanend holds the end of a plane span in each screen row +// +short spanend[MAXHEIGHT]; +BYTE *tiltlighting[MAXWIDTH]; + +int planeshade; +FVector3 plane_sz, plane_su, plane_sv; +float planelightfloat; +bool plane_shade; +fixed_t pviewx, pviewy; + +void R_DrawTiltedPlane_ASM (int y, int x1); +} + +fixed_t yslope[MAXHEIGHT]; +static fixed_t xscale, yscale; +static DWORD xstepscale, ystepscale; +static DWORD basexfrac, baseyfrac; + +#ifdef X86_ASM +extern "C" void R_SetSpanSource_ASM (const BYTE *flat); +extern "C" void R_SetSpanSize_ASM (int xbits, int ybits); +extern "C" void R_SetSpanColormap_ASM (BYTE *colormap); +extern "C" void R_SetTiltedSpanSource_ASM (const BYTE *flat); +extern "C" BYTE *ds_curcolormap, *ds_cursource, *ds_curtiltedsource; +#endif +void R_DrawSinglePlane (visplane_t *, fixed_t alpha, bool additive, bool masked); + +//========================================================================== +// +// R_InitPlanes +// +// Called at game startup. +// +//========================================================================== + +void R_InitPlanes () +{ +} + +//========================================================================== +// +// R_DeinitPlanes +// +//========================================================================== + +void R_DeinitPlanes () +{ + fakeActive = 0; + + // do not use R_ClearPlanes because at this point the screen pointer is no longer valid. + for (int i = 0; i <= MAXVISPLANES; i++) // new code -- killough + { + for (*freehead = visplanes[i], visplanes[i] = NULL; *freehead; ) + { + freehead = &(*freehead)->next; + } + } + for (visplane_t *pl = freetail; pl != NULL; ) + { + visplane_t *next = pl->next; + free (pl); + pl = next; + } +} + +//========================================================================== +// +// R_MapPlane +// +// Globals used: planeheight, ds_source, basexscale, baseyscale, +// pviewx, pviewy, xoffs, yoffs, basecolormap, xscale, yscale. +// +//========================================================================== + +void R_MapPlane (int y, int x1) +{ + int x2 = spanend[y]; + fixed_t distance; + +#ifdef RANGECHECK + if (x2 < x1 || x1<0 || x2>=viewwidth || (unsigned)y>=(unsigned)viewheight) + { + I_FatalError ("R_MapPlane: %i, %i at %i", x1, x2, y); + } +#endif + + // [RH] Notice that I dumped the caching scheme used by Doom. + // It did not offer any appreciable speedup. + + distance = xs_ToInt(planeheight * yslope[y]); + + ds_xstep = FixedMul (distance, xstepscale); + ds_ystep = FixedMul (distance, ystepscale); + ds_xfrac = FixedMul (distance, basexfrac) + pviewx; + ds_yfrac = FixedMul (distance, baseyfrac) + pviewy; + + if (plane_shade) + { + // Determine lighting based on the span's distance from the viewer. + ds_colormap = basecolormap->Maps + (GETPALOOKUP ( + GlobVis * fabs(CenterY - y), planeshade) << COLORMAPSHIFT); + } + +#ifdef X86_ASM + if (ds_colormap != ds_curcolormap) + R_SetSpanColormap_ASM (ds_colormap); +#endif + + ds_y = y; + ds_x1 = x1; + ds_x2 = x2; + + spanfunc (); +} + +//========================================================================== +// +// R_CalcTiltedLighting +// +// Calculates the lighting for one row of a tilted plane. If the definition +// of GETPALOOKUP changes, this needs to change, too. +// +//========================================================================== + +extern "C" { +void R_CalcTiltedLighting (double lval, double lend, int width) +{ + double lstep; + BYTE *lightfiller; + BYTE *basecolormapdata = basecolormap->Maps; + int i = 0; + + if (width == 0 || lval == lend) + { // Constant lighting + lightfiller = basecolormapdata + (GETPALOOKUP(lval, planeshade) << COLORMAPSHIFT); + } + else + { + lstep = (lend - lval) / width; + if (lval >= MAXLIGHTVIS) + { // lval starts "too bright". + lightfiller = basecolormapdata + (GETPALOOKUP(lval, planeshade) << COLORMAPSHIFT); + for (; i <= width && lval >= MAXLIGHTVIS; ++i) + { + tiltlighting[i] = lightfiller; + lval += lstep; + } + } + if (lend >= MAXLIGHTVIS) + { // lend ends "too bright". + lightfiller = basecolormapdata + (GETPALOOKUP(lend, planeshade) << COLORMAPSHIFT); + for (; width > i && lend >= MAXLIGHTVIS; --width) + { + tiltlighting[width] = lightfiller; + lend -= lstep; + } + } + if (width > 0) + { + lval = FIXED2DBL(planeshade) - lval; + lend = FIXED2DBL(planeshade) - lend; + lstep = (lend - lval) / width; + if (lstep < 0) + { // Going from dark to light + if (lval < 1.) + { // All bright + lightfiller = basecolormapdata; + } + else + { + if (lval >= NUMCOLORMAPS) + { // Starts beyond the dark end + BYTE *clight = basecolormapdata + ((NUMCOLORMAPS-1) << COLORMAPSHIFT); + while (lval >= NUMCOLORMAPS && i <= width) + { + tiltlighting[i++] = clight; + lval += lstep; + } + if (i > width) + return; + } + while (i <= width && lval >= 0) + { + tiltlighting[i++] = basecolormapdata + (xs_ToInt(lval) << COLORMAPSHIFT); + lval += lstep; + } + lightfiller = basecolormapdata; + } + } + else + { // Going from light to dark + if (lval >= (NUMCOLORMAPS-1)) + { // All dark + lightfiller = basecolormapdata + ((NUMCOLORMAPS-1) << COLORMAPSHIFT); + } + else + { + while (lval < 0 && i <= width) + { + tiltlighting[i++] = basecolormapdata; + lval += lstep; + } + if (i > width) + return; + while (i <= width && lval < (NUMCOLORMAPS-1)) + { + tiltlighting[i++] = basecolormapdata + (xs_ToInt(lval) << COLORMAPSHIFT); + lval += lstep; + } + lightfiller = basecolormapdata + ((NUMCOLORMAPS-1) << COLORMAPSHIFT); + } + } + } + } + for (; i <= width; i++) + { + tiltlighting[i] = lightfiller; + } +} +} // extern "C" + +//========================================================================== +// +// R_MapTiltedPlane +// +//========================================================================== + +void R_MapTiltedPlane (int y, int x1) +{ + int x2 = spanend[y]; + int width = x2 - x1; + double iz, uz, vz; + BYTE *fb; + DWORD u, v; + int i; + + iz = plane_sz[2] + plane_sz[1]*(centery-y) + plane_sz[0]*(x1-centerx); + + // Lighting is simple. It's just linear interpolation from start to end + if (plane_shade) + { + uz = (iz + plane_sz[0]*width) * planelightfloat; + vz = iz * planelightfloat; + R_CalcTiltedLighting (vz, uz, width); + } + + uz = plane_su[2] + plane_su[1]*(centery-y) + plane_su[0]*(x1-centerx); + vz = plane_sv[2] + plane_sv[1]*(centery-y) + plane_sv[0]*(x1-centerx); + + fb = ylookup[y] + x1 + dc_destorg; + + BYTE vshift = 32 - ds_ybits; + BYTE ushift = vshift - ds_xbits; + int umask = ((1 << ds_xbits) - 1) << ds_ybits; + +#if 0 // The "perfect" reference version of this routine. Pretty slow. + // Use it only to see how things are supposed to look. + i = 0; + do + { + double z = 1.f/iz; + + u = SQWORD(uz*z) + pviewx; + v = SQWORD(vz*z) + pviewy; + ds_colormap = tiltlighting[i]; + fb[i++] = ds_colormap[ds_source[(v >> vshift) | ((u >> ushift) & umask)]]; + iz += plane_sz[0]; + uz += plane_su[0]; + vz += plane_sv[0]; + } while (--width >= 0); +#else +//#define SPANSIZE 32 +//#define INVSPAN 0.03125f +//#define SPANSIZE 8 +//#define INVSPAN 0.125f +#define SPANSIZE 16 +#define INVSPAN 0.0625f + + double startz = 1.f/iz; + double startu = uz*startz; + double startv = vz*startz; + double izstep, uzstep, vzstep; + + izstep = plane_sz[0] * SPANSIZE; + uzstep = plane_su[0] * SPANSIZE; + vzstep = plane_sv[0] * SPANSIZE; + x1 = 0; + width++; + + while (width >= SPANSIZE) + { + iz += izstep; + uz += uzstep; + vz += vzstep; + + double endz = 1.f/iz; + double endu = uz*endz; + double endv = vz*endz; + DWORD stepu = SQWORD((endu - startu) * INVSPAN); + DWORD stepv = SQWORD((endv - startv) * INVSPAN); + u = SQWORD(startu) + pviewx; + v = SQWORD(startv) + pviewy; + + for (i = SPANSIZE-1; i >= 0; i--) + { + fb[x1] = *(tiltlighting[x1] + ds_source[(v >> vshift) | ((u >> ushift) & umask)]); + x1++; + u += stepu; + v += stepv; + } + startu = endu; + startv = endv; + width -= SPANSIZE; + } + if (width > 0) + { + if (width == 1) + { + u = SQWORD(startu); + v = SQWORD(startv); + fb[x1] = *(tiltlighting[x1] + ds_source[(v >> vshift) | ((u >> ushift) & umask)]); + } + else + { + double left = width; + iz += plane_sz[0] * left; + uz += plane_su[0] * left; + vz += plane_sv[0] * left; + + double endz = 1.f/iz; + double endu = uz*endz; + double endv = vz*endz; + left = 1.f/left; + DWORD stepu = SQWORD((endu - startu) * left); + DWORD stepv = SQWORD((endv - startv) * left); + u = SQWORD(startu) + pviewx; + v = SQWORD(startv) + pviewy; + + for (; width != 0; width--) + { + fb[x1] = *(tiltlighting[x1] + ds_source[(v >> vshift) | ((u >> ushift) & umask)]); + x1++; + u += stepu; + v += stepv; + } + } + } +#endif +} + +//========================================================================== +// +// R_MapColoredPlane +// +//========================================================================== + +void R_MapColoredPlane (int y, int x1) +{ + memset (ylookup[y] + x1 + dc_destorg, ds_color, spanend[y] - x1 + 1); +} + +//========================================================================== +// +// R_ClearPlanes +// +// Called at the beginning of each frame. +// +//========================================================================== + +void R_ClearPlanes (bool fullclear) +{ + int i; + + // Don't clear fake planes if not doing a full clear. + if (!fullclear) + { + for (i = 0; i <= MAXVISPLANES-1; i++) // new code -- killough + { + for (visplane_t **probe = &visplanes[i]; *probe != NULL; ) + { + if ((*probe)->sky < 0) + { // fake: move past it + probe = &(*probe)->next; + } + else + { // not fake: move to freelist + visplane_t *vis = *probe; + *freehead = vis; + *probe = vis->next; + vis->next = NULL; + freehead = &vis->next; + } + } + } + } + else + { + for (i = 0; i <= MAXVISPLANES; i++) // new code -- killough + { + for (*freehead = visplanes[i], visplanes[i] = NULL; *freehead; ) + { + freehead = &(*freehead)->next; + } + } + + // opening / clipping determination + clearbufshort (floorclip, viewwidth, viewheight); + // [RH] clip ceiling to console bottom + clearbufshort (ceilingclip, viewwidth, + !screen->Accel2D && ConBottom > viewwindowy && !bRenderingToCanvas + ? (ConBottom - viewwindowy) : 0); + + lastopening = 0; + } +} + +//========================================================================== +// +// new_visplane +// +// New function, by Lee Killough +// [RH] top and bottom buffers get allocated immediately after the visplane. +// +//========================================================================== + +static visplane_t *new_visplane (unsigned hash) +{ + visplane_t *check = freetail; + + if (check == NULL) + { + check = (visplane_t *)M_Malloc (sizeof(*check) + 3 + sizeof(*check->top)*(MAXWIDTH*2)); + memset(check, 0, sizeof(*check) + 3 + sizeof(*check->top)*(MAXWIDTH*2)); + check->bottom = check->top + MAXWIDTH+2; + } + else if (NULL == (freetail = freetail->next)) + { + freehead = &freetail; + } + + check->next = visplanes[hash]; + visplanes[hash] = check; + return check; +} + + +//========================================================================== +// +// R_FindPlane +// +// killough 2/28/98: Add offsets +//========================================================================== + +visplane_t *R_FindPlane (const secplane_t &height, FTextureID picnum, int lightlevel, double Alpha, bool additive, + const FTransform &xform, + int sky, FSectorPortal *portal) +{ + secplane_t plane; + visplane_t *check; + unsigned hash; // killough + bool isskybox; + fixed_t xoffs = FLOAT2FIXED(xform.xOffs); + fixed_t yoffs = FLOAT2FIXED(xform.yOffs + xform.baseyOffs); + fixed_t xscale = FLOAT2FIXED(xform.xScale); + fixed_t yscale = FLOAT2FIXED(xform.yScale); + fixed_t alpha = FLOAT2FIXED(Alpha); + angle_t angle = (xform.Angle + xform.baseAngle).BAMs(); + + if (picnum == skyflatnum) // killough 10/98 + { // most skies map together + lightlevel = 0; + xoffs = 0; + yoffs = 0; + xscale = 0; + yscale = 0; + angle = 0; + alpha = 0; + additive = false; + // [RH] Map floor skies and ceiling skies to separate visplanes. This isn't + // always necessary, but it is needed if a floor and ceiling sky are in the + // same column but separated by a wall. If they both try to reside in the + // same visplane, then only the floor sky will be drawn. + plane.set(0., 0., height.fC(), 0.); + isskybox = portal != NULL && !(portal->mFlags & PORTSF_INSKYBOX); + } + else if (portal != NULL && !(portal->mFlags & PORTSF_INSKYBOX)) + { + plane = height; + isskybox = true; + } + else + { + plane = height; + isskybox = false; + // kg3D - hack, store alpha in sky + // i know there is ->alpha, but this also allows to identify fake plane + // and ->alpha is for stacked sectors + if (fake3D & (FAKE3D_FAKEFLOOR|FAKE3D_FAKECEILING)) sky = 0x80000000 | fakeAlpha; + else sky = 0; // not skyflatnum so it can't be a sky + portal = NULL; + alpha = OPAQUE; + } + + // New visplane algorithm uses hash table -- killough + hash = isskybox ? MAXVISPLANES : visplane_hash (picnum.GetIndex(), lightlevel, height); + + for (check = visplanes[hash]; check; check = check->next) // killough + { + if (isskybox) + { + if (portal == check->portal && plane == check->height) + { + if (portal->mType != PORTS_SKYVIEWPOINT) + { // This skybox is really a stacked sector, so we need to + // check even more. + if (check->extralight == stacked_extralight && + check->visibility == stacked_visibility && + check->viewpos == stacked_viewpos && + ( + // headache inducing logic... :( + (portal->mType != PORTS_STACKEDSECTORTHING) || + ( + check->Alpha == alpha && + check->Additive == additive && + (alpha == 0 || // if alpha is > 0 everything needs to be checked + (plane == check->height && + picnum == check->picnum && + lightlevel == check->lightlevel && + + xoffs == check->xoffs && // killough 2/28/98: Add offset checks + yoffs == check->yoffs && + basecolormap == check->colormap && // [RH] Add more checks + xscale == check->xscale && + yscale == check->yscale && + angle == check->angle + ) + ) && + check->viewangle == stacked_angle + ) + ) + ) + { + return check; + } + } + else + { + return check; + } + } + } + else + if (plane == check->height && + picnum == check->picnum && + lightlevel == check->lightlevel && + xoffs == check->xoffs && // killough 2/28/98: Add offset checks + yoffs == check->yoffs && + basecolormap == check->colormap && // [RH] Add more checks + xscale == check->xscale && + yscale == check->yscale && + angle == check->angle && + sky == check->sky && + CurrentPortalUniq == check->CurrentPortalUniq && + MirrorFlags == check->MirrorFlags && + CurrentSkybox == check->CurrentSkybox && + ViewPos == check->viewpos + ) + { + return check; + } + } + + check = new_visplane (hash); // killough + + check->height = plane; + check->picnum = picnum; + check->lightlevel = lightlevel; + check->xoffs = xoffs; // killough 2/28/98: Save offsets + check->yoffs = yoffs; + check->xscale = xscale; + check->yscale = yscale; + check->angle = angle; + check->colormap = basecolormap; // [RH] Save colormap + check->sky = sky; + check->portal = portal; + check->left = viewwidth; // Was SCREENWIDTH -- killough 11/98 + check->right = 0; + check->extralight = stacked_extralight; + check->visibility = stacked_visibility; + check->viewpos = stacked_viewpos; + check->viewangle = stacked_angle; + check->Alpha = alpha; + check->Additive = additive; + check->CurrentPortalUniq = CurrentPortalUniq; + check->MirrorFlags = MirrorFlags; + check->CurrentSkybox = CurrentSkybox; + + clearbufshort (check->top, viewwidth, 0x7fff); + + return check; +} + +//========================================================================== +// +// R_CheckPlane +// +//========================================================================== + +visplane_t *R_CheckPlane (visplane_t *pl, int start, int stop) +{ + int intrl, intrh; + int unionl, unionh; + int x; + + assert (start >= 0 && start < viewwidth); + assert (stop > start && stop <= viewwidth); + + if (start < pl->left) + { + intrl = pl->left; + unionl = start; + } + else + { + unionl = pl->left; + intrl = start; + } + + if (stop > pl->right) + { + intrh = pl->right; + unionh = stop; + } + else + { + unionh = pl->right; + intrh = stop; + } + + for (x = intrl; x < intrh && pl->top[x] == 0x7fff; x++) + ; + + if (x >= intrh) + { + // use the same visplane + pl->left = unionl; + pl->right = unionh; + } + else + { + // make a new visplane + unsigned hash; + + if (pl->portal != NULL && !(pl->portal->mFlags & PORTSF_INSKYBOX) && viewactive) + { + hash = MAXVISPLANES; + } + else + { + hash = visplane_hash (pl->picnum.GetIndex(), pl->lightlevel, pl->height); + } + visplane_t *new_pl = new_visplane (hash); + + new_pl->height = pl->height; + new_pl->picnum = pl->picnum; + new_pl->lightlevel = pl->lightlevel; + new_pl->xoffs = pl->xoffs; // killough 2/28/98 + new_pl->yoffs = pl->yoffs; + new_pl->xscale = pl->xscale; // [RH] copy these, too + new_pl->yscale = pl->yscale; + new_pl->angle = pl->angle; + new_pl->colormap = pl->colormap; + new_pl->portal = pl->portal; + new_pl->extralight = pl->extralight; + new_pl->visibility = pl->visibility; + new_pl->viewpos = pl->viewpos; + new_pl->viewangle = pl->viewangle; + new_pl->sky = pl->sky; + new_pl->Alpha = pl->Alpha; + new_pl->Additive = pl->Additive; + new_pl->CurrentPortalUniq = pl->CurrentPortalUniq; + new_pl->MirrorFlags = pl->MirrorFlags; + new_pl->CurrentSkybox = pl->CurrentSkybox; + pl = new_pl; + pl->left = start; + pl->right = stop; + clearbufshort (pl->top, viewwidth, 0x7fff); + } + return pl; +} + + +//========================================================================== +// +// R_MakeSpans +// +// +//========================================================================== + +inline void R_MakeSpans (int x, int t1, int b1, int t2, int b2, void (*mapfunc)(int y, int x1)) +{ +} + +//========================================================================== +// +// R_DrawSky +// +// Can handle overlapped skies. Note that the front sky is *not* masked in +// in the normal convention for patches, but uses color 0 as a transparent +// color instead. +// +// Note that since ZDoom now uses color 0 as transparent for other purposes, +// you can use normal texture transparency, so the distinction isn't so +// important anymore, but you should still be aware of it. +// +//========================================================================== + +static FTexture *frontskytex, *backskytex; +static angle_t skyflip; +static int frontpos, backpos; +static double frontyScale; +static fixed_t frontcyl, backcyl; +static double skymid; +static angle_t skyangle; +static double frontiScale; + +extern float swall[MAXWIDTH]; +extern fixed_t lwall[MAXWIDTH]; +extern fixed_t rw_offset; +extern FTexture *rw_pic; + +// Allow for layer skies up to 512 pixels tall. This is overkill, +// since the most anyone can ever see of the sky is 500 pixels. +// We need 4 skybufs because wallscan can draw up to 4 columns at a time. +static BYTE skybuf[4][512]; +static DWORD lastskycol[4]; +static int skycolplace; + +// Get a column of sky when there is only one sky texture. +static const BYTE *R_GetOneSkyColumn (FTexture *fronttex, int x) +{ + angle_t column = (skyangle + xtoviewangle[x]) ^ skyflip; + return fronttex->GetColumn((UMulScale16(column, frontcyl) + frontpos) >> FRACBITS, NULL); +} + +// Get a column of sky when there are two overlapping sky textures +static const BYTE *R_GetTwoSkyColumns (FTexture *fronttex, int x) +{ + DWORD ang = (skyangle + xtoviewangle[x]) ^ skyflip; + DWORD angle1 = (DWORD)((UMulScale16(ang, frontcyl) + frontpos) >> FRACBITS); + DWORD angle2 = (DWORD)((UMulScale16(ang, backcyl) + backpos) >> FRACBITS); + + // Check if this column has already been built. If so, there's + // no reason to waste time building it again. + DWORD skycol = (angle1 << 16) | angle2; + int i; + + for (i = 0; i < 4; ++i) + { + if (lastskycol[i] == skycol) + { + return skybuf[i]; + } + } + + lastskycol[skycolplace] = skycol; + BYTE *composite = skybuf[skycolplace]; + skycolplace = (skycolplace + 1) & 3; + + // The ordering of the following code has been tuned to allow VC++ to optimize + // it well. In particular, this arrangement lets it keep count in a register + // instead of on the stack. + const BYTE *front = fronttex->GetColumn (angle1, NULL); + const BYTE *back = backskytex->GetColumn (angle2, NULL); + + int count = MIN (512, MIN (backskytex->GetHeight(), fronttex->GetHeight())); + i = 0; + do + { + if (front[i]) + { + composite[i] = front[i]; + } + else + { + composite[i] = back[i]; + } + } while (++i, --count); + return composite; +} + +static void R_DrawSky (visplane_t *pl) +{ + int x; + float swal; + + if (pl->left >= pl->right) + return; + + swal = skyiscale; + for (x = pl->left; x < pl->right; ++x) + { + swall[x] = swal; + } + + if (MirrorFlags & RF_XFLIP) + { + for (x = pl->left; x < pl->right; ++x) + { + lwall[x] = (viewwidth - x) << FRACBITS; + } + } + else + { + for (x = pl->left; x < pl->right; ++x) + { + lwall[x] = x << FRACBITS; + } + } + + for (x = 0; x < 4; ++x) + { + lastskycol[x] = 0xffffffff; + } + + rw_pic = frontskytex; + rw_offset = 0; + + frontyScale = rw_pic->Scale.Y; + dc_texturemid = skymid * frontyScale; + + if (1 << frontskytex->HeightBits == frontskytex->GetHeight()) + { // The texture tiles nicely + for (x = 0; x < 4; ++x) + { + lastskycol[x] = 0xffffffff; + } + wallscan (pl->left, pl->right, (short *)pl->top, (short *)pl->bottom, swall, lwall, + frontyScale, backskytex == NULL ? R_GetOneSkyColumn : R_GetTwoSkyColumns); + } + else + { // The texture does not tile nicely + frontyScale *= skyscale; + frontiScale = 1 / frontyScale; + R_DrawSkyStriped (pl); + } +} + +static void R_DrawSkyStriped (visplane_t *pl) +{ + short drawheight = short(frontskytex->GetHeight() * frontyScale); + double topfrac; + double iscale = frontiScale; + short top[MAXWIDTH], bot[MAXWIDTH]; + short yl, yh; + int x; + + topfrac = fmod(skymid + iscale * (1 - CenterY), frontskytex->GetHeight()); + if (topfrac < 0) topfrac += frontskytex->GetHeight(); + yl = 0; + yh = short((frontskytex->GetHeight() - topfrac) * frontyScale); + dc_texturemid = topfrac - iscale * (1 - CenterY); + + while (yl < viewheight) + { + for (x = pl->left; x < pl->right; ++x) + { + top[x] = MAX (yl, (short)pl->top[x]); + bot[x] = MIN (yh, (short)pl->bottom[x]); + } + for (x = 0; x < 4; ++x) + { + lastskycol[x] = 0xffffffff; + } + wallscan (pl->left, pl->right, top, bot, swall, lwall, rw_pic->Scale.Y, + backskytex == NULL ? R_GetOneSkyColumn : R_GetTwoSkyColumns); + yl = yh; + yh += drawheight; + dc_texturemid = iscale * (centery-yl-1); + } +} + +//========================================================================== +// +// R_DrawPlanes +// +// At the end of each frame. +// +//========================================================================== + +CVAR (Bool, tilt, false, 0); +//CVAR (Int, pa, 0, 0) + +int R_DrawPlanes () +{ + visplane_t *pl; + int i; + int vpcount = 0; + + ds_color = 3; + + for (i = 0; i < MAXVISPLANES; i++) + { + for (pl = visplanes[i]; pl; pl = pl->next) + { + // kg3D - draw only correct planes + if(pl->CurrentPortalUniq != CurrentPortalUniq || pl->CurrentSkybox != CurrentSkybox) + continue; + // kg3D - draw only real planes now + if(pl->sky >= 0) { + vpcount++; + R_DrawSinglePlane (pl, OPAQUE, false, false); + } + } + } + return vpcount; +} + +// kg3D - draw all visplanes with "height" +void R_DrawHeightPlanes(double height) +{ + visplane_t *pl; + int i; + + ds_color = 3; + + DVector3 oViewPos = ViewPos; + DAngle oViewAngle = ViewAngle; + + for (i = 0; i < MAXVISPLANES; i++) + { + for (pl = visplanes[i]; pl; pl = pl->next) + { + // kg3D - draw only correct planes + if(pl->CurrentSkybox != CurrentSkybox || pl->CurrentPortalUniq != CurrentPortalUniq) + continue; + if(pl->sky < 0 && pl->height.Zat0() == height) { + ViewPos = pl->viewpos; + ViewAngle = pl->viewangle; + MirrorFlags = pl->MirrorFlags; + R_DrawSinglePlane (pl, pl->sky & 0x7FFFFFFF, pl->Additive, true); + } + } + } + ViewPos = oViewPos; + ViewAngle = oViewAngle; +} + + +//========================================================================== +// +// R_DrawSinglePlane +// +// Draws a single visplane. +// +//========================================================================== + +void R_DrawSinglePlane (visplane_t *pl, fixed_t alpha, bool additive, bool masked) +{ + if (pl->left >= pl->right) + return; + + if (r_drawflat) + { // [RH] no texture mapping + ds_color += 4; + R_MapVisPlane (pl, R_MapColoredPlane); + } + else if (pl->picnum == skyflatnum) + { // sky flat + R_DrawSkyPlane (pl); + } + else + { // regular flat + FTexture *tex = TexMan(pl->picnum, true); + + if (tex->UseType == FTexture::TEX_Null) + { + return; + } + + if (!masked && !additive) + { // If we're not supposed to see through this plane, draw it opaque. + alpha = OPAQUE; + } + else if (!tex->bMasked) + { // Don't waste time on a masked texture if it isn't really masked. + masked = false; + } + R_SetupSpanBits(tex); + pl->xscale = fixed_t(pl->xscale * tex->Scale.X); + pl->yscale = fixed_t(pl->yscale * tex->Scale.Y); + ds_source = tex->GetPixels (); + + basecolormap = pl->colormap; + planeshade = LIGHT2SHADE(pl->lightlevel); + + if (r_drawflat || (!pl->height.isSlope() && !tilt)) + { + R_DrawNormalPlane (pl, alpha, additive, masked); + } + else + { + R_DrawTiltedPlane (pl, alpha, additive, masked); + } + } + NetUpdate (); +} + +//========================================================================== +// +// R_DrawPortals +// +// Draws any recorded sky boxes and then frees them. +// +// The process: +// 1. Move the camera to coincide with the SkyViewpoint. +// 2. Clear out the old planes. (They have already been drawn.) +// 3. Clear a window out of the ClipSegs just large enough for the plane. +// 4. Pretend the existing vissprites and drawsegs aren't there. +// 5. Create a drawseg at 0 distance to clip sprites to the visplane. It +// doesn't need to be associated with a line in the map, since there +// will never be any sprites in front of it. +// 6. Render the BSP, then planes, then masked stuff. +// 7. Restore the previous vissprites and drawsegs. +// 8. Repeat for any other sky boxes. +// 9. Put the camera back where it was to begin with. +// +//========================================================================== +CVAR (Bool, r_skyboxes, true, 0) +static int numskyboxes; + +void R_DrawPortals () +{ + static TArray interestingStack; + static TArray drawsegStack; + static TArray visspriteStack; + static TArray viewposStack; + static TArray visplaneStack; + + numskyboxes = 0; + + if (visplanes[MAXVISPLANES] == NULL) + return; + + R_3D_EnterSkybox(); + CurrentPortalInSkybox = true; + + int savedextralight = extralight; + DVector3 savedpos = ViewPos; + DAngle savedangle = ViewAngle; + ptrdiff_t savedvissprite_p = vissprite_p - vissprites; + ptrdiff_t savedds_p = ds_p - drawsegs; + ptrdiff_t savedlastopening = lastopening; + size_t savedinteresting = FirstInterestingDrawseg; + double savedvisibility = R_GetVisibility(); + AActor *savedcamera = camera; + sector_t *savedsector = viewsector; + + int i; + visplane_t *pl; + + for (pl = visplanes[MAXVISPLANES]; pl != NULL; pl = visplanes[MAXVISPLANES]) + { + // Pop the visplane off the list now so that if this skybox adds more + // skyboxes to the list, they will be drawn instead of skipped (because + // new skyboxes go to the beginning of the list instead of the end). + visplanes[MAXVISPLANES] = pl->next; + pl->next = NULL; + + if (pl->right < pl->left || !r_skyboxes || numskyboxes == MAX_SKYBOX_PLANES || pl->portal == NULL) + { + R_DrawSinglePlane (pl, OPAQUE, false, false); + *freehead = pl; + freehead = &pl->next; + continue; + } + + numskyboxes++; + + FSectorPortal *port = pl->portal; + switch (port->mType) + { + case PORTS_SKYVIEWPOINT: + { + // Don't let gun flashes brighten the sky box + ASkyViewpoint *sky = barrier_cast(port->mSkybox); + extralight = 0; + R_SetVisibility(sky->args[0] * 0.25f); + + ViewPos = sky->InterpolatedPosition(r_TicFracF); + ViewAngle = savedangle + (sky->PrevAngles.Yaw + deltaangle(sky->PrevAngles.Yaw, sky->Angles.Yaw) * r_TicFracF); + + R_CopyStackedViewParameters(); + break; + } + + case PORTS_STACKEDSECTORTHING: + case PORTS_PORTAL: + case PORTS_LINKEDPORTAL: + extralight = pl->extralight; + R_SetVisibility (pl->visibility); + ViewPos.X = pl->viewpos.X + port->mDisplacement.X; + ViewPos.Y = pl->viewpos.Y + port->mDisplacement.Y; + ViewPos.Z = pl->viewpos.Z; + ViewAngle = pl->viewangle; + break; + + case PORTS_HORIZON: + case PORTS_PLANE: + // not implemented yet + + default: + R_DrawSinglePlane(pl, OPAQUE, false, false); + *freehead = pl; + freehead = &pl->next; + numskyboxes--; + continue; + } + + port->mFlags |= PORTSF_INSKYBOX; + if (port->mPartner > 0) sectorPortals[port->mPartner].mFlags |= PORTSF_INSKYBOX; + camera = NULL; + viewsector = port->mDestination; + assert(viewsector != NULL); + R_SetViewAngle (); + validcount++; // Make sure we see all sprites + + R_ClearPlanes (false); + R_ClearClipSegs (pl->left, pl->right); + WindowLeft = pl->left; + WindowRight = pl->right; + + for (i = pl->left; i < pl->right; i++) + { + if (pl->top[i] == 0x7fff) + { + ceilingclip[i] = viewheight; + floorclip[i] = -1; + } + else + { + ceilingclip[i] = pl->top[i]; + floorclip[i] = pl->bottom[i]; + } + } + + // Create a drawseg to clip sprites to the sky plane + R_CheckDrawSegs (); + ds_p->CurrentPortalUniq = CurrentPortalUniq; + ds_p->siz1 = INT_MAX; + ds_p->siz2 = INT_MAX; + ds_p->sz1 = 0; + ds_p->sz2 = 0; + ds_p->x1 = pl->left; + ds_p->x2 = pl->right; + ds_p->silhouette = SIL_BOTH; + ds_p->sprbottomclip = R_NewOpening (pl->right - pl->left); + ds_p->sprtopclip = R_NewOpening (pl->right - pl->left); + ds_p->maskedtexturecol = ds_p->swall = -1; + ds_p->bFogBoundary = false; + ds_p->curline = NULL; + ds_p->fake = 0; + memcpy (openings + ds_p->sprbottomclip, floorclip + pl->left, (pl->right - pl->left)*sizeof(short)); + memcpy (openings + ds_p->sprtopclip, ceilingclip + pl->left, (pl->right - pl->left)*sizeof(short)); + + firstvissprite = vissprite_p; + firstdrawseg = ds_p++; + FirstInterestingDrawseg = InterestingDrawsegs.Size(); + + interestingStack.Push (FirstInterestingDrawseg); + ptrdiff_t diffnum = firstdrawseg - drawsegs; + drawsegStack.Push (diffnum); + diffnum = firstvissprite - vissprites; + visspriteStack.Push (diffnum); + viewposStack.Push(ViewPos); + visplaneStack.Push (pl); + + InSubsector = NULL; + R_RenderBSPNode (nodes + numnodes - 1); + R_3D_ResetClip(); // reset clips (floor/ceiling) + R_DrawPlanes (); + + port->mFlags &= ~PORTSF_INSKYBOX; + if (port->mPartner > 0) sectorPortals[port->mPartner].mFlags &= ~PORTSF_INSKYBOX; + } + + // Draw all the masked textures in a second pass, in the reverse order they + // were added. This must be done separately from the previous step for the + // sake of nested skyboxes. + while (interestingStack.Pop (FirstInterestingDrawseg)) + { + ptrdiff_t pd = 0; + + drawsegStack.Pop (pd); + firstdrawseg = drawsegs + pd; + visspriteStack.Pop (pd); + firstvissprite = vissprites + pd; + + // Masked textures and planes need the view coordinates restored for proper positioning. + viewposStack.Pop(ViewPos); + + R_DrawMasked (); + + ds_p = firstdrawseg; + vissprite_p = firstvissprite; + + visplaneStack.Pop (pl); + if (pl->Alpha > 0) + { + R_DrawSinglePlane (pl, pl->Alpha, pl->Additive, true); + } + *freehead = pl; + freehead = &pl->next; + } + firstvissprite = vissprites; + vissprite_p = vissprites + savedvissprite_p; + firstdrawseg = drawsegs; + ds_p = drawsegs + savedds_p; + InterestingDrawsegs.Resize ((unsigned int)FirstInterestingDrawseg); + FirstInterestingDrawseg = savedinteresting; + + lastopening = savedlastopening; + + camera = savedcamera; + viewsector = savedsector; + ViewPos = savedpos; + R_SetVisibility(savedvisibility); + extralight = savedextralight; + ViewAngle = savedangle; + R_SetViewAngle (); + + CurrentPortalInSkybox = false; + R_3D_LeaveSkybox(); + + if(fakeActive) return; + + for (*freehead = visplanes[MAXVISPLANES], visplanes[MAXVISPLANES] = NULL; *freehead; ) + freehead = &(*freehead)->next; +} + +ADD_STAT(skyboxes) +{ + FString out; + out.Format ("%d skybox planes", numskyboxes); + return out; +} + +//========================================================================== +// +// R_DrawSkyPlane +// +//========================================================================== + +void R_DrawSkyPlane (visplane_t *pl) +{ + FTextureID sky1tex, sky2tex; + double frontdpos = 0, backdpos = 0; + + if ((level.flags & LEVEL_SWAPSKIES) && !(level.flags & LEVEL_DOUBLESKY)) + { + sky1tex = sky2texture; + } + else + { + sky1tex = sky1texture; + } + sky2tex = sky2texture; + skymid = skytexturemid; + skyangle = ViewAngle.BAMs(); + + if (pl->picnum == skyflatnum) + { + if (!(pl->sky & PL_SKYFLAT)) + { // use sky1 + sky1: + frontskytex = TexMan(sky1tex, true); + if (level.flags & LEVEL_DOUBLESKY) + backskytex = TexMan(sky2tex, true); + else + backskytex = NULL; + skyflip = 0; + frontdpos = sky1pos; + backdpos = sky2pos; + frontcyl = sky1cyl; + backcyl = sky2cyl; + } + else if (pl->sky == PL_SKYFLAT) + { // use sky2 + frontskytex = TexMan(sky2tex, true); + backskytex = NULL; + frontcyl = sky2cyl; + skyflip = 0; + frontdpos = sky2pos; + } + else + { // MBF's linedef-controlled skies + // Sky Linedef + const line_t *l = &lines[(pl->sky & ~PL_SKYFLAT)-1]; + + // Sky transferred from first sidedef + const side_t *s = l->sidedef[0]; + int pos; + + // Texture comes from upper texture of reference sidedef + // [RH] If swapping skies, then use the lower sidedef + if (level.flags & LEVEL_SWAPSKIES && s->GetTexture(side_t::bottom).isValid()) + { + pos = side_t::bottom; + } + else + { + pos = side_t::top; + } + + frontskytex = TexMan(s->GetTexture(pos), true); + if (frontskytex == NULL || frontskytex->UseType == FTexture::TEX_Null) + { // [RH] The blank texture: Use normal sky instead. + goto sky1; + } + backskytex = NULL; + + // Horizontal offset is turned into an angle offset, + // to allow sky rotation as well as careful positioning. + // However, the offset is scaled very small, so that it + // allows a long-period of sky rotation. + skyangle += FLOAT2FIXED(s->GetTextureXOffset(pos)); + + // Vertical offset allows careful sky positioning. + skymid = s->GetTextureYOffset(pos) - 28; + + // We sometimes flip the picture horizontally. + // + // Doom always flipped the picture, so we make it optional, + // to make it easier to use the new feature, while to still + // allow old sky textures to be used. + skyflip = l->args[2] ? 0u : ~0u; + + int frontxscale = int(frontskytex->Scale.X * 1024); + frontcyl = MAX(frontskytex->GetWidth(), frontxscale); + if (skystretch) + { + skymid = skymid * frontskytex->GetScaledHeightDouble() / SKYSTRETCH_HEIGHT; + } + } + } + frontpos = int(fmod(frontdpos, sky1cyl * 65536.0)); + if (backskytex != NULL) + { + backpos = int(fmod(backdpos, sky2cyl * 65536.0)); + } + + bool fakefixed = false; + if (fixedcolormap) + { + dc_colormap = fixedcolormap; + } + else + { + fakefixed = true; + fixedcolormap = dc_colormap = NormalLight.Maps; + } + + R_DrawSky (pl); + + if (fakefixed) + fixedcolormap = NULL; +} + +//========================================================================== +// +// R_DrawNormalPlane +// +//========================================================================== + +void R_DrawNormalPlane (visplane_t *pl, fixed_t alpha, bool additive, bool masked) +{ +#ifdef X86_ASM + if (ds_source != ds_cursource) + { + R_SetSpanSource_ASM (ds_source); + } +#endif + + if (alpha <= 0) + { + return; + } + + angle_t planeang = pl->angle; + xscale = pl->xscale << (16 - ds_xbits); + yscale = pl->yscale << (16 - ds_ybits); + if (planeang != 0) + { + double rad = planeang * (M_PI / ANGLE_180); + double cosine = cos(rad), sine = sin(rad); + + pviewx = xs_RoundToInt(pl->xoffs + FLOAT2FIXED(ViewPos.X * cosine - ViewPos.Y * sine)); + pviewy = xs_RoundToInt(pl->yoffs - FLOAT2FIXED(ViewPos.X * sine - ViewPos.Y * cosine)); + } + else + { + pviewx = pl->xoffs + FLOAT2FIXED(ViewPos.X); + pviewy = pl->yoffs - FLOAT2FIXED(ViewPos.Y); + } + + pviewx = FixedMul (xscale, pviewx); + pviewy = FixedMul (yscale, pviewy); + + // left to right mapping + planeang = (ViewAngle.BAMs() - ANG90 + planeang) >> ANGLETOFINESHIFT; + // Scale will be unit scale at FocalLengthX (normally SCREENWIDTH/2) distance + xstepscale = fixed_t(FixedMul(xscale, finecosine[planeang]) / FocalLengthX); + ystepscale = fixed_t(FixedMul(yscale, -finesine[planeang]) / FocalLengthX); + + // [RH] flip for mirrors + if (MirrorFlags & RF_XFLIP) + { + xstepscale = (DWORD)(-(SDWORD)xstepscale); + ystepscale = (DWORD)(-(SDWORD)ystepscale); + } + + int x = pl->right - halfviewwidth - 1; + planeang = (planeang + (ANG90 >> ANGLETOFINESHIFT)) & FINEMASK; + basexfrac = FixedMul (xscale, finecosine[planeang]) + x*xstepscale; + baseyfrac = FixedMul (yscale, -finesine[planeang]) + x*ystepscale; + + planeheight = fabs(pl->height.Zat0() - ViewPos.Z); + + GlobVis = r_FloorVisibility / planeheight; + if (fixedlightlev >= 0) + ds_colormap = basecolormap->Maps + fixedlightlev, plane_shade = false; + else if (fixedcolormap) + ds_colormap = fixedcolormap, plane_shade = false; + else + plane_shade = true; + + if (spanfunc != R_FillSpan) + { + if (masked) + { + if (alpha < OPAQUE || additive) + { + if (!additive) + { + spanfunc = R_DrawSpanMaskedTranslucent; + dc_srcblend = Col2RGB8[alpha>>10]; + dc_destblend = Col2RGB8[(OPAQUE-alpha)>>10]; + } + else + { + spanfunc = R_DrawSpanMaskedAddClamp; + dc_srcblend = Col2RGB8_LessPrecision[alpha>>10]; + dc_destblend = Col2RGB8_LessPrecision[FRACUNIT>>10]; + } + } + else + { + spanfunc = R_DrawSpanMasked; + } + } + else + { + if (alpha < OPAQUE || additive) + { + if (!additive) + { + spanfunc = R_DrawSpanTranslucent; + dc_srcblend = Col2RGB8[alpha>>10]; + dc_destblend = Col2RGB8[(OPAQUE-alpha)>>10]; + } + else + { + spanfunc = R_DrawSpanAddClamp; + dc_srcblend = Col2RGB8_LessPrecision[alpha>>10]; + dc_destblend = Col2RGB8_LessPrecision[FRACUNIT>>10]; + } + } + else + { + spanfunc = R_DrawSpan; + } + } + } + R_MapVisPlane (pl, R_MapPlane); +} + +//========================================================================== +// +// R_DrawTiltedPlane +// +//========================================================================== + +void R_DrawTiltedPlane (visplane_t *pl, fixed_t alpha, bool additive, bool masked) +{ + static const float ifloatpow2[16] = + { + // ifloatpow2[i] = 1 / (1 << i) + 64.f, 32.f, 16.f, 8.f, 4.f, 2.f, 1.f, 0.5f, + 0.25f, 0.125f, 0.0625f, 0.03125f, 0.015625f, 0.0078125f, + 0.00390625f, 0.001953125f + /*, 0.0009765625f, 0.00048828125f, 0.000244140625f, + 1.220703125e-4f, 6.103515625e-5, 3.0517578125e-5*/ + }; + double lxscale, lyscale; + double xscale, yscale; + FVector3 p, m, n; + double ang; + double zeroheight; + + if (alpha <= 0) + { + return; + } + + lxscale = FIXED2DBL(pl->xscale) * ifloatpow2[ds_xbits]; + lyscale = FIXED2DBL(pl->yscale) * ifloatpow2[ds_ybits]; + xscale = 64.f / lxscale; + yscale = 64.f / lyscale; + zeroheight = pl->height.ZatPoint(ViewPos); + + pviewx = MulScale (pl->xoffs, pl->xscale, ds_xbits); + pviewy = MulScale (pl->yoffs, pl->yscale, ds_ybits); + + // p is the texture origin in view space + // Don't add in the offsets at this stage, because doing so can result in + // errors if the flat is rotated. + ang = (DAngle(270.) - ViewAngle).Radians(); + p[0] = ViewPos.X * cos(ang) - ViewPos.Y * sin(ang); + p[2] = ViewPos.X * sin(ang) + ViewPos.Y * cos(ang); + p[1] = pl->height.ZatPoint(0.0, 0.0) - ViewPos.Z; + + // m is the v direction vector in view space + ang = (DAngle(180.) - ViewAngle).Radians(); + m[0] = yscale * cos(ang); + m[2] = yscale * sin(ang); +// m[1] = pl->height.ZatPointF (0, iyscale) - pl->height.ZatPointF (0,0)); +// VectorScale2 (m, 64.f/VectorLength(m)); + + // n is the u direction vector in view space + ang += PI/2; + n[0] = -xscale * cos(ang); + n[2] = -xscale * sin(ang); +// n[1] = pl->height.ZatPointF (ixscale, 0) - pl->height.ZatPointF (0,0)); +// VectorScale2 (n, 64.f/VectorLength(n)); + + // This code keeps the texture coordinates constant across the x,y plane no matter + // how much you slope the surface. Use the commented-out code above instead to keep + // the textures a constant size across the surface's plane instead. + ang = pl->angle * (M_PI / ANGLE_180); + m[1] = pl->height.ZatPoint(ViewPos.X + yscale * sin(ang), ViewPos.Y + yscale * cos(ang)) - zeroheight; + ang += PI/2; + n[1] = pl->height.ZatPoint(ViewPos.X + xscale * sin(ang), ViewPos.Y + xscale * cos(ang)) - zeroheight; + + plane_su = p ^ m; + plane_sv = p ^ n; + plane_sz = m ^ n; + + plane_su.Z *= FocalLengthX; + plane_sv.Z *= FocalLengthX; + plane_sz.Z *= FocalLengthX; + + plane_su.Y *= IYaspectMul; + plane_sv.Y *= IYaspectMul; + plane_sz.Y *= IYaspectMul; + + // Premultiply the texture vectors with the scale factors + plane_su *= 4294967296.f; + plane_sv *= 4294967296.f; + + if (MirrorFlags & RF_XFLIP) + { + plane_su[0] = -plane_su[0]; + plane_sv[0] = -plane_sv[0]; + plane_sz[0] = -plane_sz[0]; + } + + planelightfloat = (r_TiltVisibility * lxscale * lyscale) / (fabs(pl->height.ZatPoint(ViewPos) - ViewPos.Z)) / 65536.f; + + if (pl->height.fC() > 0) + planelightfloat = -planelightfloat; + + if (fixedlightlev >= 0) + ds_colormap = basecolormap->Maps + fixedlightlev, plane_shade = false; + else if (fixedcolormap) + ds_colormap = fixedcolormap, plane_shade = false; + else + ds_colormap = basecolormap->Maps, plane_shade = true; + + if (!plane_shade) + { + for (int i = 0; i < viewwidth; ++i) + { + tiltlighting[i] = ds_colormap; + } + } + +#if defined(X86_ASM) + if (ds_source != ds_curtiltedsource) + R_SetTiltedSpanSource_ASM (ds_source); + R_MapVisPlane (pl, R_DrawTiltedPlane_ASM); +#else + R_MapVisPlane (pl, R_MapTiltedPlane); +#endif +} + +//========================================================================== +// +// R_MapVisPlane +// +// t1/b1 are at x +// t2/b2 are at x+1 +// spanend[y] is at the right edge +// +//========================================================================== + +void R_MapVisPlane (visplane_t *pl, void (*mapfunc)(int y, int x1)) +{ + int x = pl->right - 1; + int t2 = pl->top[x]; + int b2 = pl->bottom[x]; + + if (b2 > t2) + { + clearbufshort (spanend+t2, b2-t2, x); + } + + for (--x; x >= pl->left; --x) + { + int t1 = pl->top[x]; + int b1 = pl->bottom[x]; + const int xr = x+1; + int stop; + + // Draw any spans that have just closed + stop = MIN (t1, b2); + while (t2 < stop) + { + mapfunc (t2++, xr); + } + stop = MAX (b1, t2); + while (b2 > stop) + { + mapfunc (--b2, xr); + } + + // Mark any spans that have just opened + stop = MIN (t2, b1); + while (t1 < stop) + { + spanend[t1++] = x; + } + stop = MAX (b2, t2); + while (b1 > stop) + { + spanend[--b1] = x; + } + + t2 = pl->top[x]; + b2 = pl->bottom[x]; + basexfrac -= xstepscale; + baseyfrac -= ystepscale; + } + // Draw any spans that are still open + while (t2 < b2) + { + mapfunc (--b2, pl->left); + } +} + +//========================================================================== +// +// R_PlaneInitData +// +//========================================================================== + +bool R_PlaneInitData () +{ + int i; + visplane_t *pl; + + // Free all visplanes and let them be re-allocated as needed. + pl = freetail; + + while (pl) + { + visplane_t *next = pl->next; + M_Free (pl); + pl = next; + } + freetail = NULL; + freehead = &freetail; + + for (i = 0; i < MAXVISPLANES; i++) + { + pl = visplanes[i]; + visplanes[i] = NULL; + while (pl) + { + visplane_t *next = pl->next; + M_Free (pl); + pl = next; + } + } + + return true; +} diff --git a/src/r_segs.cpp b/src/r_segs.cpp new file mode 100644 index 000000000..87b5f15a2 --- /dev/null +++ b/src/r_segs.cpp @@ -0,0 +1,3311 @@ +// Emacs style mode select -*- C++ -*- +//----------------------------------------------------------------------------- +// +// $Id:$ +// +// Copyright (C) 1993-1996 by id Software, Inc. +// +// This source is available for distribution and/or modification +// only under the terms of the DOOM Source Code License as +// published by id Software. All rights reserved. +// +// The source is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License +// for more details. +// +// DESCRIPTION: +// All the clipping: columns, horizontal spans, sky columns. +// +// This file contains some code from the Build Engine. +// +// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman +// Ken Silverman's official web site: "http://www.advsys.net/ken" +// See the included license file "BUILDLIC.TXT" for license info. +// +//----------------------------------------------------------------------------- + +#include +#include + +#include "templates.h" +#include "i_system.h" + +#include "doomdef.h" +#include "doomstat.h" +#include "doomdata.h" +#include "p_lnspec.h" + +#include "r_local.h" +#include "r_sky.h" +#include "v_video.h" + +#include "m_swap.h" +#include "w_wad.h" +#include "stats.h" +#include "a_sharedglobal.h" +#include "d_net.h" +#include "g_level.h" +#include "r_bsp.h" +#include "r_plane.h" +#include "r_segs.h" +#include "r_3dfloors.h" +#include "v_palette.h" +#include "r_data/colormaps.h" + +#define WALLYREPEAT 8 + + +CVAR(Bool, r_np2, true, 0) + +//CVAR (Int, ty, 8, 0) +//CVAR (Int, tx, 8, 0) + +#define HEIGHTBITS 12 +#define HEIGHTSHIFT (FRACBITS-HEIGHTBITS) + +extern double globaluclip, globaldclip; + +PortalDrawseg* CurrentPortal = NULL; +int CurrentPortalUniq = 0; +bool CurrentPortalInSkybox = false; + +// OPTIMIZE: closed two sided lines as single sided + +// killough 1/6/98: replaced globals with statics where appropriate + +static bool segtextured; // True if any of the segs textures might be visible. +bool markfloor; // False if the back side is the same plane. +bool markceiling; +FTexture *toptexture; +FTexture *bottomtexture; +FTexture *midtexture; +fixed_t rw_offset_top; +fixed_t rw_offset_mid; +fixed_t rw_offset_bottom; + + +int wallshade; + +short walltop[MAXWIDTH]; // [RH] record max extents of wall +short wallbottom[MAXWIDTH]; +short wallupper[MAXWIDTH]; +short walllower[MAXWIDTH]; +float swall[MAXWIDTH]; +fixed_t lwall[MAXWIDTH]; +double lwallscale; + +// +// regular wall +// +extern double rw_backcz1, rw_backcz2; +extern double rw_backfz1, rw_backfz2; +extern double rw_frontcz1, rw_frontcz2; +extern double rw_frontfz1, rw_frontfz2; + +int rw_ceilstat, rw_floorstat; +bool rw_mustmarkfloor, rw_mustmarkceiling; +bool rw_prepped; +bool rw_markportal; +bool rw_havehigh; +bool rw_havelow; + +float rw_light; // [RH] Scale lights with viewsize adjustments +float rw_lightstep; +float rw_lightleft; + +static double rw_frontlowertop; + +static int rw_x; +static int rw_stopx; +fixed_t rw_offset; +static double rw_scalestep; +static double rw_midtexturemid; +static double rw_toptexturemid; +static double rw_bottomtexturemid; +static double rw_midtexturescalex; +static double rw_midtexturescaley; +static double rw_toptexturescalex; +static double rw_toptexturescaley; +static double rw_bottomtexturescalex; +static double rw_bottomtexturescaley; + +FTexture *rw_pic; + +static fixed_t *maskedtexturecol; + +static void R_RenderDecal (side_t *wall, DBaseDecal *first, drawseg_t *clipper, int pass); +static void WallSpriteColumn (void (*drawfunc)(const BYTE *column, const FTexture::Span *spans)); +void wallscan_np2(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, double top, double bot, bool mask); +static void wallscan_np2_ds(drawseg_t *ds, int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat); +static void call_wallscan(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, bool mask); + +//============================================================================= +// +// CVAR r_fogboundary +// +// If true, makes fog look more "real" by shading the walls separating two +// sectors with different fog. +//============================================================================= + +CVAR(Bool, r_fogboundary, true, 0) + +inline bool IsFogBoundary (sector_t *front, sector_t *back) +{ + return r_fogboundary && fixedcolormap == NULL && front->ColorMap->Fade && + front->ColorMap->Fade != back->ColorMap->Fade && + (front->GetTexture(sector_t::ceiling) != skyflatnum || back->GetTexture(sector_t::ceiling) != skyflatnum); +} + +//============================================================================= +// +// CVAR r_drawmirrors +// +// Set to false to disable rendering of mirrors +//============================================================================= + +CVAR(Bool, r_drawmirrors, true, 0) + +// +// R_RenderMaskedSegRange +// +float *MaskedSWall; +float MaskedScaleY; + +static void BlastMaskedColumn (void (*blastfunc)(const BYTE *pixels, const FTexture::Span *spans), FTexture *tex) +{ + // calculate lighting + if (fixedcolormap == NULL && fixedlightlev < 0) + { + dc_colormap = basecolormap->Maps + (GETPALOOKUP (rw_light, wallshade) << COLORMAPSHIFT); + } + + dc_iscale = xs_Fix<16>::ToFix(MaskedSWall[dc_x] * MaskedScaleY); + if (sprflipvert) + sprtopscreen = CenterY + dc_texturemid * spryscale; + else + sprtopscreen = CenterY - dc_texturemid * spryscale; + + // killough 1/25/98: here's where Medusa came in, because + // it implicitly assumed that the column was all one patch. + // Originally, Doom did not construct complete columns for + // multipatched textures, so there were no header or trailer + // bytes in the column referred to below, which explains + // the Medusa effect. The fix is to construct true columns + // when forming multipatched textures (see r_data.c). + + // draw the texture + const FTexture::Span *spans; + const BYTE *pixels = tex->GetColumn (maskedtexturecol[dc_x] >> FRACBITS, &spans); + blastfunc (pixels, spans); + rw_light += rw_lightstep; + spryscale += rw_scalestep; +} + +// Clip a midtexture to the floor and ceiling of the sector in front of it. +void ClipMidtex(int x1, int x2) +{ + short most[MAXWIDTH]; + + WallMost(most, curline->frontsector->ceilingplane, &WallC); + for (int i = x1; i < x2; ++i) + { + if (wallupper[i] < most[i]) + wallupper[i] = most[i]; + } + WallMost(most, curline->frontsector->floorplane, &WallC); + for (int i = x1; i < x2; ++i) + { + if (walllower[i] > most[i]) + walllower[i] = most[i]; + } +} + +void R_RenderFakeWallRange(drawseg_t *ds, int x1, int x2); + +void R_RenderMaskedSegRange (drawseg_t *ds, int x1, int x2) +{ + FTexture *tex; + int i; + sector_t tempsec; // killough 4/13/98 + double texheight, texheightscale; + bool notrelevant = false; + double rowoffset; + + const sector_t *sec; + + sprflipvert = false; + + curline = ds->curline; + + // killough 4/11/98: draw translucent 2s normal textures + // [RH] modified because we don't use user-definable translucency maps + ESPSResult drawmode; + + drawmode = R_SetPatchStyle (LegacyRenderStyles[curline->linedef->flags & ML_ADDTRANS ? STYLE_Add : STYLE_Translucent], + (float)MIN(curline->linedef->alpha, 1.), 0, 0); + + if ((drawmode == DontDraw && !ds->bFogBoundary && !ds->bFakeBoundary)) + { + return; + } + + NetUpdate (); + + frontsector = curline->frontsector; + backsector = curline->backsector; + + tex = TexMan(curline->sidedef->GetTexture(side_t::mid), true); + if (i_compatflags & COMPATF_MASKEDMIDTEX) + { + tex = tex->GetRawTexture(); + } + + // killough 4/13/98: get correct lightlevel for 2s normal textures + sec = R_FakeFlat (frontsector, &tempsec, NULL, NULL, false); + + basecolormap = sec->ColorMap; // [RH] Set basecolormap + + wallshade = ds->shade; + rw_lightstep = ds->lightstep; + rw_light = ds->light + (x1 - ds->x1) * rw_lightstep; + + if (fixedlightlev < 0) + { + if (!(fake3D & FAKE3D_CLIPTOP)) + { + sclipTop = sec->ceilingplane.ZatPoint(ViewPos); + } + for (i = frontsector->e->XFloor.lightlist.Size() - 1; i >= 0; i--) + { + if (sclipTop <= frontsector->e->XFloor.lightlist[i].plane.Zat0()) + { + lightlist_t *lit = &frontsector->e->XFloor.lightlist[i]; + basecolormap = lit->extra_colormap; + wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(foggy, *lit->p_lightlevel, lit->lightsource != NULL) + r_actualextralight); + break; + } + } + } + + mfloorclip = openings + ds->sprbottomclip - ds->x1; + mceilingclip = openings + ds->sprtopclip - ds->x1; + + // [RH] Draw fog partition + if (ds->bFogBoundary) + { + R_DrawFogBoundary (x1, x2, mceilingclip, mfloorclip); + if (ds->maskedtexturecol == -1) + { + goto clearfog; + } + } + if ((ds->bFakeBoundary && !(ds->bFakeBoundary & 4)) || drawmode == DontDraw) + { + goto clearfog; + } + + MaskedSWall = (float *)(openings + ds->swall) - ds->x1; + MaskedScaleY = ds->yscale; + maskedtexturecol = (fixed_t *)(openings + ds->maskedtexturecol) - ds->x1; + spryscale = ds->iscale + ds->iscalestep * (x1 - ds->x1); + rw_scalestep = ds->iscalestep; + + if (fixedlightlev >= 0) + dc_colormap = basecolormap->Maps + fixedlightlev; + else if (fixedcolormap != NULL) + dc_colormap = fixedcolormap; + + // find positioning + texheight = tex->GetScaledHeightDouble(); + texheightscale = fabs(curline->sidedef->GetTextureYScale(side_t::mid)); + if (texheightscale != 1) + { + texheight = texheight / texheightscale; + } + if (curline->linedef->flags & ML_DONTPEGBOTTOM) + { + dc_texturemid = MAX(frontsector->GetPlaneTexZ(sector_t::floor), backsector->GetPlaneTexZ(sector_t::floor)) + texheight; + } + else + { + dc_texturemid = MIN(frontsector->GetPlaneTexZ(sector_t::ceiling), backsector->GetPlaneTexZ(sector_t::ceiling)); + } + + rowoffset = curline->sidedef->GetTextureYOffset(side_t::mid); + + if (!(curline->linedef->flags & ML_WRAP_MIDTEX) && + !(curline->sidedef->Flags & WALLF_WRAP_MIDTEX)) + { // Texture does not wrap vertically. + double textop; + + if (MaskedScaleY < 0) + { + MaskedScaleY = -MaskedScaleY; + sprflipvert = true; + } + if (tex->bWorldPanning) + { + // rowoffset is added before the multiply so that the masked texture will + // still be positioned in world units rather than texels. + dc_texturemid += rowoffset - ViewPos.Z; + textop = dc_texturemid; + dc_texturemid *= MaskedScaleY; + } + else + { + // rowoffset is added outside the multiply so that it positions the texture + // by texels instead of world units. + textop = dc_texturemid + rowoffset / MaskedScaleY - ViewPos.Z; + dc_texturemid = (dc_texturemid - ViewPos.Z) * MaskedScaleY + rowoffset; + } + if (sprflipvert) + { + MaskedScaleY = -MaskedScaleY; + dc_texturemid -= tex->GetHeight() << FRACBITS; + } + + // [RH] Don't bother drawing segs that are completely offscreen + if (globaldclip * ds->sz1 < -textop && globaldclip * ds->sz2 < -textop) + { // Texture top is below the bottom of the screen + goto clearfog; + } + + if (globaluclip * ds->sz1 > texheight - textop && globaluclip * ds->sz2 > texheight - textop) + { // Texture bottom is above the top of the screen + goto clearfog; + } + + if ((fake3D & FAKE3D_CLIPBOTTOM) && textop < sclipBottom - ViewPos.Z) + { + notrelevant = true; + goto clearfog; + } + if ((fake3D & FAKE3D_CLIPTOP) && textop - texheight > sclipTop - ViewPos.Z) + { + notrelevant = true; + goto clearfog; + } + + WallC.sz1 = ds->sz1; + WallC.sz2 = ds->sz2; + WallC.sx1 = ds->sx1; + WallC.sx2 = ds->sx2; + + if (fake3D & FAKE3D_CLIPTOP) + { + OWallMost(wallupper, textop < sclipTop - ViewPos.Z ? textop : sclipTop - ViewPos.Z, &WallC); + } + else + { + OWallMost(wallupper, textop, &WallC); + } + if (fake3D & FAKE3D_CLIPBOTTOM) + { + OWallMost(walllower, textop - texheight > sclipBottom - ViewPos.Z ? textop - texheight : sclipBottom - ViewPos.Z, &WallC); + } + else + { + OWallMost(walllower, textop - texheight, &WallC); + } + + for (i = x1; i < x2; i++) + { + if (wallupper[i] < mceilingclip[i]) + wallupper[i] = mceilingclip[i]; + } + for (i = x1; i < x2; i++) + { + if (walllower[i] > mfloorclip[i]) + walllower[i] = mfloorclip[i]; + } + + if (CurrentSkybox) + { // Midtex clipping doesn't work properly with skyboxes, since you're normally below the floor + // or above the ceiling, so the appropriate end won't be clipped automatically when adding + // this drawseg. + if ((curline->linedef->flags & ML_CLIP_MIDTEX) || + (curline->sidedef->Flags & WALLF_CLIP_MIDTEX)) + { + ClipMidtex(x1, x2); + } + } + + mfloorclip = walllower; + mceilingclip = wallupper; + + // draw the columns one at a time + if (drawmode == DoDraw0) + { + for (dc_x = x1; dc_x < x2; ++dc_x) + { + BlastMaskedColumn (R_DrawMaskedColumn, tex); + } + } + else + { + // [RH] Draw up to four columns at once + int stop = x2 & ~3; + + if (x1 >= x2) + goto clearfog; + + dc_x = x1; + + while ((dc_x < stop) && (dc_x & 3)) + { + BlastMaskedColumn (R_DrawMaskedColumn, tex); + dc_x++; + } + + while (dc_x < stop) + { + rt_initcols(); + BlastMaskedColumn (R_DrawMaskedColumnHoriz, tex); dc_x++; + BlastMaskedColumn (R_DrawMaskedColumnHoriz, tex); dc_x++; + BlastMaskedColumn (R_DrawMaskedColumnHoriz, tex); dc_x++; + BlastMaskedColumn (R_DrawMaskedColumnHoriz, tex); + rt_draw4cols (dc_x - 3); + dc_x++; + } + + while (dc_x < x2) + { + BlastMaskedColumn (R_DrawMaskedColumn, tex); + dc_x++; + } + } + } + else + { // Texture does wrap vertically. + if (tex->bWorldPanning) + { + // rowoffset is added before the multiply so that the masked texture will + // still be positioned in world units rather than texels. + dc_texturemid = (dc_texturemid + rowoffset - ViewPos.Z) * MaskedScaleY; + } + else + { + // rowoffset is added outside the multiply so that it positions the texture + // by texels instead of world units. + dc_texturemid = (dc_texturemid - ViewPos.Z) * MaskedScaleY + rowoffset; + } + + WallC.sz1 = ds->sz1; + WallC.sz2 = ds->sz2; + WallC.sx1 = ds->sx1; + WallC.sx2 = ds->sx2; + + if (CurrentSkybox) + { // Midtex clipping doesn't work properly with skyboxes, since you're normally below the floor + // or above the ceiling, so the appropriate end won't be clipped automatically when adding + // this drawseg. + if ((curline->linedef->flags & ML_CLIP_MIDTEX) || + (curline->sidedef->Flags & WALLF_CLIP_MIDTEX)) + { + ClipMidtex(x1, x2); + } + } + + if (fake3D & FAKE3D_CLIPTOP) + { + OWallMost(wallupper, sclipTop - ViewPos.Z, &WallC); + for (i = x1; i < x2; i++) + { + if (wallupper[i] < mceilingclip[i]) + wallupper[i] = mceilingclip[i]; + } + mceilingclip = wallupper; + } + if (fake3D & FAKE3D_CLIPBOTTOM) + { + OWallMost(walllower, sclipBottom - ViewPos.Z, &WallC); + for (i = x1; i < x2; i++) + { + if (walllower[i] > mfloorclip[i]) + walllower[i] = mfloorclip[i]; + } + mfloorclip = walllower; + } + + rw_offset = 0; + rw_pic = tex; + wallscan_np2_ds(ds, x1, x2, mceilingclip, mfloorclip, MaskedSWall, maskedtexturecol, ds->yscale); + } + +clearfog: + R_FinishSetPatchStyle (); + if (ds->bFakeBoundary & 3) + { + R_RenderFakeWallRange(ds, x1, x2); + } + if (!notrelevant) + { + if (fake3D & FAKE3D_REFRESHCLIP) + { + assert(ds->bkup >= 0); + memcpy(openings + ds->sprtopclip, openings + ds->bkup, (ds->x2 - ds->x1) * 2); + } + else + { + clearbufshort(openings + ds->sprtopclip - ds->x1 + x1, x2 - x1, viewheight); + } + } + return; +} + +// kg3D - render one fake wall +void R_RenderFakeWall(drawseg_t *ds, int x1, int x2, F3DFloor *rover) +{ + int i; + double xscale; + double yscale; + + fixed_t Alpha = Scale(rover->alpha, OPAQUE, 255); + ESPSResult drawmode; + drawmode = R_SetPatchStyle (LegacyRenderStyles[rover->flags & FF_ADDITIVETRANS ? STYLE_Add : STYLE_Translucent], + Alpha, 0, 0); + + if(drawmode == DontDraw) { + R_FinishSetPatchStyle(); + return; + } + + rw_lightstep = ds->lightstep; + rw_light = ds->light + (x1 - ds->x1) * rw_lightstep; + + mfloorclip = openings + ds->sprbottomclip - ds->x1; + mceilingclip = openings + ds->sprtopclip - ds->x1; + + spryscale = ds->iscale + ds->iscalestep * (x1 - ds->x1); + rw_scalestep = ds->iscalestep; + MaskedSWall = (float *)(openings + ds->swall) - ds->x1; + + // find positioning + side_t *scaledside; + side_t::ETexpart scaledpart; + if (rover->flags & FF_UPPERTEXTURE) + { + scaledside = curline->sidedef; + scaledpart = side_t::top; + } + else if (rover->flags & FF_LOWERTEXTURE) + { + scaledside = curline->sidedef; + scaledpart = side_t::bottom; + } + else + { + scaledside = rover->master->sidedef[0]; + scaledpart = side_t::mid; + } + xscale = rw_pic->Scale.X * scaledside->GetTextureXScale(scaledpart); + yscale = rw_pic->Scale.Y * scaledside->GetTextureYScale(scaledpart); + + double rowoffset = curline->sidedef->GetTextureYOffset(side_t::mid) + rover->master->sidedef[0]->GetTextureYOffset(side_t::mid); + double planez = rover->model->GetPlaneTexZ(sector_t::ceiling); + rw_offset = FLOAT2FIXED(curline->sidedef->GetTextureXOffset(side_t::mid) + rover->master->sidedef[0]->GetTextureXOffset(side_t::mid)); + if (rowoffset < 0) + { + rowoffset += rw_pic->GetHeight(); + } + dc_texturemid = (planez - ViewPos.Z) * yscale; + if (rw_pic->bWorldPanning) + { + // rowoffset is added before the multiply so that the masked texture will + // still be positioned in world units rather than texels. + + dc_texturemid = dc_texturemid + rowoffset * yscale; + rw_offset = xs_RoundToInt(rw_offset * xscale); + } + else + { + // rowoffset is added outside the multiply so that it positions the texture + // by texels instead of world units. + dc_texturemid += rowoffset; + } + + if (fixedlightlev >= 0) + dc_colormap = basecolormap->Maps + fixedlightlev; + else if (fixedcolormap != NULL) + dc_colormap = fixedcolormap; + + WallC.sz1 = ds->sz1; + WallC.sz2 = ds->sz2; + WallC.sx1 = ds->sx1; + WallC.sx2 = ds->sx2; + WallC.tleft.X = ds->cx; + WallC.tleft.Y = ds->cy; + WallC.tright.X = ds->cx + ds->cdx; + WallC.tright.Y = ds->cy + ds->cdy; + WallT = ds->tmapvals; + + OWallMost(wallupper, sclipTop - ViewPos.Z, &WallC); + OWallMost(walllower, sclipBottom - ViewPos.Z, &WallC); + + for (i = x1; i < x2; i++) + { + if (wallupper[i] < mceilingclip[i]) + wallupper[i] = mceilingclip[i]; + } + for (i = x1; i < x2; i++) + { + if (walllower[i] > mfloorclip[i]) + walllower[i] = mfloorclip[i]; + } + + PrepLWall (lwall, curline->sidedef->TexelLength*xscale, ds->sx1, ds->sx2); + wallscan_np2_ds(ds, x1, x2, wallupper, walllower, MaskedSWall, lwall, yscale); + R_FinishSetPatchStyle(); +} + +// kg3D - walls of fake floors +void R_RenderFakeWallRange (drawseg_t *ds, int x1, int x2) +{ + FTexture *const DONT_DRAW = ((FTexture*)(intptr_t)-1); + int i,j; + F3DFloor *rover, *fover = NULL; + int passed, last; + double floorHeight; + double ceilingHeight; + + sprflipvert = false; + curline = ds->curline; + + frontsector = curline->frontsector; + backsector = curline->backsector; + + if (backsector == NULL) + { + return; + } + if ((ds->bFakeBoundary & 3) == 2) + { + sector_t *sec = backsector; + backsector = frontsector; + frontsector = sec; + } + + floorHeight = backsector->CenterFloor(); + ceilingHeight = backsector->CenterCeiling(); + + // maybe fix clipheights + if (!(fake3D & FAKE3D_CLIPBOTTOM)) sclipBottom = floorHeight; + if (!(fake3D & FAKE3D_CLIPTOP)) sclipTop = ceilingHeight; + + // maybe not visible + if (sclipBottom >= frontsector->CenterCeiling()) return; + if (sclipTop <= frontsector->CenterFloor()) return; + + if (fake3D & FAKE3D_DOWN2UP) + { // bottom to viewz + last = 0; + for (i = backsector->e->XFloor.ffloors.Size() - 1; i >= 0; i--) + { + rover = backsector->e->XFloor.ffloors[i]; + if (!(rover->flags & FF_EXISTS)) continue; + + // visible? + passed = 0; + if (!(rover->flags & FF_RENDERSIDES) || rover->top.plane->isSlope() || rover->bottom.plane->isSlope() || + rover->top.plane->Zat0() <= sclipBottom || + rover->bottom.plane->Zat0() >= ceilingHeight || + rover->top.plane->Zat0() <= floorHeight) + { + if (!i) + { + passed = 1; + } + else + { + continue; + } + } + + rw_pic = NULL; + if (rover->bottom.plane->Zat0() >= sclipTop || passed) + { + if (last) + { + break; + } + // maybe wall from inside rendering? + fover = NULL; + for (j = frontsector->e->XFloor.ffloors.Size() - 1; j >= 0; j--) + { + fover = frontsector->e->XFloor.ffloors[j]; + if (fover->model == rover->model) + { // never + fover = NULL; + break; + } + if (!(fover->flags & FF_EXISTS)) continue; + if (!(fover->flags & FF_RENDERSIDES)) continue; + // no sloped walls, it's bugged + if (fover->top.plane->isSlope() || fover->bottom.plane->isSlope()) continue; + + // visible? + if (fover->top.plane->Zat0() <= sclipBottom) continue; // no + if (fover->bottom.plane->Zat0() >= sclipTop) + { // no, last possible + fover = NULL; + break; + } + // it is, render inside? + if (!(fover->flags & (FF_BOTHPLANES|FF_INVERTPLANES))) + { // no + fover = NULL; + } + break; + } + // nothing + if (!fover || j == -1) + { + break; + } + // correct texture + if (fover->flags & rover->flags & FF_SWIMMABLE) + { // don't ever draw (but treat as something has been found) + rw_pic = DONT_DRAW; + } + else if(fover->flags & FF_UPPERTEXTURE) + { + rw_pic = TexMan(curline->sidedef->GetTexture(side_t::top), true); + } + else if(fover->flags & FF_LOWERTEXTURE) + { + rw_pic = TexMan(curline->sidedef->GetTexture(side_t::bottom), true); + } + else + { + rw_pic = TexMan(fover->master->sidedef[0]->GetTexture(side_t::mid), true); + } + } + else if (frontsector->e->XFloor.ffloors.Size()) + { + // maybe not visible? + fover = NULL; + for (j = frontsector->e->XFloor.ffloors.Size() - 1; j >= 0; j--) + { + fover = frontsector->e->XFloor.ffloors[j]; + if (fover->model == rover->model) // never + { + break; + } + if (!(fover->flags & FF_EXISTS)) continue; + if (!(fover->flags & FF_RENDERSIDES)) continue; + // no sloped walls, it's bugged + if (fover->top.plane->isSlope() || fover->bottom.plane->isSlope()) continue; + + // visible? + if (fover->top.plane->Zat0() <= sclipBottom) continue; // no + if (fover->bottom.plane->Zat0() >= sclipTop) + { // visible, last possible + fover = NULL; + break; + } + if ((fover->flags & FF_SOLID) == (rover->flags & FF_SOLID) && + !(!(fover->flags & FF_SOLID) && (fover->alpha == 255 || rover->alpha == 255)) + ) + { + break; + } + if (fover->flags & rover->flags & FF_SWIMMABLE) + { // don't ever draw (but treat as something has been found) + rw_pic = DONT_DRAW; + } + fover = NULL; // visible + break; + } + if (fover && j != -1) + { + fover = NULL; + last = 1; + continue; // not visible + } + } + if (!rw_pic) + { + fover = NULL; + if (rover->flags & FF_UPPERTEXTURE) + { + rw_pic = TexMan(curline->sidedef->GetTexture(side_t::top), true); + } + else if(rover->flags & FF_LOWERTEXTURE) + { + rw_pic = TexMan(curline->sidedef->GetTexture(side_t::bottom), true); + } + else + { + rw_pic = TexMan(rover->master->sidedef[0]->GetTexture(side_t::mid), true); + } + } + // correct colors now + basecolormap = frontsector->ColorMap; + wallshade = ds->shade; + if (fixedlightlev < 0) + { + if ((ds->bFakeBoundary & 3) == 2) + { + for (j = backsector->e->XFloor.lightlist.Size() - 1; j >= 0; j--) + { + if (sclipTop <= backsector->e->XFloor.lightlist[j].plane.Zat0()) + { + lightlist_t *lit = &backsector->e->XFloor.lightlist[j]; + basecolormap = lit->extra_colormap; + wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(foggy, *lit->p_lightlevel, lit->lightsource != NULL) + r_actualextralight); + break; + } + } + } + else + { + for (j = frontsector->e->XFloor.lightlist.Size() - 1; j >= 0; j--) + { + if (sclipTop <= frontsector->e->XFloor.lightlist[j].plane.Zat0()) + { + lightlist_t *lit = &frontsector->e->XFloor.lightlist[j]; + basecolormap = lit->extra_colormap; + wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(foggy, *lit->p_lightlevel, lit->lightsource != NULL) + r_actualextralight); + break; + } + } + } + } + if (rw_pic != DONT_DRAW) + { + R_RenderFakeWall(ds, x1, x2, fover ? fover : rover); + } + else rw_pic = NULL; + break; + } + } + else + { // top to viewz + for (i = 0; i < (int)backsector->e->XFloor.ffloors.Size(); i++) + { + rover = backsector->e->XFloor.ffloors[i]; + if (!(rover->flags & FF_EXISTS)) continue; + + // visible? + passed = 0; + if (!(rover->flags & FF_RENDERSIDES) || + rover->top.plane->isSlope() || rover->bottom.plane->isSlope() || + rover->bottom.plane->Zat0() >= sclipTop || + rover->top.plane->Zat0() <= floorHeight || + rover->bottom.plane->Zat0() >= ceilingHeight) + { + if ((unsigned)i == backsector->e->XFloor.ffloors.Size() - 1) + { + passed = 1; + } + else + { + continue; + } + } + rw_pic = NULL; + if (rover->top.plane->Zat0() <= sclipBottom || passed) + { // maybe wall from inside rendering? + fover = NULL; + for (j = 0; j < (int)frontsector->e->XFloor.ffloors.Size(); j++) + { + fover = frontsector->e->XFloor.ffloors[j]; + if (fover->model == rover->model) + { // never + fover = NULL; + break; + } + if (!(fover->flags & FF_EXISTS)) continue; + if (!(fover->flags & FF_RENDERSIDES)) continue; + // no sloped walls, it's bugged + if (fover->top.plane->isSlope() || fover->bottom.plane->isSlope()) continue; + + // visible? + if (fover->bottom.plane->Zat0() >= sclipTop) continue; // no + if (fover->top.plane->Zat0() <= sclipBottom) + { // no, last possible + fover = NULL; + break; + } + // it is, render inside? + if (!(fover->flags & (FF_BOTHPLANES|FF_INVERTPLANES))) + { // no + fover = NULL; + } + break; + } + // nothing + if (!fover || (unsigned)j == frontsector->e->XFloor.ffloors.Size()) + { + break; + } + // correct texture + if (fover->flags & rover->flags & FF_SWIMMABLE) + { + rw_pic = DONT_DRAW; // don't ever draw (but treat as something has been found) + } + else if (fover->flags & FF_UPPERTEXTURE) + { + rw_pic = TexMan(curline->sidedef->GetTexture(side_t::top), true); + } + else if (fover->flags & FF_LOWERTEXTURE) + { + rw_pic = TexMan(curline->sidedef->GetTexture(side_t::bottom), true); + } + else + { + rw_pic = TexMan(fover->master->sidedef[0]->GetTexture(side_t::mid), true); + } + } + else if (frontsector->e->XFloor.ffloors.Size()) + { // maybe not visible? + fover = NULL; + for (j = 0; j < (int)frontsector->e->XFloor.ffloors.Size(); j++) + { + fover = frontsector->e->XFloor.ffloors[j]; + if (fover->model == rover->model) + { // never + break; + } + if (!(fover->flags & FF_EXISTS)) continue; + if (!(fover->flags & FF_RENDERSIDES)) continue; + // no sloped walls, its bugged + if (fover->top.plane->isSlope() || fover->bottom.plane->isSlope()) continue; + + // visible? + if (fover->bottom.plane->Zat0() >= sclipTop) continue; // no + if (fover->top.plane->Zat0() <= sclipBottom) + { // visible, last possible + fover = NULL; + break; + } + if ((fover->flags & FF_SOLID) == (rover->flags & FF_SOLID) && + !(!(rover->flags & FF_SOLID) && (fover->alpha == 255 || rover->alpha == 255)) + ) + { + break; + } + if (fover->flags & rover->flags & FF_SWIMMABLE) + { // don't ever draw (but treat as something has been found) + rw_pic = DONT_DRAW; + } + fover = NULL; // visible + break; + } + if (fover && (unsigned)j != frontsector->e->XFloor.ffloors.Size()) + { // not visible + break; + } + } + if (rw_pic == NULL) + { + fover = NULL; + if (rover->flags & FF_UPPERTEXTURE) + { + rw_pic = TexMan(curline->sidedef->GetTexture(side_t::top), true); + } + else if (rover->flags & FF_LOWERTEXTURE) + { + rw_pic = TexMan(curline->sidedef->GetTexture(side_t::bottom), true); + } + else + { + rw_pic = TexMan(rover->master->sidedef[0]->GetTexture(side_t::mid), true); + } + } + // correct colors now + basecolormap = frontsector->ColorMap; + wallshade = ds->shade; + if (fixedlightlev < 0) + { + if ((ds->bFakeBoundary & 3) == 2) + { + for (j = backsector->e->XFloor.lightlist.Size() - 1; j >= 0; j--) + { + if (sclipTop <= backsector->e->XFloor.lightlist[j].plane.Zat0()) + { + lightlist_t *lit = &backsector->e->XFloor.lightlist[j]; + basecolormap = lit->extra_colormap; + wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(foggy, *lit->p_lightlevel, lit->lightsource != NULL) + r_actualextralight); + break; + } + } + } + else + { + for (j = frontsector->e->XFloor.lightlist.Size() - 1; j >= 0; j--) + { + if(sclipTop <= frontsector->e->XFloor.lightlist[j].plane.Zat0()) + { + lightlist_t *lit = &frontsector->e->XFloor.lightlist[j]; + basecolormap = lit->extra_colormap; + wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(foggy, *lit->p_lightlevel, lit->lightsource != NULL) + r_actualextralight); + break; + } + } + } + } + + if (rw_pic != DONT_DRAW) + { + R_RenderFakeWall(ds, x1, x2, fover ? fover : rover); + } + else + { + rw_pic = NULL; + } + break; + } + } + return; +} + +// prevlineasm1 is like vlineasm1 but skips the loop if only drawing one pixel +inline fixed_t prevline1 (fixed_t vince, BYTE *colormap, int count, fixed_t vplce, const BYTE *bufplce, BYTE *dest) +{ + dc_iscale = vince; + dc_colormap = colormap; + dc_count = count; + dc_texturefrac = vplce; + dc_source = bufplce; + dc_dest = dest; + return doprevline1 (); +} + +void wallscan (int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, + double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x)) +{ + int x, fracbits; + int y1ve[4], y2ve[4], u4, d4, z; + char bad; + float light = rw_light - rw_lightstep; + SDWORD xoffset; + BYTE *basecolormapdata; + double iscale; + + // This function also gets used to draw skies. Unlike BUILD, skies are + // drawn by visplane instead of by bunch, so these checks are invalid. + //if ((uwal[x1] > viewheight) && (uwal[x2] > viewheight)) return; + //if ((dwal[x1] < 0) && (dwal[x2] < 0)) return; + + if (rw_pic->UseType == FTexture::TEX_Null) + { + return; + } + +//extern cycle_t WallScanCycles; +//clock (WallScanCycles); + + rw_pic->GetHeight(); // Make sure texture size is loaded + fracbits = 32 - rw_pic->HeightBits; + setupvline(fracbits); + xoffset = rw_offset; + basecolormapdata = basecolormap->Maps; + + x = x1; + //while ((umost[x] > dmost[x]) && (x < x2)) x++; + + bool fixed = (fixedcolormap != NULL || fixedlightlev >= 0); + if (fixed) + { + palookupoffse[0] = dc_colormap; + palookupoffse[1] = dc_colormap; + palookupoffse[2] = dc_colormap; + palookupoffse[3] = dc_colormap; + } + + for(; (x < x2) && (x & 3); ++x) + { + light += rw_lightstep; + y1ve[0] = uwal[x];//max(uwal[x],umost[x]); + y2ve[0] = dwal[x];//min(dwal[x],dmost[x]); + if (y2ve[0] <= y1ve[0]) continue; + assert (y1ve[0] < viewheight); + assert (y2ve[0] <= viewheight); + + if (!fixed) + { // calculate lighting + dc_colormap = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); + } + + dc_source = getcol (rw_pic, (lwal[x] + xoffset) >> FRACBITS); + dc_dest = ylookup[y1ve[0]] + x + dc_destorg; + dc_count = y2ve[0] - y1ve[0]; + iscale = swal[x] * yrepeat; + dc_iscale = xs_ToFixed(fracbits, iscale); + dc_texturefrac = xs_ToFixed(fracbits, dc_texturemid + iscale * (y1ve[0] - CenterY + 1)); + + dovline1(); + } + + for(; x < x2-3; x += 4) + { + bad = 0; + for (z = 3; z>= 0; --z) + { + y1ve[z] = uwal[x+z];//max(uwal[x+z],umost[x+z]); + y2ve[z] = dwal[x+z];//min(dwal[x+z],dmost[x+z])-1; + if (y2ve[z] <= y1ve[z]) { bad += 1<> FRACBITS); + iscale = swal[x + z] * yrepeat; + vince[z] = xs_ToFixed(fracbits, iscale); + vplce[z] = xs_ToFixed(fracbits, dc_texturemid + iscale * (y1ve[z] - CenterY + 1)); + } + if (bad == 15) + { + light += rw_lightstep * 4; + continue; + } + + if (!fixed) + { + for (z = 0; z < 4; ++z) + { + light += rw_lightstep; + palookupoffse[z] = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); + } + } + + u4 = MAX(MAX(y1ve[0],y1ve[1]),MAX(y1ve[2],y1ve[3])); + d4 = MIN(MIN(y2ve[0],y2ve[1]),MIN(y2ve[2],y2ve[3])); + + if ((bad != 0) || (u4 >= d4)) + { + for (z = 0; z < 4; ++z) + { + if (!(bad & 1)) + { + prevline1(vince[z],palookupoffse[z],y2ve[z]-y1ve[z],vplce[z],bufplce[z],ylookup[y1ve[z]]+x+z+dc_destorg); + } + bad >>= 1; + } + continue; + } + + for (z = 0; z < 4; ++z) + { + if (u4 > y1ve[z]) + { + vplce[z] = prevline1(vince[z],palookupoffse[z],u4-y1ve[z],vplce[z],bufplce[z],ylookup[y1ve[z]]+x+z+dc_destorg); + } + } + + if (d4 > u4) + { + dc_count = d4-u4; + dc_dest = ylookup[u4]+x+dc_destorg; + dovline4(); + } + + BYTE *i = x+ylookup[d4]+dc_destorg; + for (z = 0; z < 4; ++z) + { + if (y2ve[z] > d4) + { + prevline1(vince[z],palookupoffse[0],y2ve[z]-d4,vplce[z],bufplce[z],i+z); + } + } + } + for(;x> FRACBITS); + dc_dest = ylookup[y1ve[0]] + x + dc_destorg; + dc_count = y2ve[0] - y1ve[0]; + iscale = swal[x] * yrepeat; + dc_iscale = xs_ToFixed(fracbits, iscale); + dc_texturefrac = xs_ToFixed(fracbits, dc_texturemid + iscale * (y1ve[0] - CenterY + 1)); + + dovline1(); + } + +//unclock (WallScanCycles); + + NetUpdate (); +} + +void wallscan_striped (int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat) +{ + FDynamicColormap *startcolormap = basecolormap; + int startshade = wallshade; + bool fogginess = foggy; + + short most1[MAXWIDTH], most2[MAXWIDTH], most3[MAXWIDTH]; + short *up, *down; + + up = uwal; + down = most1; + + assert(WallC.sx1 <= x1); + assert(WallC.sx2 >= x2); + + // kg3D - fake floors instead of zdoom light list + for (unsigned int i = 0; i < frontsector->e->XFloor.lightlist.Size(); i++) + { + int j = WallMost (most3, frontsector->e->XFloor.lightlist[i].plane, &WallC); + if (j != 3) + { + for (int j = x1; j < x2; ++j) + { + down[j] = clamp (most3[j], up[j], dwal[j]); + } + wallscan (x1, x2, up, down, swal, lwal, yrepeat); + up = down; + down = (down == most1) ? most2 : most1; + } + + lightlist_t *lit = &frontsector->e->XFloor.lightlist[i]; + basecolormap = lit->extra_colormap; + wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(fogginess, + *lit->p_lightlevel, lit->lightsource != NULL) + r_actualextralight); + } + + wallscan (x1, x2, up, dwal, swal, lwal, yrepeat); + basecolormap = startcolormap; + wallshade = startshade; +} + +static void call_wallscan(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, bool mask) +{ + if (mask) + { + if (colfunc == basecolfunc) + { + maskwallscan(x1, x2, uwal, dwal, swal, lwal, yrepeat); + } + else + { + transmaskwallscan(x1, x2, uwal, dwal, swal, lwal, yrepeat); + } + } + else + { + if (fixedcolormap != NULL || fixedlightlev >= 0 || !(frontsector->e && frontsector->e->XFloor.lightlist.Size())) + { + wallscan(x1, x2, uwal, dwal, swal, lwal, yrepeat); + } + else + { + wallscan_striped(x1, x2, uwal, dwal, swal, lwal, yrepeat); + } + } +} + +//============================================================================= +// +// wallscan_np2 +// +// This is a wrapper around wallscan that helps it tile textures whose heights +// are not powers of 2. It divides the wall into texture-sized strips and calls +// wallscan for each of those. Since only one repetition of the texture fits +// in each strip, wallscan will not tile. +// +//============================================================================= + +void wallscan_np2(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, double top, double bot, bool mask) +{ + if (!r_np2) + { + call_wallscan(x1, x2, uwal, dwal, swal, lwal, yrepeat, mask); + } + else + { + short most1[MAXWIDTH], most2[MAXWIDTH], most3[MAXWIDTH]; + short *up, *down; + double texheight = rw_pic->GetHeight(); + double partition; + double scaledtexheight = texheight / yrepeat; + + if (yrepeat >= 0) + { // normal orientation: draw strips from top to bottom + partition = top - fmod(top - dc_texturemid / yrepeat - ViewPos.Z, scaledtexheight); + if (partition == top) + { + partition -= scaledtexheight; + } + up = uwal; + down = most1; + dc_texturemid = (partition - ViewPos.Z) * yrepeat + texheight; + while (partition > bot) + { + int j = OWallMost(most3, partition - ViewPos.Z, &WallC); + if (j != 3) + { + for (int j = x1; j < x2; ++j) + { + down[j] = clamp(most3[j], up[j], dwal[j]); + } + call_wallscan(x1, x2, up, down, swal, lwal, yrepeat, mask); + up = down; + down = (down == most1) ? most2 : most1; + } + partition -= scaledtexheight; + dc_texturemid -= texheight; + } + call_wallscan(x1, x2, up, dwal, swal, lwal, yrepeat, mask); + } + else + { // upside down: draw strips from bottom to top + partition = bot - fmod(bot - dc_texturemid / yrepeat - ViewPos.Z, scaledtexheight); + up = most1; + down = dwal; + dc_texturemid = (partition - ViewPos.Z) * yrepeat + texheight; + while (partition < top) + { + int j = OWallMost(most3, partition - ViewPos.Z, &WallC); + if (j != 12) + { + for (int j = x1; j < x2; ++j) + { + up[j] = clamp(most3[j], uwal[j], down[j]); + } + call_wallscan(x1, x2, up, down, swal, lwal, yrepeat, mask); + down = up; + up = (up == most1) ? most2 : most1; + } + partition -= scaledtexheight; + dc_texturemid -= texheight; + } + call_wallscan(x1, x2, uwal, down, swal, lwal, yrepeat, mask); + } + } +} + +static void wallscan_np2_ds(drawseg_t *ds, int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat) +{ + if (rw_pic->GetHeight() != 1 << rw_pic->HeightBits) + { + double frontcz1 = ds->curline->frontsector->ceilingplane.ZatPoint(ds->curline->v1); + double frontfz1 = ds->curline->frontsector->floorplane.ZatPoint(ds->curline->v1); + double frontcz2 = ds->curline->frontsector->ceilingplane.ZatPoint(ds->curline->v2); + double frontfz2 = ds->curline->frontsector->floorplane.ZatPoint(ds->curline->v2); + double top = MAX(frontcz1, frontcz2); + double bot = MIN(frontfz1, frontfz2); + if (fake3D & FAKE3D_CLIPTOP) + { + top = MIN(top, sclipTop); + } + if (fake3D & FAKE3D_CLIPBOTTOM) + { + bot = MAX(bot, sclipBottom); + } + wallscan_np2(x1, x2, uwal, dwal, swal, lwal, yrepeat, top, bot, true); + } + else + { + call_wallscan(x1, x2, uwal, dwal, swal, lwal, yrepeat, true); + } +} + +inline fixed_t mvline1 (fixed_t vince, BYTE *colormap, int count, fixed_t vplce, const BYTE *bufplce, BYTE *dest) +{ + dc_iscale = vince; + dc_colormap = colormap; + dc_count = count; + dc_texturefrac = vplce; + dc_source = bufplce; + dc_dest = dest; + return domvline1 (); +} + +void maskwallscan (int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, + double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x)) +{ + int x, fracbits; + BYTE *p; + int y1ve[4], y2ve[4], u4, d4, startx, dax, z; + char bad; + float light = rw_light - rw_lightstep; + SDWORD xoffset; + BYTE *basecolormapdata; + double iscale; + + if (rw_pic->UseType == FTexture::TEX_Null) + { + return; + } + + if (!rw_pic->bMasked) + { // Textures that aren't masked can use the faster wallscan. + wallscan (x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol); + return; + } + +//extern cycle_t WallScanCycles; +//clock (WallScanCycles); + + rw_pic->GetHeight(); // Make sure texture size is loaded + fracbits = 32- rw_pic->HeightBits; + setupmvline(fracbits); + xoffset = rw_offset; + basecolormapdata = basecolormap->Maps; + + x = startx = x1; + p = x + dc_destorg; + + bool fixed = (fixedcolormap != NULL || fixedlightlev >= 0); + if (fixed) + { + palookupoffse[0] = dc_colormap; + palookupoffse[1] = dc_colormap; + palookupoffse[2] = dc_colormap; + palookupoffse[3] = dc_colormap; + } + + for(; (x < x2) && ((size_t)p & 3); ++x, ++p) + { + light += rw_lightstep; + y1ve[0] = uwal[x];//max(uwal[x],umost[x]); + y2ve[0] = dwal[x];//min(dwal[x],dmost[x]); + if (y2ve[0] <= y1ve[0]) continue; + + if (!fixed) + { // calculate lighting + dc_colormap = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); + } + + dc_source = getcol (rw_pic, (lwal[x] + xoffset) >> FRACBITS); + dc_dest = ylookup[y1ve[0]] + p; + dc_count = y2ve[0] - y1ve[0]; + iscale = swal[x] * yrepeat; + dc_iscale = xs_ToFixed(fracbits, iscale); + dc_texturefrac = xs_ToFixed(fracbits, dc_texturemid + iscale * (y1ve[0] - CenterY + 1)); + + domvline1(); + } + + for(; x < x2-3; x += 4, p+= 4) + { + bad = 0; + for (z = 3, dax = x+3; z >= 0; --z, --dax) + { + y1ve[z] = uwal[dax]; + y2ve[z] = dwal[dax]; + if (y2ve[z] <= y1ve[z]) { bad += 1<> FRACBITS); + iscale = swal[dax] * yrepeat; + vince[z] = xs_ToFixed(fracbits, iscale); + vplce[z] = xs_ToFixed(fracbits, dc_texturemid + iscale * (y1ve[z] - CenterY + 1)); + } + if (bad == 15) + { + light += rw_lightstep * 4; + continue; + } + + if (!fixed) + { + for (z = 0; z < 4; ++z) + { + light += rw_lightstep; + palookupoffse[z] = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); + } + } + + u4 = MAX(MAX(y1ve[0],y1ve[1]),MAX(y1ve[2],y1ve[3])); + d4 = MIN(MIN(y2ve[0],y2ve[1]),MIN(y2ve[2],y2ve[3])); + + if ((bad != 0) || (u4 >= d4)) + { + for (z = 0; z < 4; ++z) + { + if (!(bad & 1)) + { + mvline1(vince[z],palookupoffse[z],y2ve[z]-y1ve[z],vplce[z],bufplce[z],ylookup[y1ve[z]]+p+z); + } + bad >>= 1; + } + continue; + } + + for (z = 0; z < 4; ++z) + { + if (u4 > y1ve[z]) + { + vplce[z] = mvline1(vince[z],palookupoffse[z],u4-y1ve[z],vplce[z],bufplce[z],ylookup[y1ve[z]]+p+z); + } + } + + if (d4 > u4) + { + dc_count = d4-u4; + dc_dest = ylookup[u4]+p; + domvline4(); + } + + BYTE *i = p+ylookup[d4]; + for (z = 0; z < 4; ++z) + { + if (y2ve[z] > d4) + { + mvline1(vince[z],palookupoffse[0],y2ve[z]-d4,vplce[z],bufplce[z],i+z); + } + } + } + for(; x < x2; ++x, ++p) + { + light += rw_lightstep; + y1ve[0] = uwal[x]; + y2ve[0] = dwal[x]; + if (y2ve[0] <= y1ve[0]) continue; + + if (!fixed) + { // calculate lighting + dc_colormap = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); + } + + dc_source = getcol (rw_pic, (lwal[x] + xoffset) >> FRACBITS); + dc_dest = ylookup[y1ve[0]] + p; + dc_count = y2ve[0] - y1ve[0]; + iscale = swal[x] * yrepeat; + dc_iscale = xs_ToFixed(fracbits, iscale); + dc_texturefrac = xs_ToFixed(fracbits, dc_texturemid + iscale * (y1ve[0] - CenterY + 1)); + + domvline1(); + } + +//unclock(WallScanCycles); + + NetUpdate (); +} + +inline void preptmvline1 (fixed_t vince, BYTE *colormap, int count, fixed_t vplce, const BYTE *bufplce, BYTE *dest) +{ + dc_iscale = vince; + dc_colormap = colormap; + dc_count = count; + dc_texturefrac = vplce; + dc_source = bufplce; + dc_dest = dest; +} + +void transmaskwallscan (int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, + double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x)) +{ + fixed_t (*tmvline1)(); + void (*tmvline4)(); + int x, fracbits; + BYTE *p; + int y1ve[4], y2ve[4], u4, d4, startx, dax, z; + char bad; + float light = rw_light - rw_lightstep; + SDWORD xoffset; + BYTE *basecolormapdata; + double iscale; + + if (rw_pic->UseType == FTexture::TEX_Null) + { + return; + } + + if (!R_GetTransMaskDrawers (&tmvline1, &tmvline4)) + { + // The current translucency is unsupported, so draw with regular maskwallscan instead. + maskwallscan (x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol); + return; + } + +//extern cycle_t WallScanCycles; +//clock (WallScanCycles); + + rw_pic->GetHeight(); // Make sure texture size is loaded + fracbits = 32 - rw_pic->HeightBits; + setuptmvline(fracbits); + xoffset = rw_offset; + basecolormapdata = basecolormap->Maps; + fixed_t centeryfrac = FLOAT2FIXED(CenterY); + + x = startx = x1; + p = x + dc_destorg; + + bool fixed = (fixedcolormap != NULL || fixedlightlev >= 0); + if (fixed) + { + palookupoffse[0] = dc_colormap; + palookupoffse[1] = dc_colormap; + palookupoffse[2] = dc_colormap; + palookupoffse[3] = dc_colormap; + } + + for(; (x < x2) && ((size_t)p & 3); ++x, ++p) + { + light += rw_lightstep; + y1ve[0] = uwal[x];//max(uwal[x],umost[x]); + y2ve[0] = dwal[x];//min(dwal[x],dmost[x]); + if (y2ve[0] <= y1ve[0]) continue; + + if (!fixed) + { // calculate lighting + dc_colormap = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); + } + + dc_source = getcol (rw_pic, (lwal[x] + xoffset) >> FRACBITS); + dc_dest = ylookup[y1ve[0]] + p; + dc_count = y2ve[0] - y1ve[0]; + iscale = swal[x] * yrepeat; + dc_iscale = xs_ToFixed(fracbits, iscale); + dc_texturefrac = xs_ToFixed(fracbits, dc_texturemid + iscale * (y1ve[0] - CenterY + 1)); + + tmvline1(); + } + + for(; x < x2-3; x += 4, p+= 4) + { + bad = 0; + for (z = 3, dax = x+3; z >= 0; --z, --dax) + { + y1ve[z] = uwal[dax]; + y2ve[z] = dwal[dax]; + if (y2ve[z] <= y1ve[z]) { bad += 1<> FRACBITS); + iscale = swal[dax] * yrepeat; + vince[z] = xs_ToFixed(fracbits, iscale); + vplce[z] = xs_ToFixed(fracbits, dc_texturemid + vince[z] * (y1ve[z] - CenterY + 1)); + } + if (bad == 15) + { + light += rw_lightstep * 4; + continue; + } + + if (!fixed) + { + for (z = 0; z < 4; ++z) + { + light += rw_lightstep; + palookupoffse[z] = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); + } + } + + u4 = MAX(MAX(y1ve[0],y1ve[1]),MAX(y1ve[2],y1ve[3])); + d4 = MIN(MIN(y2ve[0],y2ve[1]),MIN(y2ve[2],y2ve[3])); + + if ((bad != 0) || (u4 >= d4)) + { + for (z = 0; z < 4; ++z) + { + if (!(bad & 1)) + { + preptmvline1(vince[z],palookupoffse[z],y2ve[z]-y1ve[z],vplce[z],bufplce[z],ylookup[y1ve[z]]+p+z); + tmvline1(); + } + bad >>= 1; + } + continue; + } + + for (z = 0; z < 4; ++z) + { + if (u4 > y1ve[z]) + { + preptmvline1(vince[z],palookupoffse[z],u4-y1ve[z],vplce[z],bufplce[z],ylookup[y1ve[z]]+p+z); + vplce[z] = tmvline1(); + } + } + + if (d4 > u4) + { + dc_count = d4-u4; + dc_dest = ylookup[u4]+p; + tmvline4(); + } + + BYTE *i = p+ylookup[d4]; + for (z = 0; z < 4; ++z) + { + if (y2ve[z] > d4) + { + preptmvline1(vince[z],palookupoffse[0],y2ve[z]-d4,vplce[z],bufplce[z],i+z); + tmvline1(); + } + } + } + for(; x < x2; ++x, ++p) + { + light += rw_lightstep; + y1ve[0] = uwal[x]; + y2ve[0] = dwal[x]; + if (y2ve[0] <= y1ve[0]) continue; + + if (!fixed) + { // calculate lighting + dc_colormap = basecolormapdata + (GETPALOOKUP (light, wallshade) << COLORMAPSHIFT); + } + + dc_source = getcol (rw_pic, (lwal[x] + xoffset) >> FRACBITS); + dc_dest = ylookup[y1ve[0]] + p; + dc_count = y2ve[0] - y1ve[0]; + iscale = swal[x] * yrepeat; + dc_iscale = xs_ToFixed(fracbits, iscale); + dc_texturefrac = xs_ToFixed(fracbits, dc_texturemid + iscale * (y1ve[0] - CenterY + 1)); + + tmvline1(); + } + +//unclock(WallScanCycles); + + NetUpdate (); +} + +// +// R_RenderSegLoop +// Draws zero, one, or two textures for walls. +// Can draw or mark the starting pixel of floor and ceiling textures. +// CALLED: CORE LOOPING ROUTINE. +// +// [RH] Rewrote this to use Build's wallscan, so it's quite far +// removed from the original Doom routine. +// + +void R_RenderSegLoop () +{ + int x1 = rw_x; + int x2 = rw_stopx; + int x; + double xscale; + double yscale; + fixed_t xoffset = rw_offset; + + if (fixedlightlev >= 0) + dc_colormap = basecolormap->Maps + fixedlightlev; + else if (fixedcolormap != NULL) + dc_colormap = fixedcolormap; + + // clip wall to the floor and ceiling + for (x = x1; x < x2; ++x) + { + if (walltop[x] < ceilingclip[x]) + { + walltop[x] = ceilingclip[x]; + } + if (wallbottom[x] > floorclip[x]) + { + wallbottom[x] = floorclip[x]; + } + } + + // mark ceiling areas + if (markceiling) + { + for (x = x1; x < x2; ++x) + { + short top = (fakeFloor && fake3D & 2) ? fakeFloor->ceilingclip[x] : ceilingclip[x]; + short bottom = MIN (walltop[x], floorclip[x]); + if (top < bottom) + { + ceilingplane->top[x] = top; + ceilingplane->bottom[x] = bottom; + } + } + } + + // mark floor areas + if (markfloor) + { + for (x = x1; x < x2; ++x) + { + short top = MAX (wallbottom[x], ceilingclip[x]); + short bottom = (fakeFloor && fake3D & 1) ? fakeFloor->floorclip[x] : floorclip[x]; + if (top < bottom) + { + assert (bottom <= viewheight); + floorplane->top[x] = top; + floorplane->bottom[x] = bottom; + } + } + } + + // kg3D - fake planes clipping + if (fake3D & FAKE3D_REFRESHCLIP) + { + if (fake3D & FAKE3D_CLIPBOTFRONT) + { + memcpy (fakeFloor->floorclip+x1, wallbottom+x1, (x2-x1)*sizeof(short)); + } + else + { + for (x = x1; x < x2; ++x) + { + walllower[x] = MIN (MAX (walllower[x], ceilingclip[x]), wallbottom[x]); + } + memcpy (fakeFloor->floorclip+x1, walllower+x1, (x2-x1)*sizeof(short)); + } + if (fake3D & FAKE3D_CLIPTOPFRONT) + { + memcpy (fakeFloor->ceilingclip+x1, walltop+x1, (x2-x1)*sizeof(short)); + } + else + { + for (x = x1; x < x2; ++x) + { + wallupper[x] = MAX (MIN (wallupper[x], floorclip[x]), walltop[x]); + } + memcpy (fakeFloor->ceilingclip+x1, wallupper+x1, (x2-x1)*sizeof(short)); + } + } + if(fake3D & 7) return; + + // draw the wall tiers + if (midtexture) + { // one sided line + if (midtexture->UseType != FTexture::TEX_Null && viewactive) + { + dc_texturemid = rw_midtexturemid; + rw_pic = midtexture; + xscale = rw_pic->Scale.X * rw_midtexturescalex; + yscale = rw_pic->Scale.Y * rw_midtexturescaley; + if (xscale != lwallscale) + { + PrepLWall (lwall, curline->sidedef->TexelLength*xscale, WallC.sx1, WallC.sx2); + lwallscale = xscale; + } + if (midtexture->bWorldPanning) + { + rw_offset = xs_RoundToInt(rw_offset_mid * xscale); + } + else + { + rw_offset = rw_offset_mid; + } + if (xscale < 0) + { + rw_offset = -rw_offset; + } + if (rw_pic->GetHeight() != 1 << rw_pic->HeightBits) + { + wallscan_np2(x1, x2, walltop, wallbottom, swall, lwall, yscale, MAX(rw_frontcz1, rw_frontcz2), MIN(rw_frontfz1, rw_frontfz2), false); + } + else + { + call_wallscan(x1, x2, walltop, wallbottom, swall, lwall, yscale, false); + } + } + clearbufshort (ceilingclip+x1, x2-x1, viewheight); + clearbufshort (floorclip+x1, x2-x1, 0xffff); + } + else + { // two sided line + if (toptexture != NULL && toptexture->UseType != FTexture::TEX_Null) + { // top wall + for (x = x1; x < x2; ++x) + { + wallupper[x] = MAX (MIN (wallupper[x], floorclip[x]), walltop[x]); + } + if (viewactive) + { + dc_texturemid = rw_toptexturemid; + rw_pic = toptexture; + xscale = rw_pic->Scale.X * rw_toptexturescalex; + yscale = rw_pic->Scale.Y * rw_toptexturescaley; + if (xscale != lwallscale) + { + PrepLWall (lwall, curline->sidedef->TexelLength*xscale, WallC.sx1, WallC.sx2); + lwallscale = xscale; + } + if (toptexture->bWorldPanning) + { + rw_offset = xs_RoundToInt(rw_offset_top * xscale); + } + else + { + rw_offset = rw_offset_top; + } + if (xscale < 0) + { + rw_offset = -rw_offset; + } + if (rw_pic->GetHeight() != 1 << rw_pic->HeightBits) + { + wallscan_np2(x1, x2, walltop, wallupper, swall, lwall, yscale, MAX(rw_frontcz1, rw_frontcz2), MIN(rw_backcz1, rw_backcz2), false); + } + else + { + call_wallscan(x1, x2, walltop, wallupper, swall, lwall, yscale, false); + } + } + memcpy (ceilingclip+x1, wallupper+x1, (x2-x1)*sizeof(short)); + } + else if (markceiling) + { // no top wall + memcpy (ceilingclip+x1, walltop+x1, (x2-x1)*sizeof(short)); + } + + + if (bottomtexture != NULL && bottomtexture->UseType != FTexture::TEX_Null) + { // bottom wall + for (x = x1; x < x2; ++x) + { + walllower[x] = MIN (MAX (walllower[x], ceilingclip[x]), wallbottom[x]); + } + if (viewactive) + { + dc_texturemid = rw_bottomtexturemid; + rw_pic = bottomtexture; + xscale = rw_pic->Scale.X * rw_bottomtexturescalex; + yscale = rw_pic->Scale.Y * rw_bottomtexturescaley; + if (xscale != lwallscale) + { + PrepLWall (lwall, curline->sidedef->TexelLength*xscale, WallC.sx1, WallC.sx2); + lwallscale = xscale; + } + if (bottomtexture->bWorldPanning) + { + rw_offset = xs_RoundToInt(rw_offset_bottom * xscale); + } + else + { + rw_offset = rw_offset_bottom; + } + if (xscale < 0) + { + rw_offset = -rw_offset; + } + if (rw_pic->GetHeight() != 1 << rw_pic->HeightBits) + { + wallscan_np2(x1, x2, walllower, wallbottom, swall, lwall, yscale, MAX(rw_backfz1, rw_backfz2), MIN(rw_frontfz1, rw_frontfz2), false); + } + else + { + call_wallscan(x1, x2, walllower, wallbottom, swall, lwall, yscale, false); + } + } + memcpy (floorclip+x1, walllower+x1, (x2-x1)*sizeof(short)); + } + else if (markfloor) + { // no bottom wall + memcpy (floorclip+x1, wallbottom+x1, (x2-x1)*sizeof(short)); + } + } + rw_offset = xoffset; +} + +void R_NewWall (bool needlights) +{ + double rowoffset; + double yrepeat; + + rw_markportal = false; + + sidedef = curline->sidedef; + linedef = curline->linedef; + + // mark the segment as visible for auto map + if (!r_dontmaplines) linedef->flags |= ML_MAPPED; + + midtexture = toptexture = bottomtexture = 0; + + if (sidedef == linedef->sidedef[0] && + (linedef->isVisualPortal() || (linedef->special == Line_Mirror && r_drawmirrors))) // [ZZ] compatibility with r_drawmirrors cvar that existed way before portals + { + markfloor = markceiling = true; // act like an one-sided wall here (todo: check how does this work with transparency) + rw_markportal = true; + } + else if (backsector == NULL) + { + // single sided line + // a single sided line is terminal, so it must mark ends + markfloor = markceiling = true; + // [RH] Horizon lines do not need to be textured + if (linedef->special != Line_Horizon) + { + midtexture = TexMan(sidedef->GetTexture(side_t::mid), true); + rw_offset_mid = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::mid)); + rowoffset = sidedef->GetTextureYOffset(side_t::mid); + rw_midtexturescalex = sidedef->GetTextureXScale(side_t::mid); + rw_midtexturescaley = sidedef->GetTextureYScale(side_t::mid); + yrepeat = midtexture->Scale.Y * rw_midtexturescaley; + if (yrepeat >= 0) + { // normal orientation + if (linedef->flags & ML_DONTPEGBOTTOM) + { // bottom of texture at bottom + rw_midtexturemid = (frontsector->GetPlaneTexZ(sector_t::floor) - ViewPos.Z) * yrepeat + midtexture->GetHeight(); + } + else + { // top of texture at top + rw_midtexturemid = (frontsector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat; + if (rowoffset < 0 && midtexture != NULL) + { + rowoffset += midtexture->GetHeight(); + } + } + } + else + { // upside down + rowoffset = -rowoffset; + if (linedef->flags & ML_DONTPEGBOTTOM) + { // top of texture at bottom + rw_midtexturemid = (frontsector->GetPlaneTexZ(sector_t::floor) - ViewPos.Z) * yrepeat; + } + else + { // bottom of texture at top + rw_midtexturemid = (frontsector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat + midtexture->GetHeight(); + } + } + if (midtexture->bWorldPanning) + { + rw_midtexturemid += rowoffset * yrepeat; + } + else + { + // rowoffset is added outside the multiply so that it positions the texture + // by texels instead of world units. + rw_midtexturemid += rowoffset; + } + } + } + else + { // two-sided line + // hack to allow height changes in outdoor areas + + rw_frontlowertop = frontsector->GetPlaneTexZ(sector_t::ceiling); + + if (frontsector->GetTexture(sector_t::ceiling) == skyflatnum && + backsector->GetTexture(sector_t::ceiling) == skyflatnum) + { + if (rw_havehigh) + { // front ceiling is above back ceiling + memcpy (&walltop[WallC.sx1], &wallupper[WallC.sx1], (WallC.sx2 - WallC.sx1)*sizeof(walltop[0])); + rw_havehigh = false; + } + else if (rw_havelow && frontsector->ceilingplane != backsector->ceilingplane) + { // back ceiling is above front ceiling + // The check for rw_havelow is not Doom-compliant, but it avoids HoM that + // would otherwise occur because there is space made available for this + // wall but nothing to draw for it. + // Recalculate walltop so that the wall is clipped by the back sector's + // ceiling instead of the front sector's ceiling. + WallMost (walltop, backsector->ceilingplane, &WallC); + } + // Putting sky ceilings on the front and back of a line alters the way unpegged + // positioning works. + rw_frontlowertop = backsector->GetPlaneTexZ(sector_t::ceiling); + } + + if ((rw_backcz1 <= rw_frontfz1 && rw_backcz2 <= rw_frontfz2) || + (rw_backfz1 >= rw_frontcz1 && rw_backfz2 >= rw_frontcz2)) + { + // closed door + markceiling = markfloor = true; + } + else + { + markfloor = rw_mustmarkfloor + || backsector->floorplane != frontsector->floorplane + || backsector->lightlevel != frontsector->lightlevel + || backsector->GetTexture(sector_t::floor) != frontsector->GetTexture(sector_t::floor) + || backsector->GetPlaneLight(sector_t::floor) != frontsector->GetPlaneLight(sector_t::floor) + + // killough 3/7/98: Add checks for (x,y) offsets + || backsector->planes[sector_t::floor].xform != frontsector->planes[sector_t::floor].xform + || backsector->GetAlpha(sector_t::floor) != frontsector->GetAlpha(sector_t::floor) + + // killough 4/15/98: prevent 2s normals + // from bleeding through deep water + || frontsector->heightsec + + || backsector->GetVisFlags(sector_t::floor) != frontsector->GetVisFlags(sector_t::floor) + + // [RH] Add checks for colormaps + || backsector->ColorMap != frontsector->ColorMap + + + // kg3D - add fake lights + || (frontsector->e && frontsector->e->XFloor.lightlist.Size()) + || (backsector->e && backsector->e->XFloor.lightlist.Size()) + + || (sidedef->GetTexture(side_t::mid).isValid() && + ((linedef->flags & (ML_CLIP_MIDTEX|ML_WRAP_MIDTEX)) || + (sidedef->Flags & (WALLF_CLIP_MIDTEX|WALLF_WRAP_MIDTEX)))) + ; + + markceiling = (frontsector->GetTexture(sector_t::ceiling) != skyflatnum || + backsector->GetTexture(sector_t::ceiling) != skyflatnum) && + (rw_mustmarkceiling + || backsector->ceilingplane != frontsector->ceilingplane + || backsector->lightlevel != frontsector->lightlevel + || backsector->GetTexture(sector_t::ceiling) != frontsector->GetTexture(sector_t::ceiling) + + // killough 3/7/98: Add checks for (x,y) offsets + || backsector->planes[sector_t::ceiling].xform != frontsector->planes[sector_t::ceiling].xform + || backsector->GetAlpha(sector_t::ceiling) != frontsector->GetAlpha(sector_t::ceiling) + + // killough 4/15/98: prevent 2s normals + // from bleeding through fake ceilings + || (frontsector->heightsec && frontsector->GetTexture(sector_t::ceiling) != skyflatnum) + + || backsector->GetPlaneLight(sector_t::ceiling) != frontsector->GetPlaneLight(sector_t::ceiling) + || backsector->GetFlags(sector_t::ceiling) != frontsector->GetFlags(sector_t::ceiling) + + // [RH] Add check for colormaps + || backsector->ColorMap != frontsector->ColorMap + + // kg3D - add fake lights + || (frontsector->e && frontsector->e->XFloor.lightlist.Size()) + || (backsector->e && backsector->e->XFloor.lightlist.Size()) + + || (sidedef->GetTexture(side_t::mid).isValid() && + ((linedef->flags & (ML_CLIP_MIDTEX|ML_WRAP_MIDTEX)) || + (sidedef->Flags & (WALLF_CLIP_MIDTEX|WALLF_WRAP_MIDTEX)))) + ); + } + + if (rw_havehigh) + { // top texture + toptexture = TexMan(sidedef->GetTexture(side_t::top), true); + + rw_offset_top = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::top)); + rowoffset = sidedef->GetTextureYOffset(side_t::top); + rw_toptexturescalex =sidedef->GetTextureXScale(side_t::top); + rw_toptexturescaley =sidedef->GetTextureYScale(side_t::top); + yrepeat = toptexture->Scale.Y * rw_toptexturescaley; + if (yrepeat >= 0) + { // normal orientation + if (linedef->flags & ML_DONTPEGTOP) + { // top of texture at top + rw_toptexturemid = (frontsector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat; + if (rowoffset < 0 && toptexture != NULL) + { + rowoffset += toptexture->GetHeight(); + } + } + else + { // bottom of texture at bottom + rw_toptexturemid = (backsector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat + toptexture->GetHeight(); + } + } + else + { // upside down + rowoffset = -rowoffset; + if (linedef->flags & ML_DONTPEGTOP) + { // bottom of texture at top + rw_toptexturemid = (frontsector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat + toptexture->GetHeight(); + } + else + { // top of texture at bottom + rw_toptexturemid = (backsector->GetPlaneTexZ(sector_t::ceiling) - ViewPos.Z) * yrepeat; + } + } + if (toptexture->bWorldPanning) + { + rw_toptexturemid += rowoffset * yrepeat; + } + else + { + rw_toptexturemid += rowoffset; + } + } + if (rw_havelow) + { // bottom texture + bottomtexture = TexMan(sidedef->GetTexture(side_t::bottom), true); + + rw_offset_bottom = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::bottom)); + rowoffset = sidedef->GetTextureYOffset(side_t::bottom); + rw_bottomtexturescalex = sidedef->GetTextureXScale(side_t::bottom); + rw_bottomtexturescaley = sidedef->GetTextureYScale(side_t::bottom); + yrepeat = bottomtexture->Scale.Y * rw_bottomtexturescaley; + if (yrepeat >= 0) + { // normal orientation + if (linedef->flags & ML_DONTPEGBOTTOM) + { // bottom of texture at bottom + rw_bottomtexturemid = (rw_frontlowertop - ViewPos.Z) * yrepeat; + } + else + { // top of texture at top + rw_bottomtexturemid = (backsector->GetPlaneTexZ(sector_t::floor) - ViewPos.Z) * yrepeat; + if (rowoffset < 0 && bottomtexture != NULL) + { + rowoffset += bottomtexture->GetHeight(); + } + } + } + else + { // upside down + rowoffset = -rowoffset; + if (linedef->flags & ML_DONTPEGBOTTOM) + { // top of texture at bottom + rw_bottomtexturemid = (rw_frontlowertop - ViewPos.Z) * yrepeat; + } + else + { // bottom of texture at top + rw_bottomtexturemid = (backsector->GetPlaneTexZ(sector_t::floor) - ViewPos.Z) * yrepeat + bottomtexture->GetHeight(); + } + } + if (bottomtexture->bWorldPanning) + { + rw_bottomtexturemid += rowoffset * yrepeat; + } + else + { + rw_bottomtexturemid += rowoffset; + } + } + } + + // if a floor / ceiling plane is on the wrong side of the view plane, + // it is definitely invisible and doesn't need to be marked. + + // killough 3/7/98: add deep water check + if (frontsector->GetHeightSec() == NULL) + { + int planeside; + + planeside = frontsector->floorplane.PointOnSide(ViewPos); + if (frontsector->floorplane.fC() < 0) // 3D floors have the floor backwards + planeside = -planeside; + if (planeside <= 0) // above view plane + markfloor = false; + + if (frontsector->GetTexture(sector_t::ceiling) != skyflatnum) + { + planeside = frontsector->ceilingplane.PointOnSide(ViewPos); + if (frontsector->ceilingplane.fC() > 0) // 3D floors have the ceiling backwards + planeside = -planeside; + if (planeside <= 0) // below view plane + markceiling = false; + } + } + + FTexture *midtex = TexMan(sidedef->GetTexture(side_t::mid), true); + + segtextured = midtex != NULL || toptexture != NULL || bottomtexture != NULL; + + // calculate light table + if (needlights && (segtextured || (backsector && IsFogBoundary(frontsector, backsector)))) + { + lwallscale = + midtex ? (midtex->Scale.X * sidedef->GetTextureXScale(side_t::mid)) : + toptexture ? (toptexture->Scale.X * sidedef->GetTextureXScale(side_t::top)) : + bottomtexture ? (bottomtexture->Scale.X * sidedef->GetTextureXScale(side_t::bottom)) : + 1.; + + PrepWall (swall, lwall, sidedef->TexelLength * lwallscale, WallC.sx1, WallC.sx2); + + if (fixedcolormap == NULL && fixedlightlev < 0) + { + wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(foggy, frontsector->lightlevel) + + r_actualextralight); + GlobVis = r_WallVisibility; + rw_lightleft = float (GlobVis / WallC.sz1); + rw_lightstep = float((GlobVis / WallC.sz2 - rw_lightleft) / (WallC.sx2 - WallC.sx1)); + } + else + { + rw_lightleft = 1; + rw_lightstep = 0; + } + } +} + + +// +// R_CheckDrawSegs +// + +void R_CheckDrawSegs () +{ + if (ds_p == &drawsegs[MaxDrawSegs]) + { // [RH] Grab some more drawsegs + size_t newdrawsegs = MaxDrawSegs ? MaxDrawSegs*2 : 32; + ptrdiff_t firstofs = firstdrawseg - drawsegs; + drawsegs = (drawseg_t *)M_Realloc (drawsegs, newdrawsegs * sizeof(drawseg_t)); + firstdrawseg = drawsegs + firstofs; + ds_p = drawsegs + MaxDrawSegs; + MaxDrawSegs = newdrawsegs; + DPrintf ("MaxDrawSegs increased to %zu\n", MaxDrawSegs); + } +} + +// +// R_CheckOpenings +// + +ptrdiff_t R_NewOpening (ptrdiff_t len) +{ + ptrdiff_t res = lastopening; + lastopening += len; + if ((size_t)lastopening > maxopenings) + { + do + maxopenings = maxopenings ? maxopenings*2 : 16384; + while ((size_t)lastopening > maxopenings); + openings = (short *)M_Realloc (openings, maxopenings * sizeof(*openings)); + DPrintf ("MaxOpenings increased to %zu\n", maxopenings); + } + return res; +} + + +// +// R_StoreWallRange +// A wall segment will be drawn between start and stop pixels (inclusive). +// + +void R_StoreWallRange (int start, int stop) +{ + int i; + bool maskedtexture = false; + +#ifdef RANGECHECK + if (start >= viewwidth || start >= stop) + I_FatalError ("Bad R_StoreWallRange: %i to %i", start , stop); +#endif + + // don't overflow and crash + R_CheckDrawSegs (); + + if (!rw_prepped) + { + rw_prepped = true; + R_NewWall (true); + } + + rw_offset = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::mid)); + rw_light = rw_lightleft + rw_lightstep * (start - WallC.sx1); + + ds_p->CurrentPortalUniq = CurrentPortalUniq; + ds_p->sx1 = WallC.sx1; + ds_p->sx2 = WallC.sx2; + ds_p->sz1 = WallC.sz1; + ds_p->sz2 = WallC.sz2; + ds_p->cx = WallC.tleft.X;; + ds_p->cy = WallC.tleft.Y; + ds_p->cdx = WallC.tright.X - WallC.tleft.X; + ds_p->cdy = WallC.tright.Y - WallC.tleft.Y; + ds_p->tmapvals = WallT; + ds_p->siz1 = 1 / WallC.sz1; + ds_p->siz2 = 1 / WallC.sz2; + ds_p->x1 = rw_x = start; + ds_p->x2 = stop; + ds_p->curline = curline; + rw_stopx = stop; + ds_p->bFogBoundary = false; + ds_p->bFakeBoundary = false; + if(fake3D & 7) ds_p->fake = 1; + else ds_p->fake = 0; + + // killough 1/6/98, 2/1/98: remove limit on openings + ds_p->sprtopclip = ds_p->sprbottomclip = ds_p->maskedtexturecol = ds_p->bkup = ds_p->swall = -1; + + if (rw_markportal) + { + ds_p->silhouette = SIL_BOTH; + } + else if (backsector == NULL) + { + ds_p->sprtopclip = R_NewOpening (stop - start); + ds_p->sprbottomclip = R_NewOpening (stop - start); + clearbufshort (openings + ds_p->sprtopclip, stop-start, viewheight); + memset (openings + ds_p->sprbottomclip, -1, (stop-start)*sizeof(short)); + ds_p->silhouette = SIL_BOTH; + } + else + { + // two sided line + ds_p->silhouette = 0; + + if (rw_frontfz1 > rw_backfz1 || rw_frontfz2 > rw_backfz2 || + backsector->floorplane.PointOnSide(ViewPos) < 0) + { + ds_p->silhouette = SIL_BOTTOM; + } + + if (rw_frontcz1 < rw_backcz1 || rw_frontcz2 < rw_backcz2 || + backsector->ceilingplane.PointOnSide(ViewPos) < 0) + { + ds_p->silhouette |= SIL_TOP; + } + + // killough 1/17/98: this test is required if the fix + // for the automap bug (r_bsp.c) is used, or else some + // sprites will be displayed behind closed doors. That + // fix prevents lines behind closed doors with dropoffs + // from being displayed on the automap. + // + // killough 4/7/98: make doorclosed external variable + + { + extern int doorclosed; // killough 1/17/98, 2/8/98, 4/7/98 + if (doorclosed || (rw_backcz1 <= rw_frontfz1 && rw_backcz2 <= rw_frontfz2)) + { + ds_p->sprbottomclip = R_NewOpening (stop - start); + memset (openings + ds_p->sprbottomclip, -1, (stop-start)*sizeof(short)); + ds_p->silhouette |= SIL_BOTTOM; + } + if (doorclosed || (rw_backfz1 >= rw_frontcz1 && rw_backfz2 >= rw_frontcz2)) + { // killough 1/17/98, 2/8/98 + ds_p->sprtopclip = R_NewOpening (stop - start); + clearbufshort (openings + ds_p->sprtopclip, stop - start, viewheight); + ds_p->silhouette |= SIL_TOP; + } + } + + if(!ds_p->fake && r_3dfloors && backsector->e && backsector->e->XFloor.ffloors.Size()) { + for(i = 0; i < (int)backsector->e->XFloor.ffloors.Size(); i++) { + F3DFloor *rover = backsector->e->XFloor.ffloors[i]; + if(rover->flags & FF_RENDERSIDES && (!(rover->flags & FF_INVERTSIDES) || rover->flags & FF_ALLSIDES)) { + ds_p->bFakeBoundary |= 1; + break; + } + } + } + if(!ds_p->fake && r_3dfloors && frontsector->e && frontsector->e->XFloor.ffloors.Size()) { + for(i = 0; i < (int)frontsector->e->XFloor.ffloors.Size(); i++) { + F3DFloor *rover = frontsector->e->XFloor.ffloors[i]; + if(rover->flags & FF_RENDERSIDES && (rover->flags & FF_ALLSIDES || rover->flags & FF_INVERTSIDES)) { + ds_p->bFakeBoundary |= 2; + break; + } + } + } + // kg3D - no for fakes + if(!ds_p->fake) + // allocate space for masked texture tables, if needed + // [RH] Don't just allocate the space; fill it in too. + if ((TexMan(sidedef->GetTexture(side_t::mid), true)->UseType != FTexture::TEX_Null || ds_p->bFakeBoundary || IsFogBoundary (frontsector, backsector)) && + (rw_ceilstat != 12 || !sidedef->GetTexture(side_t::top).isValid()) && + (rw_floorstat != 3 || !sidedef->GetTexture(side_t::bottom).isValid()) && + (WallC.sz1 >= TOO_CLOSE_Z && WallC.sz2 >= TOO_CLOSE_Z)) + { + float *swal; + fixed_t *lwal; + int i; + + maskedtexture = true; + + // kg3D - backup for mid and fake walls + ds_p->bkup = R_NewOpening(stop - start); + memcpy(openings + ds_p->bkup, &ceilingclip[start], sizeof(short)*(stop - start)); + + ds_p->bFogBoundary = IsFogBoundary (frontsector, backsector); + if (sidedef->GetTexture(side_t::mid).isValid() || ds_p->bFakeBoundary) + { + if(sidedef->GetTexture(side_t::mid).isValid()) + ds_p->bFakeBoundary |= 4; // it is also mid texture + + ds_p->maskedtexturecol = R_NewOpening ((stop - start) * 2); + ds_p->swall = R_NewOpening ((stop - start) * 2); + + lwal = (fixed_t *)(openings + ds_p->maskedtexturecol); + swal = (float *)(openings + ds_p->swall); + FTexture *pic = TexMan(sidedef->GetTexture(side_t::mid), true); + double yscale = pic->Scale.X * sidedef->GetTextureYScale(side_t::mid); + fixed_t xoffset = FLOAT2FIXED(sidedef->GetTextureXOffset(side_t::mid)); + + if (pic->bWorldPanning) + { + xoffset = xs_RoundToInt(xoffset * lwallscale); + } + + for (i = start; i < stop; i++) + { + *lwal++ = lwall[i] + xoffset; + *swal++ = swall[i]; + } + + double istart = *((float *)(openings + ds_p->swall)) * yscale; + double iend = *(swal - 1) * yscale; +#if 0 + ///This was for avoiding overflow when using fixed point. It might not be needed anymore. + const double mini = 3 / 65536.0; + if (istart < mini && istart >= 0) istart = mini; + if (istart > -mini && istart < 0) istart = -mini; + if (iend < mini && iend >= 0) iend = mini; + if (iend > -mini && iend < 0) iend = -mini; +#endif + istart = 1 / istart; + iend = 1 / iend; + ds_p->yscale = (float)yscale; + ds_p->iscale = (float)istart; + if (stop - start > 0) + { + ds_p->iscalestep = float((iend - istart) / (stop - start)); + } + else + { + ds_p->iscalestep = 0; + } + } + ds_p->light = rw_light; + ds_p->lightstep = rw_lightstep; + + // Masked midtextures should get the light level from the sector they reference, + // not from the current subsector, which is what the current wallshade value + // comes from. We make an exeption for polyobjects, however, since their "home" + // sector should be whichever one they move into. + if (curline->sidedef->Flags & WALLF_POLYOBJ) + { + ds_p->shade = wallshade; + } + else + { + ds_p->shade = LIGHT2SHADE(curline->sidedef->GetLightLevel(foggy, curline->frontsector->lightlevel) + + r_actualextralight); + } + + if (ds_p->bFogBoundary || ds_p->maskedtexturecol != -1) + { + size_t drawsegnum = ds_p - drawsegs; + InterestingDrawsegs.Push (drawsegnum); + } + } + } + + // render it + if (markceiling) + { + if (ceilingplane) + { // killough 4/11/98: add NULL ptr checks + ceilingplane = R_CheckPlane (ceilingplane, start, stop); + } + else + { + markceiling = false; + } + } + + if (markfloor) + { + if (floorplane) + { // killough 4/11/98: add NULL ptr checks + floorplane = R_CheckPlane (floorplane, start, stop); + } + else + { + markfloor = false; + } + } + + R_RenderSegLoop (); + + if(fake3D & 7) { + ds_p++; + return; + } + + // save sprite clipping info + if ( ((ds_p->silhouette & SIL_TOP) || maskedtexture) && ds_p->sprtopclip == -1) + { + ds_p->sprtopclip = R_NewOpening (stop - start); + memcpy (openings + ds_p->sprtopclip, &ceilingclip[start], sizeof(short)*(stop-start)); + } + + if ( ((ds_p->silhouette & SIL_BOTTOM) || maskedtexture) && ds_p->sprbottomclip == -1) + { + ds_p->sprbottomclip = R_NewOpening (stop - start); + memcpy (openings + ds_p->sprbottomclip, &floorclip[start], sizeof(short)*(stop-start)); + } + + if (maskedtexture && curline->sidedef->GetTexture(side_t::mid).isValid()) + { + ds_p->silhouette |= SIL_TOP | SIL_BOTTOM; + } + + // [RH] Draw any decals bound to the seg + // [ZZ] Only if not an active mirror + if (!rw_markportal) + { + for (DBaseDecal *decal = curline->sidedef->AttachedDecals; decal != NULL; decal = decal->WallNext) + { + R_RenderDecal (curline->sidedef, decal, ds_p, 0); + } + } + + if (rw_markportal) + { + PortalDrawseg pds; + pds.src = curline->linedef; + pds.dst = curline->linedef->special == Line_Mirror? curline->linedef : curline->linedef->getPortalDestination(); + pds.x1 = ds_p->x1; + pds.x2 = ds_p->x2; + pds.len = pds.x2 - pds.x1; + pds.ceilingclip.Resize(pds.len); + memcpy(&pds.ceilingclip[0], openings + ds_p->sprtopclip, pds.len*sizeof(*openings)); + pds.floorclip.Resize(pds.len); + memcpy(&pds.floorclip[0], openings + ds_p->sprbottomclip, pds.len*sizeof(*openings)); + + for (int i = 0; i < pds.x2-pds.x1; i++) + { + if (pds.ceilingclip[i] < 0) + pds.ceilingclip[i] = 0; + if (pds.ceilingclip[i] >= viewheight) + pds.ceilingclip[i] = viewheight-1; + if (pds.floorclip[i] < 0) + pds.floorclip[i] = 0; + if (pds.floorclip[i] >= viewheight) + pds.floorclip[i] = viewheight-1; + } + + pds.mirror = curline->linedef->special == Line_Mirror; + WallPortals.Push(pds); + } + + ds_p++; +} + +int OWallMost (short *mostbuf, double z, const FWallCoords *wallc) +{ + int bad, ix1, ix2; + double y, iy1, iy2; + double s1, s2, s3, s4; + + z = -z; + s1 = globaluclip * wallc->sz1; s2 = globaluclip * wallc->sz2; + s3 = globaldclip * wallc->sz1; s4 = globaldclip * wallc->sz2; + bad = (zs3)<<2)+((z>s4)<<3); + +#if 1 + if ((bad&3) == 3) + { + memset (&mostbuf[wallc->sx1], 0, (wallc->sx2 - wallc->sx1)*sizeof(mostbuf[0])); + return bad; + } + + if ((bad&12) == 12) + { + clearbufshort (&mostbuf[wallc->sx1], wallc->sx2 - wallc->sx1, viewheight); + return bad; + } +#endif + ix1 = wallc->sx1; iy1 = wallc->sz1; + ix2 = wallc->sx2; iy2 = wallc->sz2; +#if 1 + if (bad & 3) + { + double t = (z-s1) / (s2-s1); + double inty = wallc->sz1 + t * (wallc->sz2 - wallc->sz1); + int xcross = xs_RoundToInt(wallc->sx1 + (t * wallc->sz2 * (wallc->sx2 - wallc->sx1)) / inty); + + if ((bad & 3) == 2) + { + if (wallc->sx1 <= xcross) { iy2 = inty; ix2 = xcross; } + if (wallc->sx2 > xcross) memset (&mostbuf[xcross], 0, (wallc->sx2-xcross)*sizeof(mostbuf[0])); + } + else + { + if (xcross <= wallc->sx2) { iy1 = inty; ix1 = xcross; } + if (xcross > wallc->sx1) memset (&mostbuf[wallc->sx1], 0, (xcross-wallc->sx1)*sizeof(mostbuf[0])); + } + } + + if (bad & 12) + { + double t = (z-s3) / (s4-s3); + double inty = wallc->sz1 + t * (wallc->sz2 - wallc->sz1); + int xcross = xs_RoundToInt(wallc->sx1 + (t * wallc->sz2 * (wallc->sx2 - wallc->sx1)) / inty); + + if ((bad & 12) == 8) + { + if (wallc->sx1 <= xcross) { iy2 = inty; ix2 = xcross; } + if (wallc->sx2 > xcross) clearbufshort (&mostbuf[xcross], wallc->sx2 - xcross, viewheight); + } + else + { + if (xcross <= wallc->sx2) { iy1 = inty; ix1 = xcross; } + if (xcross > wallc->sx1) clearbufshort (&mostbuf[wallc->sx1], xcross - wallc->sx1, viewheight); + } + } + + y = z * InvZtoScale / iy1; + if (ix2 == ix1) + { + mostbuf[ix1] = (short)xs_RoundToInt(y + CenterY); + } + else + { + fixed_t yinc = FLOAT2FIXED(((z * InvZtoScale / iy2) - y) / (ix2 - ix1)); + qinterpolatedown16short (&mostbuf[ix1], ix2-ix1, FLOAT2FIXED(y + CenterY), yinc); + } +#else + double max = viewheight; + double zz = z / 65536.0; +#if 0 + double z1 = zz * InvZtoScale / wallc->sz1; + double z2 = zz * InvZtoScale / wallc->sz2 - z1; + z2 /= (wallc->sx2 - wallc->sx1); + z1 += centeryfrac / 65536.0; + + for (int x = wallc->sx1; x < wallc->sx2; ++x) + { + mostbuf[x] = xs_RoundToInt(clamp(z1, 0.0, max)); + z1 += z2; + } +#else + double top, bot, i; + + i = wallc->sx1 - centerx; + top = WallT.UoverZorg + WallT.UoverZstep * i; + bot = WallT.InvZorg + WallT.InvZstep * i; + double cy = centeryfrac / 65536.0; + + for (int x = wallc->sx1; x < wallc->sx2; x++) + { + double frac = top / bot; + double scale = frac * WallT.DepthScale + WallT.DepthOrg; + mostbuf[x] = xs_RoundToInt(clamp(zz / scale + cy, 0.0, max)); + top += WallT.UoverZstep; + bot += WallT.InvZstep; + } +#endif +#endif + return bad; +} + +int WallMost (short *mostbuf, const secplane_t &plane, const FWallCoords *wallc) +{ + if (!plane.isSlope()) + { + return OWallMost(mostbuf, plane.Zat0() - ViewPos.Z, wallc); + } + + double x, y, den, z1, z2, oz1, oz2; + double s1, s2, s3, s4; + int bad, ix1, ix2; + double iy1, iy2; + + if (MirrorFlags & RF_XFLIP) + { + x = curline->v2->fX(); + y = curline->v2->fY(); + if (wallc->sx1 == 0 && 0 != (den = wallc->tleft.X - wallc->tright.X + wallc->tleft.Y - wallc->tright.Y)) + { + double frac = (wallc->tleft.Y + wallc->tleft.X) / den; + x -= frac * (x - curline->v1->fX()); + y -= frac * (y - curline->v1->fY()); + } + z1 = ViewPos.Z - plane.ZatPoint(x, y); + + if (wallc->sx2 > wallc->sx1 + 1) + { + x = curline->v1->fX(); + y = curline->v1->fY(); + if (wallc->sx2 == viewwidth && 0 != (den = wallc->tleft.X - wallc->tright.X - wallc->tleft.Y + wallc->tright.Y)) + { + double frac = (wallc->tright.Y - wallc->tright.X) / den; + x += frac * (curline->v2->fX() - x); + y += frac * (curline->v2->fY() - y); + } + z2 = ViewPos.Z - plane.ZatPoint(x, y); + } + else + { + z2 = z1; + } + } + else + { + x = curline->v1->fX(); + y = curline->v1->fY(); + if (wallc->sx1 == 0 && 0 != (den = wallc->tleft.X - wallc->tright.X + wallc->tleft.Y - wallc->tright.Y)) + { + double frac = (wallc->tleft.Y + wallc->tleft.X) / den; + x += frac * (curline->v2->fX() - x); + y += frac * (curline->v2->fY() - y); + } + z1 = ViewPos.Z - plane.ZatPoint(x, y); + + if (wallc->sx2 > wallc->sx1 + 1) + { + x = curline->v2->fX(); + y = curline->v2->fY(); + if (wallc->sx2 == viewwidth && 0 != (den = wallc->tleft.X - wallc->tright.X - wallc->tleft.Y + wallc->tright.Y)) + { + double frac = (wallc->tright.Y - wallc->tright.X) / den; + x -= frac * (x - curline->v1->fX()); + y -= frac * (y - curline->v1->fY()); + } + z2 = ViewPos.Z - plane.ZatPoint(x, y); + } + else + { + z2 = z1; + } + } + + s1 = globaluclip * wallc->sz1; s2 = globaluclip * wallc->sz2; + s3 = globaldclip * wallc->sz1; s4 = globaldclip * wallc->sz2; + bad = (z1s3)<<2)+((z2>s4)<<3); + + ix1 = wallc->sx1; ix2 = wallc->sx2; + iy1 = wallc->sz1; iy2 = wallc->sz2; + oz1 = z1; oz2 = z2; + + if ((bad&3) == 3) + { + memset (&mostbuf[ix1], -1, (ix2-ix1)*sizeof(mostbuf[0])); + return bad; + } + + if ((bad&12) == 12) + { + clearbufshort (&mostbuf[ix1], ix2-ix1, viewheight); + return bad; + + } + + if (bad&3) + { + //inty = intz / (globaluclip>>16) + double t = (oz1-s1) / (s2-s1+oz1-oz2); + double inty = wallc->sz1 + t * (wallc->sz2-wallc->sz1); + double intz = oz1 + t * (oz2-oz1); + int xcross = wallc->sx1 + xs_RoundToInt((t * wallc->sz2 * (wallc->sx2-wallc->sx1)) / inty); + + //t = divscale30((x1<<4)-xcross*yb1[w],xcross*(yb2[w]-yb1[w])-((x2-x1)<<4)); + //inty = yb1[w] + mulscale30(yb2[w]-yb1[w],t); + //intz = z1 + mulscale30(z2-z1,t); + + if ((bad&3) == 2) + { + if (wallc->sx1 <= xcross) { z2 = intz; iy2 = inty; ix2 = xcross; } + memset (&mostbuf[xcross], 0, (wallc->sx2-xcross)*sizeof(mostbuf[0])); + } + else + { + if (xcross <= wallc->sx2) { z1 = intz; iy1 = inty; ix1 = xcross; } + memset (&mostbuf[wallc->sx1], 0, (xcross-wallc->sx1)*sizeof(mostbuf[0])); + } + } + + if (bad&12) + { + //inty = intz / (globaldclip>>16) + double t = (oz1-s3) / (s4-s3+oz1-oz2); + double inty = wallc->sz1 + t * (wallc->sz2-wallc->sz1); + double intz = oz1 + t * (oz2-oz1); + int xcross = wallc->sx1 + xs_RoundToInt((t * wallc->sz2 * (wallc->sx2-wallc->sx1)) / inty); + + //t = divscale30((x1<<4)-xcross*yb1[w],xcross*(yb2[w]-yb1[w])-((x2-x1)<<4)); + //inty = yb1[w] + mulscale30(yb2[w]-yb1[w],t); + //intz = z1 + mulscale30(z2-z1,t); + + if ((bad&12) == 8) + { + if (wallc->sx1 <= xcross) { z2 = intz; iy2 = inty; ix2 = xcross; } + if (wallc->sx2 > xcross) clearbufshort (&mostbuf[xcross], wallc->sx2-xcross, viewheight); + } + else + { + if (xcross <= wallc->sx2) { z1 = intz; iy1 = inty; ix1 = xcross; } + if (xcross > wallc->sx1) clearbufshort (&mostbuf[wallc->sx1], xcross-wallc->sx1, viewheight); + } + } + + y = z1 * InvZtoScale / iy1; + if (ix2 == ix1) + { + mostbuf[ix1] = (short)xs_RoundToInt(y + CenterY); + } + else + { + fixed_t yinc = FLOAT2FIXED(((z2 * InvZtoScale / iy2) - y) / (ix2-ix1)); + qinterpolatedown16short (&mostbuf[ix1], ix2-ix1, FLOAT2FIXED(y + CenterY), yinc); + } + + return bad; +} + +static void PrepWallRoundFix(fixed_t *lwall, fixed_t walxrepeat, int x1, int x2) +{ + // fix for rounding errors + walxrepeat = abs(walxrepeat); + fixed_t fix = (MirrorFlags & RF_XFLIP) ? walxrepeat-1 : 0; + int x; + + if (x1 > 0) + { + for (x = x1; x < x2; x++) + { + if ((unsigned)lwall[x] >= (unsigned)walxrepeat) + { + lwall[x] = fix; + } + else + { + break; + } + } + } + fix = walxrepeat - 1 - fix; + for (x = x2-1; x >= x1; x--) + { + if ((unsigned)lwall[x] >= (unsigned)walxrepeat) + { + lwall[x] = fix; + } + else + { + break; + } + } +} + +void PrepWall (float *swall, fixed_t *lwall, double walxrepeat, int x1, int x2) +{ // swall = scale, lwall = texturecolumn + double top, bot, i; + double xrepeat = fabs(walxrepeat * 65536); + double depth_scale = WallT.InvZstep * WallTMapScale2; + double depth_org = -WallT.UoverZstep * WallTMapScale2; + + i = x1 - centerx; + top = WallT.UoverZorg + WallT.UoverZstep * i; + bot = WallT.InvZorg + WallT.InvZstep * i; + + for (int x = x1; x < x2; x++) + { + double frac = top / bot; + if (walxrepeat < 0) + { + lwall[x] = xs_RoundToInt(xrepeat - frac * xrepeat); + } + else + { + lwall[x] = xs_RoundToInt(frac * xrepeat); + } + swall[x] = float(frac * depth_scale + depth_org); + top += WallT.UoverZstep; + bot += WallT.InvZstep; + } + PrepWallRoundFix(lwall, FLOAT2FIXED(walxrepeat), x1, x2); +} + +void PrepLWall (fixed_t *lwall, double walxrepeat, int x1, int x2) +{ // lwall = texturecolumn + double top, bot, i; + double xrepeat = fabs(walxrepeat * 65536); + double topstep, botstep; + + i = x1 - centerx; + top = WallT.UoverZorg + WallT.UoverZstep * i; + bot = WallT.InvZorg + WallT.InvZstep * i; + + top *= xrepeat; + topstep = WallT.UoverZstep * xrepeat; + botstep = WallT.InvZstep; + + for (int x = x1; x < x2; x++) + { + if (walxrepeat < 0) + { + lwall[x] = xs_RoundToInt(xrepeat - top / bot); + } + else + { + lwall[x] = xs_RoundToInt(top / bot); + } + top += topstep; + bot += botstep; + } + PrepWallRoundFix(lwall, FLOAT2FIXED(walxrepeat), x1, x2); +} + +// pass = 0: when seg is first drawn +// = 1: drawing masked textures (including sprites) +// Currently, only pass = 0 is done or used + +static void R_RenderDecal (side_t *wall, DBaseDecal *decal, drawseg_t *clipper, int pass) +{ + DVector2 decal_left, decal_right, decal_pos; + int x1, x2; + double yscale; + BYTE flipx; + double zpos; + int needrepeat = 0; + sector_t *front, *back; + bool calclighting; + bool rereadcolormap; + FDynamicColormap *usecolormap; + + if (decal->RenderFlags & RF_INVISIBLE || !viewactive || !decal->PicNum.isValid()) + return; + + // Determine actor z + zpos = decal->Z; + front = curline->frontsector; + back = (curline->backsector != NULL) ? curline->backsector : curline->frontsector; + switch (decal->RenderFlags & RF_RELMASK) + { + default: + zpos = decal->Z; + break; + case RF_RELUPPER: + if (curline->linedef->flags & ML_DONTPEGTOP) + { + zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling); + } + else + { + zpos = decal->Z + back->GetPlaneTexZ(sector_t::ceiling); + } + break; + case RF_RELLOWER: + if (curline->linedef->flags & ML_DONTPEGBOTTOM) + { + zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling); + } + else + { + zpos = decal->Z + back->GetPlaneTexZ(sector_t::floor); + } + break; + case RF_RELMID: + if (curline->linedef->flags & ML_DONTPEGBOTTOM) + { + zpos = decal->Z + front->GetPlaneTexZ(sector_t::floor); + } + else + { + zpos = decal->Z + front->GetPlaneTexZ(sector_t::ceiling); + } + } + + WallSpriteTile = TexMan(decal->PicNum, true); + flipx = (BYTE)(decal->RenderFlags & RF_XFLIP); + + if (WallSpriteTile == NULL || WallSpriteTile->UseType == FTexture::TEX_Null) + { + return; + } + + // Determine left and right edges of sprite. Since this sprite is bound + // to a wall, we use the wall's angle instead of the decal's. This is + // pretty much the same as what R_AddLine() does. + + FWallCoords savecoord = WallC; + + double edge_right = WallSpriteTile->GetWidth(); + double edge_left = WallSpriteTile->LeftOffset; + edge_right = (edge_right - edge_left) * decal->ScaleX; + edge_left *= decal->ScaleX; + + double dcx, dcy; + decal->GetXY(wall, dcx, dcy); + decal_pos = { dcx, dcy }; + + DVector2 angvec = (curline->v2->fPos() - curline->v1->fPos()).Unit(); + + decal_left = decal_pos - edge_left * angvec - ViewPos; + decal_right = decal_pos + edge_right * angvec - ViewPos; + + if (WallC.Init(decal_left, decal_right, TOO_CLOSE_Z)) + goto done; + + x1 = WallC.sx1; + x2 = WallC.sx2; + + if (x1 >= clipper->x2 || x2 <= clipper->x1) + goto done; + + WallT.InitFromWallCoords(&WallC); + + // Get the top and bottom clipping arrays + switch (decal->RenderFlags & RF_CLIPMASK) + { + case RF_CLIPFULL: + if (curline->backsector == NULL) + { + if (pass != 0) + { + goto done; + } + mceilingclip = walltop; + mfloorclip = wallbottom; + } + else if (pass == 0) + { + mceilingclip = walltop; + mfloorclip = ceilingclip; + needrepeat = 1; + } + else + { + mceilingclip = openings + clipper->sprtopclip - clipper->x1; + mfloorclip = openings + clipper->sprbottomclip - clipper->x1; + } + break; + + case RF_CLIPUPPER: + if (pass != 0) + { + goto done; + } + mceilingclip = walltop; + mfloorclip = ceilingclip; + break; + + case RF_CLIPMID: + if (curline->backsector != NULL && pass != 2) + { + goto done; + } + mceilingclip = openings + clipper->sprtopclip - clipper->x1; + mfloorclip = openings + clipper->sprbottomclip - clipper->x1; + break; + + case RF_CLIPLOWER: + if (pass != 0) + { + goto done; + } + mceilingclip = floorclip; + mfloorclip = wallbottom; + break; + } + + yscale = decal->ScaleY; + dc_texturemid = WallSpriteTile->TopOffset + (zpos - ViewPos.Z) / yscale; + + // Clip sprite to drawseg + x1 = MAX(clipper->x1, x1); + x2 = MIN(clipper->x2, x2); + if (x1 >= x2) + { + goto done; + } + + PrepWall (swall, lwall, WallSpriteTile->GetWidth(), x1, x2); + + if (flipx) + { + int i; + int right = (WallSpriteTile->GetWidth() << FRACBITS) - 1; + + for (i = x1; i < x2; i++) + { + lwall[i] = right - lwall[i]; + } + } + + // Prepare lighting + calclighting = false; + usecolormap = basecolormap; + rereadcolormap = true; + + // Decals that are added to the scene must fade to black. + if (decal->RenderStyle == LegacyRenderStyles[STYLE_Add] && usecolormap->Fade != 0) + { + usecolormap = GetSpecialLights(usecolormap->Color, 0, usecolormap->Desaturate); + rereadcolormap = false; + } + + rw_light = rw_lightleft + (x1 - WallC.sx1) * rw_lightstep; + if (fixedlightlev >= 0) + dc_colormap = usecolormap->Maps + fixedlightlev; + else if (fixedcolormap != NULL) + dc_colormap = fixedcolormap; + else if (!foggy && (decal->RenderFlags & RF_FULLBRIGHT)) + dc_colormap = usecolormap->Maps; + else + calclighting = true; + + // Draw it + if (decal->RenderFlags & RF_YFLIP) + { + sprflipvert = true; + yscale = -yscale; + dc_texturemid -= WallSpriteTile->GetHeight(); + } + else + { + sprflipvert = false; + } + + MaskedScaleY = float(1 / yscale); + do + { + dc_x = x1; + ESPSResult mode; + + mode = R_SetPatchStyle (decal->RenderStyle, (float)decal->Alpha, decal->Translation, decal->AlphaColor); + + // R_SetPatchStyle can modify basecolormap. + if (rereadcolormap) + { + usecolormap = basecolormap; + } + + if (mode == DontDraw) + { + needrepeat = 0; + } + else + { + int stop4; + + if (mode == DoDraw0) + { // 1 column at a time + stop4 = dc_x; + } + else // DoDraw1 + { // up to 4 columns at a time + stop4 = x2 & ~3; + } + + while ((dc_x < stop4) && (dc_x & 3)) + { + if (calclighting) + { // calculate lighting + dc_colormap = usecolormap->Maps + (GETPALOOKUP (rw_light, wallshade) << COLORMAPSHIFT); + } + R_WallSpriteColumn (R_DrawMaskedColumn); + dc_x++; + } + + while (dc_x < stop4) + { + if (calclighting) + { // calculate lighting + dc_colormap = usecolormap->Maps + (GETPALOOKUP (rw_light, wallshade) << COLORMAPSHIFT); + } + rt_initcols(); + for (int zz = 4; zz; --zz) + { + R_WallSpriteColumn (R_DrawMaskedColumnHoriz); + dc_x++; + } + rt_draw4cols (dc_x - 4); + } + + while (dc_x < x2) + { + if (calclighting) + { // calculate lighting + dc_colormap = usecolormap->Maps + (GETPALOOKUP (rw_light, wallshade) << COLORMAPSHIFT); + } + R_WallSpriteColumn (R_DrawMaskedColumn); + dc_x++; + } + } + + // If this sprite is RF_CLIPFULL on a two-sided line, needrepeat will + // be set 1 if we need to draw on the lower wall. In all other cases, + // needrepeat will be 0, and the while will fail. + mceilingclip = floorclip; + mfloorclip = wallbottom; + R_FinishSetPatchStyle (); + } while (needrepeat--); + + colfunc = basecolfunc; + hcolfunc_post1 = rt_map1col; + hcolfunc_post4 = rt_map4cols; + + R_FinishSetPatchStyle (); +done: + WallC = savecoord; +} diff --git a/src/r_utility.cpp b/src/r_utility.cpp index a2351b25b..4c00bcf06 100644 --- a/src/r_utility.cpp +++ b/src/r_utility.cpp @@ -109,6 +109,7 @@ int viewwindowy; DVector3 ViewPos; DAngle ViewAngle; DAngle ViewPitch; +DAngle ViewRoll; DVector3 ViewPath[2]; extern "C" @@ -543,11 +544,13 @@ void R_InterpolateView (player_t *player, double Frac, InterpolationViewer *ivie ViewAngle = (nviewangle + AngleToFloat(LocalViewAngle & 0xFFFF0000)).Normalized180(); DAngle delta = player->centering ? DAngle(0.) : AngleToFloat(int(LocalViewPitch & 0xFFFF0000)); ViewPitch = clamp((iview->New.Angles.Pitch - delta).Normalized180(), player->MinPitch, player->MaxPitch); + ViewRoll = iview->New.Angles.Roll.Normalized180(); } else { ViewPitch = (iview->Old.Angles.Pitch + deltaangle(iview->Old.Angles.Pitch, iview->New.Angles.Pitch) * Frac).Normalized180(); ViewAngle = (oviewangle + deltaangle(oviewangle, nviewangle) * Frac).Normalized180(); + ViewRoll = (iview->Old.Angles.Roll + deltaangle(iview->Old.Angles.Roll, iview->New.Angles.Roll) * Frac).Normalized180(); } // Due to interpolation this is not necessarily the same as the sector the camera is in. @@ -800,6 +803,7 @@ void R_SetupFrame (AActor *actor) P_AimCamera (camera, campos, camangle, viewsector, unlinked); // fixme: This needs to translate the angle, too. iview->New.Pos = campos; iview->New.Angles.Yaw = camangle; + r_showviewer = true; // Interpolating this is a very complicated thing because nothing keeps track of the aim camera's movement, so whenever we detect a portal transition // it's probably best to just reset the interpolation for this move. @@ -869,6 +873,10 @@ void R_SetupFrame (AActor *actor) double quakefactor = r_quakeintensity; DAngle an; + if (jiggers.RollIntensity != 0 || jiggers.RollWave != 0) + { + ViewRoll += QuakePower(quakefactor, jiggers.RollIntensity, jiggers.RollWave, jiggers.Falloff, jiggers.WFalloff); + } if (jiggers.RelIntensity.X != 0 || jiggers.RelOffset.X != 0) { an = camera->Angles.Yaw; diff --git a/src/r_utility.h b/src/r_utility.h index a89e4e50c..f15d42052 100644 --- a/src/r_utility.h +++ b/src/r_utility.h @@ -15,6 +15,7 @@ extern DCanvas *RenderTarget; extern DVector3 ViewPos; extern DAngle ViewAngle; extern DAngle ViewPitch; +extern DAngle ViewRoll; extern DVector3 ViewPath[2]; extern "C" int centerx, centerxwide; diff --git a/src/thingdef/thingdef_codeptr.cpp b/src/thingdef/thingdef_codeptr.cpp index d801a7e80..fe464b137 100644 --- a/src/thingdef/thingdef_codeptr.cpp +++ b/src/thingdef/thingdef_codeptr.cpp @@ -320,6 +320,40 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, GetDistance) return 0; } +//========================================================================== +// +// GetAngle +// +// NON-ACTION function to get the angle in degrees (normalized to -180..180) +// +//========================================================================== +DEFINE_ACTION_FUNCTION_PARAMS(AActor, GetAngle) +{ + if (numret > 0) + { + assert(ret != NULL); + PARAM_SELF_PROLOGUE(AActor); + PARAM_BOOL(relative); + PARAM_INT_OPT(ptr) { ptr = AAPTR_TARGET; } + + AActor *target = COPY_AAPTR(self, ptr); + + if (!target || target == self) + { + ret->SetFloat(0); + } + else + { + DVector3 diff = self->Vec3To(target); + DAngle angto = diff.Angle(); + if (relative) angto = deltaangle(self->Angles.Yaw, angto); + ret->SetFloat(angto.Degrees); + } + return 1; + } + return 0; +} + //========================================================================== // // GetSpawnHealth @@ -4951,7 +4985,10 @@ DEFINE_ACTION_FUNCTION_PARAMS(AActor, A_QuakeEx) PARAM_FLOAT_OPT(mulWaveZ) { mulWaveZ = 1.; } PARAM_INT_OPT(falloff) { falloff = 0; } PARAM_INT_OPT(highpoint) { highpoint = 0; } - P_StartQuakeXYZ(self, 0, intensityX, intensityY, intensityZ, duration, damrad, tremrad, sound, flags, mulWaveX, mulWaveY, mulWaveZ, falloff, highpoint); + PARAM_INT_OPT(rollIntensity) { rollIntensity = 0; } + PARAM_FLOAT_OPT(rollWave) { rollWave = 0.; } + P_StartQuakeXYZ(self, 0, intensityX, intensityY, intensityZ, duration, damrad, tremrad, sound, flags, mulWaveX, mulWaveY, mulWaveZ, falloff, highpoint, + rollIntensity, rollWave); return 0; } diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index 55f20df03..efb7be0c2 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -256,6 +256,7 @@ static FFlagDef ActorFlagDefs[]= DEFINE_FLAG(MF7, FORCEDECAL, AActor, flags7), DEFINE_FLAG(MF7, LAXTELEFRAGDMG, AActor, flags7), DEFINE_FLAG(MF7, ICESHATTER, AActor, flags7), + DEFINE_FLAG(MF7, ALLOWTHRUFLAGS, AActor, flags7), // Effect flags DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects), diff --git a/src/vectors.h b/src/vectors.h index 8b81d05b2..caa9c2f20 100644 --- a/src/vectors.h +++ b/src/vectors.h @@ -1135,7 +1135,8 @@ struct TRotator Angle Pitch; // up/down Angle Yaw; // left/right - Angle Roll; // rotation about the forward axis + Angle Roll; // rotation about the forward axis. + Angle CamRoll; // Roll specific to actor cameras. Used by quakes. TRotator () { diff --git a/src/version.h b/src/version.h index fba8d0786..47f3a347c 100644 --- a/src/version.h +++ b/src/version.h @@ -76,7 +76,7 @@ const char *GetVersionString(); // Use 4500 as the base git save version, since it's higher than the // SVN revision ever got. -#define SAVEVER 4543 +#define SAVEVER 4544 #define SAVEVERSTRINGIFY2(x) #x #define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x) diff --git a/wadsrc/static/actors/actor.txt b/wadsrc/static/actors/actor.txt index 9222741bd..b6543cd7e 100644 --- a/wadsrc/static/actors/actor.txt +++ b/wadsrc/static/actors/actor.txt @@ -42,6 +42,7 @@ ACTOR Actor native //: Thinker native bool IsPointerEqual(int ptr_select1, int ptr_select2); native int CountInv(class itemtype, int ptr_select = AAPTR_DEFAULT); native float GetDistance(bool checkz, int ptr = AAPTR_DEFAULT); + native float GetAngle(bool relative, int ptr = AAPTR_DEFAULT); native int GetSpawnHealth(); native int GetGibHealth(); @@ -280,7 +281,7 @@ ACTOR Actor native //: Thinker native void A_SetUserArrayFloat(name varname, int index, float value); native void A_SetSpecial(int spec, int arg0 = 0, int arg1 = 0, int arg2 = 0, int arg3 = 0, int arg4 = 0); native void A_Quake(int intensity, int duration, int damrad, int tremrad, sound sfx = "world/quake"); - native void A_QuakeEx(int intensityX, int intensityY, int intensityZ, int duration, int damrad, int tremrad, sound sfx = "world/quake", int flags = 0, float mulWaveX = 1, float mulWaveY = 1, float mulWaveZ = 1, int falloff = 0, int highpoint = 0); + native void A_QuakeEx(int intensityX, int intensityY, int intensityZ, int duration, int damrad, int tremrad, sound sfx = "world/quake", int flags = 0, float mulWaveX = 1, float mulWaveY = 1, float mulWaveZ = 1, int falloff = 0, int highpoint = 0, int rollIntensity = 0, float rollWave = 0); action native A_SetTics(int tics); native void A_SetDamageType(name damagetype); native void A_DropItem(class item, int dropamount = -1, int chance = 256); diff --git a/wadsrc/static/actors/shared/camera.txt b/wadsrc/static/actors/shared/camera.txt index 2dcc12aab..46816f9ea 100644 --- a/wadsrc/static/actors/shared/camera.txt +++ b/wadsrc/static/actors/shared/camera.txt @@ -15,6 +15,7 @@ ACTOR SecurityCamera native +NOGRAVITY +DONTSPLASH RenderStyle None + CameraHeight 0 } ACTOR AimingCamera : SecurityCamera native diff --git a/wadsrc/static/actors/shared/movingcamera.txt b/wadsrc/static/actors/shared/movingcamera.txt index 13ae8a922..4ba229154 100644 --- a/wadsrc/static/actors/shared/movingcamera.txt +++ b/wadsrc/static/actors/shared/movingcamera.txt @@ -28,6 +28,7 @@ ACTOR ActorMover : PathFollower native ACTOR MovingCamera : PathFollower native { + CameraHeight 0 } diff --git a/wadsrc/static/actors/shared/sharedmisc.txt b/wadsrc/static/actors/shared/sharedmisc.txt index 04bfe0f46..bfe7d5519 100644 --- a/wadsrc/static/actors/shared/sharedmisc.txt +++ b/wadsrc/static/actors/shared/sharedmisc.txt @@ -51,6 +51,7 @@ ACTOR MapSpot +NOGRAVITY +DONTSPLASH RenderStyle None + CameraHeight 0 } // same with different editor number for Legacy maps ----------------------- diff --git a/wadsrc/static/xlat/eternity.txt b/wadsrc/static/xlat/eternity.txt index bca1a6769..bfdac2cec 100644 --- a/wadsrc/static/xlat/eternity.txt +++ b/wadsrc/static/xlat/eternity.txt @@ -230,3 +230,4 @@ enum 443 = 0, Generic_Crusher(0) 444 = 0, Teleport(0) 445 = 0, Teleport_NoFog(0) +446 = 0, Teleport_Line(0)