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;
        }
        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;
        }
        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;
        }
        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;
        }
Exemplo n.º 7
0
        private void CreateSolidVolume(VoxelizingOctree tree, VoxelizingOctreeCell cell, Vector3i volumeLength)
        {
            if (cell.Status == CellStatus.Inside)
            {
                int min_x = cell.VoxelBounds.MinX + tree.WorldVoxelOffset.X;
                int min_y = cell.VoxelBounds.MinY + tree.WorldVoxelOffset.Y;
                int min_z = cell.VoxelBounds.MinZ + tree.WorldVoxelOffset.Z;
                int max_x = cell.VoxelBounds.MaxX + tree.WorldVoxelOffset.X;
                int max_y = cell.VoxelBounds.MaxY + tree.WorldVoxelOffset.Y;
                int max_z = cell.VoxelBounds.MaxZ + tree.WorldVoxelOffset.Z;

                for (int x = min_x; x < max_x; x++)
                {
                    for (int y = min_y; y < max_y; y++)
                    {
                        for (int z = min_z; z < max_z; z++)
                        {
                            SetVoxel(x, y, z, 1);
                        }
                    }
                }
            }

            foreach (var child in cell.Children)
            {
                CreateSolidVolume(tree, child, volumeLength);
            }
        }
        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);
        }
        public bool GenerateOctree(MeshData mesh)
        {
            if (mesh == null)
                return false;

            // Create a list of triangles from the list of faces in the model
            List<Triangle> triangles = new List<Triangle>();
            for (int i = 0; i < mesh.Tris.Length; i++)
            {
                Tri face = mesh.Tris[i];

                Triangle tri = new Triangle();
                tri.v0 = mesh.Vertices[face.P1.Vertex];
                tri.v1 = mesh.Vertices[face.P2.Vertex];
                tri.v2 = mesh.Vertices[face.P3.Vertex];

                triangles.Add(tri);
            }

            // Determine the axis-aligned bounding box for the triangles
            Vector3 center;
            CreateUniformBoundingBox(triangles, out MeshBounds, out VoxelBounds, out center, out SideLength);

            {
                SmallestVoxelSideLength = SideLength;
                for (int i = 1; i < m_maxLevels; i++)
                    SmallestVoxelSideLength *= 0.5;

                VoxelSize = new Vector3i();
                VoxelSize.X = (Int32)Math.Pow(2, m_maxLevels);
                VoxelSize.Y = (Int32)Math.Pow(2, m_maxLevels);
                VoxelSize.Z = (Int32)Math.Pow(2, m_maxLevels);
            }

            m_root = new VoxelizingOctreeCell(this, null, center, SideLength, 0);
            m_root.Root = m_root;
            m_root.Triangles = new List<Triangle>(triangles);
            m_root.Status = CellStatus.IntersectingBounds;
            m_root.RecursiveSubdivide(m_maxLevels - 1);

            WorldVoxelOffset = new Vector3i(
                0 - Root.VoxelBounds.MinX,
                0 - Root.VoxelBounds.MinY,
                0 - Root.VoxelBounds.MinZ);

            return true;
        }
        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 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;
        }