示例#1
0
    // based on this source https://github.com/camthesaxman/cubecraft/blob/master/source/field.c#L78
    // and fixes from this  https://gamedev.stackexchange.com/questions/47362/cast-ray-to-select-block-in-voxel-game

    // todo: instead of all parkour fixes just do one mod and if its on block boundary just add 0.00001f to it lol
    public static bool RaycastVoxel(World world, Vector3 origin, Vector3 dir, out RaycastVoxelHit hit)
    {
        if (dir == Vector3.zero)
        {
            Debug.LogError("RaycastVoxel dir is zero!");
            hit.bpos = Vector3i.zero;
            hit.dir  = Dir.none;
            return(false);
        }

        // make sure origin never exactly lines up to block boundary
        // this is kinda filth but seems to work more reliably
        const float ff = 0.001f;

        if (origin.x % Chunk.BLOCK_SIZE == 0)
        {
            origin.x += ff;
        }
        if (origin.y % Chunk.BLOCK_SIZE == 0)
        {
            origin.y += ff;
        }
        if (origin.z % Chunk.BLOCK_SIZE == 0)
        {
            origin.z += ff;
        }

        Vector3i pos = WorldUtils.GetBlockPos(origin);

        // check if inside a block already
        if (world.GetBlock(pos.x, pos.y, pos.z).ColliderSolid())
        {
            hit.bpos = pos;
            hit.dir  = Dir.none;
            return(true);
        }

#if _DEBUG
        lastOrigin = origin;
        lastDir    = dir;
        posAlong.Clear();
        posAlong.Add(pos);
#endif

        // how far we are from origin in blocks
        Vector3i radius = Vector3i.zero;

        // direction to increment when stepping
        Vector3i step = Vector3i.zero;

        // ray distance it takes to equal one block unit in each direction (this one doesnt change in loop)
        Vector3 tDelta = Vector3.positiveInfinity;

        // ray distance it takes to move to next block boundary in each direction (this changes)
        Vector3 tMax = Vector3.positiveInfinity;

        if (dir.x > 0.0f)
        {
            step.x   = 1;
            tDelta.x = Chunk.BLOCK_SIZE / dir.x;
            tMax.x   = (Mathf.Ceil(origin.x * Chunk.BPU) / Chunk.BPU - origin.x) / dir.x;
            //tMax.x = (ceil(origin.x * Chunk.BPU) / Chunk.BPU - origin.x) / dir.x;
        }
        else if (dir.x < 0.0f)
        {
            step.x   = -1;
            tDelta.x = -Chunk.BLOCK_SIZE / dir.x;
            tMax.x   = -(origin.x - Mathf.Floor(origin.x * Chunk.BPU) / Chunk.BPU) / dir.x;
            //bool atBoundary = Mathf.Round(origin.x * Chunk.BPU) == origin.x * Chunk.BPU;
            //bool atBoundary = Mth.Mod(origin.x, Chunk.BPU) == 0.0f;
            //tMax.x = atBoundary ? 0 : -(origin.x - Mathf.Floor(origin.x * Chunk.BPU) / Chunk.BPU) / dir.x;
        }

        if (dir.y > 0.0f)
        {
            step.y   = 1;
            tDelta.y = Chunk.BLOCK_SIZE / dir.y;
            tMax.y   = (Mathf.Ceil(origin.y * Chunk.BPU) / Chunk.BPU - origin.y) / dir.y;
            //tMax.y = (ceil(origin.y * Chunk.BPU) / Chunk.BPU - origin.y) / dir.y;
        }
        else if (dir.y < 0.0f)
        {
            step.y   = -1;
            tDelta.y = -Chunk.BLOCK_SIZE / dir.y;
            tMax.y   = -(origin.y - Mathf.Floor(origin.y * Chunk.BPU) / Chunk.BPU) / dir.y;
            //bool atBoundary = Mathf.Round(origin.y * Chunk.BPU) == origin.y * Chunk.BPU;
            //bool atBoundary = Mth.Mod(origin.y, Chunk.BPU) == 0.0f;
            //tMax.y = atBoundary ? 0 : -(origin.y - Mathf.Floor(origin.y * Chunk.BPU) / Chunk.BPU) / dir.y;
        }

        if (dir.z > 0.0f)
        {
            step.z   = 1;
            tDelta.z = Chunk.BLOCK_SIZE / dir.z;
            tMax.z   = (Mathf.Ceil(origin.z * Chunk.BPU) / Chunk.BPU - origin.z) / dir.z;
            //tMax.z = (ceil(origin.z * Chunk.BPU) / Chunk.BPU - origin.z) / dir.z;
        }
        else if (dir.z < 0.0f)
        {
            step.z   = -1;
            tDelta.z = -Chunk.BLOCK_SIZE / dir.z;
            tMax.z   = -(origin.z - Mathf.Floor(origin.z * Chunk.BPU) / Chunk.BPU) / dir.z;
            //bool atBoundary = Mathf.Round(origin.z * Chunk.BPU) == origin.z * Chunk.BPU;
            //bool atBoundary = Mth.Mod(origin.z, Chunk.BPU) == 0.0f;
            //tMax.z = atBoundary ? 0 : -(origin.z - Mathf.Floor(origin.z * Chunk.BPU) / Chunk.BPU) / dir.z;
        }

        Assert.IsTrue(tDelta.x != 0 || tDelta.y != 0 || tDelta.z != 0);

        while (radius.x * radius.x + radius.y * radius.y + radius.z * radius.z < SELECT_RADIUS * SELECT_RADIUS)
        {
            if (tMax.x < tMax.y)
            {
                if (tMax.x < tMax.z)
                {
                    // increment x
                    tMax.x += tDelta.x;
                    pos.x  += step.x;
                    radius.x++;
                    hit.dir = step.x > 0 ? Dir.west : Dir.east;
                }
                else
                {
                    // increment z
                    tMax.z += tDelta.z;
                    pos.z  += step.z;
                    radius.z++;
                    hit.dir = step.z > 0 ? Dir.south : Dir.north;
                }
            }
            else
            {
                if (tMax.y < tMax.z)
                {
                    // increment y
                    tMax.y += tDelta.y;
                    pos.y  += step.y;
                    radius.y++;
                    hit.dir = step.y > 0 ? Dir.down : Dir.up;
                }
                else     // duplicated from above
                // increment z
                {
                    tMax.z += tDelta.z;
                    pos.z  += step.z;
                    radius.z++;
                    hit.dir = step.z > 0 ? Dir.south : Dir.north;
                }
            }

#if _DEBUG
            posAlong.Add(pos);
#endif
            // if hit a solid block then return successfully!
            if (world.GetBlock(pos.x, pos.y, pos.z).ColliderSolid())
            {
                hit.bpos = pos;
                return(true);
            }
        }

        hit.bpos = Vector3i.zero;
        hit.dir  = Dir.none;
        return(false);
    }
示例#2
0
    void Update()
    {
        // scroll to select blocks (create mesh as you switch)
        float scroll  = Input.GetAxis("Mouse ScrollWheel");
        bool  changed = true;

        if (scroll > 0.0f)
        {
            blockIndex--;
        }
        else if (scroll < 0.0f)     // scroll down will progress list forward
        {
            blockIndex++;
        }
        else
        {
            changed = false;
        }
        blockIndex = Mth.Mod(blockIndex, blocks.Length);
        if (changed)
        {
            MeshBuilder.GetBlockMesh(blocks[blockIndex], blockMeshFilter);
        }

        // show square around block aiming at
        // todo: have option to show 2x2 for hammering!
        //bool mainRaycast = Physics.Raycast(transform.position, transform.forward, out hit, 1000);
        drawer.Clear();

        RaycastVoxelHit vhit;
        bool            success = BlonkPhysics.RaycastVoxel(world, transform.position, transform.forward, out vhit);

        if (success)
        {
            // move cube towards camera direction a little so it looks better when intersecting with other blocks
            Vector3 center = (vhit.bpos.ToVector3() + Vector3.one * placementSize * 0.5f) / Chunk.BPU;
            drawer.AddBounds(new Bounds(center - transform.forward * 0.01f, Vector3.one * placementSize / Chunk.BPU), Color.white);
        }

        // left click delete
        if (Input.GetMouseButtonDown(0) && success)
        {
            lastPos = transform.position;
            lastHit = vhit;

            //todo: add some better directional intuitions on where to place edit shape
            for (int y = 0; y < placementSize; ++y)
            {
                for (int z = 0; z < placementSize; ++z)
                {
                    for (int x = 0; x < placementSize; ++x)
                    {
                        world.SetBlock(vhit.bpos + new Vector3i(x, y, z), Blocks.AIR);
                    }
                }
            }
        }

        // right click place
        if (Input.GetMouseButtonDown(1) && success)
        {
            lastPos = transform.position;
            lastHit = vhit;

            for (int y = 0; y < placementSize; ++y)
            {
                for (int z = 0; z < placementSize; ++z)
                {
                    for (int x = 0; x < placementSize; ++x)
                    {
                        world.SetBlock(vhit.bpos + Dirs.GetNormal(vhit.dir) + new Vector3i(x, y, z), blocks[blockIndex]);
                    }
                }
            }
        }

        if (Input.GetKeyDown(KeyCode.UpArrow))
        {
            placementSize++;
        }
        if (Input.GetKeyDown(KeyCode.DownArrow))
        {
            placementSize--;
        }
        if (placementSize < 1)
        {
            placementSize = 1;
        }

        if (Input.GetKeyDown(KeyCode.F1))
        {
            flyCam.ToggleFlyMode();
        }
        if (Input.GetKeyDown(KeyCode.F2))
        {
            drawChunkBorders = !drawChunkBorders;
        }
        if (Input.GetKeyDown(KeyCode.F3))
        {
            ToggleDebugDay();
        }
        if (Input.GetKeyDown(KeyCode.F4))
        {
            LoadChunks.drawDebug = !LoadChunks.drawDebug;
        }
        if (Input.GetKeyDown(KeyCode.F5))
        {
            LoadChunks.updateChunks = !LoadChunks.updateChunks;
        }
        if (drawChunkBorders)
        {
            DrawChunkBorders();
        }
    }