Sign in to follow this  
Bouga

[requested] Foam - how I did it...

Recommended Posts

EDIT: something is wrong with the image server right now, the pictures are unavaileable, hopefully only temporary. Continued from: http://www.gamedev.net/community/forums/topic.asp?topic_id=278463 Quite a few people have asked me on these and other forums and mailed about how I made the shore line foam in Paradise Island. So I decided to make one good explanation and direct them all here, so everyone else can have a look see too :). But be warned before you read further - this is not the best, and in my opinion not even a good way to do this, you should improve a lot more before using this in a project of your own, but feel free to do whatever you want with this. But this may provide a good start for those that dont know where to begin. Ok, so here it goes: Step 1: Get The Foam Triangles Basically its just: loop through all terrain triangles and mark those that have atleast one vertex over the water level and atleast one under/equal to the water level. Copy them into the foam triangles, and now we have the basis for the foam. What we get is this line: Step 2: Set Under Water Verts To Water Level Well, do just that, set the Y coord of all the foam underwater vertexes to WaterHeight. Step 3: Relax The Foam Triangles This means, make the line smoother, to get something like this: I do this like that (actual code :) ) MoveOverWaterVertsFromUnderWaterNeibhours(0.25f); MoveUnderWaterVertsFromOverWaterNeibhours(0.25f); MoveUnderWaterVertsToUnderWaterNeibhours(1.0f); MoveOverWaterVertsToOverWaterNeibhours(1.0f); MoveUnderWaterVertsToUnderWaterNeibhours(1.0f); MoveOverWaterVertsToOverWaterNeibhours(1.0f); The floats mean how much weight does the vertex give to himself for each neibhour (all the neibhours are 1.0f) Heres some source so you understand what I'm talking about:
void classFoam::MoveToOverWaterNeibhours(int VertNum,float OwnWeight)
{
float Weight=(float(Verts[VertNum].Neibhours.OverCount))*OwnWeight;
//weight=OwnWeight*NeibhourCount

Vector Sum;
Sum.Set(Verts[VertNum].x,Verts[VertNum].y,Verts[VertNum].z);
Sum.Multiply(Weight);

int num;

for(num=0;num<Verts[VertNum].Neibhours.OverCount;num++)
{
Weight+=1.0f;
Sum.Add(Verts[Verts[VertNum].Neibhours.Over[num]].x,
Verts[Verts[VertNum].Neibhours.Over[num]].y,
Verts[Verts[VertNum].Neibhours.Over[num]].z);
}
//add the position of each neibhour and add 1.0f to the main weight

if(Weight)
{
Sum.Divide(Weight);
Verts[VertNum].x=Sum.x;
Verts[VertNum].z=Sum.z;
}
}
//divide the sum with the weight



and
void classFoam::MoveFromOverWaterNeibhours(int VertNum,float 
float Weight=(float(Verts[VertNum].Neibhours.OverCount))*OwnWeight;

GiGaMath_Vector Sum;
Sum.Set(Verts[VertNum].x,Verts[VertNum].y,Verts[VertNum].z);
Sum.Multiply(Weight);

int num;

for(num=0;num<Verts[VertNum].Neibhours.OverCount;num++)
{
Weight+=1.0f;
Sum.Add(Verts[Verts[VertNum].Neibhours.Over[num]].x,
Verts[Verts[VertNum].Neibhours.Over[num]].y,
Verts[Verts[VertNum].Neibhours.Over[num]].z);
}

if(Weight)
{
Sum.Divide(Weight);
Verts[VertNum].x=Verts[VertNum].x+(Verts[VertNum].x-Sum.x);
Verts[VertNum].z=Verts[VertNum].z+(Verts[VertNum].z-Sum.z);
}



the other functions are similar. As you can see in the pic, the foam triangles intersect with the terrain and we need to fix that! So next - Step 4: Find Over Water Vertex Y Coords So we just loop through all vertexes and if they are over water vertexes, set that Y coord to terrain height at those X,Z coords. We get this: As you can see, we still get some foam/terrain intersections, so we Step 5: Fix Terrain Foam Collisions What I do here is just - loop trough all triangles, check if any of their edges intersect with the terrain and if they do, raise their vertexes by a small ammount. Then check again... and so on, until I have corrected all intersections. What we get is this: Note: I dont really kill ALL the intersections, since that would sometimes mean raising the foam too high (sometimes the player could even see under it) but I often leave small intersections, since they are actuallly hard to notice with all the waving and alpha blending. Some may, of course, look real ugly, but to avoid that you have to make the shore line real flat... And the last but not least Step 6: Texture Coordinate generation I will just explain what I have theoretised here, and dont give any of my ugly code :) What we get is this: But how de do that? Real simple, actually! Have a look at this pic: Say we start generating from vertex 1; we set its U coord to 0.0f; then proceed to its same type (underwater) neibhour - vertex 2; get the distance between them, lets say its 1.2f; and lets say you want your foam texture to tile one time over the distance of 2.0f; so we get the U coords: 0.0f+1.2f/2.0f=0.6f; Then we proceed to vertex 3 - lets say the distance between 2-3 is 1.5f; so we calculate the U coord: 0.6f+1.5f/2.0f=1.35f; And so on and so on and so on, you just divide the distance to the next vertex by the texture tile distance and add current vertexes U coord to it. Then we do the same for over water vertexes, starting from the one attached to our vertex 1 so we dont get foam line bottom/top offset. One bug I have left is where the circle ends... I usually correct that by hand. The next part is the V coord generation, which is a bit trickyer, but still very simple. First give each vertex a time offset value for the calculation of the Y coord, make sure that each underwater vert has the same offset as the closest overwater vert, and that neibhouring same type verts dont have too much difference in their offsets. Heres some possible offset values: And when you render you calculate the V coord for each vert like so: A=-fabs(TIMES*sin(StartVCoef+Time)); V=1.0f+TIMES+A; //for over water verts V=A; //for underwater verts And you can also set the alpha value of that vertex to: -A/TIMES; Heres an illustration of why, and what does it all mean: The big quad is two of the foam triangles, and the small quad is the texture stretching over them so you see you have to set the vertical tiling to GL_CLAMP_TO_EDGE or whatever is the DX equivalent. Phase one is when the wave has reached the shore - opaque, and phase 2 is when the wave is in the water - transparent. The green numbers are the V coords needed at that point so the texture stretches in the desired way. TIMES = how many times you want the texture to stretch vertically over the triangle minus 1.0; Please ask if anything remains unclear, and I guess there will be a lot of questions since I'm usually bad at explaining things :) [Edited by - Bouga on November 11, 2004 2:39:16 PM]

Share this post


Link to post
Share on other sites
Yeah, very well written article, the explaination is very clear to me.
The assumption for ur method seems to be that the amplitude of the coastal water is very low, but that's ok, as amplitude of coastal waves is generally low.
Any way, thanks for posting this good article, and do think to send this to one of the GDnet staff.

Share this post


Link to post
Share on other sites
Thank you :) Hmm, I dont think it is good enough to become an article.. atleast yet, mainly becouse of what you just said - it assumes some things about the shoreline - being quite flat for example, but maybe if I (or someone else) find a solution for some of the problems and add that to this, then maybe.. anyway, I wont be the judge of whether it is good enough to be an article, but if a moderator desides that it is, I will try to write a bit better explanation (was it really that clear?).

Share this post


Link to post
Share on other sites
Nice. Very nice.

(FYI, "Neibhour" is spelled Neighbor. Well, in english anyways. (I understand, however, that many people didn't grow up learning english, or are kid mini-geniuses - I also make much worse mistakes trying to spell words in spanish, and german - and mis-spell a lot an english - this is just an FYI, since your example snippits use the mis-spelled version too ^_^. (Hey, at least it's consistant))).

Share this post


Link to post
Share on other sites
Hehe, didnt notice that - I allways tend to write it the way you pronounce it and I usually dont check my code for english grammar mistakes :) Well, thats just one of those things I'll have to fix before I send it in as an article...

Share this post


Link to post
Share on other sites
I've had a hard time with spelling mistakes like that when doing MODing, i kept writing "Foward" instead of "Forward" and of course the variable or function was reported as non-existing :p

After a few (read 50) unnecessary compiler errors, i've learned my lesson ;)

Share this post


Link to post
Share on other sites
Guest Anonymous Poster

I dont understand the part about "relaxing" the vertices, could you maybe spell it out a bit clearer for me?

ie

what exactly is a neighbor? is it a neighboring vertex or neighboring tri/quad?

how would you simply find these neighbors? I would think that it would just be a simple case of the preceeding tri and following tri in your list - but what about other islands popping up and introducing gaps...

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
(same ap)

i think i understand how to do the basic smoothing, but i guess i am just unsure about how to find the neighbors...

Share this post


Link to post
Share on other sites
Well, a vertex is considered a neighbour of another vertex if there is a triangle edge connecting them. In my app I have a vertex list and a triangle list. Each triangle has 3 vertex indexes, and if two triangles have vertexes at the same position they have the same vertex index. So to find the neighbours you just loop through all vertexes and do: loop through all triangles and do: if one of the triangle vertex indexes == current vertex index, then add the other two vertexes as neighbours to that vertex. As you can see I also keep seperate lists for over/under water neighbours for convinience when performing the "relaxation" (I call it that way becouse there is a modifier in 3DMax that has a similar effect and is called "Relax Mesh" or something like that).

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this