2
\$\begingroup\$

I have my game generate random voxel terrain, and then generate a mesh on only the visible parts of that terrain. I want it to be able to select some part of the terrain with the mouse, but I'm not sure how highlighting should be done properly. The only thing that comes to my mind is to recalculate whole chunks, updating selected voxels to brighter textures but it would by damn slow. I want to be able to change textures in that certain selected part of the mesh, without recalculating the whole chunk mesh. How to achieve that?

Edit: For better understanding, this is how the terrain looks: terrain Voxels are kept in a 3d array and chunks are recalculated every time its voxels are affected, adding mesh to chunk by checking if the voxel is exposed to air.

Chunk

Word object:
private void GenWorld()
{
    voxels = new Voxel[size * chunkSize, chunkHeight, size * chunkSize];

    for (int x = 0; x < size * chunkSize; x++)
    {
        for (int z = 0; z < size * chunkSize; z++)
        {
            int grass = PerlinNoise(x, 0, z, 100, 32, 0);
            grass += PerlinNoise(x, 0, z, 50, 16, 0);

            for (int y = 0; y < chunkHeight; y++)
            {
                if (y <= grass)
                    voxels[x, y, z] = new Voxel(new Vector3(x, y, z), VoxelType.Grass);
                else
                    voxels[x, y, z] = new Voxel(new Vector3(x, y, z), VoxelType.Air);
            }
        }
    }
}

private void GenChunks()
{
    chunks = new Chunk[size, size];

    for (int x = 0; x < size; x++)
    {
        for (int z = 0; z < size; z++)
        {
            GameObject tempChunk = Instantiate(chunkGO,
                new Vector3(x * chunkSize, 0, z * chunkSize),
                new Quaternion(0, 0, 0, 0)) as GameObject;
            tempChunk.transform.parent = transform;

            chunks[x, z] = tempChunk.GetComponent<Chunk>();
            chunks[x, z].worldGO = gameObject;
            chunks[x, z].posX = x * chunkSize;
            chunks[x, z].posY = 0;
            chunks[x, z].posZ = z * chunkSize;
        }
    }
}

Chunk object:
public void GenerateMesh()
{
    for (int x = 0; x < world.chunkSize; x++)
    for (int z = 0; z < world.chunkSize; z++)
    for (int y = 0; y < world.chunkHeight; y++)
    {
        Voxel voxel = GetVoxel(x, y, z);
        if (voxel.Type == VoxelType.Grass)
        {
            // Check if any face is exposed to air
            if (GetVoxel(x, y + 1, z).Type == VoxelType.Air)
                CubeTop(x, y, z, voxel);
            if (GetVoxel(x, y - 1, z).Type == VoxelType.Air)
                CubeBottom(x, y, z, voxel);
            if (GetVoxel(x + 1, y, z).Type == VoxelType.Air)
                CubeEast(x, y, z, voxel);
            if (GetVoxel(x - 1, y, z).Type == VoxelType.Air)
                CubeWest(x, y, z, voxel);
            if (GetVoxel(x, y, z + 1).Type == VoxelType.Air)
                CubeNorth(x, y, z, voxel);
            if (GetVoxel(x, y, z - 1).Type == VoxelType.Air)
                CubeSouth(x, y, z, voxel);
        }
    }

    UpdateMesh(); // Just passing data to unity mesh obj
}

void CubeTop(int x, int y, int z, Voxel voxel)
{
    vertices.Add(new Vector3(x, y, z + 1));
    vertices.Add(new Vector3(x + 1, y, z + 1));
    vertices.Add(new Vector3(x + 1, y, z));
    vertices.Add(new Vector3(x, y, z));

    BuildFace(voxel.TextureTop);
}


void BuildFace(TextureType type)
{
    triangles.Add(faceCount * 4);
    triangles.Add(faceCount * 4 + 1);
    triangles.Add(faceCount * 4 + 2);
    triangles.Add(faceCount * 4);
    triangles.Add(faceCount * 4 + 2);
    triangles.Add(faceCount * 4 + 3);

    Texture.SetTexture(uv, type);

    faceCount++;
}


public static void SetTexture(List<Vector2> uv, TextureType type)
{         
    Vector2 texturePos = (Vector2)textures[type];        

    uv.Add(new Vector2(textureOffset * texturePos.x + textureOffset, textureOffset * texturePos.y));
    uv.Add(new Vector2(textureOffset * texturePos.x + textureOffset, textureOffset * texturePos.y + textureOffset));
    uv.Add(new Vector2(textureOffset * texturePos.x, textureOffset * texturePos.y + textureOffset));
    uv.Add(new Vector2(textureOffset * texturePos.x, textureOffset * texturePos.y));
}
\$\endgroup\$
6
  • \$\begingroup\$ It depends how you create your blocks... How do you do that? \$\endgroup\$
    – Savlon
    Commented Jun 15, 2014 at 11:18
  • \$\begingroup\$ Blocks are just array of objects stacked in column 16x64x16. Mesh is generated by checking if block is exposed to air (if it is visible). \$\endgroup\$
    – ddl
    Commented Jun 15, 2014 at 11:24
  • \$\begingroup\$ So you are just looking for a way to display which block the cursor is over? \$\endgroup\$
    – Savlon
    Commented Jun 15, 2014 at 11:42
  • \$\begingroup\$ Yes I'm having mouse-selecting module already done but I dont know how to update certain voxel texture in whole chunk mesh to make it have 'highlight' texture. \$\endgroup\$
    – ddl
    Commented Jun 15, 2014 at 11:47
  • \$\begingroup\$ Ok well I need to know how you create your mesh and texture it so we can figure out which way to approach the highlighting... There are a lot of ways to implement voxel terrain and if you can't show how YOU implement it then I can't help you... \$\endgroup\$
    – Savlon
    Commented Jun 15, 2014 at 11:58

2 Answers 2

1
\$\begingroup\$

A projector would work here. They're a little expensive because they cause the meshes they touch to be redrawn (essentially adds one more draw call in the manner certain shaders would), but that's something Unity internally during rendering, you won't have to do anything with the chunk's data.

\$\endgroup\$
1
\$\begingroup\$

Better than a projector you should be able to write a custom shader that highlights some quads given the mouse click world coordinates. Should be pretty cheap in resources if done right, and you can both use another texture or simply multiply by a color and avoid wasting unneeded memory.

I'm not a shader expert but I'm working on something similar and you can do this in the fragment shader with a single rectangle check for the mouse click and the fragment world position (I can provide more help with that if you're interested).

\$\endgroup\$
1
  • \$\begingroup\$ Note that I might have made this sound easy, if you're not familiar with shader writing this might get tricky. If you know how to write shaders in unity this shouldn't be really difficult though. As I said I can provide more help if you need it. \$\endgroup\$
    – Setzer22
    Commented Aug 11, 2014 at 12:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .