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); }
单元测试截图如下:
文件下载(已下载 1080 次)
发布时间:2010/4/23 下午3:39:00 阅读次数:6171