Massassi Forums Logo

This is the static archive of the Massassi Forums. The forums are closed indefinitely. Thanks for all the memories!

You can also download Super Old Archived Message Boards from when Massassi first started.

"View" counts are as of the day the forums were archived, and will no longer increase.

ForumsCog Forum → Automatic rain generation system
Automatic rain generation system
2005-07-22, 12:16 AM #1
This will be a long post, and I apologize, but please read through it, I think you'll find it rather interesting. My code is well commented and formatted. Thanks.

***I recommend you copy and paste the code below into WordPad, it'll make it a LOT easier to read as I didn't bother following 80 char width conventions.

Essentially I'm writing an automatic rain generation system that generates rain from sky surfaces. This means the level designer doesn't have to place ghost objects or even choose which surfaces to have the rain fall from. I've got the design goal and description of the current functions already in the code, commented at the top, so please read through that.

Code:
#=====================================================#
# AutoRain.cog
#
# By Emon, July 2005
#
# Super awesome automatic customizable rain system.
#
# Design Goals:
# A completely automated rain/snow system. No more
# placing ghost objects, no more specifying radii,
# you won't even have to select sky surfaces or
# sectors. The COG will automatically generate rain
# from horizontal (flat, slope of zero) surfaces
# which are not adjoins and which are not floors.
# That is, only sky surfaces in the actual, physical
# sky. A sky surface on the bottom of a sector will
# not generate rain.
#
# Rain affected by wind. The wind will be defined as
# a "global" variable which other COGs can access.
# This way if you have any kind of dust/smoke particles
# they can use the global wind direction and force
# data so they match the rain. Wind should also fluctuate
# regularly. It should fluctuate in small amounts very
# often and large amounts less than often. This makes it
# possible to have a vortex/swirling effect with snow
# without significantly altering its path. Rain will be
# moving too fast to be affected much by the fluctuating
# wind.
#
# The only thing the user will have to specify will be
# templates, density, wind data and sector/surface
# exceptions should they choose to do so.
# 
# Todo:
#  o Fix droplet point generation
#  o Use projectiles for actual rain
#  o Add wind and wind fluctuations
#  o Add user-defined sector and surface exceptions
#  o Add automatic adjoin exception
#  o Add density control
#  o Move slope detection up into heapSet area for
#	 more efficient looping (store only surfaces
#	 which are horizontal, don't store all surfaces
#	 and check if they are horizontal each iteration
#	 of the loop).
#  o Make rain direction customizable in case anyone
#	 ever wants rain to fall upwards, sideways, etc.
#
# Basic Logic:
# Search through all the surfaces in the level and locate
# those which are sky surfaces. Store the reference to
# those surfaces in the heap.
#
# Then loop through each sky surface, ignoring those which
# are not horizontal (non-slope of zero). When flat surfaces
# are found, we must generate rain droplet points randomly
# inside that surface. It must work for ALL shapes and sizes
# of surfaces and must uniformly distribute throughout any
# shape. To do this, we find which vertex is furthest from
# the surface center and use this as a radius for creating
# a sphere of droplet test points. We test these points
# using Sige's point-polygon collision code. If the point
# is on the surface, we use it as a point for a droplet.
# If it's not on the surface, we throw it out and try
# another. We repeat the process until we have as many
# droplets as we need.
#
# ***NOTE***
# This is merely test code at the moment. No rain is
# generated, only a template defined in JED. This template
# is just created at what would be rain drop points. Right
# now I'm just trying to get the drop point generation
# working. Once that's done I can make actually rain.
#
#=====================================================#
flags=0x240
#=====================================================#
symbols

# The density of droplets per JKU to generate rain.
# As of now there is no real density control so this
# value is used as droplets per surface.
int			rainDensity

# A test template, defined in JED.
template	temp

int			numSurfaces					local
int			numSkySurfaces = 0			local
int			i							local
int			j							local
int			heapIndex = 0				local
int			surface						local
vector		surfaceCenter				local

int			numVertices					local
flex		newDist						local
flex		dist						local
int			numDropsCreated				local
flex		xDir						local
flex		yDir						local

# Variables for Sige's code.
#----
int			v							local
int			isInSurface					local
float		d1							local
float		d2							local
vector		v1							local
vector		v2							local
vector		edge						local
vector		edgeNormal					local
vector		testPoint					local
#----

message		startup

end
#=====================================================#
code

startup:
	sleep(2);
	# Get the number of ALL surfaces in the level.
	numSurfaces = getSurfaceCount();
	
	# Count how many sky surfaces we have. The sky flag is 0x200, the horizon
	# flat is 0x400.
	for(i = 0; i < numSurfaces; i = i + 1)
	{
		if(getSurfaceFlags(i) & 0x200 || getSurfaceFlags(i) & 0x400)
			numSkySurfaces = numSkySurfaces + 1;
	}
	
	# Create a new heap with the number of sky surfaces we have.
	heapNew(numSkySurfaces);
	
	# Search for sky surfaces again, this time adding valid sky surfaces
	# to the heap.
	for(i = 0; i < numSurfaces; i = i + 1)
	{
		if(getSurfaceFlags(i) & 0x200 || getSurfaceFlags(i) & 0x400)
		{
			heapSet(heapIndex, i);
			heapIndex = heapIndex + 1;
		}
	}
	
	# Loop through all our stored sky surfaces using the loop index ('i') and
	# heapGet().
	for(i = 0; i < numSkySurfaces; i = i + 1)
	{
		surface = heapGet(i);
		# This IF statement makes sure the surface has a slope of zero (horizontal)
		# and is pointing downards. This prevents problems with vertical sky surfaces
		# or sky surfaces on the floor (for whatever reason the designer may want that).
		if(vectorDot(getSurfaceNormal(surface), vectorSet(0.0, 0.0, -1.0)) == 1.0)
		{
			# Setup a few variables. Define the center of the surface,
			# the number of vertices in the surface and reset the drop
			# counter.
			surfaceCenter = getSurfaceCenter(surface);
			numVertices = getNumSurfaceVertices(surface);
			numDropsCreated = 0;
			
			# The following loop searches through all the vertices of
			# the sky surface and locates the one which is furthest away
			# from the surface center. We will then use this distance as
			# our radius for test point generation.
			dist = 0;
			for(j = 0; j < numVertices; j = j + 1)
			{
				newDist = vectorDist(surfaceCenter, getSurfaceVertexPos(surface, j));
				if(newDist > dist)
					dist = newDist;
			}
			
			# Keep looping until we have as many raindrops as specified by 'rainDensity'.
			# In the future this will be replaced with a density control system which
			# limits the number of raindrops to 'rainDensity' per JKU or similar.
			# As of now, a rainDensity of 100 will force small sky openings to generate
			# 100 rain drops.
			while(numDropsCreated < rainDensity)
			{	
				# Pick random -1 or 1 multipliers for direction.
				# This ensures each droplet point will be randomly
				# chosen within our radius (variable 'dist').
				if(rand() > 0.5)
					xDir = 1.0;
				else
					xDir = -1.0;
				if(rand() > 0.5)
					yDir = 1.0;
				else
					yDir = -1.0;
				# Generate the test point. We take our surface center and add
				# to its X and Y but leave its Z the same.
				testPoint = vectorSet((vectorX(surfaceCenter) + dist * rand()) * xDir,
									  (vectorY(surfaceCenter) + dist * rand()) * yDir,
									   vectorZ(surfaceCenter));
				# Call Sige's point-polygon collision code below.
				call isPointInSurface;
				# If our test point is within the sky surface, create a test object at
				# that point and add 1 to our counter.
				if(isInSurface == 1)
				{
					createThingAtPos(temp, getSurfaceSector(surface), testPoint, '0 0 0');
					numDropsCreated = numDropsCreated + 1;
				}
			}
		}
	}
	
	return;

isPointInSurface:
	# testPoint is the point being tested
	# surface is the surface to test on
	
	isInSurface = 1;
	# Already defined above
	#numVerticies = getNumSurfaceVertices(surface);
	v1 = getSurfaceVertexPos(surface, 0);
	v = 0;
	while(isInSurface == 1 && v < numVertices)
	{
		v2 = getSurfaceVertexPos(surface, (v + 1) % numVertices);
		edge = vectorSub(v1, v2);
		edgeNormal = vectorNorm(vectorCross(edge, getSurfaceNormal(surface)));
		d1 = vectorDot(edgeNormal, v1);
		d2 = vectorDot(edgeNormal, testPoint);
		if (d2 < d1)
			isInSurface = 0;
		v1 = v2;
		v = v + 1;
	}
	return;
	
end


The code almost works to spec, except for one problem. The test objects are not generated all over the sky surface, only on HALF of it, as shown here:
[http://img140.imageshack.us/img140/5642/brokenrain7cm.jpg]

At first I thought there was an error in Sige's point-polygon collision code (which I use to determine if my test points are on the sky surface) since he wasn't sure if it worked. I thought it wasn't properly checking all the vertices or something, thus rejecting half the possible points. After some debugging I came to realize that his code was fine, and it's something in my own, but I'm unsure as to what. I removed the dynamic radius (based on distance to the furthest vertex) and substituted various fixed radii. The result was very strange...I got test objects generated on one side (the same side as the picture above) as before. This time I got them on the other side of the center...but not where they should be. It seems they teleported to the outside edge of the surface. They're actually in the void but they're rendered because JK's skies are "infinite" and because their sector is fixed as that of the sky sector's. Here's what it looks like with a fixed radius of 0.5 (JKUs):
[http://img303.imageshack.us/img303/3325/rainwtf7fv.jpg]

In the above picture all objects should have been generated in an 0.5 JKU radius from the surface center, but only half of them are. The other half are mysteriously outside the sector. It's because of this that Sige's code is throwing out the other half of my test points, because they actually AREN'T on the surface. Anyone have any ideas?
Bassoon, n. A brazen instrument into which a fool blows out his brains.
2005-07-22, 3:25 AM #2
Where you write:
Code:
testPoint = vectorSet((vectorX(surfaceCenter) + dist * rand()) * xDir,
                      (vectorY(surfaceCenter) + dist * rand()) * yDir,
                      vectorZ(surfaceCenter));

i think you meant:
Code:
testPoint = vectorSet(vectorX(surfaceCenter) + (dist * rand() * xDir),
                      vectorY(surfaceCenter) + (dist * rand() * yDir),
                      vectorZ(surfaceCenter));


[Edit: reformatted code for easier viewing]
2005-07-22, 11:09 AM #3
Hah! It worked. Such a silly mistake. 3 AM coding mishapes how I loathe you.

Thanks a bunch!
Bassoon, n. A brazen instrument into which a fool blows out his brains.

↑ Up to the top!