Simple Cloud Spawner

Post your example zscripts/ACS scripts/etc here.

Simple Cloud Spawner

Postby Hey Doomer » Sun Nov 28, 2021 9:14 am

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/Screenshot-Doom-20211128-093216.png
https://i.postimg.cc/LX0x9fWw/Screenshot-Doom-20211128-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 allExpand view
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 allExpand view
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!).
You do not have the required permissions to view the files attached to this post.
Last edited by Hey Doomer on Sun Nov 28, 2021 2:02 pm, edited 3 times in total.
User avatar
Hey Doomer
 
Joined: 25 Sep 2021
Operating System: Windows 11
OS Test Version: No (Using Stable Public Version)
Graphics Processor: ATI/AMD with Vulkan Support

Re: Simple Cloud Spawner

Postby Enjay » Sun Nov 28, 2021 11:43 am

A few warnings at startup with devmode active:
Code: Select allExpand view
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?
User avatar
Enjay
Everyone is a moon, and has a dark side which he never shows to anybody. Twain
 
 
 
Joined: 15 Jul 2003
Location: Scotland

Re: Simple Cloud Spawner

Postby Hey Doomer » Sun Nov 28, 2021 2:11 pm

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.
User avatar
Hey Doomer
 
Joined: 25 Sep 2021
Operating System: Windows 11
OS Test Version: No (Using Stable Public Version)
Graphics Processor: ATI/AMD with Vulkan Support


Return to Script Library

Who is online

Users browsing this forum: No registered users and 1 guest