rendered paste bodyclass Effect_ShadowProjector extends Projector config(user);
var() vector LightDirection;
var() float LightDistance, InterpolationRate, MaxFOV, FadeSpeed, DarknessScale;
var ShadowBitmapMaterial ShadowTexture;
var bool bFadeIn;
var bool bLastAggressiveLOD;
var bool bCurrentBlobbiness;
var bool bDirtyNextTime;
var float DetailDropTime;
var float DetailIncreaseTime;
var globalconfig enum DetailEnum {
VeryLow,
Low,
Medium,
High,
Maximum
} ShadowDetail;
function PostBeginPlay()
{
//Super(Actor).PostBeginPlay();
}
// these don't need to tick, we update all shadows at the same time in the controller
function Tick(float dt) {}
// this turns the shadow off
function DisableShadow()
{
// detach
DetachProjector();
// stop shadow texture from being reuploaded to video here
if (ShadowTexture != None)
{
ShadowTexture.Dirty = false;
ShadowTexture.ShadowActor = None;
}
}
// initialize
function InitializeFor(Effect_ShadowController ShadowController)
{
if (ShadowController.Owner != None)
{
// set the Owner
SetOwner(ShadowController.Owner);
// allocate the shadow texture
if(ShadowTexture == None) CreateShadowTexture();
// set projector texture
//ProjTexture = ShadowTexture;
// already done in Create
// initialize the shadow texture
if (ShadowTexture != None)
{
ShadowTexture.Invalid = false;
ShadowTexture.ShadowActor = Owner;
ShadowTexture.bBlobShadow = false;
ShadowTexture.CullDistance = CullDistance;
//Owner.AttachToBone(self, 'shadow');
}
else
{
log(Name$".InitializeFor: No shadow texture! Escaping...");
Destroy();
}
}
else
log(Name$".InitializeFor: No Owner!");
}
// updates this shadow's location, rotation, and FOV
simulated function UpdateShadow(float dt, int LN, Effect_ShadowController ShadowController)
{
local Plane BoundingSphere;
local Actor ShadowLight;
local vector Diff, ShadowLocation, Origin;
local rotator LightRotation, AdjustedRotation;
local float Interpolation;
local bool bFadeOut;
local float DrawS;
if(Level.bAggressiveLOD) return;
DetachProjector(true);
if (Owner == None || Owner.bHidden || ShadowTexture == None)
return;
// fallback and destroy
if (ShadowTexture.Invalid)
{
Destroy();
return;
}
// cull more if we haven't rendered this pawn in 5 seconds
if (Level.TimeSeconds - Owner.LastRenderTime > 5)
CullDistance = 0.5*Default.CullDistance;
else
CullDistance = Default.CullDistance;
// cull shadows much earlier if below min framerate, important
if (Level.bDropDetail) {
if(Level.bAggressiveLOD && bLastAggressiveLOD) {
//log(self@"aggressivelod true");
DropShadowDetail();
}
ShadowTexture.CullDistance = 0.6*CullDistance;
} else {
if(!bLastAggressiveLOD)
IncreaseShadowDetail();
ShadowTexture.CullDistance = CullDistance;
}
bLastAggressiveLOD = Level.bAggressiveLOD;
// in case shadow was disabled earlier
ShadowTexture.ShadowActor = Owner;
// set light
ShadowLight = ShadowController.Lights[LN].CurrentLight;
// fade out if necessary
if (ShadowController.Lights[LN].LastLight != None)
{
if (ShadowTexture.ShadowDarkness > 5)
{
ShadowLight = ShadowController.Lights[LN].LastLight;
bFadeOut = true;
}
else
{
ShadowController.Lights[LN].LastLight = None;
bFadeOut = false;
bFadeIn = true;
DarknessScale = 0;
}
}
// fallback if no more lights after fadeout
if (ShadowLight == None)
return;
// get the direction of the light
Diff = ShadowLight.Location - Owner.Location;
// set light distance
if (ShadowLight.LightEffect == LE_Sunlight)
LightDistance = ShadowController.MaxLightDistance*0.3;
else
LightDistance = FClamp(VSize(Diff), ShadowController.MaxLightDistance*0.1, ShadowController.MaxLightDistance*0.3);
MaxTraceDistance = LightDistance * 3;
// get a location along the path of the light slightly away from center of player
ShadowLocation = Owner.Location + 4*Normal(Diff);
if (ShadowLocation.Z < Owner.Location.Z)
ShadowLocation.Z = Owner.Location.Z+4;
//ShadowLocation = Owner.GetBoneCoords('shadow').Origin;
// set location
SetLocation(ShadowLocation + vect(0,0,-8));
// calculate FOV
BoundingSphere = Owner.GetRenderBoundingSphere();
FOV = (Atan(BoundingSphere.W*2 + 160, LightDistance) * 180/PI);
// set rotation, compensate for FOV warping -- kinda hackish, but fixes shadows that
// bend so much they detach from the pawn
AdjustedRotation = rotator(Owner.Location-ShadowLight.Location);
AdjustedRotation.Pitch = Clamp(AdjustedRotation.Pitch, -20500, -9500);
SetRotation(AdjustedRotation);
// determine correct direction of light
LightDirection = -vector(AdjustedRotation)*LightDistance;
// set light direction
ShadowTexture.LightDirection = Normal(LightDirection);
// set lightdistance
ShadowTexture.LightDistance = LightDistance;
// update shadow texture FOV
ShadowTexture.LightFOV = FOV;
// set the drawscale, exaggerate a bit
DrawS = (LightDistance * 0.82) * tan(0.5 * FOV * PI / 180) / (0.45 * ShadowTexture.USize);
SetDrawScale(DrawS);
// fade out gracefully
if (bFadeOut)
ShadowTexture.ShadowDarkness = Max(ShadowTexture.ShadowDarkness - (FadeSpeed*dt), 0);
else
{
ShadowTexture.ShadowDarkness = 140*(1.0-(FClamp(VSize(ShadowLight.Location-Owner.Location)/ShadowController.MaxLightDistance, 0.0, 1.0))) + 70;
if (bFadeIn && DarknessScale < 1.0)
{
DarknessScale = FMin(DarknessScale + ((FadeSpeed*0.007)*dt), 1.0);
ShadowTexture.ShadowDarkness = float(ShadowTexture.ShadowDarkness)*DarknessScale;
}
else
bFadeIn = false;
}
// dirty texture for reuploading to video
bDirtyNextTime = !bDirtyNextTime;
if(!Level.bAggressiveLOD || bDirtyNextTime)
ShadowTexture.Dirty = true;
// reattach projector
AttachProjector();
}
function Destroyed()
{
// free shadow texture from the object pool
if (ShadowTexture != None)
{
ShadowTexture.ShadowActor = None;
if (!ShadowTexture.Invalid)
Level.ObjectPool.FreeObject(ShadowTexture);
// must set to none
ShadowTexture = None;
ProjTexture = None;
}
Super.Destroyed();
}
simulated function DropShadowDetail() {
local bool bChangeQuality;
if(ShadowTexture == None) return;
if(!Level.bAggressiveLOD) return;
if(Level.TimeSeconds - DetailDropTime < 0.5) return; // ignore dropdetails if it's been less than 0.5 sec
//log(self@" ... bing... Decreasing shadow detail from"@ShadowDetail);
switch(ShadowDetail) {
case Maximum:
bCurrentBlobbiness = False;
ShadowDetail = High;
bChangeQuality = True;
break;
case High:
bCurrentBlobbiness = False;
ShadowDetail = Medium;
bChangeQuality = True;
break;
case Medium:
bCurrentBlobbiness = False;
ShadowDetail = Low;
bChangeQuality = True;
break;
case Low:
bCurrentBlobbiness = False;
ShadowDetail = VeryLow;
bChangeQuality = True;
break;
case VeryLow:
bCurrentBlobbiness = True;
ShadowDetail = VeryLow;
ShadowTexture.bBlobShadow = true;
break;
}
if(bChangeQuality) {
DetailDropTime = Level.TimeSeconds;
if(!ShadowTexture.Invalid)
Level.ObjectPool.FreeObject(ShadowTexture);
ShadowTexture = None;
}
if(ShadowTexture == None) {
CreateShadowTexture();
}
}
simulated function IncreaseShadowDetail() {
local bool bChangeQuality;
if(Level.TimeSeconds - DetailIncreaseTime < 5 || ShadowDetail == Maximum) return;
if(!Level.bDropDetail) {
//log(self@" ... bing... Increasing shadow detail from"@ShadowDetail);
switch(ShadowDetail) {
case VeryLow:
if(ShadowTexture.bBlobShadow) {
ShadowTexture.bBlobShadow = false;
bCurrentBlobbiness = false;
} else {
ShadowDetail = Low;
bCurrentBlobbiness = false;
bChangeQuality = True;
}
break;
case Low:
bCurrentBlobbiness = False;
ShadowDetail = Medium;
bChangeQuality = True;
break;
case Medium:
bCurrentBlobbiness = False;
ShadowDetail = High;
bChangeQuality = True;
break;
case High:
bCurrentBlobbiness = False;
ShadowDetail = Maximum;
bChangeQuality = True;
break;
}
log(self@"New shadow detail="@ShadowDetail);
}
if(bChangeQuality && ShadowTexture != None) {
DetailIncreaseTime = Level.TimeSeconds;
if(!ShadowTexture.Invalid)
Level.ObjectPool.FreeObject(ShadowTexture);
ShadowTexture = None;
}
if(ShadowTexture == None) CreateShadowTexture();
}
simulated function CreateShadowTexture() {
switch (ShadowDetail) {
case VeryLow:
ShadowTexture=ShadowBitmapMaterial(Level.ObjectPool.AllocateObject(class'Effect_ShadowBitmapMaterialVLow'));
break;
case Medium:
ShadowTexture = ShadowBitmapMaterial(Level.ObjectPool.AllocateObject(class'Effect_ShadowBitmapMaterialMed'));
break;
case High:
ShadowTexture = ShadowBitmapMaterial(Level.ObjectPool.AllocateObject(class'Effect_ShadowBitmapMaterialHigh'));
break;
case Maximum:
ShadowTexture = ShadowBitmapMaterial(Level.ObjectPool.AllocateObject(class'Effect_ShadowBitmapMaterialMax'));
break;
Default:
ShadowTexture = ShadowBitmapMaterial(Level.ObjectPool.AllocateObject(class'Effect_ShadowBitmapMaterialLow'));
break;
}
if(ShadowTexture != None) {
ShadowTexture.bBlobShadow = bCurrentBlobbiness;
ShadowTexture.Invalid = false;
ShadowTexture.ShadowActor = Owner;
ShadowTexture.CullDistance = CullDistance;
ProjTexture=ShadowTexture;
}
}
// You can delete this function if you're using it on a game that has bNoProjectOnOwner, then set that
// in the Default Props.
simulated event Touch(Actor Other) {
if(Other == Owner) return;
super.Touch(Other);
}
defaultproperties
{
ShadowDetail=Low
bProjectActor=true
// how quickly we should interpolate
InterpolationRate=2.5
// this should be dynamically updated also
MaxTraceDistance=3500
// maximum FOV
MaxFOV=80.0
// how quickly to fade in/out
FadeSpeed=500.0
bProjectOnParallelBSP=true
bProjectOnAlpha=false
bProjectStaticMesh=True
bProjectParticles=False
bClipBSP=False
bGradient=True
bStatic=false
bOwnerNoSee=False
bDynamicAttach=true
bHidden=False
RemoteRole=ROLE_None
CullDistance=1200.0
}