Simple Cloud Spawner

Post your example zscripts/ACS scripts/etc here.
Forum rules
The Projects forums are only for projects. If you are asking questions about a project, either find that project's thread, or start a thread in the General section instead.

Got a cool project idea but nothing else? Put it in the project ideas thread instead!

Projects for any Doom-based engine (especially 3DGE) are perfectly acceptable here too.

Please read the full rules for more details.
Hey Doomer
Posts: 283
Joined: Sat Sep 25, 2021 3:38 am

Simple Cloud Spawner

Post by Hey Doomer »

A big problem with clouds in outdoor areas in Doom is the obvious sky illusion that makes an outdoor sector seem much taller than it really is. That leaves one with few possibilities for convincing clouds. And surely Mars is a windy and dusty place. I've checked out the Realm667 fog and cloud generators that only go part way. The problem, it seems to me, is that one only wants clouds in sectors tall enough to make them not seem absurd and have a Goldilocks number of them. (Too few look silly, too many look silly.)

I think I've done that, but the results are rough so far. Here are screenshots:

https://i.postimg.cc/2ySNJ4Zt/Screensho ... 093216.png
https://i.postimg.cc/LX0x9fWw/Screensho ... 093159.png

As you can see there are a number of reddish-brown clouds in the courtyard. These gently float this way and that while slowly changing size and opacity as much as can be allowed using integers. Note the cloud shadows. These pan the courtyard also.

ZScript:

Code: Select all

class clouds_EventHandler : EventHandler
{
	// returns area of sector (irregular polygon)
	float polygonArea(Sector sec)
	{
		Array<float> lineAreas;
		for (int i = 0; i < sec.Lines.Size(); i++)
		{
			int v1x = sec.Lines[i].V1.P.x;
			int v1y = sec.Lines[i].V1.P.y;

			int v2x = sec.Lines[i].V2.P.x;
			int v2y = sec.Lines[i].V2.P.y;

			float avgY = (v1y + v2y) * 0.5 * .015625;
			float widthX = (v2x - v1x) * .015625;
			lineAreas.Push(avgY * widthX);
		}
		float totArea;
		for (int i = 0; i < lineAreas.Size(); i++)
		{
			totArea += lineAreas[i];
		}
		return totArea;
	}
	// add cloud spawners
	override void WorldLoaded(WorldEvent e)
	{
		Array<string> sky = {
			"F_SKY1",
			"F_SKY2",
			"F_SKY3",
			"F_SKY4"
		};

		int FLOOR = 0;
		int CEILING = 1;
		TexMan texture;

		for(int i = 0; i < Level.Sectors.Size(); i++)
		{
			textureid ceilingid = Level.Sectors[i].GetTexture(CEILING);
			SecPlane ceilingplane = Level.Sectors[i].CeilingPlane;
			SecPlane floorplane = Level.Sectors[i].FloorPlane;

			int ceilingZ = ceilingplane.ZAtPoint((Level.Sectors[i].CenterSpot.x, Level.Sectors[i].CenterSpot.y));
			int floorZ = floorplane.ZAtPoint((Level.Sectors[i].CenterSpot.x, Level.Sectors[i].CenterSpot.y));
			int centerheight = ceilingZ - floorZ;

			if (sky.Find(texture.GetName(ceilingid)) != sky.Size() && centerheight > 128)
			{
				float pArea = polygonArea(Level.Sectors[i]);
				int clouds = int(pArea * .03125);

				vector3 v3 = (Level.Sectors[i].CenterSpot.x, Level.Sectors[i].CenterSpot.y, floorZ);

				if (CVar.FindCVar("cloud_debug").GetBool())
				{
					console.printf("Sector: %d, Polygon Area: %f, needing %d clouds", i, pArea, clouds);
				}

				for (int ii = 0; ii <= clouds; ii++)
				{
					Actor.Spawn("CloudSpawner", v3);
				}
			}
		}
	}
}
How this works is very simple. (It's the only way I could understand the math.) The script loops through sectors and spawns clouds in any sector with a defined SKY and a mid sector height of at least 128. It spawns a number of cloud spawners dependent on sector area. Sectors, note, are 2D irregular polygons; the area is calculated in the polygonArea function. The larger the sector, the more cloud spawners are dumped into it.

DECORATE:

Code: Select all

Actor CloudSpawner
{
	States
	{
	Spawn:
		TNT1 A 1
		TNT1 A 1 A_SpawnItemEx("Cloud", FRandom(-128, -128), FRandom(-128, -128), FRandom(ceilingZ - 32, ceilingZ - 16), FRandom(-0.25, 0.25), FRandom(-0.5, 0.5), 0)
		STOP
	}
}

Actor Cloud
{
	var int user_XScale;
	var int user_YScale;
	var int user_dir;
	
	+MISSILE
	+NOBLOCKMAP
	+NOGRAVITY
	+CASTSPRITESHADOW
	+BOUNCEONWALLS
	+BOUNCEONCEILINGS
	+NOBOUNCESOUND
	+INTERPOLATEANGLES
	RenderStyle Add
	XScale 0.5
	YScale 0.5
	Alpha 0.15
	
	States
	{
	Spawn:
		TNT1 A 0
		TNT1 A 1
		{
			user_XScale = 5;
			user_YScale = 5;
			user_dir = 1;
		}
		TNT1 A 1
		{
			user_XScale += user_dir;
			user_YScale += user_dir;
			if (abs(user_dir) == 10)
			{
				user_dir *= -1;
			}
		}
		CMFX A 1 A_FadeTo(0.15 + (0.05 * user_dir), 0.1, FTF_CLAMP)
		CMFX AAAAA Random(35, 72) A_SetScale((user_XScale + 5) * 0.05, user_YScale * 0.05)
		GOTO SPAWN+2
	}
}
Not fancy but functional. Each spawner spawns a cloud at a random location within 16-32 units of the ceiling, moving at a random speed and direction. The Cloud itself starts with the same X and Y scale and slowly increases or decreases scale along with opacity. I've added a fudge factor to the Y scale to make clouds appear oblong. My cloud sprite is a tinted PNG of a real cumulus cloud. I've left the original for the curious. The plain image is too hard to see against a bright sky; I like to think Martian clouds are reddish. I've watched this run for a few minutes here and there, and it's not unconvincing if that makes sense. For sprite clouds in an inevitably low ceiling area they look OK I think.

A lot of these choices are arbitrary, of course, and in very tall sectors the clouds will still be close to the top. I suppose in those cases they can be layered to create a greater illusion of depth. I didn't want them too close to the player, since that's fog and otherwise different.

I'm not sure all the flags are needed in the DECORATE definition, such as INTERPOLATEANGLES. It's pretty cool when the slowly drifting clouds cast shadows. Interestingly, these seem to mirror the density of the cloud. Changing the cloud_debug CVar will report some sector statistics, but it's all simple math (fortunately Doom sectors are not curved!).
Last edited by Hey Doomer on Sun Nov 28, 2021 2:02 pm, edited 3 times in total.
User avatar
Enjay
 
 
Posts: 26536
Joined: Tue Jul 15, 2003 4:58 pm
Location: Scotland

Re: Simple Cloud Spawner

Post by Enjay »

A few warnings at startup with devmode active:

Code: Select all

Script warning, "clouds.pk3:clouds_events.zs" line 15:
Truncation of floating point value
Script warning, "clouds.pk3:clouds_events.zs" line 16:
Truncation of floating point value
Script warning, "clouds.pk3:clouds_events.zs" line 18:
Truncation of floating point value
Script warning, "clouds.pk3:clouds_events.zs" line 19:
Truncation of floating point value
Script warning, "clouds.pk3:clouds_events.zs" line 52:
Truncation of floating point value
Script warning, "clouds.pk3:clouds_events.zs" line 53:
Truncation of floating point value
Might it be better to set the flag to force XY billboarding to avoid the clouds looking like paper cutouts if you look straight up at them?
Hey Doomer
Posts: 283
Joined: Sat Sep 25, 2021 3:38 am

Re: Simple Cloud Spawner

Post by Hey Doomer »

Holy smokes! I missed that for sure. I assumed vector2 x,y are integers. I didn't look at the ZAtPoint function closely enough. I was too busy thinking about integer map locations, possibly. Fixed. In one of the early builds I remembered +FORCEXYBILLBOARD and removed it for troubleshooting. I was not aware of devmode. That's extremely useful.

Many thanks. Great stuff.

Return to “Script Library”