Bagaimana cara menghasilkan trek balap 3D dari spline?

9

Saya ingin membuat trek balap 3 dimensi di sekitar spline yang menjelaskan bentuknya. Ini video ilustratif .

Jalurnya seharusnya berupa terowongan tanpa akhir yang menyapu sepanjang spline 3D, dengan beberapa rintangan dilemparkan. Ide terbaik saya sejauh ini adalah untuk melompati bentuk lingkaran di sepanjang spline.

Saya juga ingin beberapa petunjuk tentang bagaimana mengelola geometri yaitu cara membuatnya dari operasi loteng, mengelola 'siklus hidup' dalam memori, tabrakan dan tekstur.

Valentin Galea
sumber
1
Tampak sangat mirip dengan proun: proun-game.com . Jika tidak ada masalah hak cipta, Anda mungkin mendapatkan jawaban yang bagus dari pembuat game ini. Anda bisa mendapatkan informasinya di sini: blog.oogst3d.net .
Vite Falcon
ya saya sedang memikirkan itu - tnx!
Valentin Galea

Jawaban:

5

Saya tidak yakin bahasa apa yang Anda gunakan, tetapi ada contoh ekstrusi mesh prosedural untuk Unity3D yang terletak di sini:

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

Saya yakin Anda dapat melihat kode dan mengolahnya untuk situasi Anda.

EDIT: Saya sedang mengerjakan game yang menggunakan sistem rel prosedural prosedural seperti yang Anda mulai tetapi dalam C # di Unity3d. Saya akan memberi Anda gambaran tentang bagaimana saya membuat ekstrusi rel saya berdasarkan jalur Bezier Cubic jadi meskipun mesh rel secara prosedural dihasilkan, itu didasarkan pada jalur Bezier yang saya tentukan sebelumnya di editor. Ini akan menjadi seperti editor level dalam kasus gim Anda, dalam kasus saya, ini mendesain tabel pinball. Di bawah ini adalah contoh bagaimana saya melakukannya:

1.) Bangun / Temukan dan Terapkan Bezier Path Class. Ini akan memberi Anda sumber data untuk ekstrusi mesh Anda. Ada satu di C # di sini yang dapat Anda porting ke c ++.

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

2.) Setelah Bezier Path dibuat, titik data dari jalur ini dijadikan sampel. Ini dapat dilakukan melalui metode Interp pada kelas yang disediakan di atas. Ini akan memberi Anda daftar / array poin Vector3 di sepanjang jalur Bezier.

3.) Buat kelas pembantu untuk mengkonversi data jalur Vector3 Bezier dari langkah 2. Dalam hal ini, saya memiliki kelas sederhana yang disebut ExtrudedTrailSection seperti yang didefinisikan di bawah ini:

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

    public ExtrudedTrailSection() { }
}

4.) Iterasi melalui data sampel Vector3 Anda dan konversikan ke array ExtrudedTrailSections yang memasoknya dengan data sampel dan matriks dasar yang akan menjadi lokasi root dari mesh yang diekstrusi.

  1. ) Gunakan larik ExtrudedTrailSections untuk membuat larik akhir Matrix4x4 [] menggunakan kode berikut:

Matrix4x4 worldToLocal = rootTransform.worldToLocalMatrix;

    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.) Sekarang Anda memiliki array Matrix4x4 [] dan dapat mengusir mesh tetapi pertama-tama kita membutuhkan mesh referensi untuk mengusir dari. Saya memiliki kelas utilitas yang akan membuat permukaan mata bundar yang akan kami suplai ke metode ekstrusi mesh.

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.) Temukan pusat data ini:

    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.) Sekarang kami memiliki data tepi dan tengah untuk mesh wajah radial, Anda dapat membuat objek mesh menggunakan data Anda. Vertex terakhir dalam mesh adalah titik pusat yang kami hitung. Jala akhir hanyalah wajah yang disuplai ke metode ekstrusi jala yang saya berikan contoh dalam kelas ekstrusi jala prosedural paket Unity. Sekali lagi, ini adalah metode saya dan jelas Anda harus memberi makan data ini ke OpenGL. Jika Anda memiliki perpustakaan utilitas 3d yang Anda gunakan atau dapat menulis kelas mesh Anda sendiri, mungkin akan bekerja lebih baik untuk menghasilkan mesh ekstrusi akhir Anda karena data ini tidak benar-benar dibutuhkan oleh OpenGL untuk rendering. Mesh wajah ini hanya digunakan sebagai referensi untuk ekstrusi mesh.

    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.) Temukan tepi luar mesh melingkar sesuai kebutuhan dengan metode ekstrusi mesh. Sekali lagi, kode ini disediakan dalam paket kesatuan.

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.) Masukkan semua data ini ke metode Mesh Extrusion ..

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

Hasil akhir dalam kasus saya terlihat seperti ini ..

masukkan deskripsi gambar di sini

Semoga berhasil, gim Anda terlihat sangat keren! Beritahu saya jika Anda mengetahuinya?

Membuang

Chuck D
sumber
Saya terutama tertarik pada OpenGL dan C ++, tetapi 'pseudocode' juga membantu :)
Valentin Galea
Saya telah memperbarui posting asli saya untuk memasukkan solusi yang baru saja saya selesaikan.
Chuck D