3D网格旋转

时间:2012-02-07 03:54:19

标签: c# 3d xna

我对3D图形很新,我在旋转方面找到了很多例子,但我相信我的代码可以创建3D网格或旋转本身。我正在使用primatives example for xna的修改版本。

Example Image

基本上我的问题归结为我绕着红色x线而不是蓝色x线旋转。理想情况下,我想旋转蓝色Y线和蓝色X线相交的地方。要查看旋转功能,请参阅下面列出的microsoft类Geometric Primative,并查看RotateX。

感谢您的帮助。

以下是我设置场景的方法:

public RenderScene3D(short _depth, GraphicsDevice GD) : base(_depth)
{
    PrimativeCollection = new List<GeometricPrimitive>();
    cameraPosition = new Vector3(0, 0, 2.5f);
    fAspect = GD.Viewport.AspectRatio;

    CameraWorld = Matrix.CreateTranslation(cameraPosition);
    world = Matrix.CreateFromYawPitchRoll(0, 0, 0);
    view = Matrix.CreateLookAt(cameraPosition, Vector3.Zero, Vector3.Up);
    projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, fAspect, .01f, 500f);
    // This serves as a base line for latter ray cursor calculations.
    ScreenProjection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, fAspect, .01f, 2.5f);
    graphicsDevice = GD;          
}

Microsoft class Geometric Primative的稍微修改版本:

public abstract class GeometricPrimitive : IDisposable
{
    #region Fields

    // During the process of constructing a primitive model, vertex
    // and index data is stored on the CPU in these managed lists.
    protected List<VertexPositionNormal> vertices = new List<VertexPositionNormal>();
    protected List<ushort> indices = new List<ushort>();
    public Vector3 v3Position;
    protected Matrix world;
    protected Matrix worldPosition;
    protected Matrix worldRotation;

    // 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.
    protected VertexBuffer vertexBuffer;
    protected IndexBuffer indexBuffer;
    protected BasicEffect basicEffect;

    #endregion

    #region Initialization


    /// <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)
    {
        vertices.Add(new VertexPositionNormal(_position, _position));
    }


    /// <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; }
    }


    /// <summary>
    /// Once all the geometry has been specified by calling AddVertex and AddIndex,
    /// this method copies the vertex and index data into GPU format buffers, ready
    /// for efficient rendering.
    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();
        RotateX(0);
        UpdateWorld();
    }

    public void Move(Vector3 Position)
    {
        v3Position = Position;
        worldPosition = Matrix.CreateTranslation(v3Position);
    }

    public void UpdateWorld()
    {
        world = worldRotation * worldPosition;
    }

    public void RotateX(float X)
    {
        Matrix rotation = worldPosition;

        Matrix.CreateRotationX(X, out rotation);
        worldRotation = rotation;
    }


    /// <summary>
    /// Finalizer.
    /// </summary>
    ~GeometricPrimitive()
    {
        Dispose(false);
    }


    /// <summary>
    /// Frees resources used by this object.
    /// </summary>
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }


    /// <summary>
    /// Frees resources used by this object.
    /// </summary>
    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (vertexBuffer != null)
                vertexBuffer.Dispose();

            if (indexBuffer != null)
                indexBuffer.Dispose();

            if (basicEffect != null)
                basicEffect.Dispose();
        }
    }


    #endregion

    #region Draw


    /// <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;            


        foreach (EffectPass effectPass in effect.CurrentTechnique.Passes)
        {
            effectPass.Apply();

            int primitiveCount = indices.Count / 3;

            graphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vertices.Count, 0, primitiveCount);

        }
    }


    /// <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(Matrix view, Matrix projection, Color color)
    {
        // Set BasicEffect parameters.
        basicEffect.World = world;
        //Matrix worldMatrix = Matrix.CreateScale(0.0005f, 0.0005f, 0.0005f) * Matrix.CreateRotationY(MathHelper.Pi) * Matrix.CreateFromQuaternion(xwingRotation) * Matrix.CreateTranslation(xwingPosition);
        //Matrix worldMatrix = Matrix.CreateScale(0.0005f, 0.0005f, 0.0005f) * Matrix.CreateRotationY(MathHelper.Pi) * Matrix.CreateFromQuaternion(xwingRotation) * Matrix.CreateTranslation(xwingPosition);
        //Matrix test = view.Translation * new Vector3(0,0,0.001f);
        basicEffect.View = view;
        basicEffect.Projection = projection;
        basicEffect.DiffuseColor = color.ToVector3();
        basicEffect.Alpha = 128.0f / color.A;

        GraphicsDevice device = basicEffect.GraphicsDevice;
        // Reset the fill mode renderstate.
        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.AlphaBlend;
        }

        // Draw the model, using BasicEffect.
        Draw(basicEffect);
    }
}

最后我的方形课程:

public class SquarePrimitive : GeometricPrimitive
{
    public float size;
    /// <summary>
    /// Constructs a new square primitive, using default settings.
    /// </summary>
    public SquarePrimitive(Vector3 position, Vector3 _v3Rotation)
        : this(position, _v3Rotation, 1)
    {

    }

    /// <summary>
    /// Constructs a new square primitive, with the specified size.
    /// </summary>
    public SquarePrimitive(Vector3 position, Vector3 _v3Rotation, float _size)
    {
        size = _size;
        // Get two vectors perpendicular to the face normal and to each other.
        Vector3 topLeft = new Vector3(-size, size, 0);
        Vector3 topRight = new Vector3(size, size, 0);
        Vector3 bottomLeft = new Vector3(-size, -size, 0);
        Vector3 bottomRight = new Vector3(size, -size, 0);

        // Six indices (two triangles) per face.
        AddIndex(CurrentVertex + 0);
        AddIndex(CurrentVertex + 1);
        AddIndex(CurrentVertex + 2);

        AddIndex(CurrentVertex + 0);
        AddIndex(CurrentVertex + 2);
        AddIndex(CurrentVertex + 3);

        // Four vertices per face.
        size = 0.1f;
        AddVertex((position + topLeft), position);
        AddVertex((position + topRight), position);
        AddVertex((position + bottomRight), position);
        AddVertex((position + bottomLeft), position);

        Move(position);
    }
}

1 个答案:

答案 0 :(得分:1)

它与偏移(平移)网格的几何中心有关,使得所需的旋转点在轴上,并且该轴穿过世界原点,然后应用旋转,然后将其平移回到它的位置是。或者......修改代码以防止需要这样做。

您的代码中有足够的细微之处,我不得不使用它来提供一系列适用于您的代码,但这只是一两个想法。

    AddVertex((position + topLeft), position);
    AddVertex((position + topRight), position);
    AddVertex((position + bottomRight), position);
    AddVertex((position + bottomLeft), position);

通过在正方形的角上添加“位置”,您将偏移正方形的几何中心与网格的局部空间原点。

在旋转过程中,局部原点(和所有顶点)始终围绕穿过世界原点的轴旋转。您的代码将本地空间原点与该轴内联,但是您希望的旋转轴围绕几何中心(而不是在本地原点周围),在旋转期间需要平移偏移。

通常,您不会将'position'添加到这样的顶点(从而完全避免该问题)。您可以在局部空间中对称地构建原点周围的顶点,然后将它们的束转换为您希望它们位于世界空间中的任何位置。然后,旋转偏移 更容易处理。

乍一看,如果您从角顶点移除了“位置”数量,那么您的代码就可以按预期执行。

希望至少能为您提供一条线索,让您朝着正确的方向前进。