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)); }
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(); } } }
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(); }
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); }
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(); } }
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); }
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); }
// 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); // } // } }
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."); }
// 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; } }
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); }
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); }
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!"); }