protected static float FindDistanceToAbnormalVoxelInRange(
            VoxelField volume,
            Vector3i voxel,
            int startX, int startY, int startZ,
            int endX, int endY, int endZ)
        {
            // 1. Ignore directions that take us outside the bounds of the voxel volume.
            if (startX < 0 || startY < 0 || startZ < 0)
                return float.MaxValue;

            float closestValueDist = float.MaxValue;

            // 2. Find the value along the axis
            for (Int32 z = startZ; z <= endZ; ++z)
            {
                for (Int32 y = startY; y <= endY; ++y)
                {
                    for (Int32 x = startX; x <= endX; ++x)
                    {
                        byte value = volume.GetVoxel(x, y, z);
                        if (value != 1)
                        {
                            float dist = (float)(voxel - new Vector3i(x, y, z)).LengthSquared;
                            if (dist < closestValueDist)
                            {
                                closestValueDist = dist;
                            }
                        }
                    }
                }
            }

            return closestValueDist;
        }
Пример #2
0
    // initialization
    void Start()
    {
        m_leapController = handController.GetLeapController();
        addDebugText("Leap connected:\t" + m_leapController.IsConnected);

        updateRadiusText();
        updateStrengthText();
        toolMaterial.SetFloat("_Radius", voxelObjectGPU.modManager.getToolRadius());
        voxel = new VoxelField(voxelFieldSize);
        voxel.createNoisyGround();
        initMesh(false);
        timeRemaining = timeMax;
    }
        protected static bool FillRange(VoxelField volume, Vector3i start, Vector3i end, Byte fillByte)
        {
            // 1. Ensure that we can safely expand into the area.
            if (!TestRangeForFreeSpace(volume, start, end))
                return false;

            // 2. Fill area
            for (Int32 z = start.Z; z <= end.Z; ++z)
            {
                for (Int32 y = start.Y; y <= end.Y; ++y)
                {
                    for (Int32 x = start.X; x <= end.X; ++x)
                    {
                        volume.SetVoxel(x, y, z, fillByte);
                    }
                }
            }

            return true;
        }
        protected static Vector3i FindHighestDensityVoxel(VoxelField volume)
        {
            float denestDistance = float.MinValue;
            Vector3i densestVoxel = new Vector3i(0, 0, 0);

            Object syncroot = new Object();

            Parallel.For(0, volume.VoxelSize.Z, z =>
            {
                for (Int32 y = 0; y < volume.VoxelSize.Y; ++y)
                {
                    for (Int32 x = 0; x < volume.VoxelSize.X; ++x)
                    {
                        byte value = volume.GetVoxel(x, y, z);

                        // Ignore empty voxels and already boxed voxels
                        if (value != 1)
                            continue;

                        float closestExtDist = FindShortestDistanceToAbnormalVoxel(volume, x, y, z);

                        if (closestExtDist > denestDistance)
                        {
                            lock (syncroot)
                            {
                                if (closestExtDist > denestDistance)
                                {
                                    denestDistance = closestExtDist;
                                    densestVoxel = new Vector3i(x, y, z);
                                }
                            }
                        }
                    }
                }
            });

            return densestVoxel;
        }
Пример #5
0
    /// <summary>
    /// writes the initial mesh to the GPU
    /// </summary>
    /// <param name="voxel">voxel field that is written to the GPU</param>
    /// <param name="rotation">rotation of the object</param>
    /// <param name="withSmooth">boolean, if the object should be smoothed</param>
    public void initMesh(VoxelField voxel, Vector3 rotation, bool withSmooth)
    {
        //Debug.Log("Update Voxel Buffer");

        //before creating a new vertexBuffer the old one must be disposed
        vertexBuffer.Dispose();
        vertexBuffer = new ComputeBuffer(maxVerticesSize, sizeof(float) * 6);

        //send the current voxel field to the gpu
        voxelBuffer.Dispose();
        voxelBuffer = new ComputeBuffer(voxelFieldSize * voxelFieldSize * voxelFieldSize, sizeof(float));
        voxelBuffer.SetData(voxel.getField());
        modManager.setDensityBuffer(voxelBuffer);

        if (withSmooth)
        {
            modManager.InitialSmooth(10); // 10 shader passes with smooth
        }

        rotation.x = rotation.x / 180 * (float)Math.PI;
        rotation.y = rotation.y / 180 * (float)Math.PI;
        //calculate new vertices in vertexBuffer
        voxelComputeShader.SetFloat("rotationXaxis", rotation.x);
        voxelComputeShader.SetFloat("rotationYaxis", rotation.y);
        voxelComputeShader.SetFloat("scale", scaling);
        voxelComputeShader.SetFloats("positionOffset", 0f, 0f, 0f);
        voxelComputeShader.SetInt("cubeDimension", voxelCubeSize);
        voxelComputeShader.SetInt("dimension", voxelCubeSize + 1);
        voxelComputeShader.SetFloat("isolevel", 0.0f);
        voxelComputeShader.SetBuffer(0, "cubeEdgeFlags", edgeTable);
        voxelComputeShader.SetBuffer(0, "triangleConnectionTable", triTable);
        voxelComputeShader.SetBuffer(0, "voxel", voxelBuffer);
        //voxelComputeShader.SetBuffer(0, "normals", normalBuffer);
        voxelComputeShader.SetBuffer(0, "vertexBuffer", vertexBuffer);
        voxelComputeShader.Dispatch(0, voxelCubeSize / 8, voxelCubeSize / 8, voxelCubeSize / 8);
    }
Пример #6
0
    private void resetAll(bool resetByServer)
    {
        if (resetByServer)
        {
            //todo check if current object should be exported
            export(); // transmit current id?
            //todo answer to server "all okay"?
        }
        //Debug.Log("Reseting environment");
        updateRadiusText();
        updateStrengthText();
        toolMaterial.SetFloat("_Radius", voxelObjectGPU.modManager.getToolRadius());

        objectScaling = 1.0f;
        scaling       = objectSize * objectScaling / (float)voxelCubeSize;

        rotation  = Vector3.zero;
        posOffset = Vector3.zero;
        resetBoundingBoxPosition();
        voxel = new VoxelField(voxelFieldSize);
        voxel.createNoisyGround();
        initMesh(true);
        voxelObjectGPU.modManager.ResetToolRange();
    }
        AABBi SimulatedAnnealingFill(VoxelizationInput input, SilhouetteOcclusionValidator sov, VoxelField volume, ref Vector3i densestVoxel, byte fillByte, List<Occluder> currentOccluders)
        {
            AABBi current = new AABBi(densestVoxel.X, densestVoxel.Y, densestVoxel.Z, densestVoxel.X + 1, densestVoxel.Y + 1, densestVoxel.Z + 1);
            AABBi next = new AABBi(0, 0, 0, 0, 0, 0);

            int iteration = -1;

            List<AABBi> relevantOccluders = GetRelevantOccluders(input, currentOccluders);

            List<AABBi> occluders = relevantOccluders.ToList();
            occluders.Add(current);
            long coverage = MeasureOccluderOcclusion(sov, input, occluders);

            double coolignAlpha = 0.999;
            double temperature = 400.0;
            double epsilon = 0.001;

            Random random = new Random(1337);

            int maxItterations = 1000;

            long delta = 0;
            while (temperature > epsilon && iteration < maxItterations)
            {
                iteration++;

                ComputeNext(random, current, next, volume, ref densestVoxel, delta, temperature);

                occluders = relevantOccluders.ToList();
                occluders.Add(next);
                delta = MeasureOccluderOcclusion(sov, input, occluders) - coverage;

                if (delta < 0)
                {
                    next.Clone(current);
                    coverage = delta + coverage;
                }
                else
                {
                    double probability = random.NextDouble();

                    if (probability < Math.Exp(-delta / temperature))
                    {
                        next.Clone(current);
                        coverage = delta + coverage;
                    }
                }

                temperature *= coolignAlpha;

                if (iteration % 400 == 0)
                    Console.WriteLine(coverage);
            }

            FillRange(volume,
                new Vector3i(current.MinX, current.MinY, current.MinZ),
                new Vector3i(current.MaxX, current.MaxY, current.MaxZ),
                fillByte);

            return current;
        }
        public virtual VoxelizationOutput Generate(VoxelizationInput input, Action<VoxelizationProgress> progress)
        {
            VoxelizationProgress vp = new VoxelizationProgress();

            DateTime start = DateTime.Now;

            vp.Status = "Building voxel field from octree";
            progress(vp);

            VoxelField voxelField = new VoxelField(input.Octree);

            Byte fillByte = 2;
            float oldPercent = 1.0f;
            float newPercent = 1.0f;

            List<Occluder> occluders = new List<Occluder>();

            vp.Status = "Calculating original mesh silhouette coverage";
            progress(vp);

            SilhouetteOcclusionValidator sov = new SilhouetteOcclusionValidator(1024, 1024);

            long groundSideCoverage, groundTopCoverage;
            sov.ComputeCoverage(input.OriginalMesh, input.Octree.MeshBounds, out groundSideCoverage, out groundTopCoverage);
            long totalCoverage = groundSideCoverage + groundTopCoverage;
            if (totalCoverage == 0)
                totalCoverage = 1;

            vp.Status = "Fitting boxes into mesh...";
            progress(vp);

            long oldOcclusion = 0;

            do
            {
                Vector3i densestVoxel = FindHighestDensityVoxel(voxelField);

                AABBi occluderBounds;
                if (input.Type == OcclusionType.BoxExpansion)
                {
                    occluderBounds = ExpandAndFillBox(voxelField, ref densestVoxel, fillByte);
                }
                //else if (input.Type == OcclusionType.SimulatedAnnealing)
                //{
                //    occluderBounds = SimulatedAnnealingFill(input, sov, voxelField, ref densestVoxel, fillByte, occluders);
                //}
                else if (input.Type == OcclusionType.BruteForce)
                {
                    occluderBounds = BruteForceFill(input, sov, voxelField, densestVoxel, fillByte, occluders);
                }
                else
                {
                    throw new Exception("Unknown occluder generation type!");
                }

                List<AABBi> relevantOccluders = GetRelevantOccluders(input, occluders);
                relevantOccluders.Add(occluderBounds);

                long newOcclusion = MeasureOccluderOcclusion(sov, input, relevantOccluders);

                Occluder occluder = new Occluder();
                occluder.Bounds = occluderBounds;
                occluder.DeltaOcclusion = (newOcclusion - oldOcclusion) / (double)totalCoverage;

                occluders.Add(occluder);

                if (occluder.DeltaOcclusion > input.MinimumOcclusion)
                    oldOcclusion = newOcclusion;

                Debug.WriteLine("Coverage " + occluder.DeltaOcclusion);
                Debug.WriteLine("Bounds (" + occluder.Bounds.MinX + "x" + occluder.Bounds.MaxX + " " + occluder.Bounds.MinY + "x" + occluder.Bounds.MaxY + " " + occluder.Bounds.MinZ + "x" + occluder.Bounds.MaxZ + ")");

                oldPercent = newPercent;
                newPercent = MeasureUnboxedVoxels(voxelField);

                Debug.WriteLine("(" + densestVoxel.X + "," + densestVoxel.Y + "," + densestVoxel.Z + ")\tCoverage=" + ((1 - newPercent) * 100) + "%\tDelta=" + ((oldPercent - newPercent) * 100) + "%");

                vp.Progress = Math.Min(((1 - newPercent) / input.MinimumVolume), 1.0f);
                vp.SilhouetteCoverage = oldOcclusion / (double)totalCoverage;
                vp.VolumeCoverage = 1 - newPercent;
                vp.Status = String.Format("Occlusion Progress : {0:0.##}%", (100 * vp.Progress));

                progress(vp);

            } while (newPercent > (1 - input.MinimumVolume));

            Mesh mesh = BuildMeshFromBoxes(input, GetRelevantOccluders(input, occluders));

            VoxelizationOutput output = new VoxelizationOutput();

            if (input.Retriangulate)
            {
                vp.Status = "Retriangulating occluder mesh";
                progress(vp);

                Mesh triangulatedMesh = MeshOptimizer.Retriangulate(input, mesh, out output.DebugLines);
                if (triangulatedMesh != null)
                    mesh = triangulatedMesh;
            }

            vp.Status = "Filtering polygons";
            progress(vp);

            mesh = PolygonFilter.Filter(input, mesh);

            vp.Status = "Generating final occlusion mesh";
            progress(vp);

            // Prepare the output
            output.Octree = input.Octree;
            output.TimeTaken = DateTime.Now - start;
            output.VolumeCoverage = 1 - newPercent;
            output.SilhouetteCoverage = oldOcclusion / (double)totalCoverage;
            output.OccluderMesh = new RenderableMesh(mesh, true);

            vp.Status = "Cleanup...";
            progress(vp);

            sov.Dispose();

            return output;
        }
        void ComputeNext(Random random, AABBi current, AABBi next, VoxelField volume, ref Vector3i densestVoxel, long delta, double temperature)
        {
            current.Clone(next);

            do
            {
                double probability = random.NextDouble();
                if (probability < Math.Exp(-delta / temperature))
                    next.MinX = densestVoxel.X + random.Next(0 - densestVoxel.X, 0);
                probability = random.NextDouble();
                if (probability < Math.Exp(-delta / temperature))
                    next.MinY = densestVoxel.Y + random.Next(0 - densestVoxel.Y, 0);
                probability = random.NextDouble();
                if (probability < Math.Exp(-delta / temperature))
                    next.MinZ = densestVoxel.Z + random.Next(0 - densestVoxel.Z, 0);

                probability = random.NextDouble();
                if (probability < Math.Exp(-delta / temperature))
                    next.MaxX = densestVoxel.X + random.Next(0, volume.VoxelSize.X - densestVoxel.X) + 1;
                probability = random.NextDouble();
                if (probability < Math.Exp(-delta / temperature))
                    next.MaxY = densestVoxel.Y + random.Next(0, volume.VoxelSize.Y - densestVoxel.Y) + 1;
                probability = random.NextDouble();
                if (probability < Math.Exp(-delta / temperature))
                    next.MaxZ = densestVoxel.Z + random.Next(0, volume.VoxelSize.Z - densestVoxel.Z) + 1;

            } while (!TestRangeForFreeSpace(volume,
                new Vector3i(next.MinX, next.MinY, next.MinZ),
                new Vector3i(next.MaxX, next.MaxY, next.MaxZ)));
        }
        AABBi BruteForceFill(VoxelizationInput input, SilhouetteOcclusionValidator sov, VoxelField voxelField, Vector3i densestVoxel, byte fillByte, List<Occluder> currentOccluders)
        {
            Object syncroot = new Object();
            Int64 largestVolume = 1;
            AABBi largestOccluder = new AABBi(densestVoxel.X, densestVoxel.Y, densestVoxel.Z, densestVoxel.X + 1, densestVoxel.Y + 1, densestVoxel.Z + 1);

            int MaxTopOccluders = 2000;
            List<AABBi> bestOccluders = new List<AABBi>(MaxTopOccluders);

            Parallel.For(densestVoxel.Z + 1, voxelField.VoxelSize.Z, max_z =>
            {
                for (Int32 min_z = densestVoxel.Z; min_z >= 0; --min_z)
                {
                    for (Int32 max_y = densestVoxel.Y + 1; max_y < voxelField.VoxelSize.Y; ++max_y)
                    {
                        for (Int32 min_y = densestVoxel.Y; min_y >= 0; --min_y)
                        {
                            for (Int32 max_x = densestVoxel.X + 1; max_x < voxelField.VoxelSize.X; ++max_x)
                            {
                                for (Int32 min_x = densestVoxel.X; min_x >= 0; --min_x)
                                {
                                    Int32 dx = max_x - min_x;
                                    Int32 dy = max_y - min_y;
                                    Int32 dz = max_z - min_z;
                                    Int64 volume = dx * dy * dz;

                                    if (TestRangeForFreeSpace(voxelField, new AABBi(min_x, min_y, min_z, max_x, max_y, max_z)))
                                    {
                                        lock (syncroot)
                                        {
                                            if (volume > largestVolume)
                                            {
                                                largestVolume = volume;
                                                largestOccluder = new AABBi(min_x, min_y, min_z, max_x, max_y, max_z);
                                                if (bestOccluders.Count >= MaxTopOccluders)
                                                    bestOccluders.RemoveAt(MaxTopOccluders - 1);
                                                bestOccluders.Insert(0, largestOccluder);
                                            }
                                        }
                                    }
                                    else
                                    {
                                        // if we can't expand outward any further there's no point in checking more.
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }

                Debug.WriteLine("Checked " + max_z);
            });

            List<AABBi> relevantOccluders = GetRelevantOccluders(input, currentOccluders);

            long bestCoverage = 0;
            AABBi bestCoverageVolume = largestOccluder;
            foreach (AABBi occluder in bestOccluders)
            {
                List<AABBi> tempOccluders = relevantOccluders.ToList();
                tempOccluders.Add(occluder);
                long coverage = MeasureOccluderOcclusion(sov, input, tempOccluders);
                if (coverage > bestCoverage)
                {
                    bestCoverage = coverage;
                    bestCoverageVolume = occluder;
                }
            }

            FillRange(voxelField, bestCoverageVolume, fillByte);

            return bestCoverageVolume;
        }
        protected static float FindShortestDistanceToAbnormalVoxel(VoxelField volume, int x, int y, int z)
        {
            int pX, nX, pY, nY, pZ, nZ;
            pX = nX = pY = nY = pZ = nZ = 0;

            Vector3i voxel = new Vector3i(x, y, z);

            do
            {
                // +Z Axis
                float distance = FindDistanceToAbnormalVoxelInRange(volume, voxel,
                    x - nX, y - nY, z + (pZ + 1),
                    x + pX, y + pY, z + (pZ + 1));
                if (distance != float.MaxValue)
                    return distance;
                pZ++;

                // -Z Axis
                distance = FindDistanceToAbnormalVoxelInRange(volume, voxel,
                    x - nX, y - nY, z - (nZ + 1),
                    x + pX, y + pY, z - (nZ + 1));
                if (distance != float.MaxValue)
                    return distance;
                nZ++;

                // +Y Axis
                distance = FindDistanceToAbnormalVoxelInRange(volume, voxel,
                    x - nX, y + (pY + 1), z - nZ,
                    x + pX, y + (pY + 1), z + pZ);
                if (distance != float.MaxValue)
                    return distance;
                pY++;

                // -Y Axis
                distance = FindDistanceToAbnormalVoxelInRange(volume, voxel,
                    x - nX, y - (nY + 1), z - nZ,
                    x + pX, y - (nY + 1), z + pZ);
                if (distance != float.MaxValue)
                    return distance;
                nY++;

                // +X Axis
                distance = FindDistanceToAbnormalVoxelInRange(volume, voxel,
                    x + (pX + 1), y - nY, z - nZ,
                    x + (pX + 1), y + pY, z + pZ);
                if (distance != float.MaxValue)
                    return distance;
                pX++;

                // -X Axis
                distance = FindDistanceToAbnormalVoxelInRange(volume, voxel,
                    x - (nX + 1), y - nY, z - nZ,
                    x - (nX + 1), y + pY, z + pZ);
                if (distance != float.MaxValue)
                    return distance;
                nX++;

            } while (true);
        }
 protected static bool FillRange(VoxelField volume, AABBi box, Byte fillByte)
 {
     return FillRange(volume, new Vector3i(box.MinX, box.MinY, box.MinZ), new Vector3i(box.MaxX - 1, box.MaxY - 1, box.MaxZ - 1), fillByte);
 }
        protected static bool TestRangeForFreeSpace(VoxelField volume, Vector3i start, Vector3i end)
        {
            // 1. Ignore directions that take us outside the bounds of the voxel volume.
            if (start.X < 0 || start.Y < 0 || start.Z < 0)
                return false;

            // 2. Ensure that we can safely expand into the area, expand as long as the voxel isn't empty.
            //    Note: it is ok to expand into a space already occupied.
            for (Int32 z = start.Z; z <= end.Z; ++z)
            {
                for (Int32 y = start.Y; y <= end.Y; ++y)
                {
                    for (Int32 x = start.X; x <= end.X; ++x)
                    {
                        byte value = volume.GetVoxel(x, y, z);
                        // If a voxel has a '1' then it's unused volume space, if it is '0' it is empty,
                        // and if anything else, it has a box in it already.
                        if (value != 1)
                            return false;
                    }
                }
            }

            return true;
        }
 protected static bool TestRangeForFreeSpace(VoxelField volume, AABBi box)
 {
     return TestRangeForFreeSpace(volume, new Vector3i(box.MinX, box.MinY, box.MinZ), new Vector3i(box.MaxX - 1, box.MaxY - 1, box.MaxZ - 1));
 }
        protected static AABBi ExpandAndFillBox(VoxelField volume, ref Vector3i originVoxel, Byte fillByte)
        {
            int pX, nX, pY, nY, pZ, nZ;
            pX = nX = pY = nY = pZ = nZ = 0;

            volume.SetVoxel(originVoxel.X, originVoxel.Y, originVoxel.Z, fillByte);

            bool boxGrew = false;
            do
            {
                // +Z Axis
                bool pZGrew = FillRange(volume,
                    new Vector3i(originVoxel.X - nX, originVoxel.Y - nY, originVoxel.Z + (pZ + 1)),
                    new Vector3i(originVoxel.X + pX, originVoxel.Y + pY, originVoxel.Z + (pZ + 1)), fillByte);
                if (pZGrew)
                    pZ++;

                // -Z Axis
                bool nZGrew = FillRange(volume,
                    new Vector3i(originVoxel.X - nX, originVoxel.Y - nY, originVoxel.Z - (nZ + 1)),
                    new Vector3i(originVoxel.X + pX, originVoxel.Y + pY, originVoxel.Z - (nZ + 1)), fillByte);
                if (nZGrew)
                    nZ++;

                // +Y Axis
                bool pYGrew = FillRange(volume,
                    new Vector3i(originVoxel.X - nX, originVoxel.Y + (pY + 1), originVoxel.Z - nZ),
                    new Vector3i(originVoxel.X + pX, originVoxel.Y + (pY + 1), originVoxel.Z + pZ), fillByte);
                if (pYGrew)
                    pY++;

                // -Y Axis
                bool nYGrew = FillRange(volume,
                    new Vector3i(originVoxel.X - nX, originVoxel.Y - (nY + 1), originVoxel.Z - nZ),
                    new Vector3i(originVoxel.X + pX, originVoxel.Y - (nY + 1), originVoxel.Z + pZ), fillByte);
                if (nYGrew)
                    nY++;

                // +X Axis
                bool pXGrew = FillRange(volume,
                    new Vector3i(originVoxel.X + (pX + 1), originVoxel.Y - nY, originVoxel.Z - nZ),
                    new Vector3i(originVoxel.X + (pX + 1), originVoxel.Y + pY, originVoxel.Z + pZ), fillByte);
                if (pXGrew)
                    pX++;

                // -X Axis
                bool nXGrew = FillRange(volume,
                    new Vector3i(originVoxel.X - (nX + 1), originVoxel.Y - nY, originVoxel.Z - nZ),
                    new Vector3i(originVoxel.X - (nX + 1), originVoxel.Y + pY, originVoxel.Z + pZ), fillByte);
                if (nXGrew)
                    nX++;

                boxGrew = (pZGrew || nZGrew || pYGrew || nYGrew || pXGrew || nXGrew);
            } while (boxGrew);

            AABBi box = new AABBi();
            box.MinX = originVoxel.X - nX;
            box.MinY = originVoxel.Y - nY;
            box.MinZ = originVoxel.Z - nZ;

            box.MaxX = originVoxel.X + pX + 1;
            box.MaxY = originVoxel.Y + pY + 1;
            box.MaxZ = originVoxel.Z + pZ + 1;

            return box;
        }
        protected static float MeasureUnboxedVoxels(VoxelField volume)
        {
            int unboxedVoxels = 0;
            int totalvoxels = 0;

            for (Int32 x = 0; x < volume.VoxelSize.X; ++x)
            {
                for (Int32 y = 0; y < volume.VoxelSize.Y; ++y)
                {
                    for (Int32 z = 0; z < volume.VoxelSize.Z; ++z)
                    {
                        byte value = volume.GetVoxel(x, y, z);
                        if (value == 1)
                            unboxedVoxels++;
                        if (value != 0)
                            totalvoxels++;
                    }
                }
            }

            return unboxedVoxels / (float)totalvoxels;
        }