Моя игра использует конвейер 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];
}
}
}