Rendering the Particle System


There is only one more method to implement in the special effects class before you can hook it into the game engine and see the results of your work. If you guessed the Draw method, you are absolutely correct. See Listing 18.7.

Listing 18.7. Rendering the Particle System
 /// <summary> /// Renders the particles currently in use for the device /// </summary> public void Draw(GameEngine engine) {     // Set the render states for using point sprites     engine.RenderingDevice.RenderState.ZBufferWriteEnable = false;     engine.RenderingDevice.RenderState.AlphaBlendEnable = true;     engine.RenderingDevice.RenderState.SourceBlend = Blend.One;     engine.RenderingDevice.RenderState.DestinationBlend = Blend.One;     engine.RenderingDevice.RenderState.Lighting = false;     engine.SetWorldTransform(Matrix.Identity);     engine.RenderingDevice.RenderState.PointSpriteEnable = true;     engine.RenderingDevice.RenderState.PointScaleEnable = true;     engine.RenderingDevice.RenderState.PointSize = Bullet.BulletMass / 2.0f;     engine.RenderingDevice.RenderState.PointScaleA = 0.0f;     engine.RenderingDevice.RenderState.PointScaleB = 1.0f;     engine.RenderingDevice.RenderState.PointScaleC = 1.0f;     // Set up the vertex buffer to be rendered     engine.RenderingDevice.SetTexture(0, particleTexture);     engine.RenderingDevice.SetStreamSource(0, vertexBuffer, 0,                                      CustomVertex.PositionColored.StrideSize);     engine.RenderingDevice.VertexFormat = CustomVertex.PositionColored.Format;     int numParticlesToRender = 0;     // Lock the vertex buffer. We fill the vertex buffer in small     // chunks, using LockFlags.NoOverWrite. When we are done filling     // each chunk, we call DrawPrim, and lock the next chunk. When     // we run out of space in the vertex buffer, we start over at     // the beginning, using LockFlags.Discard.     baseParticle += FlushSize;     if (baseParticle >= MaximumParticles)     {         // We've gone too far, reset back to 0         baseParticle = 0;     }     GraphicsStream vertexData = vertexBuffer.Lock(baseParticle *         CustomVertex.PositionColored.StrideSize, FlushLockSize,         (baseParticle != 0) ? LockFlags.NoOverwrite : LockFlags.Discard);     foreach(Particle p in particlesList)     {         Vector3 vPos = p.positionVector;         Vector3 velocityVector = p.velocityVector;         float LengthSq = velocityVector.LengthSq();         uint steps;         if (LengthSq < 1.0f)        steps = 2;         else if (LengthSq <  4.00f) steps = 3;         else if (LengthSq <  9.00f) steps = 4;         else if (LengthSq < 12.25f) steps = 5;         else if (LengthSq < 16.00f) steps = 6;         else if (LengthSq < 20.25f) steps = 7;         else                          steps = 8;         velocityVector *= -0.01f / (float)steps;         ColorValue diffuse = ColorOperator.Lerp(p.fadeColor, p.diffuseColor,                                                 p.fadeProgression);         // Render each particle a bunch of times to get a blurring effect         for (int i = 0; i < steps; i++)         {             // Write the position and color             vertexData.Write(vPos);             vertexData.Write(diffuse.ToArgb());             if (++numParticlesToRender == FlushSize)             {                 // Done filling this chunk of the vertex buffer. Let's unlock and                 // draw this portion so we can begin filling the next chunk.                 vertexBuffer.Unlock();                 engine.RenderingDevice.DrawPrimitives(PrimitiveType.PointList,                    baseParticle, numParticlesToRender);                 // Lock the next chunk of the vertex buffer. If we are at the                 // end of the vertex buffer, LockFlags.Discard the vertex buffer                 // and start at the beginning. Otherwise, specify                 // LockFlags.NoOverWrite, so we can continue filling the VB                 // while the previous chunk is drawing.                 baseParticle += FlushSize;                 if (baseParticle >= MaximumParticles)                 {                     baseParticle = 0;                 }                 vertexData = vertexBuffer.Lock(baseParticle *                     CustomVertex.PositionColored.StrideSize, FlushLockSize,                     (baseParticle != 0) ? LockFlags.NoOverwrite :                      LockFlags.Discard);                                  numParticlesToRender = 0;             }             vPos += velocityVector;         }     }     // Unlock the vertex buffer     vertexBuffer.Unlock();     // Render any remaining particles     if (numParticlesToRender > 0)     {         engine.RenderingDevice.DrawPrimitives(PrimitiveType.PointList,              baseParticle, numParticlesToRender);     }     // Reset render states     engine.RenderingDevice.RenderState.PointSpriteEnable = false;     engine.RenderingDevice.RenderState.PointScaleEnable = false;     engine.RenderingDevice.RenderState.Lighting = true;     engine.RenderingDevice.RenderState.ZBufferWriteEnable = true;     engine.RenderingDevice.RenderState.AlphaBlendEnable = false; } 

Unfortunately (as you can tell), this method is quite long, but it does have a lot to do. Before any particle rendering starts, the device must be set up for rendering the point sprites. You'll notice the z buffer and lighting are turned off (as when you render the sky box), alpha blending is turned on, and then a number of render states related to point sprites are set. You can see the DirectX documentation for more information on these render states.

You need to prepare the device for rendering your particles. First, you set the texture to the particle texture that was created earlier. Next, you set the stream source to the vertex buffer you've created earlier. This part is telling Direct3D when you eventually are ready to render some primitives to use the data stored in this vertex buffer to do the rendering. You then tell Direct3D the vertex format of the point sprites (which you also used when creating the vertex buffer).

Here you are about to render the particles, and you've yet to fill the vertex buffer with any data. Remember earlier when you created the vertex buffer, one of the usage parameters you passed in was Dynamic, which tells Direct3D to optimize the buffer for frequent small updates, and that's exactly what you do now.

Because you're going to be rendering the point sprites in chunks, you keep track of the number of particles left in the queue to render, and naturally because you're just starting, you initialize it to zero. You also update the baseParticle variable and ensure that it hasn't gone past the maximum number of particles. The next section of code is quite important because the entire vertex buffer and rendering logic is encapsulated here.

First, you call the Lock method on the vertex buffer, which is telling Direct3D you are ready to add data to the buffer now. This method returns a GraphicsStream, which you use to write data into the buffer. Now, you start a loop through each of the particles in the active particle list. You get the position and velocity of the particle and use the velocity to determine how many point sprites to render for each particle. The faster the particle is moving, the more point sprites you use to render it (up to a maximum of 8). This part gives the effect of "blurring."

After you have that information, you calculate the color of the particle by interpolating between the normal color (white smoke) and the faded color (black). You then use the Write method on the returned stream to update the vertex buffer data with this information, and if you've got enough particles to render now, you unlock the buffer, render the primitives, and then relock the buffer. A locked buffer cannot be used during rendering, so this step is required.

After each particle is processed, the vertex buffer is unlocked a final time, and if any particles are left to be rendered, that is done. Finally, the device's state is returned back to the original values it had before this call.

Construction Cue

You can save and restore the device settings more efficiently using a StateBlock object; however, that discussion goes beyond the scope of what was intended for this chapter.




Beginning 3D Game Programming
Beginning 3D Game Programming
ISBN: 0672326612
EAN: 2147483647
Year: 2003
Pages: 191
Authors: Tom Miller

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net