Search code examples
c#unity-game-engineraycastingmarching-cubessimplex-noise

Physics.Raycast not working with a Marching Cubes -generated mesh


What it spits out

My raycast spits out a position way off from what it's supposed to be. I'm trying to place objects procedurally on a procedural mesh. I've been scratching my head at this for a while. Please help. Sorry for the long script.

The start of the code is just some declares and stuff. GenObjects is run once in FixedUpdate after Start has finished. I'm using a marching cubes library by Scrawk and a noise library by Auburn

    void GenMesh()
    {
        Marching marching = new MarchingCubes();
        marching.Surface = 0.0f;
        voxels = new float[width * height * length];

        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                for (int z = 0; z < length; z++)
                {
                    float fx = x / (width - 1.0f);
                    float fy = y / (height - 1.0f);
                    float fz = z / (length - 1.0f);

                    int idx = x + y * width + z * width * height;


                    float surfaceHeight = noise2.GetNoise(x,z) * amplitude + offset;
                    float currentHeight = Mathf.Clamp(y, surfaceHeight - threshold, surfaceHeight + threshold);
                    float t = Mathf.Abs(currentHeight - surfaceHeight) / threshold;
                    voxels[idx] = Mathf.Lerp(Mathf.Clamp(noise.GetNoise(x,y,z), 0.65f, 1), -1f, t);
                }
            }
        }
        List<Vector3> verts = new List<Vector3>();
        List<int> indices = new List<int>();
        marching.Generate(voxels, width, height, length, verts, indices);
        int maxVertsPerMesh = 30000;
        int numMeshes = verts.Count / maxVertsPerMesh + 1;

        for (int i = 0; i < numMeshes; i++)
        {

            List<Vector3> splitVerts = new List<Vector3>();
            List<int> splitIndices = new List<int>();

            for (int j = 0; j < maxVertsPerMesh; j++)
            {
                int idx = i * maxVertsPerMesh + j;

                if (idx < verts.Count)
                {
                    splitVerts.Add(verts[idx]);
                    splitIndices.Add(j);
                }
            }

            if (splitVerts.Count == 0) continue;

            Mesh mesh = new Mesh();
            mesh.SetVertices(splitVerts);
            mesh.SetTriangles(splitIndices, 0);
            mesh.RecalculateBounds();
            mesh.RecalculateNormals();

            MeshWelder meshWelder = new MeshWelder(mesh);
            meshWelder.Weld();

            GameObject go = new GameObject("Mesh");
            go.layer = LayerMask.NameToLayer("Ground");
            go.transform.parent = transform;
            go.transform.localScale = new Vector3(100f, 100f, 100f);
            go.AddComponent<MeshFilter>();
            go.AddComponent<MeshRenderer>();
            go.AddComponent<MeshCollider>();
            go.GetComponent<Renderer>().material = m_material;
            go.GetComponent<MeshFilter>().mesh = mesh;
            go.GetComponent<MeshCollider>().sharedMesh = mesh;
            go.GetComponent<MeshCollider>().contactOffset = 0f;
            go.transform.localPosition = new Vector3(-width * 100 / 2, -height * 100 / 4, -length * 100 / 2);

            meshes.Add(go);
        }
    }

    void GenObjects(GameObject prefab, float radius, Vector2 sampleRegionSize, Vector2 origin, int seed)
    {
        List<Vector2> points = PoissonDiscSampling.GeneratePoints(radius, sampleRegionSize, seed);
        Physics.queriesHitBackfaces = true;
        foreach (Vector2 point in points)
        {
            RaycastHit hit;
            Vector3 objPos = new Vector3(0,0,0);
            bool validPosFound = false;
            if (Physics.Raycast(new Vector3(point.x + origin.x, 0, point.y + origin.y), Vector3.down, out hit, height * 100, layerMask))
            {
                objPos = hit.point;
                validPosFound = true;
            } else if (Physics.Raycast(new Vector3(point.x + origin.x, 0, point.y + origin.y), Vector3.up, out hit, height * 100, layerMask))
            {
                objPos = hit.point;
                validPosFound = true;
            }
            if (validPosFound)
            {
                GameObject newObject = Instantiate(prefab, objPos, Quaternion.Euler(0, 0, 0));
            }
        }
        Physics.queriesHitBackfaces = false;
    }
}


Solution

  • Fixed! My mistake was really stupid. I wasn't assigning the welded mesh, leaving a filthy mesh with lots of empty verts floating about. The raycast was hitting them.

    The fixed lines for anyone who cares:

                Mesh mesh = new Mesh();
                Mesh mesh_temp = new Mesh();
    
                mesh_temp.SetVertices(splitVerts);
                mesh_temp.SetTriangles(splitIndices, 0);
                mesh_temp.RecalculateBounds();
                mesh_temp.RecalculateNormals();
    
                MeshWelder meshWelder = new MeshWelder();
                meshWelder.customMesh = new CustomMesh();
                meshWelder.customMesh.vertices = splitVerts.ToArray();
                meshWelder.customMesh.triangles = splitIndices.ToArray();
                meshWelder.customMesh.normals = mesh_temp.normals;
                meshWelder.Weld();
    
                mesh.SetVertices(meshWelder.customMesh.vertices);
                mesh.SetTriangles(meshWelder.customMesh.triangles, 0);
                mesh.SetNormals(meshWelder.customMesh.normals);
                mesh.RecalculateBounds();