All pastes #279788 Raw Edit

Stuff

public text v1 · immutable
·published 2006-12-15 09:44 UTC
rendered paste body
class Effect_ShadowController extends Actor
	config(User);

// the type of shadow projector we're going to use
var class<Effect_ShadowProjector> ShadowClass;

// an array of shadows, the maximum allowed per player can be specified below
var array<Effect_ShadowProjector> Shadows;

// an array of static lights
var array<Light> LightList;

// a special variable type, lastlight is used for fading
struct LightGroup
{
	var Actor CurrentLight;
	var Actor LastLight;
};

// an array of lightgroups, filled when spawned
var array<LightGroup> Lights;

// the maximum distance a light can be from a player to cast a shadow
var float MaxLightDistance;

// update frequency for filling light array
var float UpdateFrequency;

// the sun
var SunLight SunLightActor;

// turns on/off all shadows
var bool bShadowActive;

// maximum allowed shadows per player, configurable
var globalconfig int MaxShadows;

simulated function Initialize() {
	local Light LightActor;
	local array<SunLight> SunLights;
	local int i;

	// set the sunlight, pick the brightest one if we have multiple
	foreach AllActors(class'Light',LightActor) 	{
		if (SunLight(LightActor) != None) {
			SunLights[i] = SunLight(LightActor);
			i++;
		}
		else if (LightActor.LightType != LT_None && LightActor.LightBrightness > 1) {
			LightList[LightList.Length] = LightActor;
		}
	}
	for (i=0;i<SunLights.Length;i++) {
		if (SunLightActor == None || SunLightActor.LightBrightness < SunLights[i].LightBrightness)
			SunLightActor = SunLights[i];
	}

	// fill the arrays with placeholders
	Shadows.Insert(0,MaxShadows);
	Lights.Insert(0,MaxShadows);

	// enable
	bShadowActive = true;

	// build light array
	FillLights();
	//Owner.AttachToBone(self, 'shadow');
}

simulated function Timer() {
	FillLights();
}

simulated function FillLights() {
	local int i, j;
	local actor LightActor;
	local array<Actor> OrigLight;
	local float Diff;

	if(Pawn(Owner) != None && Pawn(Owner).Health < 1)
		return;
	// clear array of lights, leave LastLight alone to fade
 	for (i=0;i<MaxShadows;i++) {
		OrigLight[i] = Lights[i].CurrentLight;
		Lights[i].CurrentLight = None;
	}

	// set the location of the controller, for light detection purposes
	SetLocation(Owner.Location);

	// shadow is off
	if (!bShadowActive)
		return;

	// build the array of lights, we prioritize by both brightness and distance
	for (i=0;i<LightList.Length;i++) {
		if( (LightList[i].bStatic || LightList[i].bDynamicLight)) {
			Diff = VSize(LightList[i].Location-Owner.Location);
			if(Diff < MaxLightDistance && Diff > (Owner.CollisionRadius * 0.5) &&
				IsVisible(LightList[i].Location)) {

				if (Lights[0].CurrentLight != None &&
					LightPriority(Lights[0].CurrentLight.LightBrightness,Lights[0].CurrentLight.LightRadius,VSize(Lights[0].CurrentLight.Location-Owner.Location)) > LightPriority(LightList[i].LightBrightness,LightList[i].LightRadius,Diff))
					continue;

			// this puts them in order of priority
				for (j=1;j<MaxShadows;j++)
					Lights[j].CurrentLight = Lights[j-1].CurrentLight;

				Lights[0].CurrentLight = LightList[i];
			}
		}
	}
	foreach DynamicActors(class'Actor',LightActor) {
		// if we are within the light's radius, and it's shining on at least one part of us, then add it to the array
		if(!LightActor.bUnlit &&
			(LightActor.bStatic || LightActor.bDynamicLight) &&
			LightActor.LightEffect != LE_SUNLIGHT &&
			LightActor.LightBrightness > 1) {
				Diff = VSize(LightActor.Location - Owner.Location);
				if(Diff < MaxLightDistance &&
					LightActor.LightRadius >= (Diff * 0.041) &&
					Diff > (Owner.CollisionRadius * 0.5) &&
					IsVisible(LightActor.Location)) {

					if (Lights[0].CurrentLight != None &&
				LightPriority(Lights[0].CurrentLight.LightBrightness,Lights[0].CurrentLight.LightRadius,VSize(Lights[0].CurrentLight.Location-Owner.Location)) > LightPriority(LightActor.LightBrightness,LightActor.LightRadius,Diff))
						continue;

				// this puts them in order of priority
					for (i=1;i<MaxShadows;i++)
						Lights[i].CurrentLight = Lights[i-1].CurrentLight;

					Lights[0].CurrentLight = LightActor;
				}
		}
	}

	// we'll use the sunlight as the very lowest priority of lights
	for (i=0;i<MaxShadows;i++)
		if (Lights[i].CurrentLight == None &&
			SunlightActor != None &&
			(IsVisible(SunLightActor.Location) || Lights[0].CurrentLight == None))
		{
			Lights[i].CurrentLight = SunLightActor;
			break;
		}


	// set up last light for fading
	for (i=0;i<MaxShadows;i++) {
		if (OrigLight[i] != Lights[i].CurrentLight) {
			if (i > 0) {
				if (OrigLight[i] == Lights[i-1].CurrentLight || OrigLight[i] == OrigLight[i-1])
					OrigLight[i] = None;
			}
			Lights[i].LastLight = OrigLight[i];
		}
	}

	// loop it, loop it good
	SetTimer(UpdateFrequency,false);
}

// simple function for determining the priority of a light
function float LightPriority(float Brightness, float Radius, float Distance) {
	local float Priority;

	// brightness takes higher priority
	Priority = (Brightness*10)/Distance;

	// lights that are very close get higher priority
	if (Distance < 0.1 * MaxLightDistance)
		Priority *= 1.5;

	// lights with very small radii shouldn't really cast shadows
	if (Radius < 2.0)
		Priority *= (Radius*0.38);

	return Priority;
}

function bool IsVisible(vector Loc) {
	local vector FootLocation;

	// get a location near the feet
	FootLocation = Owner.Location;
	FootLocation.Z -= Owner.CollisionHeight*0.49;

	// not very clean, returns true if either the head, feet, or middle torso of the player is visible to Loc
	if (FastTrace(Loc,Owner.Location) || FastTrace(Loc,Owner.GetBoneCoords('head').Origin) || FastTrace(Loc,FootLocation))
		return true;
}

function Tick(float dt) {
	// fallback
	if (Owner == None)
		return;

	// update all shadows
	UpdateShadows(dt);
}

function UpdateShadows(float dt) {
	local int i;

	for (i=0;i<Lights.Length;i++) {
		// disable the shadow attached to this slot if light no longer active, or if manually made inactive
		if ((Lights[i].CurrentLight == None && Lights[i].LastLight == None) || !bShadowActive) {
			if (Shadows[i] != None)
				Shadows[i].DisableShadow();
			continue;
		}

		// spawn a new shadow if it doesn't already exist
		if (Shadows[i] == None)
			Shadows[i] = SpawnShadow(rotator(Lights[i].CurrentLight.Location-Location));

		// update each shadow
		Shadows[i].UpdateShadow(dt,i,self);
	}
}

function Effect_ShadowProjector SpawnShadow(rotator LightRotation) {
	local Effect_ShadowProjector NewShadow;

	// spawn and initialize a shadow projector
	NewShadow = spawn(ShadowClass,Owner,,Location,LightRotation);
	NewShadow.Disable('Tick');
	NewShadow.InitializeFor(self);

	return NewShadow;
}

function Destroyed() {
	local int i;

	Disable('Tick');

	// destroy all shadows
	for (i=0;i<MaxShadows;i++) {
		if (Shadows[i] != None)
			Shadows[i].Destroy();
	}
}

defaultproperties {
	// internal
	UpdateFrequency=0.20
	MaxShadows=1
	MaxLightDistance=1000.0
	ShadowClass=class'Effect_ShadowProjector'

	// settings
	bNoDelete=false
	bStatic=false
	bHidden=true
	DrawType=DT_None
	RemoteRole=ROLE_None
	Physics=PHYS_None
}