/// <summary> /// Get the size diviser used for the displayability of the Ocean dataset (structured grid) /// </summary> /// <returns>The field diviser applied along all axis</returns> public UInt32 GetFieldSizeDiviser() { VTKStructuredPoints ptsDesc = m_parser.GetStructuredPointsDescriptor(); UInt32 x = (ptsDesc.Size[0] + DesiredDensity - 1) / DesiredDensity; UInt32 y = (ptsDesc.Size[1] + DesiredDensity - 1) / DesiredDensity; UInt32 z = (ptsDesc.Size[2] + DesiredDensity - 1) / DesiredDensity; return(Math.Max(Math.Max(x, y), z)); }
/// <summary> /// get the displayable size of the vector field. Indeed, due to hardware limitation, we cannot display all the vector field at once /// </summary> /// <returns>The vector field size displayable</returns> public Vector3Int GetDisplayableSize() { VTKStructuredPoints ptsDesc = m_parser.GetStructuredPointsDescriptor(); if (ptsDesc.Size[0] == 0 || ptsDesc.Size[1] == 0 || ptsDesc.Size[2] == 0) { return(new Vector3Int(0, 0, 0)); } int maxRatio = (int)GetFieldSizeDiviser(); return(new Vector3Int((int)ptsDesc.Size[0] / maxRatio, (int)ptsDesc.Size[1] / maxRatio, (int)ptsDesc.Size[2] / maxRatio)); }
/// <summary> /// Create a small multiple object /// </summary> /// <param name="dataID">The parameter ID to use. Use Parser.GetPointFieldValueDescriptors(); to get the field ID</param> /// <returns>A VTKUnitySmallMultiple object.</returns> public VTKUnitySmallMultiple CreatePointFieldSmallMultiple(Int32 dataID) { VTKStructuredPoints ptsDesc = m_parser.GetStructuredPointsDescriptor(); VTKUnitySmallMultiple sm = GameObject.Instantiate(SmallMultiplePrefab); Vector3Int density = Density; unsafe { if (sm.InitFromPointField(m_parser, m_mesh, m_availableUVIDs[m_availableUVIDs.Count - 1], dataID, new Vector3Int((int)(ptsDesc.Size[0] / density.x), (int)(ptsDesc.Size[1] / density.y * ptsDesc.Size[0]), (int)(ptsDesc.Size[2] / density.z * ptsDesc.Size[1] * ptsDesc.Size[0])), density, m_mask)) { m_smallMultiples.Add(sm); m_availableUVIDs.RemoveAt(m_availableUVIDs.Count - 1); return(sm); } } Destroy(sm); return(null); }
/// <summary> /// Initialize the StructuredGrid representation /// </summary> /// <param name="parser">The VKTParser to use. /// It should not be closed while this object is intented to being modified (e.g adding small multiples)</param> /// <param name="mask">The mask to apply point per point. if mask==null, we do not use it</param> /// <returns></returns> public unsafe bool Init(VTKParser parser, byte *mask = null) { m_parser = parser; m_mask = mask; if (m_parser.GetDatasetType() != VTKDatasetType.VTK_STRUCTURED_POINTS) { Debug.Log("Error: The dataset should be a structured points dataset"); return(false); } //Get the points and modify the points / normals buffer VTKStructuredPoints ptsDesc = m_parser.GetStructuredPointsDescriptor(); Vector3Int density = Density; Vector3[] pts = new Vector3[density.x * density.y * density.z]; //Determine the positions for "normalizing" the object (the biggest axis of the object at "size = 1") Vector3 minPos = new Vector3((float)((density.x / 2) * ptsDesc.Size[0] / density.x * ptsDesc.Spacing[0]), (float)((density.y / 2) * ptsDesc.Size[1] / density.y * ptsDesc.Spacing[1]), (float)((density.z / 2) * ptsDesc.Size[2] / density.z * ptsDesc.Spacing[2])); Vector3 maxPos = new Vector3((float)((density.x - 1 + density.x / 2) * ptsDesc.Size[0] / density.x * ptsDesc.Spacing[0]), (float)((density.y - 1 + density.y / 2) * ptsDesc.Size[1] / density.y * ptsDesc.Spacing[1]), (float)((density.z - 1 + density.z / 2) * ptsDesc.Size[2] / density.z * ptsDesc.Spacing[2])); float maxAxis = Math.Max(maxPos.x - minPos.x, Math.Max(maxPos.y - minPos.y, maxPos.z - minPos.z)); for (int k = 0; k < density.z; k++) { for (int j = 0; j < density.y; j++) { for (int i = 0; i < density.x; i++) { pts[i + density.x * j + density.x * density.y * k] = new Vector3((float)((i - density.x / 2) * ptsDesc.Size[0] / density.x * ptsDesc.Spacing[0]) / maxAxis, (float)((j - density.y / 2) * ptsDesc.Size[1] / density.y * ptsDesc.Spacing[1]) / maxAxis, (float)((k - density.z / 2) * ptsDesc.Size[2] / density.z * ptsDesc.Spacing[2]) / maxAxis); } } } //Store the maximas positions m_minPos = pts[0]; m_maxPos = pts[pts.Length - 1]; //The element buffer Vector3Int offsetMask = new Vector3Int((int)(ptsDesc.Size[0] / density.x), (int)(ptsDesc.Size[1] / density.y * ptsDesc.Size[0]), (int)(ptsDesc.Size[2] / density.z * ptsDesc.Size[1] * ptsDesc.Size[0])); int[] triangles = new int[(density.x - 1) * (density.y - 1) * (density.z - 1) * 36]; for (int k = 0; k < density.z - 1; k++) { for (int j = 0; j < density.y - 1; j++) { for (int i = 0; i < density.x - 1; i++) { int offset = (i + j * (density.x - 1) + k * (density.x - 1) * (density.y - 1)) * 36; //Test the mask //If we are encountering a missing mask, put the triangles to to point "0" (i.e empty triangle) //And go at the end of the loop if (mask != null) { for (int k2 = 0; k2 < 2; k2++) { for (int j2 = 0; j2 < 2; j2++) { for (int i2 = 0; i2 < 2; i2++) { if (*(mask + (i + i2) * offsetMask.x + (j + j2) * offsetMask.y + (k + k2) * offsetMask.z) == 0) { for (int t = 0; t < 36; t++) { triangles[offset + t] = 0; } goto endTriangleLoop; } } } } } //Front triangles[offset] = i + 1 + j * density.x + k * density.x * density.y; triangles[offset + 1] = i + j * density.x + k * density.x * density.y; triangles[offset + 2] = i + 1 + (j + 1) * density.x + k * density.x * density.y; triangles[offset + 3] = i + 1 + (j + 1) * density.x + k * density.x * density.y; triangles[offset + 4] = i + j * density.x + k * density.x * density.y; triangles[offset + 5] = i + (j + 1) * density.x + k * density.x * density.y; //Back triangles[offset + 6] = i + 1 + (j + 1) * density.x + (k + 1) * density.x * density.y; triangles[offset + 7] = i + j * density.x + (k + 1) * density.x * density.y; triangles[offset + 8] = i + 1 + j * density.x + (k + 1) * density.x * density.y; triangles[offset + 9] = i + (j + 1) * density.x + (k + 1) * density.x * density.y; triangles[offset + 10] = i + j * density.x + (k + 1) * density.x * density.y; triangles[offset + 11] = i + 1 + (j + 1) * density.x + (k + 1) * density.x * density.y; //Left triangles[offset + 12] = i + j * density.x + k * density.x * density.y; triangles[offset + 13] = i + j * density.x + (k + 1) * density.x * density.y; triangles[offset + 14] = i + (j + 1) * density.x + (k + 1) * density.x * density.y; triangles[offset + 15] = i + (j + 1) * density.x + k * density.x * density.y; triangles[offset + 16] = i + j * density.x + k * density.x * density.y; triangles[offset + 17] = i + (j + 1) * density.x + (k + 1) * density.x * density.y; //Right triangles[offset + 18] = (i + 1) + j * density.x + (k + 1) * density.x * density.y; triangles[offset + 19] = (i + 1) + j * density.x + k * density.x * density.y; triangles[offset + 20] = (i + 1) + (j + 1) * density.x + (k + 1) * density.x * density.y; triangles[offset + 21] = (i + 1) + j * density.x + k * density.x * density.y; triangles[offset + 22] = (i + 1) + (j + 1) * density.x + k * density.x * density.y; triangles[offset + 23] = (i + 1) + (j + 1) * density.x + (k + 1) * density.x * density.y; //Top triangles[offset + 24] = (i + 1) + (j + 1) * density.x + k * density.x * density.y; triangles[offset + 25] = i + (j + 1) * density.x + k * density.x * density.y; triangles[offset + 26] = i + (j + 1) * density.x + (k + 1) * density.x * density.y; triangles[offset + 27] = (i + 1) + (j + 1) * density.x + (k + 1) * density.x * density.y; triangles[offset + 28] = (i + 1) + (j + 1) * density.x + k * density.x * density.y; triangles[offset + 29] = i + (j + 1) * density.x + (k + 1) * density.x * density.y; //Bottom triangles[offset + 30] = (i + 1) + j * density.x + k * density.x * density.y; triangles[offset + 31] = i + j * density.x + (k + 1) * density.x * density.y; triangles[offset + 32] = i + j * density.x + k * density.x * density.y; triangles[offset + 33] = (i + 1) + j * density.x + k * density.x * density.y; triangles[offset + 34] = (i + 1) + j * density.x + (k + 1) * density.x * density.y; triangles[offset + 35] = i + j * density.x + (k + 1) * density.x * density.y; //End the the triangle loop endTriangleLoop: continue; } } } //Create the mesh m_mesh = new Mesh(); m_mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; m_mesh.vertices = pts; m_mesh.triangles = triangles; m_mesh.UploadMeshData(false); //Small multiples array m_smallMultiples = new List <VTKUnitySmallMultiple>(); m_availableUVIDs = new List <Int32>(); for (Int32 i = 0; i < 8; i++) { m_availableUVIDs.Add(i); } return(true); }
/// <summary> /// Initialize the small multiple from point field data /// </summary> /// <param name="parser">The VTK Parser</param> /// <param name="mesh">The mesh to display</param> /// <param name="uvID">The ID where to put the color data</param> /// <param name="valueID">The ID of the VTKFieldPointValue</param> /// <param name="offset">The offset along each axis to apply : pos = x*offset.x + y*offset.y + z*offset.z</param> /// <param name="density">The density in use</param> /// <param name="mask">The mask to apply along each point</param> /// <returns></returns> public unsafe bool InitFromPointField(VTKParser parser, Mesh mesh, Int32 uvID, Int32 valueID, Vector3Int offset, Vector3Int density, byte *mask) { m_material = new Material(ColorMaterial); m_values = new float[density.x * density.y * density.z]; //Check the ID if (uvID > 7) { Debug.Log("The uvID must be between 0 to 7. This is a limitation of Unity for working within a common mesh..."); return(false); } m_mesh = mesh; VTKStructuredPoints descPts = parser.GetStructuredPointsDescriptor(); //Determine the maximum and minimum positions Vector3 minModelPos = new Vector3((float)(-descPts.Size[0] / 2.0 * descPts.Spacing[0]), (float)(-descPts.Size[1] / 2.0 * descPts.Spacing[1]), (float)(-descPts.Size[2] / 2.0 * descPts.Spacing[2])); Vector3 maxModelPos = -minModelPos; //The value buffer List <VTKFieldValue> fieldDesc = parser.GetPointFieldValueDescriptors(); if (fieldDesc.Count < valueID) { Debug.Log("No value to display"); return(false); } VTKValue val = parser.ParseAllFieldValues(fieldDesc[valueID]); List <Vector4> colors = new List <Vector4>((int)(density.x * density.y * density.z)); //Determine the minimum and maximum value and their position m_maxVal = float.MinValue; m_minVal = float.MaxValue; Vector3 minLoc = new Vector3(); Vector3 maxLoc = new Vector3(); for (UInt32 i = 0; i < val.NbValues; i++) { if (mask != null && mask[i] == 0) { continue; } float v = (float)val.ReadAsDouble(i * fieldDesc[valueID].NbValuesPerTuple); if (m_maxVal < v) { m_maxVal = v; maxLoc = new Vector3(i % descPts.Size[0], (i / descPts.Size[0]) % descPts.Size[1], i / (descPts.Size[0] * descPts.Size[1])); } if (m_minVal > v) { m_minVal = v; minLoc = new Vector3(i % descPts.Size[0], (i / descPts.Size[0]) % descPts.Size[1], i / (descPts.Size[0] * descPts.Size[1])); } } //Normalize the location (between 0.0 and 1.0 for the most "long" axis) Vector3[] vec = new Vector3[2] { maxLoc, minLoc }; Vector3 modelDist = maxModelPos - minModelPos; float maxModelDist = Math.Max(modelDist.x, Math.Max(modelDist.y, modelDist.z)); for (int i = 0; i < vec.Length; i++) { Vector3 l = vec[i]; l = (new Vector3((float)(l.x * descPts.Spacing[0]), (float)(l.y * descPts.Spacing[1]), (float)(l.z * descPts.Spacing[2])) + minModelPos) / maxModelDist; vec[i] = l; } Debug.Log($"Min : {m_minVal} Max : {m_maxVal}"); //Fill the color array maxLoc = vec[0]; minLoc = vec[1]; UInt64 colorValueOff = 0; for (UInt32 k = 0; k < density.z; k++) { for (UInt32 j = 0; j < density.y; j++) { for (UInt32 i = 0; i < density.x; i++, colorValueOff++) { UInt64 fieldOff = (UInt64)(i * offset.x + j * offset.y + k * offset.z); //Check the mask if (mask != null && *(mask + fieldOff) == 0) { colors.Add(new Vector4(0.0f, 0.0f, 0.0f, 0.0f)); m_values[colorValueOff] = m_minVal; continue; } float c = val.ReadAsFloat(fieldOff * fieldDesc[valueID].NbValuesPerTuple); m_values[colorValueOff] = c; c = (float)((c - m_minVal) / (m_maxVal - m_minVal)); //LAB color space (warm - cold) Color?col = null; if (c < 0.5) { col = LABColor.Lerp(coldColor, whiteColor, 2.0f * c).ToXYZ().ToRGB(); } else { col = LABColor.Lerp(whiteColor, warmColor, 2.0f * (c - 0.5f)).ToXYZ().ToRGB(); } colors.Add(new Vector4(col.Value.r, col.Value.g, col.Value.b, 1.0f)); } } } //Update the mesh / material m_mesh.SetUVs(uvID, colors); m_mesh.UploadMeshData(false); m_colorID = uvID; for (int i = 0; i < 8; i++) { if (i != m_colorID) { m_material.DisableKeyword($"TEXCOORD{i}_ON"); } } m_material.EnableKeyword($"TEXCOORD{m_colorID}_ON"); PlaneEnabled = false; SphereEnabled = false; CreateGameObjects(vec); return(true); }