Выбор треугольника XNA на SkinnedModel - PullRequest
2 голосов
/ 30 ноября 2011

Моя игра использует конвейер SkinnedModel и AnimationPlayer для загрузки и отображения моделей.Я реализовал выбор лучей с помощью треугольников, построив список вершин и индексов из конвейера.Это работает, однако индексы статичны и работают только для начальной позы модели (видя, как она ходит, руки двигаются взад и вперед, это не совсем хорошо, так как сборка видит модель только в одной позиции)

Я изменил его на выбор лучей с помощью треугольников, получая вершины и индексы во время выполнения, которые затем могут преобразовывать вершины с помощью boneTransforms, заданного из AnimationPlayer.У меня сейчас проблема в том, что я не совсем знаю, как преобразовывать вершины, когда они собираются

Я выложу код ниже, но я почти уверен, что это что-тоделать со строкой: Matrix worldMatrix = Orientation * Matrix.CreateTranslation (Position);Затем позже, когда получена конкретная вершина: vert = Vector3.Transform (vert, boneTransforms [mesh.ParentBone.Index] * worldMatrix);

Я обнаружил, что пропуск * Matrix.CreateTranslation (Position) получаетвершины близко, но не совсем верно.Я не очень опытен в работе с трехмерным пространством и преобразованиями, поэтому помощь будет полезна.

Код:

// Вот моя функция для получения вершин из модели класса

public List<Vector3> GetVertices()
    {
        List<Vector3> vertices = new List<Vector3>();

        Matrix[] boneTransforms = AnimPlayer.GetSkinTransforms();
        Matrix worldMatrix = Orientation;// *Matrix.CreateTranslation(Position);

        foreach (ModelMesh mesh in Model.Meshes)
        {
            // There may be multiple parts in a mesh (different materials etc.) so loop through each
            foreach (ModelMeshPart part in mesh.MeshParts)
            {
                // The stride is how big, in bytes, one vertex is in the vertex buffer
                // We have to use this as we do not know the make up of the vertex
                int stride = part.VertexBuffer.VertexDeclaration.VertexStride;

                byte[] vertexData = new byte[stride * part.NumVertices];
                part.VertexBuffer.GetData(part.VertexOffset * stride, vertexData, 0, part.NumVertices, 1); // fixed 13/4/11

                //part.IndexBuffer.GetData<float>(part.StartIndex * sizeof(float),
                short[] indices = new short[part.PrimitiveCount * 3];
                part.IndexBuffer.GetData(part.StartIndex * sizeof(short), indices, 0, part.PrimitiveCount * 3);

                // Find minimum and maximum xyz values for this mesh part
                // We know the position will always be the first 3 float values of the vertex data
                Vector3 vert = new Vector3();
                for(int i = 0; i < indices.Length; i++)//for (int ndx = 0; ndx < vertexData.Length; ndx += stride)
                {
                    vert.X = BitConverter.ToSingle(vertexData, (indices[i] * stride));
                    vert.Y = BitConverter.ToSingle(vertexData, (indices[i] * stride) + sizeof(float));
                    vert.Z = BitConverter.ToSingle(vertexData, (indices[i] * stride) + sizeof(float) * 2);

                    vert = Vector3.Transform(vert, boneTransforms[mesh.ParentBone.Index] * worldMatrix);

                    vertices.Add(vert);
                }
            }
        }

        return vertices;
    }

// Вот функция для получения луча выбора

public static Ray GetPickRay(Viewport vp, Matrix projectionMatrix, Matrix viewMatrix, MouseState mouseState)
    {
        int mouseX = mouseState.X;
        int mouseY = mouseState.Y;

        Vector3 nearsource = new Vector3((float)mouseX, (float)mouseY, 0f);
        Vector3 farsource = new Vector3((float)mouseX, (float)mouseY, 1f);

        Matrix world = Matrix.CreateTranslation(0, 0, 0);//TODO: ?

        Vector3 nearPoint = vp.Unproject(nearsource, projectionMatrix, viewMatrix, Matrix.Identity);

        Vector3 farPoint = vp.Unproject(farsource, projectionMatrix, viewMatrix, Matrix.Identity);

        // Create a ray from the near clip plane to the far clip plane.
        Vector3 direction = farPoint - nearPoint;
        direction.Normalize();
        Ray pickRay = new Ray(nearPoint, direction);

        return pickRay;
    }

// Вот две функции для проверки пересечения.

public static float? RayIntersectsModel(Ray ray, Person person, Matrix modelTransform,
                                     out bool insideBoundingSphere,
                                     out Vector3 vertex1, out Vector3 vertex2,
                                     out Vector3 vertex3)
    {
        vertex1 = vertex2 = vertex3 = Vector3.Zero;

        Matrix inverseTransform = Matrix.Invert(modelTransform);

        ray.Position = Vector3.Transform(ray.Position, inverseTransform);
        ray.Direction = Vector3.TransformNormal(ray.Direction, inverseTransform);

        insideBoundingSphere = true;

        float? closestIntersection = null;

        // Loop over the vertex data, 3 at a time (3 vertices = 1 triangle).
        Vector3[] vertices = person.GetVertices().ToArray();// tagData.Vertices.ToArray();

        for (int i = 0; i < vertices.Length; i += 3)
        {
            // Perform a ray to triangle intersection test.
            float? intersection;

            RayIntersectsTriangle(ref ray,
                                  ref vertices[i],
                                  ref vertices[i + 1],
                                  ref vertices[i + 2],
                                  out intersection);

            // Does the ray intersect this triangle?
            if (intersection != null)
            {
                // If so, is it closer than any other previous triangle?
                if ((closestIntersection == null) ||
                    (intersection < closestIntersection))
                {
                    // Store the distance to this triangle.
                    closestIntersection = intersection;

                    // Transform the three vertex positions into world space,
                    // and store them into the output vertex parameters.
                    Vector3.Transform(ref vertices[i],
                                      ref modelTransform, out vertex1);

                    Vector3.Transform(ref vertices[i + 1],
                                      ref modelTransform, out vertex2);

                    Vector3.Transform(ref vertices[i + 2],
                                      ref modelTransform, out vertex3);
                }
            }
        }

        return closestIntersection;
    }

    public static void RayIntersectsTriangle(ref Ray ray,
                                      ref Vector3 vertex1,
                                      ref Vector3 vertex2,
                                      ref Vector3 vertex3, out float? result)
    {
        // Compute vectors along two edges of the triangle.
        Vector3 edge1, edge2;

        Vector3.Subtract(ref vertex2, ref vertex1, out edge1);
        Vector3.Subtract(ref vertex3, ref vertex1, out edge2);

        // Compute the determinant.
        Vector3 directionCrossEdge2;
        Vector3.Cross(ref ray.Direction, ref edge2, out directionCrossEdge2);

        float determinant;
        Vector3.Dot(ref edge1, ref directionCrossEdge2, out determinant);

        // If the ray is parallel to the triangle plane, there is no collision.
        if (determinant > -float.Epsilon && determinant < float.Epsilon)
        {
            result = null;
            return;
        }

        float inverseDeterminant = 1.0f / determinant;

        // Calculate the U parameter of the intersection point.
        Vector3 distanceVector;
        Vector3.Subtract(ref ray.Position, ref vertex1, out distanceVector);

        float triangleU;
        Vector3.Dot(ref distanceVector, ref directionCrossEdge2, out triangleU);
        triangleU *= inverseDeterminant;

        // Make sure it is inside the triangle.
        if (triangleU < 0 || triangleU > 1)
        {
            result = null;
            return;
        }

        // Calculate the V parameter of the intersection point.
        Vector3 distanceCrossEdge1;
        Vector3.Cross(ref distanceVector, ref edge1, out distanceCrossEdge1);

        float triangleV;
        Vector3.Dot(ref ray.Direction, ref distanceCrossEdge1, out triangleV);
        triangleV *= inverseDeterminant;

        // Make sure it is inside the triangle.
        if (triangleV < 0 || triangleU + triangleV > 1)
        {
            result = null;
            return;
        }

        // Compute the distance along the ray to the triangle.
        float rayDistance;
        Vector3.Dot(ref edge2, ref distanceCrossEdge1, out rayDistance);
        rayDistance *= inverseDeterminant;

        // Is the triangle behind the ray origin?
        if (rayDistance < 0)
        {
            result = null;
            return;
        }

        result = rayDistance;
    }

// И, наконец, если это поможет, раздел моего обновления, который делает все это

Ray cursorRay = ModelHelper.GetPickRay(ScreenManager.GraphicsDevice.Viewport, projectionMatrix, viewMatrix, mouseState);

                //pickedModelName = null;
                Person pickedPerson = null;

                // Keep track of the closest object we have seen so far, so we can
                // choose the closest one if there are several models under the cursor.
                float closestIntersection = float.MaxValue;

                // Loop over all our models.
                for (int i = 0; i < People.Count; i++)
                {
                    bool insideBoundingSphere;
                    Vector3 vertex1, vertex2, vertex3;

                    // Perform the ray to model intersection test.
                    float? intersection = ModelHelper.RayIntersectsModel(cursorRay, People[i],
                                                             People[i].Orientation * Matrix.CreateTranslation(People[i].Position),
                                                             out insideBoundingSphere,
                                                             out vertex1, out vertex2,
                                                             out vertex3);

                    // Do we have a per-triangle intersection with this model?
                    if (intersection != null)
                    {
                        // If so, is it closer than any other model we might have
                        // previously intersected?
                        if (intersection < closestIntersection)
                        {
                            // Store information about this model.
                            closestIntersection = intersection.Value;

                            //pickedModelName = ModelFilenames[i];
                            pickedPerson = People[i];
                        }
                    }
                }
...