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  阅读次数:6113

2006 - 2024,推荐分辨率 1024*768 以上,推荐浏览器 Chrome、Edge 等现代浏览器,截止 2021 年 12 月 5 日的访问次数:1872 万 9823 站长邮箱

沪 ICP 备 18037240 号-1

沪公网安备 31011002002865 号