Пример #1
0
    public byte[] DICOMSeriesToVolume(DicomDirectoryRecord series, out Int3 size)
    {
        size = GetSizeForRecord(series);
        var voxels = new VolumeBuffer <Color32>(size);

        var tex = new Texture2D(2, 2);

        var instanceNumbers = series.LowerLevelDirectoryRecordCollection.Select(x => x.Get <int>(DicomTag.InstanceNumber)).OrderBy(x => x).ToArray();

        for (var z = 0; z < instanceNumbers.Length; z++)
        {
            var frame = instanceNumbers[z];
            tex = loadDicomInstance.GetTexture2DForRecord(series, frame);
            var fromPixels = tex.GetPixels32();
            for (var y = 0; y < size.y; y++)
            {
                for (var x = 0; x < size.x; x++)
                {
                    var from = fromPixels[x * resolution + ((size.y - 1 - y) * size.x) * resolution * 2];
                    voxels.SetVoxel(new Int3(x, y, z), from);
                }
            }
        }

        voxels.ClearEdges(new Color32(0, 0, 0, 0));
        return(VolumeTextureUtils.Color32ArrayToByteArray(voxels.DataArray));
    }
Пример #2
0
    public void EnsureSetup()
    {
        if (this.mColorBuffer != null)
        {
            return;                            // check if already setup
        }
        Debug.Log("Setting up volume '" + this.gameObject.name + "'");

        if (mVolumeSource == null)
        {
            this.mVolumeSource = this.GetComponent <VolumeSourceBehavior>();
            this.mVolumeSource.EnsureSetup();
        }

        if (mVolumeSource.IsSlowerPlatform())
        {
            return;
        }

        var fullsize = this.mVolumeSource.VolumeSize;


        if (IsOnlyShowMultiChakra && (!this.IsMultiChakras))
        {
            this.mColorBuffer = new VolumeBuffer <Color>(fullsize);
            this.mColorBuffer.ClearAll(Color.clear);
            this.UpdateTextureFromBuffer();
            return;
        }

        if ((!this.RefreshCache) && TryLoadCached())
        {
            this.UpdateTextureFromBuffer();
        }
        else
        {
            this.mColorBuffer = new VolumeBuffer <Color> (fullsize);

            if (this.IsLEWFlowField)
            {
                this.SlowlyExecuting = this.DoFlowField().GetEnumerator();
                this.UpdateSlowlyExecuting();
            }
            else
            if (this.IsAura)
            {
                this.SlowlyExecuting = this.UpdateAura().GetEnumerator();
                this.UpdateSlowlyExecuting();
            }
            else if (this.IsMultiChakras)
            {
                this.SlowlyExecuting = this.UpdateMultiChakras().GetEnumerator();
                this.UpdateSlowlyExecuting();
            }
            else
            {
                this.DoFieldUpdate();
            }
        }
    }
Пример #3
0
 public static void SaveFloatVolToFile(VolumeBuffer <float> vol, string filename)
 {
     System.IO.BinaryWriter bw = new System.IO.BinaryWriter(new System.IO.FileStream(filename, System.IO.FileMode.Create));
     for (int i = 0; i < vol.Array.Length; i++)
     {
         var c = vol.Array[i];
         bw.Write(c);
     }
     bw.Close();
 }
Пример #4
0
    public static VolumeBuffer <float> ReadFloatVolFromFile(Cubic <int> size, string filename)
    {
        System.IO.BinaryReader br  = new System.IO.BinaryReader(new System.IO.FileStream(filename, System.IO.FileMode.Open));
        VolumeBuffer <float>   ans = new VolumeBuffer <float>(size);

        for (int i = 0; i < ans.Array.Length; i++)
        {
            float c = br.ReadSingle();
            ans.Array[i] = c;
        }
        br.Close();
        return(ans);
    }
Пример #5
0
    public VolumeBufferMip(Int3 size)
    {
        int h = MipHeight(size.X);

        Mips = new VolumeBuffer <T> [h];
        var curSize = size;

        for (int i = 0; i < h; i++)
        {
            Mips[i] = new VolumeBuffer <T>(curSize.AsCubic());
            curSize = curSize.ShiftDownOne();
        }
    }
Пример #6
0
    public bool TryLoadCached()
    {
        var sz   = this.mVolumeSource.VolumeSize;
        var path = VolumeBufferFile.CacheFilePath(this.gameObject.name, sz);

        if (VolumeBufferFile.CacheFileExists(path))
        {
            Debug.Log("Loading from cache '" + path + "'");
            this.mColorBuffer = VolumeBufferFile.ReadVolFromFile(sz, path);
            return(this.mColorBuffer != null);
        }
        return(false);
    }
Пример #7
0
    public static VolumeBuffer <Color> ReadVolFromFile(Cubic <int> size, string filename)
    {
        System.IO.BinaryReader br  = new System.IO.BinaryReader(new System.IO.FileStream(filename, System.IO.FileMode.Open));
        VolumeBuffer <Color>   ans = new VolumeBuffer <Color>(size);

        for (int i = 0; i < ans.Array.Length; i++)
        {
            Color c = new Color();
            c.r          = br.ReadSingle();
            c.g          = br.ReadSingle();
            c.b          = br.ReadSingle();
            c.a          = br.ReadSingle();
            ans.Array[i] = c;
        }
        br.Close();
        return(ans);
    }
Пример #8
0
    //	public static IEnumerable<Int3> AllIndices_pXpYpZ(VolumeHeader header) {
    //		for (int i=0; i<header.TotalCount; i++) {
    //			var pnt = header.LinearToCubic(i);
    //			yield return new Int3(pnt);
    //		}
    //
    //	}

    public static void ConvertOpacityToDistanceBuffer(VolumeBuffer <float> opac)
    {
        //		VolumeBuffer<Int3> nearest = new VolumeBuffer<Int3> (opac.Size);
        //		Int3 prevPos = new Cubic<int> (0, 0, 0);
        //		float prevOpac = opac.Read (prevPos);
        //		foreach (var i in AllIndices_pXpYpZ(opac.Header)) {
        //			var curOpac = opac.Read(i);
        //			if (curOpac >= prevOpac) {
        //				prevPos = i;
        //				prevOpac = curOpac;
        //				nearest.Write(i,i);
        //			}
        //			else {
        //				nearest.Write(i, prevPos);
        //			}
        //		}
    }
Пример #9
0
    void TestField()
    {
        int s = 3;
        VolumeBuffer <bool> test = new VolumeBuffer <bool> (new Cubic <int> (s, s, s));

        test.Write(new Int3(0, 0, 0), true);
        test.Write(new Int3(1, 0, 0), true);
        test.Write(new Int3(0, 1, 0), true);
        test.Write(new Int3(0, 0, 1), true);
        test.Write(new Int3(0, 0, 2), true);

        test.Write(new Int3(2, 2, 2), true);
        //test.Write (new Int3 (1, 2, 2), true);

        var mesh = VolumeTetrahedraSurfacer.GenerateSurfaceVolume(test,
                                                                  (t => (t ? 1.0f : -3.0f)),
                                                                  GetRelativeCameraPos());

        SetMeshFromVolume(mesh);
        Debug.Log("Done meshing.");
    }
Пример #10
0
    // Use this for initialization
    void Start()
    {
        if (this.Controller == null)
        {
            this.Controller = GameObject.FindObjectOfType <ElectromagneticFieldControllerScript> ();
        }

        FieldValues = new VolumeBuffer <Vector3> (Cubic <int> .CreateSame(CubeSize));

        if (this.UseDebugMesh)
        {
            var mf = this.GetComponent <MeshFilter> ();
            if (mf == null)
            {
                this.UseDebugMesh = false;
                Debug.Assert(mf != null, "Please setup a MeshFilter and MeshRenderer on this object");
            }
            SurfaceMesh  = new Mesh();
            MeshVertices = new Vector3[FieldValues.Length * 3];
            MeshNormals  = new Vector3[FieldValues.Length * 3];
            mf.mesh      = SurfaceMesh;
        }
    }
Пример #11
0
    public void EnsureSetup()
    {
        if (isSetup)
        {
            return;
        }
        isSetup = true;

        FieldOverallAlpha = 1.0f;
        ParticleFlowRate  = 1.0f;
        if (!this.Body)
        {
            this.Body = this.gameObject.GetComponentInParent <BodyLandmarks> ();
        }
        if (!this.Body)
        {
            if (!this.Hand)
            {
                this.Hand = this.gameObject.GetComponentInParent <MainEnergyApp> ();
            }
            this.ChiBall = this.gameObject.GetComponentInParent <ChiHandEnergyBall> ();
        }
        Debug.Assert((this.Body) || (this.Hand) || (this.ChiBall));
        if (!this.ExcersizeSystem)
        {
            this.ExcersizeSystem = GameObject.FindObjectOfType <ExcersizeSharedScheduler> ();
        }
        bool isMobile = (Application.platform == UnityEngine.RuntimePlatform.IPhonePlayer);
        int  sideRes  = (isMobile ? this.VoxelSideResMobile : this.VoxelSideRes);      // 8;

        if (this.Body)
        {
            this.Body.EnsureSetup();
            int chakraToShow = CurrentFocusChakra;             //3;
            this.FieldsCells = new VolumeBuffer <DynFieldCell> (Cubic <int> .CreateSame(sideRes));
            var scl  = OneOver(this.FieldsCells.Header.Size.AsVector3());
            var l2w  = this.transform.localToWorldMatrix;
            var cntr = Body.Chakras.AllChakras [chakraToShow].transform.position;            //  l2w.MultiplyPoint (Vector3.zero);
            foreach (var nd in this.FieldsCells.AllIndices3())
            {
                var cell = this.FieldsCells.Read(nd.AsCubic());
                cell.Pos = this.transform.localToWorldMatrix.MultiplyPoint(FieldsCells.Header.CubicToDecimalUnit(nd) - (Vector3.one * 0.5f));
                if (!(SkipRandomPlacement))
                {
                    cell.Pos += Scale(Random.insideUnitSphere, scl);                      // add random offset
                }
                cell.Direction   = cntr - cell.Pos;
                cell.LatestColor = Color.white;
                cell.Twist       = 0.0f;
                cell.VoxelIndex  = nd;
                this.FieldsCells.Write(nd.AsCubic(), cell);
            }
        }
        else if (this.Hand)
        {
            var arrows = this.Hand.FindAllFlowNodes();
            var n      = arrows.Count;
            this.IsStaticLayout = true;
            this.FieldsCells    = new VolumeBuffer <DynFieldCell> (Cubic <int> .Create(n, 1, 1));
            for (int i = 0; i < n; i++)
            {
                var cell  = this.FieldsCells.Array [i];
                var arrow = arrows [i];
                cell.Pos                   = arrow.transform.position;
                cell.Direction             = arrow.transform.up * 50.0f;
                cell.LatestColor           = Color.green;
                cell.Twist                 = 0.0f;
                cell.VoxelIndex            = new Int3(i, 0, 0);
                this.FieldsCells.Array [i] = cell;
            }
        }
        else if (this.ChiBall)
        {
            this.FieldsCells = new VolumeBuffer <DynFieldCell> (Cubic <int> .CreateSame(sideRes));
            var scl  = OneOver(this.FieldsCells.Header.Size.AsVector3());
            var cntr = this.ChiBall.transform.position;
            var l2w  = this.transform.localToWorldMatrix;
            foreach (var nd in this.FieldsCells.AllIndices3())
            {
                var cell = this.FieldsCells.Read(nd.AsCubic());
                cell.Pos = this.transform.localToWorldMatrix.MultiplyPoint(FieldsCells.Header.CubicToDecimalUnit(nd) - (Vector3.one * 0.5f));
                if (!(SkipRandomPlacement))
                {
                    cell.Pos += Scale(Random.insideUnitSphere, scl);                      // add random offset
                }
                cell.Direction   = cntr - cell.Pos;
                cell.LatestColor = Color.white;
                cell.Twist       = 0.0f;
                cell.VoxelIndex  = nd;
                this.FieldsCells.Write(nd.AsCubic(), cell);
            }
        }

        this.UpdateCellFieldDir(snapToCurrent: true);
    }
Пример #12
0
    public static Mesh GenerateSurfaceVolume <T>(VolumeBuffer <T> vol, Func <T, float> signTest, Vector3 relativeCameraPos, DynamicFieldModel optionalModel = null)
    {
        int tetCorners = 4;
        Dictionary <int, int> ndxToVertex = new Dictionary <int, int> ();
        List <int>            triangles   = new List <int> ();
        List <int>            quads       = new List <int> ();

        float[]      signes       = new float[tetCorners];
        List <int>   edgesInTetra = new List <int> ();
        VolumeHeader h2           = new VolumeHeader(new Int3(2, 2, 2));

        foreach (var ndx in vol.AllIndices3())
        {
            if ((ndx.X != 0) && (ndx.Y != 0) && (ndx.Z != 0))
            {
                var start = ndx.Add(new Int3(-1, -1, -1));

                foreach (var ts in TetrasInCube)
                {
                    int countPos = 0, countNeg = 0;
                    int signKey = 0;
                    for (int cornerIndex = 0; cornerIndex < ts.Length; cornerIndex++)
                    {
                        var lndx = ts[cornerIndex];
                        var cur  = AddInvertTileOffset3(start, (h2.LinearToCubic(lndx)));
                        var sv   = signTest(vol.Read(cur));
                        if (sv >= 0.0f)
                        {
                            countPos++;
                            signKey |= (1 << cornerIndex);
                        }
                        else
                        {
                            countNeg++;
                        }
                        signes [cornerIndex] = sv;
                    }
                    if ((countPos > 0) && (countNeg > 0))
                    {
                        edgesInTetra.Clear();
                        // we have something in this tetrahedra!
                        for (int ei = 0; ei < VolumeTetrahedraSurfacer.EdgesInTetra.Length; ei += 2)
                        {
                            var ef = VolumeTetrahedraSurfacer.EdgesInTetra [ei + 0];
                            var et = VolumeTetrahedraSurfacer.EdgesInTetra [ei + 1];
                            if (((signes [ef]) * (signes [et])) < 0.0f)
                            {
                                // we have something on this edge!
                                var tf      = ts[ef];
                                var tt      = ts[et];
                                var vf      = AddInvertTileOffset3(start, h2.LinearToCubic(tf));
                                var vt      = AddInvertTileOffset3(start, h2.LinearToCubic(tt));
                                int encoded = PackVoxelEdgeIdSorted(vol.Header, vf, vt);
                                edgesInTetra.Add(encoded);
                            }
                        }
                        int prevVid = -1, prevPrevVid = -1, pppv = -1;
                        foreach (var ee in edgesInTetra)
                        {
                            int vid;
                            if (ndxToVertex.ContainsKey(ee))
                            {
                                vid = ndxToVertex [ee];
                            }
                            else
                            {
                                vid = ndxToVertex.Count;
                                ndxToVertex.Add(ee, vid);
                            }
                            if (edgesInTetra.Count == 3)
                            {
                                triangles.Add(vid);
                            }
                            else if (edgesInTetra.Count == 4)
                            {
                                quads.Add(vid);
                            }
                            else
                            {
                                Debug.Assert(false, "Really?? c=" + edgesInTetra.Count);
                            }
                            pppv        = prevPrevVid;
                            prevPrevVid = prevVid;
                            prevVid     = vid;
                        }
                    }
                }
            }
        }

        List <Vector3> vertices       = new List <Vector3> ();
        List <Vector3> vertexSigns    = new List <Vector3> ();
        List <Vector4> vertexTangents = null;

        if (optionalModel != null)
        {
            vertexTangents = new List <Vector4> ();
        }
        Vector3 invScale = new Vector3(1.0f / (vol.Size.X - 1), 1.0f / (vol.Size.Y - 1), 1.0f / (vol.Size.Z - 1));

        foreach (var kv in ndxToVertex)
        {
            // setup vertices from edge data:
            var packed = kv.Key;
            var vid    = kv.Value;
            while (vertices.Count <= vid)
            {
                vertices.Add(Vector3.zero);
                vertexSigns.Add(Vector3.zero);;
                if (optionalModel != null)
                {
                    vertexTangents.Add(Vector4.zero);
                }
            }
            Int3 a3, b3;
            UnpackVoxelEdgeId(vol.Header, packed, out a3, out b3);
            var wa   = signTest(vol.Read(a3));
            var wb   = signTest(vol.Read(b3));
            var wab  = Mathf.Abs(wa) / (Mathf.Abs(wa) + Mathf.Abs(wb));
            var pos  = Vector3.Lerp(a3.AsVector3(), b3.AsVector3(), wab);               //0.5f); // TODO: weight value based on signed root
            var upos = new Vector3(pos.x * invScale.x, pos.y * invScale.y, pos.z * invScale.z) - (Vector3.one * 0.5f);
            vertices [vid]    = upos;
            vertexSigns [vid] = (a3.AsVector3() - b3.AsVector3()) * Mathf.Sign(wa - wb);
            if (optionalModel != null)
            {
                var ta = CalcFlowTangent(optionalModel, optionalModel.FieldsCells.Read(a3));
                var tb = CalcFlowTangent(optionalModel, optionalModel.FieldsCells.Read(b3));
                vertexTangents [vid] = Vector4.Lerp(ta, tb, wab);                  // ((ta + tb) *0.5f);
            }
        }
        for (int qi = 0; qi < quads.Count; qi += 4)
        {
            // ensure quad covers whole space (a and b must be furthest from each other):
            var a = vertices[quads[qi + 0]];
            var b = vertices[quads[qi + 1]];
            var c = vertices[quads[qi + 2]];
            var d = vertices[quads[qi + 3]];

            // add triangle a-b-c:
            triangles.Add(quads[qi + 0]);
            triangles.Add(quads[qi + 1]);
            triangles.Add(quads[qi + 2]);

            triangles.Add(quads[qi + 1]);
            triangles.Add(quads[qi + 3]);
            triangles.Add(quads[qi + 2]);
        }
        var trisToSort = new List <SortTri> ();

        for (int i = 0; i < triangles.Count; i += 3)
        {
            // ensure triangle is oriented correctly:
            var a = vertices[triangles[i + 0]];
            var b = vertices[triangles[i + 1]];
            var c = vertices[triangles[i + 2]];
            var n = Vector3.Cross(b - a, c - b);
            if (Vector3.Dot(vertexSigns [triangles [i + 0]], n) >= 0.0f)
            {
                SwapListValues(triangles, i + 1, i + 2);
            }

            // add triangle to list
            SortTri tri;
            tri.I0          = triangles [i + 0];
            tri.I1          = triangles [i + 1];
            tri.I2          = triangles [i + 2];
            tri.DistFromCam = (relativeCameraPos - ((a + b + c) * (1.0f / 3.0f))).magnitude;
            trisToSort.Add(tri);
        }
        // sort the triangles:
        if (true)
        {
            trisToSort.Sort((a, b) => - (a.DistFromCam.CompareTo(b.DistFromCam)));
            triangles.Clear();
            foreach (var t in trisToSort)
            {
                triangles.Add(t.I0);
                triangles.Add(t.I1);
                triangles.Add(t.I2);
            }
        }

        Mesh result = new Mesh();

        //Debug.Log ("Meshing info: verts=" + vertices.Count + " tris=" + triangles.Count);
        result.SetVertices(vertices);
        if (optionalModel != null)
        {
            result.SetTangents(vertexTangents);
        }
        result.triangles = (triangles.ToArray());
        result.RecalculateNormals();
        return(result);
    }
Пример #13
0
    private IEnumerable <bool> UpdateAura()
    {
        Debug.Log("UPDATING AURA.");

        var m2w = this.transform.localToWorldMatrix;

        bool debugMe = true;

        var proj = SpanAPI.spanProjFromCubicDomainAndRange(
            mColorBuffer.Size.Select(k => SpanAPI.span(0, k - 1)),
            Cubic <SpanF> .CreateSame(SpanAPI.span(-0.5, 0.5))
            );
        var invProj = SpanAPI.spanProjFromCubicDomainAndRange(
            Cubic <SpanF> .CreateSame(SpanAPI.span(-0.5, 0.5)),
            mColorBuffer.Size.Select(k => SpanAPI.span(0, k - 1))
            );
        var invProjForMath = SpanAPI.spanProjFromCubicDomainAndRange(
            Cubic <SpanF> .CreateSame(SpanAPI.span(-0.5, 0.5)),
            Cubic <SpanF> .CreateSame(SpanAPI.span(-1.0, 1.0))
            );

        VolumeBuffer <float> distances = null;
        bool needsDistances            = false;
        bool needsSurface = true;
        var  sz           = this.mColorBuffer.Size;
        var  path         = VolumeBufferFile.CacheFilePath(this.gameObject.name + "_sdf", sz);
        var  pathOpacity  = VolumeBufferFile.CacheFilePath(this.gameObject.name + "_opacity", sz);

        if ((!this.RefreshDistances) && VolumeBufferFile.CacheFileExists(path))
        {
            distances = VolumeBufferFile.ReadFloatVolFromFile(sz, path);
        }
        if ((!this.RefreshDistances) && VolumeBufferFile.CacheFileExists(pathOpacity))
        {
            distances      = VolumeBufferFile.ReadFloatVolFromFile(sz, pathOpacity);
            needsDistances = true;
            needsSurface   = false;
        }
        if (distances == null)
        {
            distances = new VolumeBuffer <float>(sz);
            distances.ClearAll(-1.0f);
            needsDistances = true;
        }

        if (needsDistances)
        {
            if (needsSurface)
            {
                Debug.Log("AURA SURFACES...");
                // First see which points are touching:
                Color defaultVal = Color.clear;
                for (int i = 0; i < this.mColorBuffer.Length; i++)
                {
                    var   ndx         = this.mColorBuffer.UnprojectIndex(i);
                    var   mpos        = SpanAPI.spanProjCubicInt(proj, ndx);
                    Color curColor    = defaultVal;
                    float curDistance = -1.0f;

                    if (this.mColorBuffer.IsEdge(ndx))
                    {
                        // it's an edge, ignore it
                    }
                    else
                    {
                        var center  = mpos.Select(k => (k.From + k.To) * 0.5f).AsVector();
                        var radius  = mpos.Select(k => Mathf.Abs((float)((k.From - k.To) * 0.5f))).Aggregate((a, b) => ((a + b) * 0.5f));
                        var wcenter = m2w.MultiplyPoint(center);
                        var wradius = m2w.MultiplyVector(Vector3.one * radius).magnitude;
                        if (Physics.CheckSphere(wcenter, wradius))
                        {
                            //Debug.Log ("Touched something");
                            curColor    = Color.white;
                            curColor.a  = 0.2f;
                            curDistance = 0.0f;
                        }
                    }

                    this.mColorBuffer.Write(ndx, curColor);
                    distances.Write(ndx, curDistance);

                    if (((i % 100)) == 99)
                    {
                        this.UpdateTextureFromBuffer();
                        yield return(false);
                    }
                }
                VolumeBufferFile.SaveFloatVolToFile(distances, pathOpacity);
            }

            // Now calculate the actual distances:
            Debug.Log("AURA DISTANCES...");
            if (false)
            {
                // if this works:
                VolumeBufferUtil.ConvertOpacityToDistanceBuffer(distances);
            }
            else
            {
                // this works but is super slow:
                float maxDist = CubicIntDist(Cubic <int> .CreateSame(0), distances.Size);
                for (int i = 0; i < this.mColorBuffer.Length; i++)
                {
                    var ndx = this.mColorBuffer.UnprojectIndex(i);

                    var centerDist = distances.Read(ndx);
                    if (centerDist < 0.0f)
                    {
                        // needs recalc, slowest possible way O(n^6):

                        float closestDistance = -1.0f;
                        for (int j = 0; j < distances.Array.Length; j++)
                        {
                            if (distances.Array[j] == 0.0f)
                            {
                                float dist = CubicIntDist(distances.UnprojectIndex(j), ndx) / maxDist;
                                if ((closestDistance < 0.0f) || (dist < closestDistance))
                                {
                                    closestDistance = dist;
                                }
                            }
                        }
                        distances.Write(ndx, closestDistance);

                        Color testColor = Color.white;
                        testColor.a = closestDistance;
                        this.mColorBuffer.Write(ndx, testColor);
                        if (((i % 100)) == 99)
                        {
                            this.UpdateTextureFromBuffer();
                            yield return(false);
                        }
                    }
                }
            }

            // Write out the results:
            VolumeBufferFile.SaveFloatVolToFile(distances, path);
        }

        ChakraAuraSettings auraSettings = this.GetComponent <ChakraAuraSettings>();


        if (auraSettings.UseChakraAuraDistance)
        {
            Debug.Log("AURA-TO-CHAKRA DISTANCES...");

            // modify distances based on chakra locality:
            var allChakras = this.gameObject
                             .GetComponentInParent <ChakraControl>()
                             .AllPoints
                             .Where(k => ((!k.IsAura) && (!k.IsMultiChakras)))
                             .Where(k => (!k.ChakraOneWay))
                             .Select(k => Matrix4x4.TRS(k.transform.position, k.transform.rotation, Vector3.one).inverse)
                             .ToArray();

            Debug.Log("Chakra Count = " + allChakras.Length);

            for (int li = 0; li < distances.Array.Length; li++)
            {
                var ndx      = this.mColorBuffer.UnprojectIndex(li);
                var mpos     = SpanAPI.spanProjCubicInt(proj, ndx).Select(k => (k.From + k.To) * 0.5f).AsVector();
                var wpos     = m2w.MultiplyPoint(mpos);
                var dist     = distances.Array[li];
                var origdist = dist;

                foreach (var c in allChakras)
                {
                    var lpos  = c.MultiplyPoint(wpos);
                    var ldist = Mathf.Sqrt((lpos.x * lpos.x) + (lpos.z * lpos.z)) / (Mathf.Abs(lpos.y) * 1.5f);
                    ldist = 0.5f - (ldist * ldist);
                    if (ldist > dist)
                    {
                        dist = ldist;                         //(origdist - ldist);
                    }
                }

                //if (origdist == dist) dist = 0.0f; // HACK REMOVE

                distances.Array[li] = dist;

                if (((li % 100)) == 99)
                {
                    float pct = ((float)li) / ((float)distances.Array.Length);
                    Debug.Log("Updating aura-chakra distance (" + (pct * 100.0f) + "%)");
                    yield return(false);
                }
            }
        }


        // Calculate the colors from the distances:
        float[] idealDistances = auraSettings.AuraDistances; // { 0.0165f, 0.05f, 0.1f };
        Color[] idealColors    = auraSettings.AuraColors;    // { Color.red, Color.green, new Color(0.0f, 1.0f, 1.0f, 1.0f) };
        //float IdealDistance = 0.0165f;
        //float AuraWidth = 0.0125f;
        Debug.Log("AURA COLORS...");
        float previousDist = 0.0f;

        for (int i = 0; i < this.mColorBuffer.Length; i++)
        {
            var ndx = this.mColorBuffer.UnprojectIndex(i);

            var dist = distances.Read(ndx);

            dist = dist * dist; // 1.0f / (dist * dist);

            Color resColor = Color.clear;
            for (int layerNdx = idealDistances.Length - 1; layerNdx >= 0; layerNdx--)
            {
                var edgeDist = idealDistances[layerNdx] * auraSettings.WholeScaleDistances;

                if (dist < edgeDist)
                {
                    var edgeColor = idealColors[layerNdx];
                    var nextDist  = 0.0f; // ((layerNdx > 0) ? (idealDistances[layerNdx - 1] * auraSettings.WholeScaleDistances) : 0.0f);

                    float strength = Mathf.Clamp01(1.0f - (Mathf.Abs(dist - edgeDist) / (edgeDist - nextDist)));
                    Color curColor = edgeColor;
                    curColor.a = curColor.a * strength;

                    resColor = curColor;// Colors_BlendOver(auraSettings, resColor, curColor);
                }
                previousDist = edgeDist;
            }

            this.mColorBuffer.Write(ndx, resColor);
            if (((i % 100)) == 99)
            {
                Debug.Log("cur = " + dist);
                this.UpdateTextureFromBuffer();
                yield return(false);
            }

            while (auraSettings.IsPauseCalculation)
            {
                yield return(false);
            }
        }
        this.mColorBuffer.ClearEdges(Color.clear);

        //return;

        this.UpdateTextureFromBuffer();
        this.TrySaveToCache();

        Debug.Log("AURA UPDATED!");
    }