CCloudSceneNode - clouds with levels of detail
Great Work!
Great Work Bitplane! This is just what I have been looking for... I will use it for my Azura Racer game. Maybe this could be included into the svn version of Irrlicht? I think it is a really useful addition which many other 3d engines already have.
-
- Posts: 322
- Joined: Tue Aug 30, 2005 10:34 am
- Location: slovakia
How do I compile this? Dev-cpp is set up for Irrlicht, but I get the following error when I try to compile:
[Linker error] undefined reference to `_imp___ZN3irr12createDeviceENS_5video13E_DRIVER_TYPEERKNS_4core11dimension2dIiEEjbbbPNS_14IEventReceiverEPKc'
EDIT- Nevermind, I had gotten one of the directories wrong. However, I'm not seeing any clouds.
[Linker error] undefined reference to `_imp___ZN3irr12createDeviceENS_5video13E_DRIVER_TYPEERKNS_4core11dimension2dIiEEjbbbPNS_14IEventReceiverEPKc'
EDIT- Nevermind, I had gotten one of the directories wrong. However, I'm not seeing any clouds.
Yes, I don't see any clouds either. If you change the material type from EMT_TRANSPARENT_APLHA_CHANNEL to EMT_TRANSPARENT_ADD_COLOR, you'll see dense, white clouds instead of the normal gray clouds. I've tried out the other EMT_TRANSPARENT_* with hit or miss (either see black borders and square billboard clouds or nothing at all).Baiame wrote:How do I compile this? Dev-cpp is set up for Irrlicht, but I get the following error when I try to compile:
[Linker error] undefined reference to `_imp___ZN3irr12createDeviceENS_5video13E_DRIVER_TYPEERKNS_4core11dimension2dIiEEjbbbPNS_14IEventReceiverEPKc'
EDIT- Nevermind, I had gotten one of the directories wrong. However, I'm not seeing any clouds.
![Image](http://i59.photobucket.com/albums/g319/lug_irr/Bitplane%20cloudscene%20node/Cloud_EMT_TRANSPARENT_ADD_COLOR.jpg)
Or you can change the texture from "cloud4.png" to either "cloud2.png" or "cloud3.png" to see tiny sparse cloud formations. I've tried this with the SVN rev 276 of irrlicht but I suspect you'll get a similar affect if using the release v1.1 of irrlicht.
![Image](http://i59.photobucket.com/albums/g319/lug_irr/Bitplane%20cloudscene%20node/Cloud_EMT_TRANSPARENT_ALPHA_CHANNEL.jpg)
-
- Posts: 34
- Joined: Wed Apr 25, 2007 3:48 am
Hi, I made it work in Irrlicht 1.3.1 with some changes.
First I moved the whole class and functions definitions from the cpp file to the header file.
Then I replaced the OnPrerender() function with the OnRegisterSceneNode()
Finally changed the s32 getMaterialCount() to u32 and I did the same with other s32 functions all now declared as u32.
It works fine.
Here is the code:
Now my question is how can I change the size of the clouds to something huge covering my...36sq miles terrain!!!![/quote]
First I moved the whole class and functions definitions from the cpp file to the header file.
Then I replaced the OnPrerender() function with the OnRegisterSceneNode()
Finally changed the s32 getMaterialCount() to u32 and I did the same with other s32 functions all now declared as u32.
It works fine.
Here is the code:
#define CLOUD_PARTICLE_LIMIT 2000
using namespace irr;
using namespace core;
using namespace io;
using namespace scene;
using namespace video;
using namespace gui;
//! Struct for holding particle data
struct SCloudParticle
{
//! distance from camera
f32 distance;
//! Position of the particle, or position relative to parent cloud
core::vector3d<f32> pos;
//! Cloud size
core::dimension2d<f32> size;
//! Cloud colour - (tsk tsk, AmEn)
video::SColor color;
// Number of children
s16 children;
// position of first child
s16 firstchild;
// how much children will be scaled
f32 childscale;
};
struct SCloudBuffer
{
};
//! A particle system scene node.
class CCloudSceneNode : public ISceneNode
{
public:
//! constructor
CCloudSceneNode(ISceneNode* parent, ISceneManager* mgr, ITimer *devicetimer , s32 id,
const core::vector3df& position = core::vector3d<f32>(0,0,0),
const core::vector3df& rotation = core::vector3d<f32>(0,0,0),
const core::vector3df& scale = core::vector3d<f32>(1,1,1))
: ISceneNode(parent, mgr, id, position, rotation, scale)
{
ZOrdering = true;
IgnoreChildColor = true;
MaxDepth = 2;
LOD = 4;
timer = devicetimer;
// create static buffers if required
if (Particles.size() < CLOUD_PARTICLE_LIMIT)
{
Particles.set_used(CLOUD_PARTICLE_LIMIT);
ParticlesToDraw = CLOUD_PARTICLE_LIMIT;
reallocateBuffers();
ParticlesToDraw = 0;
}
#ifdef _DEBUG
setDebugName("CCloudSceneNode");
#endif
//addition by Greg to make alphas below 128 feasible.
Material.MaterialTypeParam = 0.01f;
this->setMaterialFlag(video::EMF_ZWRITE_ENABLE,false);
}
//! destructor
~CCloudSceneNode()
{
}
//! Adds new particle effector to the particle system.
void addAffector(IParticleAffector* affector)
{
affector->grab();
AffectorList.push_back(affector);
}
//! Removes all particle affectors in the particle system.
void removeAllAffectors()
{
core::list<IParticleAffector*>::Iterator it = AffectorList.begin();
while (it != AffectorList.end())
{
(*it)->drop();
it = AffectorList.erase(it);
}
}
//! Returns the material based on the zero based index i.
video::SMaterial& getMaterial(u32 i)
{
return Material;
}
//! Returns amount of materials used by this scene node.
u32 getMaterialCount()
{
return 1;
}
//! pre render event
void OnRegisterSceneNode()
{
if (IsVisible)
{
SceneManager->registerNodeForRendering(this);
// make cloud particles
makeParticles();
lastcount = ParticlesToDraw;
// printf("built %d in %d\n", lastcount,lasttime);
// order them if needed
if (ZOrdering)
sortParticles();
// call iscenenode's pre-render
ISceneNode::OnRegisterSceneNode();
}
}
//! render
void render()
{
video::IVideoDriver* driver = SceneManager->getVideoDriver();
ICameraSceneNode* camera = SceneManager->getActiveCamera();
if (!camera || !driver)
return;
// calculate vectors for letting particles look to camera
core::vector3df campos = camera->getAbsolutePosition();
core::vector3df target = camera->getTarget();
core::vector3df up = camera->getUpVector();
core::vector3df view = target - campos;
view.normalize();
core::vector3df horizontal = up.crossProduct(view);
horizontal.normalize();
core::vector3df vertical = horizontal.crossProduct(view);
vertical.normalize();
view *= -1.0f;
// reallocate arrays, if they are too small
reallocateBuffers();
u32 sp=0;
// if there's more particles that what's allowed, cull the back ones
if (ParticlesToDraw > CLOUD_PARTICLE_LIMIT)
{
sp = ParticlesToDraw - CLOUD_PARTICLE_LIMIT;
}
// lets not use niko's arrays, use a pointer instead
video::S3DVertex *p = Vertices.pointer();
// create particle vertex data
for (u32 i=sp; i<ParticlesToDraw; i++)
{
SCloudParticle& particle = Particles;
core::vector3df h = horizontal * 0.5f * particle.size.Width;
core::vector3df v = vertical * 0.5f * particle.size.Height;
s32 idx = (i-sp)*4;
//p[0+idx].Pos = particle.pos + h + v;
p[0+idx].Pos.X = particle.pos.X + h.X + v.X;
p[0+idx].Pos.Y = particle.pos.Y + h.Y + v.Y;
p[0+idx].Pos.Z = particle.pos.Z + h.Z + v.Z;
p[0+idx].Color = particle.color;
// p[0+idx].Color = particle.color;
p[0+idx].Normal.X = view.X;
p[0+idx].Normal.Y = view.Y;
p[0+idx].Normal.Z = view.Z;
//p[1+idx].Pos = particle.pos + h - v;
p[1+idx].Pos.X = particle.pos.X + h.X - v.X;
p[1+idx].Pos.Y = particle.pos.Y + h.Y - v.Y;
p[1+idx].Pos.Z = particle.pos.Z + h.Z - v.Z;
//p[1+idx].Color = particle.color;
p[1+idx].Normal.X = view.X;
p[1+idx].Normal.Y = view.Y;
p[1+idx].Normal.Z = view.Z;
//p[2+idx].Pos = particle.pos - h - v;
p[2+idx].Pos.X = particle.pos.X - h.X - v.X;
p[2+idx].Pos.Y = particle.pos.Y - h.Y - v.Y;
p[2+idx].Pos.Z = particle.pos.Z - h.Z - v.Z;
p[2+idx].Color = particle.color;
//p[2+idx].Normal = view;
p[2+idx].Normal.X = view.X;
p[2+idx].Normal.Y = view.Y;
p[2+idx].Normal.Z = view.Z;
// p[3+idx].Pos = particle.pos - h + v;
p[3+idx].Pos.X = particle.pos.X - h.X + v.X;
p[3+idx].Pos.Y = particle.pos.Y - h.Y + v.Y;
p[3+idx].Pos.Z = particle.pos.Z - h.Z + v.Z;
p[3+idx].Color = particle.color;
// p[3+idx].Normal = view;
p[3+idx].Normal.X = view.X;
p[3+idx].Normal.Y = view.Y;
p[3+idx].Normal.Z = view.Z;
}
core::matrix4 mat;
driver->setTransform(video::ETS_WORLD, mat);
// driver->draw3DBox( Box, video::SColor(0,255,255,255));
driver->setMaterial(Material);
// get the time
u32 t = timer->getTime();
// draw the mesh
driver->drawIndexedTriangleList(Vertices.pointer(), (ParticlesToDraw-sp)*4,
Indices.pointer(), (ParticlesToDraw-sp)*2);
// get the time taken, so we can cull far clouds next loop
lasttime = timer->getTime()-t;
lastcount = ParticlesToDraw;
//printf("drew %d in %d\n", lastcount,lasttime);
// for debug purposes only:
if (DebugDataVisible)
{
driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
video::SMaterial m;
m.Lighting = false;
driver->setMaterial(m);
driver->draw3DBox(Box, video::SColor(0,255,255,255));
}
}
//! returns the axis aligned bounding box of this node
const core::aabbox3d<f32>& getBoundingBox() const
{
return Box;
}
// gets the current number of clouds on screen
s16 getCurrentCloudCount()
{
return ParticlesToDraw;
}
void makeRandomCloud(s16 count)
{
// this sets the number of top level clouds
ParticleCount = count;
// ParticleData[<ParticleCount] = top level cloud data
// ParticleData[>=ParticleCount] = child node data
if (ParticleData.size() < count+50)
ParticleData.set_used(count+50);
u32 i;
// make particles
for (i=1; i<count+25; i++)
{
f32 a,b,c;
// random position
if (i < count)
{
a = rand() / 10 - (rand() / 10);
c = rand() / 10 - (rand() / 10);
b = rand() / 500 - (rand() / 500);
}
else
{
a = rand() / 100 - (rand() / 100);
c = rand() / 100 - (rand() / 100);
b = rand() / 100 - (rand() / 100);
}
ParticleData.pos = core::vector3d<f32>(a,b,c);
f32 f = rand() / 50;
if (f < 0) f = -f;
// set the size
ParticleData.size = core::dimension2d<f32>(200+f,200+f);
// distance to dark spot
int f2 = int(rand())%128;
f2+=1;
ParticleData.color = video::SColor(255,255-f2,255-f2,255-f2);
ParticleData.children = 0;
if (rand()/500 > 20) ParticleData.children = int(rand()) %3;
int j = int(rand()) %10;
ParticleData.firstchild = count +j;
ParticleData.childscale = 0.5;
}
// a recursive pattern here-
/*
f32 f = 100.0;
f32 cs = 0.5;
f32 d = rand()/1500;
f32 e = rand()/1500;
//1
ParticleData.color = video::SColor(255,0,0,0);
ParticleData.children = 0;
ParticleData[i].firstchild = 50+4;
ParticleData[i].childscale = cs;
ParticleData[i].pos = core::vector3d<f32>(d,0,e);
ParticleData[i].size = core::dimension2d<f32>(f,f);
i++;
//2
ParticleData[i].color = video::SColor(255,0,0,0);
ParticleData[i].children = 0;
ParticleData[i].firstchild = 50+4;
ParticleData[i].childscale = cs;
ParticleData[i].pos = core::vector3d<f32>(-d,0,e);
ParticleData[i].size = core::dimension2d<f32>(f,f);
i++;
//3
ParticleData[i].color = video::SColor(255,0,0,0);
ParticleData[i].children = 4;
ParticleData[i].firstchild = 50+4;
ParticleData[i].childscale = cs;
ParticleData[i].pos = core::vector3d<f32>(d,0,-e);
ParticleData[i].size = core::dimension2d<f32>(f,f);
i++;
//4
ParticleData[i].color = video::SColor(255,0,0,0);
ParticleData[i].children = 4;
ParticleData[i].firstchild = 50+4;
ParticleData[i].childscale = cs;
ParticleData[i].pos = core::vector3d<f32>(-d,0,-e);
ParticleData[i].size = core::dimension2d<f32>(f,f);
i++;
d = rand()/1500;
e = rand()/1500;
//1
ParticleData[i].color = video::SColor(255,0,0,0);
ParticleData[i].children = 0;
ParticleData[i].firstchild = 50;
ParticleData[i].childscale = cs;
ParticleData[i].pos = core::vector3d<f32>(d,e,0);
ParticleData[i].size = core::dimension2d<f32>(f,f);
i++;
//2
ParticleData[i].color = video::SColor(255,0,0,0);
ParticleData[i].children = 0;
ParticleData[i].firstchild = 50;
ParticleData[i].childscale = cs;
ParticleData[i].pos = core::vector3d<f32>(-d,e,0);
ParticleData[i].size = core::dimension2d<f32>(f,f);
i++;
d = rand()/1500;
//3
ParticleData[i].color = video::SColor(255,0,0,0);
ParticleData[i].children = 4;
ParticleData[i].firstchild = 50;
ParticleData[i].childscale =cs;
ParticleData[i].pos = core::vector3d<f32>(d,-e,0);
ParticleData[i].size = core::dimension2d<f32>(f,f);
i++;
//4
ParticleData[i].color = video::SColor(255,0,0,0);
ParticleData[i].children = 4;
ParticleData[i].firstchild = 50;
ParticleData[i].childscale = cs;
ParticleData[i].pos = core::vector3d<f32>(-d,-e,0);
ParticleData[i].size = core::dimension2d<f32>(f,f);
i++;
d = rand()/1500;
*/
}
//! turns z sorting on or off.
//! speed increase for alpha blend clouds, or clouds that are one colour
void setZOrdering(bool zo)
{
ZOrdering = zo;
}
//! decides whether child nodes are the same colour as their parents
void setIgnoreChildColor(bool b)
{
IgnoreChildColor = b;
}
//! sets the maximum detail depth to draw
void setMaxDepth(s16 maxdepth)
{
MaxDepth = maxdepth;
}
//! sets the level of detail ("distance/size" - scale of 1 to 10 works well)
void setLOD(f32 levelofdetail)
{
LOD = levelofdetail;
}
//! sets the maximum time allowed to draw the screen.
void setMaxTimePerFrame(u32 t)
{
MaxTimePerFrame = t;
}
private:
// functions
void reallocateBuffers()
{
if (ParticlesToDraw * 4 > Vertices.size() ||
ParticlesToDraw * 6 > Indices.size())
{
s32 oldSize = Vertices.size();
Vertices.set_used(ParticlesToDraw * 4);
u32 i;
// fill remaining vertices
for (i=oldSize; i<Vertices.size(); i+=4)
{
Vertices[0+i].TCoords.set(0.0f, 0.0f);
Vertices[1+i].TCoords.set(0.0f, 1.0f);
Vertices[2+i].TCoords.set(1.0f, 1.0f);
Vertices[3+i].TCoords.set(1.0f, 0.0f);
}
// fill remaining indices
s32 oldIdxSize = Indices.size();
s32 oldvertices = oldSize;
Indices.set_used(ParticlesToDraw * 6);
for (i=oldIdxSize; i<Indices.size(); i+=6)
{
Indices[0+i] = 0+oldvertices;
Indices[1+i] = 2+oldvertices;
Indices[2+i] = 1+oldvertices;
Indices[3+i] = 0+oldvertices;
Indices[4+i] = 3+oldvertices;
Indices[5+i] = 2+oldvertices;
oldvertices += 4;
}
}
}
void sortParticles()
{
u32 i,j, increment;
// because copying particles around in the shell sort was showing up as 99% of
// all cpu time, we'll deal with a list of pointers instead.
// init arrays
if (tmppointers.size() < ParticlesToDraw) tmppointers.set_used(ParticlesToDraw);
if (tmpclouds.size() < ParticlesToDraw) tmpclouds.set_used(ParticlesToDraw);
SCloudParticle *temp = Particles.pointer();
// copy pointers
for (i=0;i<ParticlesToDraw;i++)
tmppointers[i] = temp +i;
// sort the list (shell sort)
// Niko's arrays make function calls, so we'll use a double pointer
SCloudParticle **tp = tmppointers.pointer();
increment = 3;
while (increment > 0)
{
for (i=0; i < ParticlesToDraw; i++)
{
j = i;
temp = tp[i];
while ((j >= increment) && (tp[j-increment]->distance < temp->distance))
{
tp[j] = tp[j - increment];
j = j - increment;
}
tp[j] = temp;
}
if (increment/2 != 0)
increment = increment/2;
else if (increment == 1)
increment = 0;
else
increment = 1;
}
// take list to temp buffer
for (i=0;i<ParticlesToDraw;i++)
tmpclouds[i] = tp[i][0];
// memcopy back to particles
memcpy( Particles.pointer(),tmpclouds.const_pointer() , sizeof(SCloudParticle)*ParticlesToDraw);
// for (i=0;i<ParticlesToDraw;i++)
// Particles[i] = tmpclouds[i];
}
//! makes particle list for rendering/sorting
void makeParticles()
{
core::vector3df rot = getRotation();
core::vector3df pos = getPosition();
// printf("pos = %f, %f, %f\n", pos.X,pos.Y,pos.Z);
core::vector3df campos = SceneManager->getActiveCamera()->getAbsolutePosition();
core::vector3df camrot = SceneManager->getActiveCamera()->getRotation();
//f32 far;
//far = SceneManager->getActiveCamera()->getFarValue();
core::matrix4 m;
core::matrix4 m2;
s16 i,c=0;
m.setInverseRotationDegrees(rot);
m2.setInverseRotationDegrees(camrot);
core::vector3df t;
// reset bounding box
Box.reset(getPosition());
// make each particle
for (i=0; i < ParticleCount; i++)
{
core::vector3df tmppos = ParticleData[i].pos;
// rotate
m.transformVect(tmppos);
// move globally
tmppos += pos;
// set distance from camera
f64 tmpdist = campos.getDistanceFrom(tmppos);
// level of detail culled?
f32 lod = f32(tmpdist) / ParticleData[i].size.Width;
// cull if it's behind the camera
bool cull=false;
// far plane culled
/*if(far < tmpdist)
cull = true;*/
if (lod < LOD*5 && !cull)
{
// copy particle (swap for mem copy?)
Particles[c] = ParticleData[i];
// set it up
Particles[c].pos = tmppos;
Particles[c].distance = tmpdist;
// expand the box
Box.addInternalPoint(Particles[c].pos);
if (lod < LOD && Particles[c].children > 0 && c < CLOUD_PARTICLE_LIMIT)
{
// we add child nodes
depth = 0;
c+= makeChildClouds(Particles[c],c+1,m,campos);
}
if (Particles.size() < c+10)
{
// grow buffer
Particles.set_used(Particles.size()+50);
}
// we added at least one
c+=1;
}
}
ParticlesToDraw = c;
core::matrix4 absinv = AbsoluteTransformation;
absinv.makeInverse();
absinv.transformBox(Box);
}
// adds child clouds to the particle buffer
s16 makeChildClouds(SCloudParticle &parent,s16 start,core::matrix4 &m, core::vector3df &campos )
{
depth+=1;
if (depth>MaxDepth)
{
return (0);
}
if (Particles.size() < start+parent.children)
{
// grow buffer
Particles.set_used(Particles.size()+50);
// buffer too big?
if (start+parent.children > CLOUD_PARTICLE_LIMIT)
{
return (0);
}
}
s16 count;
s16 c = start;
for(count=0;count<parent.children;count++)
{
// copy
Particles[c] = ParticleData[ parent.firstchild + count ];
// scale
Particles[c].pos.X *= parent.childscale;
Particles[c].pos.Y *= parent.childscale;
Particles[c].pos.Z *= parent.childscale;
Particles[c].size.Width *= parent.childscale;
Particles[c].size.Height *= parent.childscale;
Particles[c].childscale *= parent.childscale;
// same colour?
if (IgnoreChildColor)
Particles[c].color = parent.color;
// rotate
m.transformVect(Particles[c].pos);
// position
Particles[c].pos += parent.pos;
// expand the box
Box.addInternalPoint(Particles[c].pos);
// set distance from camera
Particles[c].distance = campos.getDistanceFrom(Particles[c].pos);
// level of detail
f32 lod = f32(Particles[c].distance) / Particles[c].size.Width;
if (lod < LOD && Particles[c].children > 0)
{
// we add child nodes
c+= makeChildClouds(Particles[c],c+1,m,campos);
depth-=1;
// grow buffer?
if (Particles.size() < c+Particles[c].children)
{
Particles.set_used(c+Particles[c].children);
}
if (c > CLOUD_PARTICLE_LIMIT)
{
return (0);
}
}
// we added at least one
c+=1;
}
return (c-start);
}
// shared variables
core::array<SCloudParticle> Particles; //
core::array<video::S3DVertex> Vertices; // 4 vertices per particle
core::array<u16> Indices; // 6 indices per particle
// instance variables
core::list<IParticleAffector*> AffectorList;
core::array<SCloudParticle> ParticleData;
s16 ParticleCount;
s16 ParticlesToDraw;
s16 depth; // for max depth
bool ZOrdering;
bool IgnoreChildColor;
s16 MaxDepth;
f32 LOD;
ITimer *timer;
u32 MaxTimePerFrame; // maximum time allowed to render
u32 lasttime; // last time to draw frame
u32 lastcount; // last number of polys
video::SMaterial Material;
core::aabbox3d<f32> Box;
core::array<SCloudParticle*> tmppointers;
core::array<SCloudParticle> tmpclouds;
};
Now my question is how can I change the size of the clouds to something huge covering my...36sq miles terrain!!!![/quote]
-
- Posts: 34
- Joined: Wed Apr 25, 2007 3:48 am
I moved the class a while back, over to here:
http://irrlicht-plugins.googlecode.com/ ... SceneNode/
if you want access to change and make additions, just let me know by pm or email and i'll give you access![Smile :)](./images/smilies/icon_smile.gif)
http://irrlicht-plugins.googlecode.com/ ... SceneNode/
if you want access to change and make additions, just let me know by pm or email and i'll give you access
![Smile :)](./images/smilies/icon_smile.gif)
It's probably the bounding box being wrong. I've got a new shader based idea for clouds with volumetric lighting, different cloud types (bought "the cloudspotter's guide" last week
) as well as the recursive detail levels.. It will probably be several months by the time I get round to coding it though.
I'm probably not gonna have time to fix this for 1.4 for a while, so if anyone else wants to have a go, feel free![Smile :)](./images/smilies/icon_smile.gif)
![Very Happy :D](./images/smilies/icon_biggrin.gif)
I'm probably not gonna have time to fix this for 1.4 for a while, so if anyone else wants to have a go, feel free
![Smile :)](./images/smilies/icon_smile.gif)