简体   繁体   中英

3D Mesh Rotation

I'm newish to 3D graphics and I've found a number of examples on rotation, however I believe something is off with my code for either the creation of the 3D mesh or the rotation itself. I'm using a modified version of the primatives example for xna .

范例图片

So essentially my problem boils down to that I'm rotating around the red x line instead of the blue x line. Ideally I want to rotate where the blue Y line and the Blue X line meet. Too look at the rotation function see the microsoft class Geometric Primative listed below and look at RotateX.

Thank you for any help.

Here is how I setup my scene:

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

Slightly modified version of the 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);
    }
}

Finally my square class:

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

It has to do with offsetting (translating) the geometric center of the mesh such that the desired rotation point is on the axis and that axis runs through the world origin, applying the rotation then, and subsequently translating it back to where it was. Or... modifying your code to prevent needing to do that.

There are enough subtleties in your code that I would have to play around with it to offer a line of code that would work for you but here is a thought or two.

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

By adding 'position' to the corners of the square, you are offsetting the geometric center of the square from the local space origin of the mesh.

During rotation, the local origin (and all vertices) are always rotated around an axis that runs through the world origin. Your code brings the local space origin inline with that axis, but With your desired rotation axis being around the geometric center, (instead of around the local origin), a translational offset is necessary during rotation.

Typically, you would not add 'position' to a vertex like that (thus avoiding that issue altogether). You build the vertices around the origin symmetrically in local space and translate the bunch of them later to wherever you want them to be in world space. Then that rotational offset is much easier to deal with.

It looks to me at first glance that if you removed that 'position' amount from the corner vertices, your code would do as you expect.

Hope that at least give you a clue to set you in the right direction.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM