Hi all.
I found a cool site that has Lightning here.
Now I'm stuck on what storage type to use, list or vector or other.????
What I have done is create a queue but it does not rap back to the start gets to the last element and I call it full.
//this is working but it fills off.
What would you all do.?????
How should I structure this code or what way would any of the Dev Team go about it.?????
All that being said I'm doinging it on the GPU Shader 4 with streamout this buffer holds all the points. and I have a c++ one I made
the same way I'll post the data type so you can see what I'm going on about.
Here Is what It looks like CPU Generated ones on the left side, GPU to the Right Notice the short one. I changed the splitend rule and it blows short still ok.
//this is the shader version(Don't laugh too hard when you see my code)
#define SEGMENT_TYPE_BRANCH 0
#define SEGMENT_TYPE_SPLITEND 1
#define MAXARRAY 200
//to build our lightning we need to have a list for start points and end points
static float3 StartPoints[MAXARRAY];
static float3 EndPoints[MAXARRAY];
static float2 GenerationSize[MAXARRAY];//we now make the splits smaller
static uint SegmentType[MAXARRAY];
static float Incert = 0.0;//start empty
static float Pop = 0.0;//allows us to remove the head element
//---------------------------------------------------------
//this function allows usto add to the above lists
//---------------------------------------------------------
void AddSegment(float3 start, float3 end, float2 size, uint type)
{
float temp = Incert + 1.0;
if(temp >= MAXARRAY)
return;//full
//add our points
StartPoints[Incert] = start;
EndPoints[Incert] = end;
GenerationSize[Incert] = size;
SegmentType[Incert] = type;
Incert = temp;
}//end AddSegment
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
//---------------------------------------------------------
//this function allows usto pop the head off
//---------------------------------------------------------
void PopSegment(void)
{
if(Pop == Incert)
return;//empty
Pop++;//move through the list
if(Pop >= MAXARRAY)
{
Pop = 0.0;//go back to the start
}
}//end PopSegment
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
//---------------------------------------------------------
//this function allows us to get the segmenttype
//---------------------------------------------------------
uint GetType(void)
{
return SegmentType[Pop];
}//end GetType
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
//---------------------------------------------------------
//this function allows usto get the menber call it before
//you pop the list
//returns the element
//---------------------------------------------------------
float3 GetStart(void)
{
return StartPoints[Pop];
}//end GetStart
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
//---------------------------------------------------------
//this function allows usto get the menber call it before
//you pop the list
//returns the element
//---------------------------------------------------------
float3 GetEnd(void)
{
return EndPoints[Pop];
}//end GetEnd
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
//--------------------------------------------------------------
//gets the size
//--------------------------------------------------------------
float2 GetSize(void)
{
return GenerationSize[Pop];
}//end GetSize
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
//---------------------------------------------------------------
//returns the mid point between 2 points
//---------------------------------------------------------------
float3 Perpendicular(float3 direction)
{
//float3 a = float3(0.0, 1.0, 0.0);
//float3 rt = cross(a, v1);
//return rt;
// to be filled in
float3 quasiPerp;// a direction which is "almost perpendicular"
float3 result;// the computed perpendicular to be returned
// three mutually perpendicular basis vectors
float3 i = float3(1, 0, 0);
float3 j = float3(0, 1, 0);
float3 k = float3(0, 0, 1);
// measure the projection of "direction" onto each of the axes
float id = dot(i, direction);//i.dot (direction);
float jd = dot(j, direction);//j.dot (direction);
float kd = dot(k, direction);//k.dot (direction);
// set quasiPerp to the basis which is least parallel to "direction"
if ((id <= jd) && (id <= kd))
{
quasiPerp = i; // projection onto i was the smallest
}
else
{
if ((jd <= id) && (jd <= kd))
quasiPerp = j; // projection onto j was the smallest
else
quasiPerp = k; // projection onto k was the smallest
}
// return the cross product (direction x quasiPerp)
// which is guaranteed to be perpendicular to both of them
// result.cross (direction, quasiPerp);
result = cross(direction, quasiPerp);
return result;
}//end Perpendicular
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
//---------------------------------------------------------------
//returns the mid point between 2 points
//---------------------------------------------------------------
float3 Average(float3 v1, float3 v2)
{//float3((v1.x + v2.x) /2, (v1.y + v2.y) /2, (v1.z + v2.z) /2);
float3 a = (v1 + v2) * 0.5;
return a;
}//end average
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
Streamout GeometryShader.
struct Particle
{
float3 initialPosW : POSITION;
float3 initialVelW : VELOCITY;
float3 pDirW : PDIR;//partile direction
float2 sizeW : SIZE;
float age : AGE;
uint type : TYPE;
uint arrayid : ARRAYID;
};
Particle StreamOutVS(Particle vIn)
{
return vIn;
}
//Im abusing my particle shader to make it lightning it does this once or when ever the particle system gets reset.
//GeometryShader does all the grunt work here.
//we can only return 72 particle structure it at max for the size of the particle Im not using all the values here but to keep it the same
//as the other particle shaders it is
// different.
//it says the shader model 4 can only have 64 but I think its the size of the data that cant go over 1024 mb
[maxvertexcount(72)]
void StreamOutGS(point Particle gIn[1],
inout PointStream<Particle> ptStream)
{
gIn[0].age += gTimeStep;
if( gIn[0].type == PT_EMITTER )
{
//reset the list
Incert = 0.0;
Pop = 0.0;
//we spawn once only we run the L-system to build our points
float offsetamount = gSpawnNewParticalTime;//3900.0;//when the points are At great distance apart we need a larger value here
float3 midpoint = 0;
float generation = 5.0;
float3 start = gEmitPosW.xyz;//start location
float3 end = gEmitDirW.xyz;//end location
float po;
float inc;
float lengthScale = gParticleSpreadAmount;//1500.7;//we reduce this each time we make a split
float subsequentgeneration = 0.0;//when this gets to 2 we create a split end then we reset it
float2 Fatness = float2(gParticleWidth, gParticleHeight);
uint Type = SEGMENT_TYPE_BRANCH;
//add our start locations to the list to start it off
AddSegment(start,
end,
Fatness,//the size
SEGMENT_TYPE_BRANCH);//
[loop]
for(float gen = 0.0; gen < generation; gen++)
{
inc = Incert;
po = Pop;
[loop]
for(float segments = po; segments < inc; segments++)
{
//get the start and end
start = GetStart();
end = GetEnd();
Fatness = GetSize();
subsequentgeneration++;
float3 direction = (start - end);
//only if we are not to close
if(length(direction) >= gGravity.x)//30)
{
//we can pop this node
PopSegment();
//ok we can get the midpoint
midpoint = Average(start, end);
// Offset the midpoint by a random amount along the normal.
float3 vRandom = RandUnitVec3(gGameTime * gTimeStep);
midpoint += Perpendicular(normalize(end-start)) * (vRandom *offsetamount);
//add 2 segments that we just split
AddSegment(start,
midpoint,
Fatness,
SEGMENT_TYPE_BRANCH);
AddSegment(midpoint,
end,
Fatness,
SEGMENT_TYPE_BRANCH);
//see if we want to add a split end this here will limmit the length if we run out of points
float choice[4] = {3.0, 2.0,3.0, 1.0};
uint index = gGameTime%4;
if(subsequentgeneration >= choice[index])//2.0)
{
vRandom = RandUnitVec3(gGameTime * gTimeStep);//4.5);
//add split end we create a rendom end point at some angle and length
//if(Type == SEGMENT_TYPE_BRANCH)//reset to the max lenght
// lengthScale = gParticleSpreadAmount;
float3 direction = (midpoint - start);
//only if we are not to close
float sl = length(direction);
if(sl >= gGravity.y)//6.0)
{
float3 newendpoint = midpoint;
newendpoint += normalize(direction *clamp(length(vRandom), -20.0, 20.0))* sl;// lengthScale;
//each split can offset at max half as much as the generation before
lengthScale /= 2.0;
//float l = length(Fatness * 0.5);
//if(l <= 4.0)
Fatness *= 0.5;
float3 splitEnd = newendpoint;
AddSegment(midpoint,
splitEnd,
Fatness,
SEGMENT_TYPE_SPLITEND);
}//end length
subsequentgeneration = 0.0;//reset
}//end subsequentgeneration
}//end past the length of split
}//end for segments
offsetamount /= 2.0; //Each subsequent generation offsets at max half as much as the generation before
}//end generation
float numadded = 0.0;
//ok from Pop to Incert should have our particle points
for(float segments = Pop; segments < Incert; segments++)
{
//get the start and end
start = StartPoints[segments];
end = EndPoints[segments];
Particle p;
p.initialPosW = start;
p.initialVelW = end;
p.pDirW = 0;
p.sizeW = GenerationSize[segments];//float2(gParticleWidth, gParticleHeight);
p.age = 0.0f;//vRandom.x;
p.type = PT_FLARE;
p.arrayid = 0;
//add new particle
ptStream.Append(p);
}//end all list points
}
else
{
// Specify conditions to keep particle; this may vary from system to system.
if( gIn[0].age <= gFlashspeed)//1000.0)
ptStream.Append(gIn[0]);
}
}//end
///////////////////////////////////////////////////////////////////////////////////////////////////
and here is the c++ version
//defines for a branck and a splitends
#define SEGMENT_TYPE_BRANCH 0
#define SEGMENT_TYPE_SPLITEND 1
//#define SEGMENT_TYPE_
//----------------------------------------------------
//this holds data we need for the processing of the
//lightning
//----------------------------------------------------
class cLightningSegment
{
public:
//lightning we need to have a list for start points and end points
D3DXVECTOR3 StartPoints;
D3DXVECTOR3 EndPoints;
D3DXVECTOR2 GenerationSize;//we now make the splits smaller
UINT SegmentType;
bool Valid;//if true we can use the locations
cLightningSegment(void)
{
StartPoints = D3DXVECTOR3(0.0, 0.0, 0.0);
EndPoints = D3DXVECTOR3(0.0, 0.0, 0.0);
GenerationSize = D3DXVECTOR2(10.0, 10.0);//we now make the splits smaller
SegmentType = SEGMENT_TYPE_BRANCH;
Valid = false;
}//end
/////////////////////////////////////////////////////////
~cLightningSegment(void)
{
}//end
///////////////////////////////////////////////////////////
//------------------------------------------------------
//sets all the elements
//-------------------------------------------------------
void AddLightningSegment(D3DXVECTOR3 &start, D3DXVECTOR3 &end, D3DXVECTOR2 &size, UINT type)
{
StartPoints = start;
EndPoints = end;
GenerationSize = size;//we now make the splits smaller
SegmentType = type;
Valid = true;
}
};//end class cLightningSegment
////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
// File: LightningGenerator.cpp
//generates a list of lightning 3dpoints when ran depending on the generations
//it could get slow its the same as the lightning shader but we have more points we can use
//this works with my particle system to inject these points into its streamed out vertex buffer
//using some sort of L-System
//we now need to create a buffer for stagging so we can copy this to the particles systems gpu buffer
//-----------------------------------------------------------------------------
class cLightningGenerator
{
private:
std::vector<cLightningSegment>::iterator segments;
std::vector<cLightningSegment>::iterator Pop;
std::vector<cLightningSegment>::iterator Incert;
ID3D10Buffer *mInitVB;
DWORD AmountCopyedToBuffer;//we need to pass this info back with the buffer to who uses it
public:
int NumberToCreate;//how many we create we dont go over this value
//holds the points we add when done this will have all the locations to create good lightning
//we will allocate for 400 and it can expand after that sould not need that many the shader gets by with 72
//doing it the same way as the shader we move the to interators to suit
std::vector<cLightningSegment> LightningSegments;
cLightningGenerator(void);
~cLightningGenerator(void);
//----------------------------------------------------------------
//we now have a int function
//----------------------------------------------------------------
bool LightningInitialize(ID3D10Device* md3dDevice, int amount);
//-----------------------------------------------------------------------------------------
//get the heads data before we pop this data
//returns NULL if empty
//------------------------------------------------------------------------------------------
cLightningSegment *GetSegment(void)
{
if(LightningSegments.empty())
return NULL;//error
//we have members get the head data
return &*Pop;
}//end GetSegment
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
//this here will generate the points for the lightning each frame it will create a new build
//-------------------------------------------------------------------------------------------------
void CalculateLightning(D3DXVECTOR3 &EmitPosW, D3DXVECTOR3 &EmitDirW,
float width, float height,//size of the lightning
float deltatime,//the time delta
float gametime);//the timer class
//----------------------------------------------------------------------------------------------
//this will return out buffer so we can copy this to the particle systems buffer
//also has the number of vertex in this buffer
//----------------------------------------------------------------------------------------------
ID3D10Buffer *GetBuffer(DWORD *holdsthismany)
{
*holdsthismany = AmountCopyedToBuffer;
return mInitVB;
}//end GetBuffer
//////////////////////////////////////////////////////////////////////////////////////////////////
private:
//------------------------------------------------------
//sets all the elements
//-------------------------------------------------------
void AddLightningSegment(D3DXVECTOR3 &start, D3DXVECTOR3 &end, D3DXVECTOR2 &size, UINT type);
//---------------------------------------------------------------
//returns the mid point between 2 points
//---------------------------------------------------------------
D3DXVECTOR3 Average(D3DXVECTOR3 &v1, D3DXVECTOR3 &v2)
{
D3DXVECTOR3 a = (v1 + v2) * 0.5;
return a;
}//end average
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
//---------------------------------------------------------
//this function allows usto pop the head off
//---------------------------------------------------------
void PopSegment(void)
{
if(Pop == Incert)
return;//empty
Pop++;//move through the list
if(Pop == LightningSegments.end())
{
Pop = LightningSegments.begin();//go back to the start
}
}//end PopSegment
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
};///end class cLightningGenerator
////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////
Cpp
cLightningGenerator::cLightningGenerator(void)
{
mInitVB = NULL;
AmountCopyedToBuffer = 0;
}//end
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
cLightningGenerator::~cLightningGenerator(void)
{
SAFE_RELEASE(mInitVB);
}//end
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
//----------------------------------------------------------------
//we now have a int function
//----------------------------------------------------------------
bool cLightningGenerator::LightningInitialize(ID3D10Device* md3dDevice, int amount)
{
//how many this can hold you will need to matck the particles
NumberToCreate = amount;
//we need a staging buffer so we can copy our data to thye particle systems gpu buffer
D3D10_BUFFER_DESC vbd;
vbd.Usage = D3D10_USAGE_STAGING;
vbd.ByteWidth = sizeof(ParticleVertex);// * 1;
vbd.BindFlags = 0;//D3D10_BIND_VERTEX_BUFFER;
vbd.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
vbd.MiscFlags = 0;
//
// Create the ping-pong buffers for stream-out and drawing.
//
vbd.ByteWidth = sizeof(ParticleVertex) * NumberToCreate;
HRESULT hr = md3dDevice->CreateBuffer(&vbd, 0, &mInitVB);
if(hr != S_OK)
{
OutPutDebugText("Failed LOAD buffer",//the message to display
"cLightningGenerator::LightningInitialize()",//TCHAR *locmsg,//the location the message was called Eg. what function
true);
return false;//error
}
return true;
}//end LightningInitialize
///////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////
//------------------------------------------------------
//sets all the elements
//-------------------------------------------------------
void cLightningGenerator::AddLightningSegment(D3DXVECTOR3 &start, D3DXVECTOR3 &end, D3DXVECTOR2 &size, UINT type)
{
if(Incert == LightningSegments.end())
{
Incert = LightningSegments.end();
return;//full
}
cLightningSegment LightningSegment;
LightningSegment.AddLightningSegment(start, end, size, type);
*Incert = LightningSegment;
Incert++;
if(Incert == LightningSegments.end())
{
Incert = LightningSegments.end();
return;//full
}
}//end AddLightningSegment
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
//-------------------------------------------------------------------------------------------------
//this here will generate the points for the lightning each frame it will create a new build
//-------------------------------------------------------------------------------------------------
void cLightningGenerator::CalculateLightning(D3DXVECTOR3 &EmitPosW, D3DXVECTOR3 &EmitDirW,
float width, float height,//size of the lightning
float deltatime,//the time delta
float gametime)//the timer class
{
if(mInitVB == NULL)
return;//error failed
// static bool once = false;
// if(once)
// return;//test only once
// once = true;
//clear it
LightningSegments.resize(NumberToCreate, cLightningSegment());
//set the interrators
Pop = LightningSegments.begin();
Incert = LightningSegments.begin();
float gGameTime = gametime;
float gTimeStep = deltatime;
//this code is the same as in the shader but we can hold more locations
//we spawn once only we run the L-system to build our points
float offsetamount = 3000.0;//3900.0;//gSpawnNewParticalTime;//when the points are At great distance apart we need a larger value here
D3DXVECTOR3 midpoint;
float generation = 5.0;
D3DXVECTOR3 start = EmitPosW;//start location
D3DXVECTOR3 end = EmitDirW;//end location
std::vector<cLightningSegment>::iterator po;
std::vector<cLightningSegment>::iterator inc;
float lengthScale = 1000.0;//1500.7;//gParticleSpreadAmount;//we reduce this each time we make a split
float subsequentgeneration = 0.0;//when this gets to 2 we create a split end then we reset it
D3DXVECTOR2 Fatness = D3DXVECTOR2(width, height);
UINT Type = SEGMENT_TYPE_BRANCH;
//add our start locations to the list to start it off
AddLightningSegment(start,
end,
Fatness,//the size
SEGMENT_TYPE_BRANCH);//
for(int gen = 0.0; gen < generation; gen++)
{
po = Pop;
inc = Incert;
for(segments = po; segments != inc; segments++)
{
//get the start and end
cLightningSegment *seg = GetSegment();
start = seg->StartPoints;//GetStart();
end = seg->EndPoints;//GetEnd();
Fatness = seg->GenerationSize;//GetSize();
Type = seg->SegmentType;
subsequentgeneration++;
D3DXVECTOR3 direction = (start - end);
float l = D3DXVec3Length(&direction);
//only if we are not to closelength(direction)
//if(l >= 30)//gGravity.x)//30)
{
//we can pop this node
PopSegment();
//ok we can get the midpoint
midpoint = Average(start, end);
// Offset the midpoint by a random amount along the normal.
//D3DXVECTOR3 vRandom;//RandUnitVec3();//(gGameTime * gTimeStep);
D3DXVECTOR3 n = end-start;
D3DXVec3Normalize(&n, &n); //need a clamp here on the cpu version
midpoint += findPerpendicularIn3d(n) * RandInRange(-offsetamount, offsetamount);//* ( Clamp<float>(gGameTime * gTimeStep, -30.0, 30.0) *offsetamount);
//add 2 segments that we just split
AddLightningSegment(start,
midpoint,
Fatness,
SEGMENT_TYPE_BRANCH);
AddLightningSegment(midpoint,
end,
Fatness,
SEGMENT_TYPE_BRANCH);
//see if we want to add a split end this here will limmit the length if we run out of points
float choice[4] = {3.0, 2.0,3.0, 1.0};
UINT index = rand()%4;
if(subsequentgeneration >= choice[index])//2.0)
{
//vRandom = RandUnitVec3();//gGameTime * gTimeStep);//4.5);
//add split end we create a rendom end point at some angle and length
//if(Type == SEGMENT_TYPE_BRANCH)//reset to the max lenght
// lengthScale = gParticleSpreadAmount;
D3DXVECTOR3 direction = (midpoint - start);
//only if we are not to close
//float sl = length(direction);
l = D3DXVec3Length(&direction);
//if(l >= 10)//gGravity.y)//6.0)
{
D3DXVECTOR3 newendpoint = midpoint;
float r = Clamp<float>(rand(), -lengthScale, lengthScale);//RandF(-lengthScale, lengthScale);//
D3DXVECTOR3 n1 = (direction * r);//);
D3DXVec3Normalize(&n1, &n1);
newendpoint += n1* l;// lengthScale;
//each split can offset at max half as much as the generation before
lengthScale /= 2.0;
//Fatness *= 0.5;
D3DXVECTOR3 splitEnd = newendpoint;
AddLightningSegment(midpoint,
splitEnd,
Fatness,
SEGMENT_TYPE_SPLITEND);
}//end length
subsequentgeneration = 0.0;//reset
}//end subsequentgeneration
}//end past the length of split
}//end for segments
offsetamount /= 2.0; //Each subsequent generation offsets at max half as much as the generation before
}//end generation
//ok we want to lock our buffer
ParticleVertex *particl = NULL;//our data type
HRESULT hr = mInitVB->Map(D3D10_MAP_WRITE,//[in] D3D10_MAP MapType,
D3D10_MAP_FLAG_DO_NOT_WAIT,//[in] UINT MapFlags,
(void**)&particl);//[out] void **ppData
if(hr != S_OK)
return;//could be bizzy
//ok we can copy our data here but we can only copy mMaxParticles but
int numbertocopy = NumberToCreate;
if(LightningSegments.size() < numbertocopy)
numbertocopy = LightningSegments.size();
//reset so we can draw the correct amount later
AmountCopyedToBuffer = 0;
for(inc = Pop; inc != Incert; inc++)
{
if(inc->Valid)
{
//fill in the buffers data
particl[AmountCopyedToBuffer].Pos = inc->StartPoints;//in the shader is initialPosW
particl[AmountCopyedToBuffer].Velocity = inc->EndPoints;//in the shader is initialVelW
particl[AmountCopyedToBuffer].Size = inc->GenerationSize;
particl[AmountCopyedToBuffer].Type = inc->SegmentType;
AmountCopyedToBuffer++;//increase our count of data added
if(AmountCopyedToBuffer >= NumberToCreate)
{
//we are at max vertex buffer stop set flag and quit
AmountCopyedToBuffer = NumberToCreate -1;
//unmap the buffer
mInitVB->Unmap();
return;//done
}
}
//particl[ctr].Velocity;not used for lightning
//particl[ctr].Age;
//particl[ctr].ArrayID;
}//end all data
//unmap the buffer
mInitVB->Unmap();
}//end CalculateLightning
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////