Beispiel #1
0
 private void attemptPropagate(Vector3i target, ushort sourceLight)
 {
     if (sourceLight <= 1)
     {
         return;
     }
     if (target.x >= 0 && target.x < 16 && target.y >= 0 && target.y < 16 && target.z >= 0 && target.z < 16)
     {
         if (_data[target.x, target.y, target.z] < sourceLight - 1)
         {
             if (_chunk.GetBlock(target).isTransparent())
             {
                 write(target.x, target.y, target.z, (ushort)(sourceLight - 1));
                 _work_propagate.Enqueue(target);
             }
         }
     }
     else
     {
         // pass into the neighbor chunk
         Chunk chunk = _chunk.Owner.GetChunkAt(_chunk.Key * 16 + target);
         if (chunk != null)
         {
             ChunkLightmap lightmap = _chunk.Owner.GetLightmap(chunk.Key);
             lightmap.attemptPropagate(_chunk.Key * 16 + target - chunk.Key * 16, sourceLight);
         }
     }
 }
    // FIXME if lighting affects gameplay, it is unacceptable to delay it!!!
    // mesh rebuilds can be delayed, it's just representation

    IEnumerator CheckLighting()
    {
        // first wait for previous work to finish, then check once per second
        do
        {
            yield return(new WaitForSeconds(1.0f));
        } while (_foofoo.Count > 0);

        // queue new work
        foreach (Chunk chunk in _world.Chunks.Values)
        {
            ChunkLightmap lightmap = _world.GetLightmapFor(chunk);
            if (lightmap.NeedsPropagate())
            {
                _foofoo.Enqueue(chunk);
            }
        }
        foreach (VisualChunk vis in _vis.Values)
        {
            if (vis.Dirty)
            {
                _foofoo.Enqueue(vis.Chunk);
            }
        }
    }
Beispiel #3
0
    public ChunkLightmap GetLightmapFor(Chunk chunk)
    {
        ChunkLightmap lightmap;

        if (!_lightmaps.TryGetValue(chunk.Key, out lightmap))
        {
            lightmap = new ChunkLightmap(chunk);
            _lightmaps.Add(chunk.Key, lightmap);
        }
        return(lightmap);
    }
Beispiel #4
0
    public ushort GetLight(Vector3i worldPosition)
    {
        // FIXME basically, this shouldn't be used because repeated lookups are bad, but it's easier to have for prototyping
        Chunk chunk = GetChunkAt(worldPosition);

        // TODO refactor - returning darkness for out of bounds
        if (chunk == null)
        {
            return(0);
        }
        ChunkLightmap lightmap = GetLightmapFor(chunk);
        Vector3i      localpos = worldPosition % 16;

        return(lightmap.GetLight(localpos));
    }
Beispiel #5
0
    public void RebuildMesh()
    {
        ChunkLightmap lightmap = _world.GetLightmap(_chunk.Key);

        var appearances = new System.Collections.Generic.Dictionary <BlockType, BlockAppearance>();

        // air
        appearances.Add(BlockType.Empty, new BlockAppearance());
        // stone
        BlockAppearance tmp = new BlockAppearance();

        for (int i = 0; i < 6; i++)
        {
            tmp.UVRects[i] = _atlas.Rects[0];
            tmp.Colors[i]  = new Color(0.4f, 0.4f, 0.4f);
        }
        appearances.Add(new BlockType(2), tmp);
        // dirt
        tmp = new BlockAppearance();
        for (int i = 0; i < 6; i++)
        {
            if (i != (int)BlockFace.Top)
            {
                tmp.UVRects[i] = _atlas.Rects[1];
            }
            else
            {
                tmp.UVRects[i] = _atlas.Rects[3];
            }
        }
        appearances.Add(new BlockType(1), tmp);
        // grass
        tmp = new BlockAppearance();
        for (int i = 0; i < 6; i++)
        {
            tmp.UVRects[i] = _atlas.Rects[2];
        }
        tmp.UVRects[(int)BlockFace.Top]    = _atlas.Rects[4];
        tmp.UVRects[(int)BlockFace.Bottom] = _atlas.Rects[1];

        appearances.Add(new BlockType(3), tmp);

        Profiler.BeginSample("RebuildMesh");
        for (int x = 0; x < 16; x++)
        {
            for (int y = 0; y < 16; y++)
            {
                for (int z = 0; z < 16; z++)
                {
                    BlockType block = _chunk.GetBlock(x, y, z);
                    if (block.isTransparent() == false && _chunk.IsBlockVisible(x, y, z))
                    {
                        BlockAppearance appearance;
                        if (!appearances.TryGetValue(block, out appearance))
                        {
                            appearance = new BlockAppearance();
                        }

                        CubeBuilder.buildCube(_meshBuilder, new Vector3i(x, y, z), _chunk, lightmap, appearance);
                    }
                }
            }
        }

        _mesh.Clear();
        _meshBuilder.BuildMesh(_mesh);
        _meshBuilder.Clear();
        _dirty = false;
        Profiler.EndSample();
    }
Beispiel #6
0
    public static void buildCube(MeshBuilder mb, Vector3i pos, Chunk chunk, ChunkLightmap lightmap, BlockAppearance appearance)
    {
        // FIXME foo...
        Color brown = new Color(0.65f, 0.4f, 0.2f);
        //Color[] foo = {brown*0.8f, brown*0.8f, Color.green, brown*0.5f, brown, brown};
        //Color[] foo = {brown, brown, Color.green, brown, brown, brown};

        BlockType block = chunk.GetBlock(pos);

        for (int i = 0; i < 6; i++)
        {
            if (!chunk.IsBlockFaceVisible(pos, (BlockFace)i))
            {
                continue;
            }

            Rect  UVs  = appearance.UVRects[i];
            Color tint = appearance.Colors[i];

            Vector3i normal = cubeNormals[i];

            mb.UVs.Add(new Vector2(UVs.xMin, UVs.yMin));
            mb.UVs.Add(new Vector2(UVs.xMax, UVs.yMin));
            mb.UVs.Add(new Vector2(UVs.xMin, UVs.yMax));
            mb.UVs.Add(new Vector2(UVs.xMax, UVs.yMax));

            float[] ao = new float[4];

            for (int v = 0; v < 4; v++)
            {
                Vector3i vertex = cubeVertices[cubeFaces[i, v]];

                // for sake of simplicity, get diagonal light value too even though it could be blocked by the two adjacent blocks
                if (i == 0 || i == 1)
                {
                    // x
                    ao[v] = lightmap.GetLightValue(pos + new Vector3i(-1 + normal.x + 1, -1 + vertex.y, -1 + vertex.z)) +
                            lightmap.GetLightValue(pos + new Vector3i(-1 + normal.x + 1, -1 + vertex.y + 1, -1 + vertex.z)) +
                            lightmap.GetLightValue(pos + new Vector3i(-1 + normal.x + 1, -1 + vertex.y, -1 + vertex.z + 1)) +
                            lightmap.GetLightValue(pos + new Vector3i(-1 + normal.x + 1, -1 + vertex.y + 1, -1 + vertex.z + 1));
                }
                else if (i == 2 || i == 3)
                {
                    // y
                    ao[v] = lightmap.GetLightValue(pos + new Vector3i(-1 + vertex.x, -1 + normal.y + 1, -1 + vertex.z)) +
                            lightmap.GetLightValue(pos + new Vector3i(-1 + vertex.x + 1, -1 + normal.y + 1, -1 + vertex.z)) +
                            lightmap.GetLightValue(pos + new Vector3i(-1 + vertex.x, -1 + normal.y + 1, -1 + vertex.z + 1)) +
                            lightmap.GetLightValue(pos + new Vector3i(-1 + vertex.x + 1, -1 + normal.y + 1, -1 + vertex.z + 1));
                }
                else
                {
                    // z
                    ao[v] = lightmap.GetLightValue(pos + new Vector3i(-1 + vertex.x, -1 + vertex.y, -1 + normal.z + 1)) +
                            lightmap.GetLightValue(pos + new Vector3i(-1 + vertex.x + 1, -1 + vertex.y, -1 + normal.z + 1)) +
                            lightmap.GetLightValue(pos + new Vector3i(-1 + vertex.x, -1 + vertex.y + 1, -1 + normal.z + 1)) +
                            lightmap.GetLightValue(pos + new Vector3i(-1 + vertex.x + 1, -1 + vertex.y + 1, -1 + normal.z + 1));
                }
                vertex += pos;

                mb.Vertices.Add((Vector3)vertex);
                mb.Normals.Add((Vector3)normal);

                float aoFactor = Mathf.Clamp(ao[v], 0, 4);
                aoFactor /= 4;

                Color color = tint * aoFactor;

                mb.Colors32.Add(color);
            }
            // prefer the bright edge in the middle, suitable for outdoors but bad for drastic light level transitions into darkness
            if (ao[0] + ao[3] < ao[1] + ao[2])
            {
                mb.AddTriangle(-2, -3, -4);
                mb.AddTriangle(-3, -2, -1);
            }
            else
            {
                mb.AddTriangle(-2, -1, -4);
                mb.AddTriangle(-1, -3, -4);
            }
        }
    }
    void Start()
    {
        // DEBUG foofoo testing...
        _targeting = GameObject.Find("Targeting").transform;

        ChunkGenerator chunkgen = new ChunkGenerator();

        var sw = new System.Diagnostics.Stopwatch();

        sw.Start();
        for (int cx = 0; cx < _world.SizeX; cx++)
        {
            for (int cy = 0; cy < _world.SizeY; cy++)
            {
                for (int cz = 0; cz < _world.SizeZ; cz++)
                {
                    var chunk = _world.CreateChunk(new Vector3i(cx, cy, cz), chunkgen);
                }
            }
        }
        Debug.Log("World generation took " + sw.ElapsedMilliseconds + " ms");
        sw.Reset();
        sw.Start();

        // make some dummy content
        _world.BeginBatchUpdate();
        for (int x = 0; x < _world.SizeX * 16; x++)
        {
            int z = 91;

            _world.SetBlock(new Vector3i(x, 85, z - 2), new BlockType(2));
            _world.SetBlock(new Vector3i(x, 85, z - 1), new BlockType(2));
            _world.SetBlock(new Vector3i(x, 85, z), new BlockType(2));
            _world.SetBlock(new Vector3i(x, 85, z + 1), new BlockType(2));
            _world.SetBlock(new Vector3i(x, 85, z + 2), new BlockType(2));

            _world.SetBlock(new Vector3i(x, 86, z), new BlockType(0));
            _world.SetBlock(new Vector3i(x, 87, z), new BlockType(0));
            _world.SetBlock(new Vector3i(x, 88, z), new BlockType(0));
            _world.SetBlock(new Vector3i(x, 86, z - 1), new BlockType(0));
            _world.SetBlock(new Vector3i(x, 87, z - 1), new BlockType(0));
            _world.SetBlock(new Vector3i(x, 86, z + 1), new BlockType(0));
            _world.SetBlock(new Vector3i(x, 87, z + 1), new BlockType(0));


            // pillars
            if (x % 6 == 3)
            {
                for (int y = 84; y > 0 && _world.GetBlock(new Vector3i(x, y, z)).isTransparent(); y--)
                {
                    _world.SetBlock(new Vector3i(x, y, z), new BlockType(2));
                }
            }
        }
        _world.EndBatchUpdate();

        _world.Sunlight.CalculateAll();

        // Do initial direct lighting
        for (int cy = _world.SizeY - 1; cy >= 0; cy--)
        {
            for (int cx = 0; cx < _world.SizeX; cx++)
            {
                for (int cz = 0; cz < _world.SizeZ; cz++)
                {
                    ChunkLightmap lightmap = _world.GetLightmap(new Vector3i(cx, cy, cz));
                    lightmap.ApplyDirectSunlight();
                }
            }
        }

        // spread the sunlight all around the place
        for (int cy = _world.SizeY - 1; cy >= 0; cy--)
        {
            for (int cx = 0; cx < _world.SizeX; cx++)
            {
                for (int cz = 0; cz < _world.SizeZ; cz++)
                {
                    ChunkLightmap lightmap = _world.GetLightmap(new Vector3i(cx, cy, cz));
                    lightmap.PropagateSunlight();
                }
            }
        }

        Debug.Log("Sunlight initialization took " + sw.ElapsedMilliseconds + " ms");
        sw.Reset();
        sw.Start();


        foreach (Chunk chunk in _world.Chunks.Values)
        {
            _foofoo.Enqueue(chunk);
        }

        StartCoroutine(CheckLighting());
    }
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Escape))
        {
            Application.Quit();
        }

        float wheel = Input.GetAxis("Mouse ScrollWheel");

        _cut += wheel * Time.deltaTime * wheelspeed;

        //_cut += Mathf.Sign(Input.GetAxisRaw("Mouse ScrollWheel"))*1;

        // snap to level afterwards
        if (Input.GetButtonDown("Depth Scroll") && Input.GetAxisRaw("Depth Scroll") > 0)
        {
            _cut = Mathf.Round(_cut + 1);
        }
        else if (Input.GetButtonDown("Depth Scroll") && Input.GetAxisRaw("Depth Scroll") < 0)
        {
            _cut = Mathf.Round(_cut - 1);
        }
        else if (Input.GetButton("Depth Scroll"))
        {
            // slowly scroll after snap if key held down
            _cut += Input.GetAxis("Depth Scroll") * Time.deltaTime * keyspeed;
        }

        _cut = Mathf.Clamp(_cut, 0, _world.SizeY * 16);

        int levelDisplayed = Mathf.RoundToInt(_cut);

        Shader.SetGlobalFloat("_VoxelCutY", levelDisplayed + 0.5f);

        // ------ raycast ----

        Ray        ray = Camera.main.ScreenPointToRay(new Vector3(Screen.width / 2, Screen.height / 2, 0));
        RaycastHit hit;

        //Debug.DrawLine(ray.origin, ray.direction*100);

        //Plane cutPlane = new Plane(new Vector3(0,1,0), levelDisplayed + 0.5f);


        // FIXME rewrite, we need custom voxel raytracer due to cutplane nonsense,
        // other objects can be collided in some other way (based on their visibility)
        while (Physics.Raycast(ray, out hit))
        {
            Vector3  p           = hit.point - hit.normal * 0.5f;
            Vector3i targetCoord = new Vector3i(Mathf.FloorToInt(p.x), Mathf.FloorToInt(p.y), Mathf.FloorToInt(p.z));
            if (targetCoord.y > levelDisplayed)
            {
                ray.origin = hit.point;
                continue;
            }
            BlockType block = _world.GetBlock(targetCoord);
            Chunk     chunk = _world.GetChunkAt(targetCoord);
            string    infotext;
            if (chunk == null)
            {
                infotext = "null chunk!? " + targetCoord.ToString() + " ... " + _world.Chunks.Count;
            }
            else
            {
                infotext = string.Format("Chunk {0} @ {1} blocktype {2}", chunk.Key.ToString(), targetCoord.ToString(), block.Raw);
            }
            // FIXME performance
            GameObject.Find("TargetingInfo").GetComponent <UnityEngine.UI.Text>().text = infotext;
            GameObject.Find("Targeting").transform.localPosition = (Vector3)targetCoord + new Vector3(0.5f, 0.5f, 0.5f);
            break;
        }

        // ---------- magic rebuild -----------

        // TODO any better way to not waste time? threads? :P
        var sw = new System.Diagnostics.Stopwatch();

        sw.Start();

        // process visual chunks (rebuild meshes if world has changed etc)
        while (_foofoo.Count > 0)
        {
            // spend a maximum of 10ms on this nonsense
            if (sw.ElapsedMilliseconds > 10)
            {
                break;
            }

            Chunk       chunk = _foofoo.Dequeue();
            VisualChunk vis;
            _vis.TryGetValue(chunk.Key, out vis);
            if (vis != null)
            {
                ChunkLightmap lightmap = _world.GetLightmapFor(chunk);
                lightmap.Propagate();
                vis.RebuildMesh();
            }
            else
            {
                vis = new VisualChunk(chunk, _world, gameObject);
                _vis.Add(chunk.Key, vis);
            }
        }
    }