XNA shield effect with a Primative sphere problem
- by Sparky41
I'm having issue with a shield effect i'm trying to develop. I want to do a shield effect that surrounds part of a model like this: http://i.imgur.com/jPvrf.png
I currently got this:
http://i.imgur.com/Jdin7.png
(The red likes are a simple texture a black background with a red cross in it, for testing purposes: http://i.imgur.com/ODtzk.png where the smaller cross in the middle shows the contact point)
This sphere is drawn via a primitive (DrawIndexedPrimitives)
This is how i calculate the pieces of the sphere using a class i've called Sphere
(this class is based off the code here: http://xbox.create.msdn.com/en-US/education/catalog/sample/primitives_3d)
public class Sphere
{
// During the process of constructing a primitive model, vertex
// and index data is stored on the CPU in these managed lists.
List vertices = new List();
List indices = new List();
// Once all the geometry has been specified, the InitializePrimitive
// method copies the vertex and index data into these buffers, which
// store it on the GPU ready for efficient rendering.
VertexBuffer vertexBuffer;
IndexBuffer indexBuffer;
BasicEffect basicEffect;
public Vector3 position = Vector3.Zero;
public Matrix RotationMatrix = Matrix.Identity;
public Texture2D texture;
/// <summary>
/// Constructs a new sphere primitive,
/// with the specified size and tessellation level.
/// </summary>
public Sphere(float diameter, int tessellation, Texture2D text, float up, float down, float portstar, float frontback)
{
texture = text;
if (tessellation < 3)
throw new ArgumentOutOfRangeException("tessellation");
int verticalSegments = tessellation;
int horizontalSegments = tessellation * 2;
float radius = diameter / 2;
// Start with a single vertex at the bottom of the sphere.
AddVertex(Vector3.Down * ((radius / up) + 1), Vector3.Down, Vector2.Zero);//bottom position5
// Create rings of vertices at progressively higher latitudes.
for (int i = 0; i < verticalSegments - 1; i++)
{
float latitude = ((i + 1) * MathHelper.Pi /
verticalSegments) - MathHelper.PiOver2;
float dy = (float)Math.Sin(latitude / up);//(up)5
float dxz = (float)Math.Cos(latitude);
// Create a single ring of vertices at this latitude.
for (int j = 0; j < horizontalSegments; j++)
{
float longitude = j * MathHelper.TwoPi / horizontalSegments;
float dx = (float)(Math.Cos(longitude) * dxz) / portstar;//port and starboard (right)2
float dz = (float)(Math.Sin(longitude) * dxz) * frontback;//front and back1.4
Vector3 normal = new Vector3(dx, dy, dz);
AddVertex(normal * radius, normal, new Vector2(j, i));
}
}
// Finish with a single vertex at the top of the sphere.
AddVertex(Vector3.Up * ((radius / down) + 1), Vector3.Up, Vector2.One);//top position5
// Create a fan connecting the bottom vertex to the bottom latitude ring.
for (int i = 0; i < horizontalSegments; i++)
{
AddIndex(0);
AddIndex(1 + (i + 1) % horizontalSegments);
AddIndex(1 + i);
}
// Fill the sphere body with triangles joining each pair of latitude rings.
for (int i = 0; i < verticalSegments - 2; i++)
{
for (int j = 0; j < horizontalSegments; j++)
{
int nextI = i + 1;
int nextJ = (j + 1) % horizontalSegments;
AddIndex(1 + i * horizontalSegments + j);
AddIndex(1 + i * horizontalSegments + nextJ);
AddIndex(1 + nextI * horizontalSegments + j);
AddIndex(1 + i * horizontalSegments + nextJ);
AddIndex(1 + nextI * horizontalSegments + nextJ);
AddIndex(1 + nextI * horizontalSegments + j);
}
}
// Create a fan connecting the top vertex to the top latitude ring.
for (int i = 0; i < horizontalSegments; i++)
{
AddIndex(CurrentVertex - 1);
AddIndex(CurrentVertex - 2 - (i + 1) % horizontalSegments);
AddIndex(CurrentVertex - 2 - i);
}
//InitializePrimitive(graphicsDevice);
}
/// <summary>
/// Adds a new vertex to the primitive model. This should only be called
/// during the initialization process, before InitializePrimitive.
/// </summary>
protected void AddVertex(Vector3 position, Vector3 normal, Vector2 texturecoordinate)
{
vertices.Add(new VertexPositionNormal(position, normal, texturecoordinate));
}
/// <summary>
/// Adds a new index to the primitive model. This should only be called
/// during the initialization process, before InitializePrimitive.
/// </summary>
protected void AddIndex(int index)
{
if (index > ushort.MaxValue)
throw new ArgumentOutOfRangeException("index");
indices.Add((ushort)index);
}
/// <summary>
/// Queries the index of the current vertex. This starts at
/// zero, and increments every time AddVertex is called.
/// </summary>
protected int CurrentVertex
{
get { return vertices.Count; }
}
public void InitializePrimitive(GraphicsDevice graphicsDevice)
{
// Create a vertex declaration, describing the format of our vertex data.
// Create a vertex buffer, and copy our vertex data into it.
vertexBuffer = new VertexBuffer(graphicsDevice,
typeof(VertexPositionNormal),
vertices.Count, BufferUsage.None);
vertexBuffer.SetData(vertices.ToArray());
// Create an index buffer, and copy our index data into it.
indexBuffer = new IndexBuffer(graphicsDevice, typeof(ushort),
indices.Count, BufferUsage.None);
indexBuffer.SetData(indices.ToArray());
// Create a BasicEffect, which will be used to render the primitive.
basicEffect = new BasicEffect(graphicsDevice);
//basicEffect.EnableDefaultLighting();
}
/// <summary>
/// Draws the primitive model, using the specified effect. Unlike the other
/// Draw overload where you just specify the world/view/projection matrices
/// and color, this method does not set any renderstates, so you must make
/// sure all states are set to sensible values before you call it.
/// </summary>
public void Draw(Effect effect)
{
GraphicsDevice graphicsDevice = effect.GraphicsDevice;
// Set our vertex declaration, vertex buffer, and index buffer.
graphicsDevice.SetVertexBuffer(vertexBuffer);
graphicsDevice.Indices = indexBuffer;
graphicsDevice.BlendState = BlendState.Additive;
foreach (EffectPass effectPass in effect.CurrentTechnique.Passes)
{
effectPass.Apply();
int primitiveCount = indices.Count / 3;
graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0,
vertices.Count, 0, primitiveCount);
}
graphicsDevice.BlendState = BlendState.Opaque;
}
/// <summary>
/// Draws the primitive model, using a BasicEffect shader with default
/// lighting. Unlike the other Draw overload where you specify a custom
/// effect, this method sets important renderstates to sensible values
/// for 3D model rendering, so you do not need to set these states before
/// you call it.
/// </summary>
public void Draw(Camera camera, Color color)
{
// Set BasicEffect parameters.
basicEffect.World = GetWorld();
basicEffect.View = camera.view;
basicEffect.Projection = camera.projection;
basicEffect.DiffuseColor = color.ToVector3();
basicEffect.TextureEnabled = true;
basicEffect.Texture = texture;
GraphicsDevice device = basicEffect.GraphicsDevice;
device.DepthStencilState = DepthStencilState.Default;
if (color.A < 255)
{
// Set renderstates for alpha blended rendering.
device.BlendState = BlendState.AlphaBlend;
}
else
{
// Set renderstates for opaque rendering.
device.BlendState = BlendState.Opaque;
}
// Draw the model, using BasicEffect.
Draw(basicEffect);
}
public virtual Matrix GetWorld()
{
return /*world */ Matrix.CreateScale(1f) * RotationMatrix * Matrix.CreateTranslation(position);
}
}
public struct VertexPositionNormal : IVertexType
{
public Vector3 Position;
public Vector3 Normal;
public Vector2 TextureCoordinate;
/// <summary>
/// Constructor.
/// </summary>
public VertexPositionNormal(Vector3 position, Vector3 normal, Vector2 textCoor)
{
Position = position;
Normal = normal;
TextureCoordinate = textCoor;
}
/// <summary>
/// A VertexDeclaration object, which contains information about the vertex
/// elements contained within this struct.
/// </summary>
public static readonly VertexDeclaration VertexDeclaration = new VertexDeclaration
(
new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0),
new VertexElement(12, VertexElementFormat.Vector3, VertexElementUsage.Normal, 0),
new VertexElement(24, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0)
);
VertexDeclaration IVertexType.VertexDeclaration
{
get { return VertexPositionNormal.VertexDeclaration; }
}
}
A simple call to the class to initialise it. The Draw method is called in the master draw method in the Gamecomponent.
My current thoughts on this are:
The direction of the weapon hitting the ship is used to get the middle position for the texture
Wrap a texture around the drawn sphere based on this point of contact
Problem is i'm not sure how to do this. Can anyone help or if you have a better idea please tell me i'm open for opinion? :-) Thanks.