Bir spline'dan nasıl 3D yarış pisti oluşturabilirim?


9

Bir spline etrafında şeklini tanımlayan 3 boyutlu bir yarış pisti oluşturmak istiyorum. İşte açıklayıcı bir video .

Pist, 3D bir spline boyunca uzanan ve bazı engeller atılan sonsuz bir tünel olmalıdır. Şimdiye kadarki en iyi fikrim spline boyunca bir daire şekli oluşturmaktı.

Ayrıca geometrinin nasıl yönetileceği, yani loft operasyonundan nasıl yaratılacağı, bellek, çarpışma ve tekstüre içindeki 'yaşam döngüsünü' nasıl yöneteceğine dair bazı işaretçiler istiyorum.


1
Proun: proun- game.com'a çok benziyor . Herhangi bir telif hakkı sorunu yoksa, bu oyunun yazarından iyi bir yanıt alabilirsiniz. Bilgilerini buradan edinebilirsiniz: blog.oogst3d.net .
Vite Falcon

evet bunu düşünüyordum - tnx!
Valentin Galea

Yanıtlar:


5

Hangi dilde çalıştığınızdan emin değilim, ancak burada bulunan Unity3D için yordamsal bir ekstrüzyon örneği var:

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

Eminim koda bakıp durumunuz için yeniden çalışabilirsiniz.

EDIT: Başladığınız gibi yordamsal bir haddelenmiş ray sistemi kullanan bir oyun üzerinde çalışıyorum ama Unity3d C #. Ray ekstrüzyonumu bir Kübik Bezier yoluna dayalı olarak nasıl oluşturduğuma dair bir genel bakış sunacağım, bu nedenle rayın ağı prosedürel olarak oluşturulmuş olsa da, bir editörde önceden tanımladığım Bezier yolunu temel alıyor. Oyununuzda seviye editörü gibi olurdu, benim durumumda langırt masaları tasarlıyor. Aşağıda, bunu nasıl yaptığımın bir örneği verilmiştir:

1.) Bir Bezier Path Sınıfı oluşturun / bulun ve uygulayın. Bu, mesh ekstrüzyonunuz için kaynak verilerini verecektir. C # 'da c ++' a bağlanabileceğiniz bir tane var.

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

2.) Bir Bezier Yolu oluşturduktan sonra, bu yoldan veri noktaları örneklenir. Bu, yukarıda verilen sınıfta Interp yöntemi ile yapılabilir. Bu size Bezier yolu boyunca Vector3 noktalarının bir listesini / dizisini verecektir.

3.) 2. adımdan Vector3 Bezier yolu verilerini dönüştürmek için bir yardımcı sınıf oluşturun. Bu durumda, aşağıda tanımlandığı gibi ExtrudedTrailSection adlı basit bir sınıfım var:

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

    public ExtrudedTrailSection() { }
}

4.) Vector3 örnek verilerinizden tekrarlayın ve örnek veriler ve ekstrüde edilmiş örgünüzün kök konumu olacak bir temel matris içeren bir dizi ExtrudedTrailSections öğesine dönüştürün.

  1. ) Aşağıdaki kodu kullanarak bir son Matrix4x4 [] dizisi oluşturmak için ExtrudedTrailSections dizisini kullanın:

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.) Şimdi bir dizi Matrix4x4 [] var ve bir kafes ekstrüde edebilirsiniz ama önce ekstrüde etmek için bir referans filme ihtiyacımız var. Kafes ekstrüzyon yöntemine sağlayacağımız dairesel bir ağ yüzü oluşturacak bir yardımcı sınıfım var.

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.) Bu verilerin merkezini bulun:

    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.) Radyal bir yüz kafes için kenar ve merkez verilerine sahip olduğumuza göre, verilerinizi kullanarak bir kafes nesnesi oluşturabilirsiniz. Ağdaki son tepe noktası hesapladığımız merkez noktadır. Son ağ, Unity paketinin Yordamsal ekstrüzyon sınıfında bir örnek verdiğim ağ ekstrüzyon yöntemine sağlanan bir yüzdür. Yine, bu benim yöntemim ve açıkçası bu verileri OpenGL'ye beslemek zorunda kalacaksınız. Kullanmakta olduğunuz veya kendi mesh sınıfınızı yazabileceğiniz bir 3d yardımcı program kütüphaneniz varsa, bu veri işleme için opengl tarafından gerçekten gerekli olmadığından, nihai ekstrüzyon ağınızı oluşturmak daha iyi çalışır. Bu yüz ağı sadece ağ ekstrüzyonu için referans olarak kullanılır.

    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.) Dairesel ekstrüzyon yönteminin gerektirdiği şekilde dairesel ağın dış kenarlarını bulun. Yine, bu kod birlik paketinde sağlanır.

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.) Tüm bu verileri Mesh Ekstrüzyon yöntemine besleyin.

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

Benim durumumdaki son çıktı böyle görünüyor ..

resim açıklamasını buraya girin

İyi şanslar, oyununuz gerçekten harika görünüyor! Anlarsan bana haber ver.

atmak


Öncelikle OpenGL ve C ++ ile ilgileniyorum, ancak 'sözde kod' da yardımcı olmalı :)
Valentin Galea

Orijinal yayımı yeni tamamladığım bir çözümü içerecek şekilde güncelledim.
Chuck D
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.