Làm cách nào để tạo đường đua 3D từ spline?


9

Tôi muốn tạo một đường đua 3 chiều xung quanh một spline mô tả hình dạng của nó. Đây là một video minh họa .

Đường đua phải là một đường hầm vô tận quét dọc theo một đường nối 3D, với một số chướng ngại vật được ném vào. Ý tưởng tốt nhất của tôi cho đến nay là gác xép một hình tròn dọc theo đường nối.

Tôi cũng muốn một số gợi ý về cách quản lý hình học, tức là cách tạo nó từ hoạt động của gác xép, quản lý 'vòng đời' của nó trong bộ nhớ, va chạm và kết cấu.


1
Trông rất giống như danh từ: proun-game.com . Nếu không có vấn đề bản quyền, bạn có thể nhận được câu trả lời tốt từ tác giả của trò chơi này. Bạn có thể lấy thông tin của anh ấy ở đây: blog.oogst3d.net .
Vite Falcon

vâng tôi đã suy nghĩ về điều đó - tnx!
Valentin Galea

Câu trả lời:


5

Tôi không chắc bạn đang làm việc với ngôn ngữ nào nhưng có một ví dụ đùn lưới thủ tục cho Unity3D nằm ở đây:

http://unity3d.com/support/resource/example-projects/procedural-examples

Tôi chắc rằng bạn có thể xem mã và làm lại nó cho tình huống của bạn.

EDIT: Tôi đang làm việc trên một trò chơi sử dụng hệ thống đường ray ép đùn thủ tục giống như trò chơi bạn đang bắt đầu nhưng nó ở C # trong Unity3d. Tôi sẽ cung cấp cho bạn tổng quan về cách tôi tạo khối đùn đường sắt dựa trên đường dẫn Khối Bezier để mặc dù lưới của đường ray được tạo theo thủ tục, nó dựa trên đường dẫn Bezier mà tôi xác định trước trong trình soạn thảo. Nó sẽ giống như một trình soạn thảo cấp độ trong trường hợp trò chơi của bạn, trong trường hợp của tôi, đó là thiết kế các bảng pinball. Được liệt kê dưới đây là một ví dụ về cách tôi đang làm điều đó:

1.) Xây dựng / Tìm và thực hiện một lớp đường dẫn Bezier. Điều này sẽ cung cấp cho bạn dữ liệu nguồn cho đùn lưới của bạn. Có một cái trong C # ở đây mà bạn có thể chuyển sang c ++.

http://forum.unity3d.com/threads/32954-Waypoint-and-constant-variable-speed-probols?p=213942

2.) Khi bạn đã tạo Đường dẫn Bezier, các điểm dữ liệu từ đường dẫn này được lấy mẫu. Điều này có thể được thực hiện thông qua phương thức Interp trên lớp được cung cấp ở trên. Điều này sẽ cung cấp cho bạn một danh sách / mảng các điểm Vector3 dọc theo đường dẫn Bezier.

3.) Tạo một lớp trợ giúp để chuyển đổi dữ liệu đường dẫn Vector3 Bezier từ bước 2. Trong trường hợp này, tôi có một lớp đơn giản gọi là ExtrudedTrailSection như được định nghĩa dưới đây:

public class ExtrudedTrailSection
{
    public Vector3 point;
    public Matrix4x4 matrix;
    public float time;

    public ExtrudedTrailSection() { }
}

4.) Lặp lại thông qua dữ liệu mẫu Vector3 của bạn và chuyển đổi thành một mảng của ExtrudedTrailSections cung cấp dữ liệu mẫu và ma trận cơ sở sẽ là vị trí gốc của lưới được ép đùn của bạn.

  1. ) Sử dụng mảng ExtrudedTrailSections để tạo một mảng Matrix4x4 cuối cùng [] bằng mã sau:

Matrix4x4 worldToLocal = rootTransform.wworldToLocalMatrix;

    for (int i = 0; i < trailSections.Count; i++)
    {
            if (i == 0)
            {
                direction = trailSections[0].point - trailSections[1].point;
                rotation = Quaternion.LookRotation(direction, Vector3.up);
                previousRotation = rotation;
                finalSections[i] = worldToLocal * Matrix4x4.TRS(position, rotation, Vector3.one);
            }
            // all elements get the direction by looking up the next section
            else if (i != trailSections.Count - 1)
            {
                direction = trailSections[i].point - trailSections[i + 1].point;
                rotation = Quaternion.LookRotation(direction, Vector3.up);

                // When the angle of the rotation compared to the last segment is too high
                // smooth the rotation a little bit. Optimally we would smooth the entire sections array.
                if (Quaternion.Angle(previousRotation, rotation) > 20)
                    rotation = Quaternion.Slerp(previousRotation, rotation, 0.5f);

                previousRotation = rotation;
                finalSections[i] = worldToLocal * Matrix4x4.TRS(trailSections[i].point, rotation, Vector3.one);
            }
            // except the last one, which just copies the previous one
            else
            {
                finalSections[i] = finalSections[i - 1];
            }
        }

6.) Bây giờ bạn có một mảng Matrix4x4 [] và có thể đùn một lưới nhưng trước tiên chúng ta cần một lưới tham chiếu để đùn từ. Tôi có một lớp tiện ích sẽ tạo ra một mặt lưới tròn mà chúng ta sẽ cung cấp cho phương pháp đùn lưới.

public static List<Vector2> CreateCircle (double radius, int sides)
{
    List<Vector2> vectors = new List<Vector2> ();

    const float max = 2.0f * Mathf.PI;
    float step = max / sides;

    for (float theta = 0.0f; theta < max; theta += step) {
        vectors.Add (new Vector2 ((float)(radius * Mathf.Cos (theta)), (float)(radius * Mathf.Sin (theta))));
    }


    return vectors;
}

7.) Tìm trung tâm của dữ liệu này:

    public static Vector2 CalculateCentroid(List<Vector2> vectorList)
    {
        //////////////////////////////////////////////////////////////////////////
        // Local variables.
        float fArea = 0.0f, fDistance = 0.0f;
        Vector2 vCenter = Vector2.zero;
        int nIndex = 0, nLastPointIndex = vectorList.Count - 1;
        //
        //////////////////////////////////////////////////////////////////////////

        //////////////////////////////////////////////////////////////////////////
        // Run through the list of positions.
        for (int i = 0; i <= nLastPointIndex; ++i)
        {
            //////////////////////////////////////////////////////////////////////////
            // Cacluate index.
            nIndex = (i + 1) % (nLastPointIndex + 1);

            // Calculate distance.
            fDistance = vectorList[i].x * vectorList[nIndex].y - vectorList[nIndex].x * vectorList[i].y;

            // Acculmate area.
            fArea += fDistance;

            // Move center positions based on positions and distance.
            vCenter.x += (vectorList[i].x + vectorList[nIndex].x) * fDistance;
            vCenter.y += (vectorList[i].y + vectorList[nIndex].y) * fDistance;
        }
        //
        //////////////////////////////////////////////////////////////////////////

        //////////////////////////////////////////////////////////////////////////
        // Calculate the final center position.
        fArea *= 0.5f;
        vCenter.x *= 1.0f / (6.0f * fArea);
        vCenter.y *= 1.0f / (6.0f * fArea);
        //
        //////////////////////////////////////////////////////////////////////////

        return vCenter;
    }

8.) Bây giờ chúng ta có dữ liệu cạnh và trung tâm cho lưới mặt xuyên tâm, bạn có thể xây dựng một đối tượng lưới bằng dữ liệu của mình. Đỉnh cuối cùng trong lưới là điểm trung tâm chúng tôi tính toán. Lưới cuối cùng chỉ là một mặt được cung cấp cho phương pháp đùn lưới mà tôi đã cung cấp một ví dụ về trong lớp đùn lưới Thủ tục của gói Unity. Một lần nữa, đây là phương pháp của tôi và rõ ràng bạn sẽ phải cung cấp dữ liệu này vào OpenGL. Nếu bạn có một thư viện tiện ích 3d mà bạn đang sử dụng hoặc có thể viết lớp lưới của riêng bạn, nó có thể hoạt động tốt hơn để tạo lưới được ép đùn cuối cùng của bạn vì opengl không thực sự cần thiết cho kết xuất. Lưới mặt này chỉ được sử dụng làm tài liệu tham khảo cho đùn lưới.

    List<Vector3> levelVerts = new List<Vector3>();
    List<Vector2> levelUVBary = new List<Vector2>();
    List<Vector2> levelUVs = new List<Vector2>();
    List<int> levelTris = new List<int>();

    int verticesPerNode = 4;
    int edgeCount = sourceMeshData.Count;

    List<Vector3> sourceVerts = new List<Vector3>();
    //Debug.Log("smd.c:" + sourceMeshData.Count);
    for (int i = 0; i < edgeCount; i++)
    {
        //Debug.Log("adding:"+levelShapeData[i].x+"/"+levelShapeData[i].y);
        sourceVerts.Add(new Vector3(sourceMeshData[i].x, sourceMeshData[i].y, 0));
        levelUVs.Add(new Vector2(0, 0));
        //sourceVerts.Add(new Vector3(levelShapeData[i].x, levelShapeData[i].y, modelLength / 2f));
    }

    sourceVerts.Add(new Vector3(sourceMeshCenter.x, sourceMeshCenter.y, 0));
    levelUVs.Add(new Vector2(0, 0));

    for (int i = 0; i < edgeCount - 1; i++)
    {                                       //0, 1, 2, 3
        levelTris.Add(sourceVerts.Count - 1); //4, 4, 4, 4 
        levelTris.Add(i);                   //0, 1, 2, 
        levelTris.Add(i + 1);               //1, 2, 3,
    }

    levelTris.Add(sourceVerts.Count - 1);
    levelTris.Add(edgeCount - 1);
    levelTris.Add(0);

9.) Tìm các cạnh bên ngoài của lưới tròn khi cần bằng phương pháp đùn lưới. Một lần nữa, mã này được cung cấp trong gói thống nhất.

public class Edge
{
    // The indiex to each vertex
    public int[]  vertexIndex = new int[2];
    // The index into the face.
    // (faceindex[0] == faceindex[1] means the edge connects to only one triangle)
    public int[]  faceIndex = new int[2];
}

public static Edge[] BuildManifoldEdges (Mesh mesh)
{
    // Build a edge list for all unique edges in the mesh
    Edge[] edges = BuildEdges(mesh.vertexCount, mesh.triangles);

    // We only want edges that connect to a single triangle
    ArrayList culledEdges = new ArrayList();
    foreach (Edge edge in edges)
    {
        if (edge.faceIndex[0] == edge.faceIndex[1])
        {
            culledEdges.Add(edge);
        }
    }

    return culledEdges.ToArray(typeof(Edge)) as Edge[];
}

10.) Đưa tất cả dữ liệu này vào phương pháp Lưới Đùn ..

public static void ExtrudeMesh (Mesh srcMesh, Mesh extrudedMesh, Matrix4x4[] extrusion, Edge[] edges, bool invertFaces)
{
    int extrudedVertexCount = edges.Length * 2 * extrusion.Length;
    int triIndicesPerStep = edges.Length * 6;
    int extrudedTriIndexCount = triIndicesPerStep * (extrusion.Length -1);

    Vector3[] inputVertices = srcMesh.vertices;
    Vector2[] inputUV = srcMesh.uv;
    int[] inputTriangles = srcMesh.triangles;

    //Debug.Log("inputUV:" + inputUV.Length);

    Vector3[] vertices = new Vector3[extrudedVertexCount + srcMesh.vertexCount * 2];
    Vector2[] uvs = new Vector2[vertices.Length];
    int[] triangles = new int[extrudedTriIndexCount + inputTriangles.Length * 2];

    // Build extruded vertices
    int v = 0;
    for (int i=0;i<extrusion.Length;i++)
    {
        Matrix4x4 matrix = extrusion[i];
        float vcoord = (float)i / (extrusion.Length -1);
        foreach (Edge e in edges)
        {
            //Debug.Log(e.vertexIndex.Length);
            vertices[v+0] = matrix.MultiplyPoint(inputVertices[e.vertexIndex[0]]);
            vertices[v+1] = matrix.MultiplyPoint(inputVertices[e.vertexIndex[1]]);

            uvs[v+0] = new Vector2 (inputUV[e.vertexIndex[0]].x, vcoord);
            uvs[v+1] = new Vector2 (inputUV[e.vertexIndex[1]].x, vcoord);

            v += 2;
        }
    }       

    // Build cap vertices
    // * The bottom mesh we scale along it's negative extrusion direction. This way extruding a half sphere results in a capsule.
    for (int c=0;c<2;c++)
    {
        Matrix4x4 matrix = extrusion[c == 0 ? 0 : extrusion.Length-1];
        int firstCapVertex = c == 0 ? extrudedVertexCount : extrudedVertexCount + inputVertices.Length;
        for (int i=0;i<inputVertices.Length;i++)
        {
            vertices[firstCapVertex + i] = matrix.MultiplyPoint(inputVertices[i]);
            uvs[firstCapVertex + i] = inputUV[i];
        }
    }

    // Build extruded triangles
    for (int i=0;i<extrusion.Length-1;i++)
    {
        int baseVertexIndex = (edges.Length * 2) * i;
        int nextVertexIndex = (edges.Length * 2) * (i+1);
        for (int e=0;e<edges.Length;e++)
        {
            int triIndex = i * triIndicesPerStep + e * 6;

            triangles[triIndex + 0] = baseVertexIndex + e * 2;
            triangles[triIndex + 1] = nextVertexIndex  + e * 2;
            triangles[triIndex + 2] = baseVertexIndex + e * 2 + 1;
            triangles[triIndex + 3] = nextVertexIndex + e * 2;
            triangles[triIndex + 4] = nextVertexIndex + e * 2 + 1;
            triangles[triIndex + 5] = baseVertexIndex  + e * 2 + 1;
        }
    }

    // build cap triangles
    int triCount = inputTriangles.Length / 3;
    // Top
    {
        int firstCapVertex = extrudedVertexCount;
        int firstCapTriIndex = extrudedTriIndexCount;
        for (int i=0;i<triCount;i++)
        {
            triangles[i*3 + firstCapTriIndex + 0] = inputTriangles[i * 3 + 1] + firstCapVertex;
            triangles[i*3 + firstCapTriIndex + 1] = inputTriangles[i * 3 + 2] + firstCapVertex;
            triangles[i*3 + firstCapTriIndex + 2] = inputTriangles[i * 3 + 0] + firstCapVertex;
        }
    }

    // Bottom
    {
        int firstCapVertex = extrudedVertexCount + inputVertices.Length;
        int firstCapTriIndex = extrudedTriIndexCount + inputTriangles.Length;
        for (int i=0;i<triCount;i++)
        {
            triangles[i*3 + firstCapTriIndex + 0] = inputTriangles[i * 3 + 0] + firstCapVertex;
            triangles[i*3 + firstCapTriIndex + 1] = inputTriangles[i * 3 + 2] + firstCapVertex;
            triangles[i*3 + firstCapTriIndex + 2] = inputTriangles[i * 3 + 1] + firstCapVertex;
        }
    }

    if (invertFaces)
    {
        for (int i=0;i<triangles.Length/3;i++)
        {
            int temp = triangles[i*3 + 0];
            triangles[i*3 + 0] = triangles[i*3 + 1];
            triangles[i*3 + 1] = temp;
        }
    }

    extrudedMesh.vertices = vertices;
    extrudedMesh.uv = uvs;
    extrudedMesh.triangles = triangles;
}

Đầu ra cuối cùng trong trường hợp của tôi trông như thế này ..

nhập mô tả hình ảnh ở đây

Chúc may mắn, trò chơi của bạn trông thực sự mát mẻ! Hãy cho tôi biết nếu bạn tìm ra nó?

Chuck


Tôi chủ yếu quan tâm đến OpenGL và C ++, nhưng 'giả' sẽ giúp quá :)
Valentin Galea

Tôi đã cập nhật bài viết gốc của mình để bao gồm một giải pháp mà tôi vừa hoàn thành.
Chuck D
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.