26. 绘制球体的SphereSceneNode类
首先还是SphereSceneNode类的代码:
namespace StunEngine.SceneNodes
{
///
/// 创建一个球体
///
public class SphereSceneNode : GenericMaterialSceneNode
{
private int sphereCylinders;
private int sphereSlices;
private float radius = 1;
///
/// 创建一个默认的球体,纬度有16段,经度有64段。
///
/// 引擎
/// 纹理名称
/// 半径
public SphereSceneNode(StunXnaGE engine, Scene setScene, string setDiffuseTextureName, float radius) : this(engine,setScene, setDiffuseTextureName,Vector2 .One , null,Vector2 .Zero , radius, 16, 64) { }
///
/// 创建一个SphereSceneNode对象。
///
/// 引擎
/// 漫反射纹理名称
/// /// 漫反射纹理UV重复次数
/// 细节纹理名称
/// 细节纹理UV重复次数
/// 半径
/// 水平切片数量
/// 竖直切片数量
public SphereSceneNode(StunXnaGE engine, Scene setScene, string setDiffuseTextureName, Vector2 setDiffuseTiles, string setDetailTextureName,Vector2 setDetailTiles, float radius, int cylinders, int slices)
: base(engine,setScene,setDiffuseTextureName ,setDiffuseTiles ,setDetailTextureName ,setDetailTiles)
{
if (cylinders < 3 || slices < 3)
throw new ArgumentException("slices/cylinders小于3无法计算SphereSceneNode");
this.sphereCylinders = cylinders;
this.sphereSlices = slices;
this.radius = radius;
this.pose.LocalSize = this.radius *2;
Vector3 scale = Vector3.One;
this.Pose.SetScale(ref scale);
}
///
/// 初始化节点。这个函数由引擎调用,不要直接使用它。
///
public override void Initialize()
{
this.UpdateOrder = SceneManagement.SceneNodeOrdering.SceneNode.GetValue();
base.Initialize();
this.mesh =MeshBuilder .CreateSphere (engine .GraphicsDevice ,(byte) sphereCylinders, (byte)sphereSlices, this.radius);
}
#region 单元测试
#if DEBUG
///
/// 测试SphereSceneNode类
///
public static void TestSphereSceneNode()
{
SphereSceneNode sphere1 = null;
SphereSceneNode sphere2 = null;
TestGame.Start("测试SphereSceneNode类",
delegate
{
//添加一个放置在左边的覆盖有草地纹理的椭球体
sphere1 = new SphereSceneNode(TestGame.engine, TestGame.scene,"Textures\\Grass", 1);
TestGame.scene.AddNode(sphere1);
Vector3 position = new Vector3(-4f, 1.5f, 0.0f);
sphere1.Pose.SetPosition(ref position);
Vector3 scale = new Vector3(2.0f, 1.0f, 2.0f);
sphere1.Pose.SetScale(ref scale);
//添加一个放置在右边的白色不带纹理的球体
sphere2 = new SphereSceneNode(TestGame.engine, TestGame.scene, null, 1);
TestGame.scene.AddNode(sphere2);
position = new Vector3(4f, 1.5f, 0.0f);
sphere2.Pose.SetPosition(ref position);
sphere2.Material.DiffuseColor = new Vector3(1.0f, 1.0f, 1.0f);
sphere2.Material.SpecularColor = new Vector3(1.0f, 1.0f, 1.0f);
sphere2.Material.SpecularPower = 50;
//不显示光标
TestGame.scene.IsShowMouse = false;
},
delegate
{
//按数字1键则切换使用的technique
if (Input.KeyboardKeyJustPressed(Keys.D1))
{
if (sphere1.Material.CurrentTechniqueName == "SimpleTextured")
sphere1.Material.CurrentTechniqueName = "TexturedLights";
else
sphere1.Material.CurrentTechniqueName = "SimpleTextured";
}
//按数字2键则切换漫反射纹理
if (Input.KeyboardKeyJustPressed(Keys.D2))
{
if (sphere1.Material.DiffuseTextureName == "Textures\\Grass")
sphere1.Material.DiffuseTextureName = "Textures\\Rock";
else
sphere1.Material.DiffuseTextureName = "Textures\\Grass";
}
//按数字3键切换漫反射纹理平铺值
if (Input.KeyboardKeyJustPressed(Keys.D3))
{
if (sphere1.Material.DiffuseUVTile == Vector2.One)
sphere1.Material.DiffuseUVTile = new Vector2(2.0f, 2.0f);
else
sphere1.Material.DiffuseUVTile = Vector2.One;
}
});
}
#endif
#endregion
}
}
创建顶点数据的示意图如下所示:

顶点和索引的数据是通过两个循环计算得出的,具体代码在MeshBuilder类的静态方法CreateSphere中:
////// 创建一个球体的顶点和索引。 /// /// 图形设备 /// 水平切片数量 /// 竖直切片数量 /// 半径 ///public static Mesh CreateSphere(GraphicsDevice g, byte sphereCylinders, byte sphereSlices, float radius) { //计算顶点和索引数量 int n_Vertices = (sphereCylinders + 1) * (sphereSlices + 1); int dwIndices = (3 * sphereCylinders * (sphereSlices + 1)) * 2; int[] indices = new int[dwIndices]; VertexPositionNormalTexture[] vertices = new VertexPositionNormalTexture[n_Vertices]; float StackAngle = MathHelper.Pi / (float)sphereCylinders; float SliceAngle = (float)(Math.PI * 2.0) / (float)sphereSlices; //生成球的顶点和索引 int vertcount = 0; int Indexcount = 0; int wVertexIndex = 0; //计算每一条水平带 for (int stack = 0; stack < (sphereCylinders + 1); stack++) { float r = (float)Math.Sin((float)stack * StackAngle); float y = (float)Math.Cos((float)stack * StackAngle); //生成当前水平带上的竖直切片 for (int slice = 0; slice < (sphereSlices + 1); slice++) { float x = r * (float)Math.Sin((float)slice * SliceAngle); float z = r * (float)Math.Cos((float)slice * SliceAngle); //计算每个顶点的位置、法线和纹理坐标 vertices[vertcount].Position = new Vector3(x * radius, y * radius, z * radius); vertices[vertcount].Normal = Vector3.Normalize(new Vector3(x, y, z)); vertices[vertcount].TextureCoordinate = new Vector2((float)slice / (float)sphereSlices, (float)stack / (float)sphereCylinders); vertcount++; //生成索引数据 if (stack < sphereCylinders) { indices[Indexcount++] = wVertexIndex + (sphereSlices + 1); indices[Indexcount++] = wVertexIndex; indices[Indexcount++] = wVertexIndex + 1; indices[Indexcount++] = wVertexIndex + (sphereSlices); indices[Indexcount++] = wVertexIndex; indices[Indexcount++] = wVertexIndex + (sphereSlices + 1); wVertexIndex++; } } } VertexDeclaration vxDeclaration = new VertexDeclaration(g, VertexPositionNormalTexture.VertexElements); VertexBuffer vb = new VertexBuffer(g, typeof(VertexPositionNormalTexture), vertcount, BufferUsage.None); vb.SetData(vertices, 0, vertcount); IndexBuffer ib = new IndexBuffer(g, typeof(int), Indexcount, BufferUsage.None); ib.SetData(indices, 0, Indexcount); return new Mesh(PrimitiveType.TriangleList, vb, vxDeclaration, ib); }
单元测试截图如下:

发布时间:2010/4/23 下午3:39:00 阅读次数:6750
