Fractal Cloud Generator
by Josef Jahn · 11/29/2002 (12:26 pm) · 51 comments
Download Code File
Okay, here's my feeble attempt at coding ;)
You can either just copy the attached sky.cc over your existing (head) one at engine/terrain/sky.cc, or follow my step-by-step guide.
1) Edit engine/terrain/sky.cc, and insert this at the top:
2) Locate a line starting with renderSkyBox(banHeights and comment it out, so that it looks like this:
3) Insert this function above Sky::loadDml()
4) Inside Sky::loadDml(), locate the line starting with "for(S32 x = 0; x < mMaterialList.size()" and REMOVE THE WHOLE FOR-LOOP.
5) Where you've removed the for, insert this instead:
6) Search for void Cloud::setPoints(). Insert this on top:
7) Scroll down a little, locate the line starting with "mPoints[y*5+x].set(-mRadius+(xyDiff*x)" and replace with..
8) In Cloud::calcAlpha(), at the first line inside the for-loop, change mAlpha = 1.3f - ..... to mAlpha = 5.3f - ....
9) Search for Cloud::render, change glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) to glBlendFunc(GL_SRC_ALPHA,GL_ONE)
10) Scroll down a little, comment out the call to clipToPlane along with the for-statement one line above it.
11) A few lines down, locate the line glColor4f(1.0,1.0,1.0, renderAlpha[k]*renderSAlpha[k]) and change it to this:
12) Inside Cloud::setTexPer, replace the single line in side the function with this:
EDIT 30th Nov 02: Uploaded updated sky.cc with additional dark shrouds of clouds.
Okay, here's my feeble attempt at coding ;)
You can either just copy the attached sky.cc over your existing (head) one at engine/terrain/sky.cc, or follow my step-by-step guide.
1) Edit engine/terrain/sky.cc, and insert this at the top:
#include <stdlib.h> #include <stdio.h> #include <string.h>
2) Locate a line starting with renderSkyBox(banHeights and comment it out, so that it looks like this:
// if(alphaBan[1] < 1.0f || mNumFogVolumes == 0) // renderSkyBox(banHeights[0], alphaBan[1]);
3) Insert this function above Sky::loadDml()
void generateFractal(unsigned char* retSky)
{
long x, z;
long bsize, csize;
long i, j;
int r, g, b;
int startvalue=0;
F32 sky1[256*256*3];
for(i=0; i<256; i++)
{
for(j=0; j<256; j++)
{
sky1[(i + (j*256))] = 0.0f;
}
}
r = 70; // Roughness
bsize = 256;
if (bsize>0)
for(i=0; i<9; i++)
{
if (bsize>0)
for(x=0; x<256; x+=bsize) {
for(z=0; z<256; z+=bsize) {
sky1[(x + (z*256))] += (float)(rand()%(r+1) - r/2);
if ((x==0) || (x==255) || (z==0) || (z==255))
sky1[(x + (z*256))] = 0.0f;
}
}
if(i>2) {
r = r/2;
}
csize = bsize/2;
if(csize > 0) {
for(x=0; x<256; x+=bsize) {
for(z=0; z<256; z+=bsize) {
sky1[(x+csize + (z*256))] = (float)((sky1[(x + (z*256))] + sky1[(x+bsize + (z*256))])/2.0f);
sky1[(x + ((z+csize)*256))] = (float)((sky1[(x + (z*256))] + sky1[(x + ((z+bsize)*256))])/2.0f);
sky1[(x+csize + ((z+csize)*256))] = (float)((sky1[(x + (z*256))] +
sky1[(x+bsize + (z*256))] +
sky1[(x+bsize + ((z+bsize)*256))] +
sky1[(x + ((z+bsize)*256))])/4.0f);
}
}
}
bsize /= 2;
}
if (bsize>0)
startvalue = 0;
else
startvalue = 4;
if (bsize>0)
for(x=0+startvalue; x<256-startvalue; x++) {
for(z=0+startvalue; z<256-startvalue; z++) {
sky1[(x + (z*256))] = (float)((sky1[(x-1 + ((z-1)*256))] +
sky1[(x-1 + ((z)*256))] +
sky1[(x-1 + ((z+1)*256))] +
sky1[(x + ((z-1)*256))] +
sky1[(x + ((z)*256))] +
sky1[(x + ((z+1)*256))] +
sky1[(x+1 + ((z-1)*256))] +
sky1[(x+1 + ((z)*256))] +
sky1[(x+1 + ((z+1)*256))])/9.0f);
}
}
//Optional: "Cutting" the fractal data at 0 level
for(i=0; i<256; i++) {
for(j=0; j<256; j++) {
if(sky1[(i + (j*256))] < 1.0f)
sky1[(i + (j*256))] = 1.0f;
if(sky1[(i + (j*256))] > 255.0f)
sky1[(i + (j*256))] = 255.0f;
}
}
for(i=0; i<256; i++)
for (j=0;j<256;j++) {
r = (int)(sky1[(i + (j*256))]*4.0f);
g = (int)(sky1[(i + (j*256))]*4.0f);
b = (int)(sky1[(i + (j*256))]*4.0f);
if (r<0)
r=0;
if (g<0)
g=0;
if (b<0)
b=0;
if (r>255)
r=255;
if (g>255)
g=255;
if (b>255)
b=255;
retSky[(int)(i + (j*256))*3 + 0] = (unsigned char)r;
retSky[(int)(i + (j*256))*3 + 1] = (unsigned char)g;
retSky[(int)(i + (j*256))*3 + 2] = (unsigned char)b;
}
}4) Inside Sky::loadDml(), locate the line starting with "for(S32 x = 0; x < mMaterialList.size()" and REMOVE THE WHOLE FOR-LOOP.
5) Where you've removed the for, insert this instead:
unsigned char retSky[256*256*3];
memset(retSky,0,(256*256*3));
for(S32 x = 0; x < mMaterialList.size() - CloudMaterialOffset; ++x, ++mNumCloudLayers)
{
generateFractal(retSky);
GBitmap* pFractal = new GBitmap(256, 256, false, GBitmap::RGB);
void* pDest = pFractal->getWritableBits(0);
dMemcpy(pDest, retSky, (pFractal->getWidth(0) *
pFractal->getHeight(0) *
pFractal->bytesPerPixel));
TextureHandle handle;
char texname[256];
sprintf (texname, "fractalsky_%i", (int)x);
handle.set(texname,pFractal,false);
mCloudLayer[x].setTexture(handle);
}6) Search for void Cloud::setPoints(). Insert this on top:
mRadius = 1000.0f;
7) Scroll down a little, locate the line starting with "mPoints[y*5+x].set(-mRadius+(xyDiff*x)" and replace with..
mPoints[y*5+x].set(-mRadius+(xyDiff*x),mRadius - (xyDiff*y),30);
8) In Cloud::calcAlpha(), at the first line inside the for-loop, change mAlpha = 1.3f - ..... to mAlpha = 5.3f - ....
9) Search for Cloud::render, change glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) to glBlendFunc(GL_SRC_ALPHA,GL_ONE)
10) Scroll down a little, comment out the call to clipToPlane along with the for-statement one line above it.
11) A few lines down, locate the line glColor4f(1.0,1.0,1.0, renderAlpha[k]*renderSAlpha[k]) and change it to this:
glColor4f(1.0,1.0,1.0,1.8);
12) Inside Cloud::setTexPer, replace the single line in side the function with this:
mTextureScale.set(cloudTextPer, cloudTextPer);
EDIT 30th Nov 02: Uploaded updated sky.cc with additional dark shrouds of clouds.
About the author
Recent Blogs
• Project Indigo MMOFPS updates• AI Pathfinding
• Plan for Josef Jahn
• Plan for Josef Jahn
#2
Anyways, great to see more work with the skies!
11/29/2002 (3:29 pm)
I believe you can use 'string.h' instead.Anyways, great to see more work with the skies!
#3
BTW-i get hosts of errors on any file substituted for strings.h, memory.h, string.h dosnt werk either :(
c:\torque\engine\terrain\sky.cc(68) : error C2065: 'mNoRenderBans' : undeclared identifier
c:\torque\engine\terrain\sky.cc(308) : error C2065: 'addGroup' : undeclared identifier
c:\torque\engine\terrain\sky.cc(310) : error C2065: 'endGroup' : undeclared identifier
c:\torque\engine\terrain\sky.cc(344) : error C2039: 'mNoRenderBans' : is not a member of 'Sky'
c:\torque\engine\terrain\sky.h(131) : see declaration of 'Sky'
Error executing cl.exe.
sky.obj - 4 error(s), 0 warning(s)
11/29/2002 (7:26 pm)
WOOHOO this opens the door to everyone having a better engine than the bare naked TGE. Would you be interested in adding some day/night transition coloration? could you bind the position of melv's sun to the coloring of the sky?BTW-i get hosts of errors on any file substituted for strings.h, memory.h, string.h dosnt werk either :(
c:\torque\engine\terrain\sky.cc(68) : error C2065: 'mNoRenderBans' : undeclared identifier
c:\torque\engine\terrain\sky.cc(308) : error C2065: 'addGroup' : undeclared identifier
c:\torque\engine\terrain\sky.cc(310) : error C2065: 'endGroup' : undeclared identifier
c:\torque\engine\terrain\sky.cc(344) : error C2039: 'mNoRenderBans' : is not a member of 'Sky'
c:\torque\engine\terrain\sky.h(131) : see declaration of 'Sky'
Error executing cl.exe.
sky.obj - 4 error(s), 0 warning(s)
#4
11/29/2002 (9:11 pm)
Same here, tried string.h and memory.h.. neither work. :(
#5
11/30/2002 (12:04 am)
You're not using head. This code is only tested on the latest head.
#6
12/01/2002 (10:39 am)
#7
12/01/2002 (11:01 am)
Um, HEAD means the "head branch" of torque inside the garagegames CVS.
#8
:(
12/01/2002 (5:05 pm)
LOL yeah everyone who got errors was using 1.1.2 i guess... Well im gonna update to head just for this resource. I hope nothing breaks :(
#9
edit: Here's a WaterWorld shot:
Better Water World
12/02/2002 (9:48 am)
This works great for me. Very nice. I'm using a modified HEAD engine. I did have to use memory.h to compile on an XP machine using VC++ Other then that no problems.edit: Here's a WaterWorld shot:
Better Water World
#10
12/02/2002 (9:57 am)
I tried it on a unmodified copy of HEAD, and it works. I'm using XP and MinGW with GCC2.
#11
12/03/2002 (7:11 am)
Ok I'm really lost here, I did all that re built it with no errors and the sky doesn't look anything like that! IN scorched planet its all dark grey and in water world its light grey with slightly darker grey clouds... how do I get all teh pretty colours?
#12
-Sabrecyd
12/03/2002 (7:19 am)
You need to change the fogcolor setting for the sky. Here's what I used to get the above pic in WaterWorldfogColor = "0.120000 0.528000 0.644000 1.000000";
-Sabrecyd
#13
12/03/2002 (7:21 am)
Heck yes! I forgot to mention that too much fog makes the sky disappear.
#14
12/04/2002 (9:39 pm)
I did implement this in HEAD and it got all crazy :/ Im so frustrated... Ill try it again soon by hand..
#15
With that in mind..
I'm using VC6 on win2k After downloading and dropping the sky.cc into the engine and rebuilding it I got the standard error about strings.h not found.
You can use memory.h if you want, however the only reason for including this is to do a single memset call. Just use dMemset then you don't need to include string/strings/memory.h
I changed the call to dMemset and it built fine.
When I load a mission I got soem crazy memory access error, so i ran it in debug mode in VC and it turns out to be a stack overflow problem, it happens during the call to generateFractal or generateAlphaFractal.
When I change
This is very odd.
12/06/2002 (5:57 pm)
I'm using HEAD, I checked it out a few days ago. The thing I've added is fxGrassReplicator which doesn't seem to have anything to do with this code.With that in mind..
I'm using VC6 on win2k After downloading and dropping the sky.cc into the engine and rebuilding it I got the standard error about strings.h not found.
You can use memory.h if you want, however the only reason for including this is to do a single memset call. Just use dMemset then you don't need to include string/strings/memory.h
I changed the call to dMemset and it built fine.
When I load a mission I got soem crazy memory access error, so i ran it in debug mode in VC and it turns out to be a stack overflow problem, it happens during the call to generateFractal or generateAlphaFractal.
When I change
F32 sky1[256*256*3];to
static F32 sky1[256*256*3];in both methods this fixes the problem and the code works fine.
This is very odd.
#16
These are the two generation methods note that i made them members of class Sky so you'll have to added in the header file the method prototypes.
I changed the for loop inside bool Sky::loadDml() to look like so
Other then that all the little changes from before are the same.
Great job on this btw! :)
12/06/2002 (6:10 pm)
Anyway, in order to get this to work without the static hack I made some changes. Actually I made a lot of changes but they are not all related.These are the two generation methods note that i made them members of class Sky so you'll have to added in the header file the method prototypes.
unsigned char* Sky::generateFractal()
{
// Declare
long x, z, bsize, csize, i, j;
int r, g, b, startvalue;
F32 sky1[256*256*3];
unsigned char *retSky = new unsigned char[256*256*3];
// Init
dMemset( sky1, 0, sizeof(sky1) );
dMemset( retSky, 0, sizeof(retSky) );
r = 70; // Roughness
bsize = 256;
if( bsize>0 )
{
for( i=0; i<9; i++ )
{
if( bsize>0 )
{
for( x=0; x<256; x+=bsize )
{
for( z=0; z<256; z+=bsize )
{
sky1[(x + (z*256))] += (float)(rand()%(r+1) - r/2);
if ((x==0) || (x==255) || (z==0) || (z==255))
{
sky1[(x + (z*256))] = 0.0f;
}//if
}//for
}//for
}//if
if( i>2 )
{
r = r/2;
}//if
csize = bsize/2;
if( csize>0 )
{
for( x=0; x<256; x+=bsize )
{
for( z=0; z<256; z+=bsize )
{
sky1[(x+csize + (z*256))] = (float)((sky1[(x + (z*256))] + sky1[(x+bsize + (z*256))])/2.0f);
sky1[(x + ((z+csize)*256))] = (float)((sky1[(x + (z*256))] + sky1[(x + ((z+bsize)*256))])/2.0f);
sky1[(x+csize + ((z+csize)*256))] = (float)((sky1[(x + (z*256))] +
sky1[(x+bsize + (z*256))] +
sky1[(x+bsize + ((z+bsize)*256))] +
sky1[(x + ((z+bsize)*256))])/4.0f);
}//for
}//for
}//if
bsize/=2;
}//for
}//if
if( bsize>0 )
{
startvalue=0;
}
else
{
startvalue=4;
}//if
if( bsize>0 )
{
for( x=0+startvalue; x<256-startvalue; x++ )
{
for( z=0+startvalue; z<256-startvalue; z++ )
{
sky1[(x + (z*256))] = (float)((sky1[(x-1 + ((z-1)*256))] +
sky1[(x-1 + ((z)*256))] +
sky1[(x-1 + ((z+1)*256))] +
sky1[(x + ((z-1)*256))] +
sky1[(x + ((z)*256))] +
sky1[(x + ((z+1)*256))] +
sky1[(x+1 + ((z-1)*256))] +
sky1[(x+1 + ((z)*256))] +
sky1[(x+1 + ((z+1)*256))])/9.0f);
}//for
}//for
}//if
for( i=0; i<256; i++ )
{
for( j=0; j<256; j++ )
{
int index = (int)(i + (j * 256));
r = (int)(sky1[index] * 4.0f);
g = r;
b = r;
if( r<0 ) r=0; else if( r>225 ) r=255;
if( g<0 ) g=0; else if( g>215 ) g=255;
if( b<0 ) b=0; else if( b>225 ) b=255;
index *= 3;
retSky[index] = (unsigned char)r;
retSky[index + 1] = (unsigned char)g;
retSky[index + 2] = (unsigned char)b;
}//for
}//for
return retSky;
}
unsigned char* Sky::generateAlphaFractal()
{
// Declare
long x, z, i, j, bsize, csize;
int r, g, b, startvalue, darkness;
F32 sky1[256*256*3];
unsigned char *retSky = new unsigned char[256*256*4];
// Init
dMemset( sky1, 0, sizeof(sky1) );
dMemset( retSky, 0, sizeof(retSky) );
r = 50; // Roughness
bsize = 256;
darkness = (int)(rand()%180);
if( bsize>0 )
{
for( i=0; i<9; i++ )
{
if( bsize>0 )
{
for( x=0; x<256; x+=bsize )
{
for( z=0; z<256; z+=bsize )
{
sky1[(x + (z*256))] += (float)(rand()%(r+1) - r/2);
if( (x==0) || (x==255) || (z==0) || (z==255) )
{
sky1[(x + (z*256))] = 0.0f;
}//if
}//for
}//for
}//if
if( i>2 )
{
r = r/2;
}//if
csize = bsize/2;
if( csize>0 )
{
for( x=0; x<256; x+=bsize )
{
for( z=0; z<256; z+=bsize )
{
sky1[(x+csize + (z*256))] = (float)((sky1[(x + (z*256))] + sky1[(x+bsize + (z*256))])/2.0f);
sky1[(x + ((z+csize)*256))] = (float)((sky1[(x + (z*256))] + sky1[(x + ((z+bsize)*256))])/2.0f);
sky1[(x+csize + ((z+csize)*256))] = (float)((sky1[(x + (z*256))] +
sky1[(x+bsize + (z*256))] +
sky1[(x+bsize + ((z+bsize)*256))] +
sky1[(x + ((z+bsize)*256))])/4.0f);
}//for
}//for
}//if
bsize/=2;
}//for
}//if
if( bsize>0 )
{
startvalue = 0;
}
else
{
startvalue = 4;
}//if
if( bsize>0 )
{
for( x=0+startvalue; x<256-startvalue; x++ )
{
for( z=0+startvalue; z<256-startvalue; z++ )
{
sky1[(x + (z*256))] = (float)((sky1[(x-1 + ((z-1)*256))] +
sky1[(x-1 + ((z)*256))] +
sky1[(x-1 + ((z+1)*256))] +
sky1[(x + ((z-1)*256))] +
sky1[(x + ((z)*256))] +
sky1[(x + ((z+1)*256))] +
sky1[(x+1 + ((z-1)*256))] +
sky1[(x+1 + ((z)*256))] +
sky1[(x+1 + ((z+1)*256))])/9.0f);
}//for
}//for
}//if
for( i=0; i<256; i++ )
{
for( j=0; j<256; j++ )
{
int index = (i + (j * 256));
r = (int)(sky1[index] * 4.0f);
if( r<0 ) r=0; else if( r>225 ) r=255;
index *= 4;
retSky[(int)index] = (unsigned char)(darkness);
retSky[(int)index + 1] = (unsigned char)(darkness);
retSky[(int)index + 2] = (unsigned char)(darkness);
retSky[(int)index + 3] = (unsigned char)r;
}//for
}//for
return retSky;
}I changed the for loop inside bool Sky::loadDml() to look like so
for( S32 x = 0; x < MAX_NUM_LAYERS; ++x, ++mNumCloudLayers )
{
if( FractalSky )
{
unsigned char *retSky = NULL;
GBitmap* pFractal = NULL;
if( x < 2 )
{
retSky = generateFractal();
pFractal = new GBitmap( 256, 256, false, GBitmap::RGB );
}
else
{
retSky = generateAlphaFractal();
pFractal = new GBitmap( 256, 256, false, GBitmap::RGBA );
}//if
void* pDest = pFractal->getWritableBits(0);
//Copy fractal texture into final storage space
dMemcpy( pDest, retSky, (pFractal->getWidth(0) * pFractal->getHeight(0) * pFractal->bytesPerPixel) );
TextureHandle handle;
char texname[256];
sprintf (texname, "fractalsky_%i", (int)x);
handle.set(texname,pFractal,false);
mCloudLayer[x].setTexture(handle);
if( retSky != NULL )
{
delete []retSky;
retSky = NULL;
}
}
else
{
mMaterialList.getMaterial(x + CloudMaterialOffset).setClamp(false);
mCloudLayer[x].setTexture(mMaterialList.getMaterial(x + CloudMaterialOffset));
}//if
}//forOther then that all the little changes from before are the same.
Great job on this btw! :)
#17
Anyway, thanks for the code cleanup. I know that my programming isn't the cleanest, and I'm sure you're made at least a dozen people happy with your fix.
12/07/2002 (11:58 am)
I still wonder why I don't have any of these problems with GCC2 and MinGW...Anyway, thanks for the code cleanup. I know that my programming isn't the cleanest, and I'm sure you're made at least a dozen people happy with your fix.
#18
C:\torque\engine\terrain\sky.cc(68) : error C2065: 'mNoRenderBans' : undeclared identifier
C:\torque\engine\terrain\sky.cc(308) : error C2065: 'addGroup' : undeclared identifier
C:\torque\engine\terrain\sky.cc(310) : error C2065: 'endGroup' : undeclared identifier
C:\torque\engine\terrain\sky.cc(344) : error C2039: 'mNoRenderBans' : is not a member of 'Sky'
../engine\terrain/sky.h(131) : see declaration of 'Sky'
I recently downloaded the latest version of HEAD using CVS (or so I thought) so I am not sure that it's a version problem, but hopefully some one can tell me for sure.
12/17/2002 (1:05 pm)
When I coppied your sky.cc over to my directory and tried to compile I got the following errors:C:\torque\engine\terrain\sky.cc(68) : error C2065: 'mNoRenderBans' : undeclared identifier
C:\torque\engine\terrain\sky.cc(308) : error C2065: 'addGroup' : undeclared identifier
C:\torque\engine\terrain\sky.cc(310) : error C2065: 'endGroup' : undeclared identifier
C:\torque\engine\terrain\sky.cc(344) : error C2039: 'mNoRenderBans' : is not a member of 'Sky'
../engine\terrain/sky.h(131) : see declaration of 'Sky'
I recently downloaded the latest version of HEAD using CVS (or so I thought) so I am not sure that it's a version problem, but hopefully some one can tell me for sure.
#19
12/17/2002 (1:07 pm)
Er, I guess I need to read more carefully. Sorry, please ignore my post. I will try downloading HEAD again.
#20
12/23/2002 (11:40 pm)
I'm having problems with this, cna the updated code be posted up? and maybe a .mis file? because it seems odd, I'm using a head etc and it seems it should be working. 
Torque Owner Anthony McCrary
....