quinta-feira, 7 de novembro de 2013

Cubes: Unity Minecraft Clone [Part 2]

PART 1
Hi! The main modifications of this release are:
- Easy uvs on cubes.
- Block lightning.

Doing the light was very trick and is very nice to see how a tile based engine works with a LOT of iterations. When nothing is changed you have an incredible performance, but when something needs to be recalculated the iterations eat your performance away. Most of it optimizations are ways of iterate faster or less. Kudos to the Mojang crew!

About the light: It spreads over the air tiles, and the solid tiles iluminates its faces based on the air neighbors.
The way i found for making the sun light works with an acceptable performance are more or less this:

- Iterate over the blocks on the height limit.
- If the block is air, iluminate it with the maximum light(1.0F).
- If the bottom block is already air, iluminate it with the maximum light and check for solid neighbors (left, right, front, back).
- Repeat the previous until some solid neighbor are found.
- If a solid neighbor is found, apply a fill algorithm with attenuation in that direction.

Check the code
private void SunFill(int x, int y, int z, float lightValue, bool[] startSpreading)
    {
        Cube tile = WorldMap.Instance.GetTile(new Vector3(x, y, z));
        if (tile == null || !tile.isTranslucent) { return; }
        tile.meshInfo.sunLight = lightValue;

        if (startSpreading[0]) { FillAttenuation(x - 1, y, z, lightValue - WorldAttribs.LIGHT_MIN); }
        if (startSpreading[1]) { FillAttenuation(x + 1, y, z, lightValue - WorldAttribs.LIGHT_MIN); }
        if (startSpreading[2]) { FillAttenuation(x, y, z - 1, lightValue - WorldAttribs.LIGHT_MIN); }
        if (startSpreading[3]) { FillAttenuation(x, y, z + 1, lightValue - WorldAttribs.LIGHT_MIN); }

        Cube tileBack = WorldMap.Instance.GetTile(new Vector3(x, y, z - 1));
        Cube tileFront = WorldMap.Instance.GetTile(new Vector3(x, y, z + 1));
        Cube tileLeft = WorldMap.Instance.GetTile(new Vector3(x - 1, y, z));
        Cube tileRight = WorldMap.Instance.GetTile(new Vector3(x + 1, y, z));

        if (tileLeft != null && !tileLeft.isTranslucent) { startSpreading[0] = true; }
        if (tileRight != null && !tileRight.isTranslucent) { startSpreading[1] = true; }
        if (tileBack != null && !tileBack.isTranslucent) { startSpreading[2] = true; }
        if (tileFront != null && !tileFront.isTranslucent) { startSpreading[3] = true; }

        SunFill(x, y - 1, z, lightValue, startSpreading);
    }

    private void FillAttenuation(int x, int y, int z, float lightValue)
    {
        Cube tile = GetTile(new Vector3(x, y, z));
        if (tile == null ||
            !tile.isTranslucent ||
            lightValue <= WorldAttribs.LIGHT_MIN ||
            tile.meshInfo.sunLight >= lightValue)
        {
            return;
        }

        tile.meshInfo.sunLight = lightValue;

        if (lightValue == 1f) { FillAttenuation(x, y - 1, z, lightValue); }
        else { FillAttenuation(x, y - 1, z, lightValue - WorldAttribs.LIGHT_MIN); } 
        FillAttenuation(x, y + 1, z, lightValue - WorldAttribs.LIGHT_MIN);        
        FillAttenuation(x, y, z - 1, lightValue - WorldAttribs.LIGHT_MIN);
        FillAttenuation(x, y, z + 1, lightValue - WorldAttribs.LIGHT_MIN);
        FillAttenuation(x + 1, y, z, lightValue - WorldAttribs.LIGHT_MIN);
        FillAttenuation(x - 1, y, z, lightValue - WorldAttribs.LIGHT_MIN);  
    }

And when something changes:

- Turn off the light of [Spread Steps] tiles radius.
- Get the neighbors of this radius chunk with some light in it.
- Apply the FillAttenuation on this guys to recover the light.

Thats pretty much it. I know there is a lot of room for optimizations, but this are ways i found to be easy to implement with an acceptable performance.
In the next updates i will try to focus on weapons and GUI.

The textures i´m using are from John Smith Texture Pack: http://www.jslegacy.com/
The source code is available on Bitbucket: https://bitbucket.org/sunwar/cubes (i will try to do frequent updates here)
Or you can check the webplayer, executable rar or project rar.

[ ]s