Merge pull request #59 from jasonks2/PARCHG---Electron-Density-Volume-Renderer

VASP support + store dataset values as float instead of int.
This commit is contained in:
Matias Lavik 2021-12-31 11:26:17 +01:00 коммит произвёл GitHub
Родитель 303d2ce76e 9c0724e267
Коммит 711cd51609
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
27 изменённых файлов: 56914 добавлений и 431 удалений

Просмотреть файл

@ -1,8 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using UnityEditor; using UnityEditor;
using UnityEngine;
namespace UnityVolumeRendering namespace UnityVolumeRendering
{ {
@ -11,7 +12,6 @@ namespace UnityVolumeRendering
public static void ImportDataset(string filePath) public static void ImportDataset(string filePath)
{ {
DatasetType datasetType = DatasetImporterUtility.GetDatasetType(filePath); DatasetType datasetType = DatasetImporterUtility.GetDatasetType(filePath);
switch (datasetType) switch (datasetType)
{ {
case DatasetType.Raw: case DatasetType.Raw:
@ -50,6 +50,21 @@ namespace UnityVolumeRendering
} }
break; break;
} }
case DatasetType.PARCHG:
{
ParDatasetImporter importer = new ParDatasetImporter(filePath);
VolumeDataset dataset = importer.Import();
if (dataset != null)
{
VolumeRenderedObject obj = VolumeObjectFactory.CreateObject(dataset);
}
else
{
Debug.LogError("Failed to import datset");
}
break;
}
} }
} }
} }

Просмотреть файл

@ -1,4 +1,4 @@
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
using System.IO; using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
@ -23,6 +23,20 @@ namespace UnityVolumeRendering
} }
} }
[MenuItem("Volume Rendering/Load dataset/Load PARCHG dataset")]
static void ShowParDatasetImporter()
{
string file = EditorUtility.OpenFilePanel("Select a dataset to load", "DataFiles", "");
if (File.Exists(file))
{
EditorDatasetImporter.ImportDataset(file);
}
else
{
Debug.LogError("File doesn't exist: " + file);
}
}
[MenuItem("Volume Rendering/Load dataset/Load DICOM")] [MenuItem("Volume Rendering/Load dataset/Load DICOM")]
static void ShowDICOMImporter() static void ShowDICOMImporter()
{ {

Просмотреть файл

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -17,11 +17,18 @@ namespace UnityVolumeRendering
{ {
GUILayout.BeginVertical(); GUILayout.BeginVertical();
// Show dataset import buttons // Show dataset import buttons
if(GUILayout.Button("Import RAW dataset")) if(GUILayout.Button("Import RAW dataset"))
{ {
RuntimeFileBrowser.ShowOpenFileDialog(OnOpenRAWDatasetResult, "DataFiles"); RuntimeFileBrowser.ShowOpenFileDialog(OnOpenRAWDatasetResult, "DataFiles");
} }
if(GUILayout.Button("Import PARCHG dataset"))
{
RuntimeFileBrowser.ShowOpenFileDialog(OnOpenPARDatasetResult, "DataFiles");
}
if (GUILayout.Button("Import DICOM dataset")) if (GUILayout.Button("Import DICOM dataset"))
{ {
RuntimeFileBrowser.ShowOpenDirectoryDialog(OnOpenDICOMDatasetResult); RuntimeFileBrowser.ShowOpenDirectoryDialog(OnOpenDICOMDatasetResult);
@ -42,10 +49,26 @@ namespace UnityVolumeRendering
GUILayout.EndVertical(); GUILayout.EndVertical();
} }
private void OnOpenPARDatasetResult(RuntimeFileBrowser.DialogResult result)
{
if (!result.cancelled)
{
DespawnAllDatasets();
string filePath = result.path;
ParDatasetImporter parimporter = new ParDatasetImporter(filePath);
VolumeDataset dataset = parimporter.Import(); //overriden somewhere
if (dataset != null)
{
VolumeObjectFactory.CreateObject(dataset);
}
}
}
private void OnOpenRAWDatasetResult(RuntimeFileBrowser.DialogResult result) private void OnOpenRAWDatasetResult(RuntimeFileBrowser.DialogResult result)
{ {
if(!result.cancelled) if(!result.cancelled)
{ {
// We'll only allow one dataset at a time in the runtime GUI (for simplicity) // We'll only allow one dataset at a time in the runtime GUI (for simplicity)
DespawnAllDatasets(); DespawnAllDatasets();

Просмотреть файл

@ -133,7 +133,7 @@ namespace UnityVolumeRendering
dataset.dimZ = files.Count; dataset.dimZ = files.Count;
int dimension = dataset.dimX * dataset.dimY * dataset.dimZ; int dimension = dataset.dimX * dataset.dimY * dataset.dimZ;
dataset.data = new int[dimension]; dataset.data = new float[dimension];
for (int iSlice = 0; iSlice < files.Count; iSlice++) for (int iSlice = 0; iSlice < files.Count; iSlice++)
{ {
@ -153,7 +153,7 @@ namespace UnityVolumeRendering
int pixelValue = pixelArr[pixelIndex]; int pixelValue = pixelArr[pixelIndex];
float hounsfieldValue = pixelValue * slice.slope + slice.intercept; float hounsfieldValue = pixelValue * slice.slope + slice.intercept;
dataset.data[dataIndex] = (int)Mathf.Clamp(hounsfieldValue, -1024.0f, 3071.0f); dataset.data[dataIndex] = Mathf.Clamp(hounsfieldValue, -1024.0f, 3071.0f);
} }
} }
} }

Просмотреть файл

@ -1,5 +1,7 @@

using System.IO; using System.IO;
using UnityEngine;
using System;
using UnityEditor;
namespace UnityVolumeRendering namespace UnityVolumeRendering
{ {
@ -7,7 +9,8 @@ namespace UnityVolumeRendering
{ {
Unknown, Unknown,
Raw, Raw,
DICOM DICOM,
PARCHG
} }
public class DatasetImporterUtility public class DatasetImporterUtility
@ -19,17 +22,29 @@ namespace UnityVolumeRendering
// Check file extension // Check file extension
string extension = Path.GetExtension(filePath); string extension = Path.GetExtension(filePath);
if (extension == ".dat" || extension == ".raw" || extension == ".vol")
if (String.Equals(extension, ".vasp"))
{
datasetType = DatasetType.PARCHG;
}
else if (extension == ".dat" || extension == ".raw" || extension == ".vol")
datasetType = DatasetType.Raw; datasetType = DatasetType.Raw;
else if (extension == ".ini") else if (extension == ".ini")
{ {
filePath = filePath.Substring(0, filePath.LastIndexOf(".")); filePath = filePath.Substring(0, filePath.LastIndexOf("."));
datasetType = DatasetType.Raw; datasetType = DatasetType.Raw;
} }
else if (extension == ".dicom" || extension == ".dcm") else if (extension == ".dicom" || extension == ".dcm")
{
datasetType = DatasetType.DICOM; datasetType = DatasetType.DICOM;
}
else else
{
datasetType = DatasetType.Unknown; datasetType = DatasetType.Unknown;
}
return datasetType; return datasetType;
} }

Просмотреть файл

@ -140,7 +140,7 @@ namespace UnityVolumeRendering
{ {
name = name, name = name,
datasetName = name, datasetName = name,
data = data, data = Array.ConvertAll(data, new Converter<int, float>((int val) => { return Convert.ToSingle(val); })),
dimX = dimensions.x, dimX = dimensions.x,
dimY = dimensions.y, dimY = dimensions.y,
dimZ = dimensions.z, dimZ = dimensions.z,

Просмотреть файл

@ -0,0 +1,353 @@
/*----------------------------------------------------------------------------
# file made by Jason (jasonks2)
# project start 8-10-2021
# finished 9-7-2021
#
# Thank you to prof. Andre Schleife
# Thank you to dano - "chg2cube.pl" (perl vasp library)
# Thank you to mlavik - Unity Volume Rendering
# Thank you to Sung Sakong, Dept. of Phys., Univsity Duisburg-Essen
#
# RCS INFORMATION:
# $RCSfile: vaspparchgplugin.c,v $
# $Author: johns $
------------------------------------------------------------------------------*/
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEngine;
namespace UnityVolumeRendering
{
public class ParDatasetImporter
{
string filePath;
string fileName;
float latticeConstant;
string[] atomNames;
int[] atomCount;
int totalAtomCount;
float[][] basisCells;
float[][] coordinatebasisCells;
float[][] cartesiancoordinatebasisCells;
bool isDirect;
int nx;
int ny;
int nz;
int gridDataLines;
float volume;
float volumeScale;
float[] dataGrid;
string[] densityLine;
int[] dimArray;
int dimTotal;
string[] densityTrim;
float[] volumeScaledData;
string[] fileContentLines;
int fileContentIndex;
public ParDatasetImporter(string filePath)
{
this.filePath = filePath;
}
public VolumeDataset Import() //fills VolumeDataset object
{
var extension = Path.GetExtension(filePath);
if (!File.Exists(filePath))
{
Debug.LogError("The file does not exist: " + filePath);
return null;
}
fileContentLines = File.ReadLines(filePath).Where(x => x.Trim(' ') != "").ToArray();
fileContentIndex = 0;
ReadSystemTitle();
ReadLatticeConstant();
ReadLatticeVectors();
GetVolume();
ReadAtomNames();
ReadAtomSum();
ReadCoordinateSystemType();
ReadCoordinates();
if (isDirect)
{
cartesiancoordinatebasisCells = ToCartesian();
}
ReadDimensions();
dimTotal = dimArray[0] * dimArray[1] * dimArray[2];
nx = dimArray[0];
ny = dimArray[1];
nz = dimArray[2]; // dimensions
CalculateDataLines();
ReadGrid();
VolumeDataset dataFiller = new VolumeDataset(); //volume object then gets sent to VolumeObjectFactory
dataFiller.datasetName = fileName;
dataFiller.filePath = filePath;
dataFiller.dimX = nx;
dataFiller.dimY = ny;
dataFiller.dimZ = nz;
dataFiller.volumeScale = (float)(1 / volumeScale);
dataFiller.data = new float[dimTotal];
volumeScaledData = new float[dimTotal];
for (int ix = 0; ix < nx; ix++)
{
for (int iy = 0; iy < ny; iy++)
{
for (int iz = 0; iz < nz; iz++)
{
int itr = (iz * nx * ny) + (iy * nx) + ix;
volumeScaledData[itr] = (float)dataGrid[itr] * (float)dataFiller.volumeScale * (float)0.036749309; //density * volumescale * e_units
}
}
}
for (int i = 0; i < dimTotal; i++)
{
dataFiller.data[i] = dataGrid[i];
}
Debug.Log("Loaded dataset in range: " + dataFiller.GetMinDataValue() + " - " + dataFiller.GetMaxDataValue());
return dataFiller;
}
private string ParseLine()
{
Debug.Assert(fileContentIndex < fileContentLines.Length);
return fileContentLines[fileContentIndex++];
}
private string PeekLine()
{
Debug.Assert(fileContentIndex < fileContentLines.Length);
return fileContentLines[fileContentIndex];
}
public void ReadSystemTitle()
{
ParseLine(); // We don't use header comment for anything now
}
/// <summary>
/// Reads lattice parameter
/// </summary>
public void ReadLatticeConstant()
{
var line = ParseLine();
string[] bits = line.Trim().Split(' ').Where(x => x != "").ToArray();
latticeConstant = float.Parse(bits[0]);
}
/// <summary>
/// Multiplies basis by lattice parameter
/// </summary>
public void ReadLatticeVectors()
{
basisCells = new float[3][];
basisCells[0] = new float[3];
basisCells[1] = new float[3];
basisCells[2] = new float[3];
for (int i = 0; i < 3; i++)
{
string latticeLine = ParseLine();
string[] vectorString = latticeLine.Trim().Split(' ').Where(t => t.Length > 0).ToArray();
Debug.Assert(vectorString.Length == 3);
basisCells[i][0] = float.Parse(vectorString[0]) * latticeConstant;
basisCells[i][1] = float.Parse(vectorString[1]) * latticeConstant;
basisCells[i][2] = float.Parse(vectorString[2]) * latticeConstant;
}
}
/// <summary>
/// Density is written in as p * V we must divide the volume (same as multiplying the scale)
/// </summary>
public void GetVolume()
{
volume = basisCells[0][0] * (basisCells[1][1] * basisCells[2][2] - basisCells[2][1] * basisCells[1][2])
- basisCells[1][0] * (basisCells[0][1] * basisCells[2][2] - basisCells[2][1] * basisCells[0][2])
+ basisCells[2][0] * (basisCells[0][1] * basisCells[1][2] - basisCells[1][1] * basisCells[0][2]);
Debug.Log(volume);
// make sure volume is +
// this volume is in units selected (default bohr) but we need it in ang**3
volumeScale = Math.Abs(volume) / ((float)Math.Pow(1.889725992, 3)); //bohr/hartree -> ang/eV
}
/// <summary>
/// Reads molecule works for as many atoms
/// </summary>
public void ReadAtomNames()
{
var line = PeekLine();
string[] names = line.Trim().Split(' ').Where(t => t.Length > 0).ToArray();
int num = 0;
if (int.TryParse(names[0], out num))
return; // Current line is atom count (no atom names specified in this file)
else
{
ParseLine(); // Increment line index
atomNames = names;
}
}
public void ReadAtomSum()
{
var line = ParseLine();
string[] atomCountStrings = line.Trim().Split(' ').Where(t => t.Length > 0).ToArray();
atomCount = new int[atomCountStrings.Length];
for (int i = 0; i < atomCountStrings.Length; i++)
atomCount[i] = Int16.Parse(atomCountStrings[i]);
totalAtomCount = atomCount.Sum();
}
public void ReadCoordinates()
{
coordinatebasisCells = new float[totalAtomCount][];
string latticeLine = null;
// unspecified m-array size initializer for loop M given N = 3
for (int x = 0; x < totalAtomCount; x++)
{
coordinatebasisCells[x] = new float[3]; //3 for x y z
}
for (int i = 0; i < totalAtomCount; i++)
{
latticeLine = ParseLine();
string[] vectorString = latticeLine.Trim().Split(' ').Where(x => x != "").ToArray();
coordinatebasisCells[i][0] = float.Parse(vectorString[0]);
coordinatebasisCells[i][1] = float.Parse(vectorString[1]);
coordinatebasisCells[i][2] = float.Parse(vectorString[2]);
}
}
// Reads whether system is in Direct or Cartesian
public void ReadCoordinateSystemType()
{
string molecule = null;
string compare = "Direct";
var line = ParseLine();
string cardinality = line.Trim();
isDirect = string.Equals(cardinality, compare);
}
/// <summary>
/// Direct mode -> Cartesian.
/// TODO: took one day to debug b/c the sum was subject to change via function call getatomcount() SOMEONE FIX
/// </summary>
public float[][] ToCartesian()
{
float[][] cartesiancoordinatebasisCells = new float[totalAtomCount][];
//initialize memory
for (int x = 0; x < totalAtomCount; x++)
{
cartesiancoordinatebasisCells[x] = new float[3];
}
float[][] coordinatebasisCells = new float[totalAtomCount][];
for (int x = 0; x < totalAtomCount; x++)
{
coordinatebasisCells[x] = new float[3];
}
if (!isDirect)
{
Debug.Log("Input atomic position array is already Cartesian");
}
else
{
//conversion here
for (int i = 0; i < totalAtomCount; i++)
{
float v1 = coordinatebasisCells[i][0] * basisCells[0][0] + coordinatebasisCells[i][1] * basisCells[0][1] + coordinatebasisCells[i][2] * basisCells[0][2];
float v2 = coordinatebasisCells[i][0] * basisCells[1][0] + coordinatebasisCells[i][1] * basisCells[1][1] + coordinatebasisCells[i][2] * basisCells[1][2];
float v3 = coordinatebasisCells[i][0] * basisCells[2][0] + coordinatebasisCells[i][1] * basisCells[2][1] + coordinatebasisCells[i][2] * basisCells[2][2];
cartesiancoordinatebasisCells[i][0] = v1;
cartesiancoordinatebasisCells[i][1] = v2;
cartesiancoordinatebasisCells[i][2] = v3;
}
}
return cartesiancoordinatebasisCells;
}
/// <summary>
/// Calculates nx * ny * nz
/// </summary>
public void ReadDimensions()
{
var line = ParseLine();
string grid = line.Trim();
dimArray = new int[3]; //size of atom types (Cd Se) -> 2
// Split on one or more non-digit characters.
string[] numbers = Regex.Split(grid, @"\D+");
for (int q = 0; q < numbers.Count(); q++)
{
if (!string.IsNullOrEmpty(numbers[q]))
{
dimArray[q] = int.Parse(numbers[q]);
}
}
for (int m = 0; m < 3; m++)
{
nx = dimArray[0];
ny = dimArray[1];
}
}
/// <summary>
/// Each line contains 10 elements, therefore there are 10 colums. Divide by 10
/// </summary>
public void CalculateDataLines()
{
gridDataLines = dimTotal / 10;
}
public void ReadGrid()
{
dataGrid = new float[dimTotal];
List<float> data = new List<float>();
for (int i = 0; i < gridDataLines + 1; i++)
{
if (fileContentIndex == fileContentLines.Length)
break; // TODO: Find a more elegant solution (some datasets will have one extra line -> see the "+ 1" in loop above.)
string gridRow = ParseLine().Trim();
densityLine = Regex.Split(gridRow, @"(/^[+\-]?(?=.)(0|[1-9]\d*)?(\.\d*)?(?:(\d)[eE][+\-]?\d+)?$/)"); //thank stackoverflow
densityTrim = densityLine[0].Trim().Split(' ');
for (int r = 0; r < densityTrim.Length; r++)
{
if (!string.IsNullOrEmpty(densityTrim[r]) && (Regex.IsMatch(densityTrim[r], @"\d")) && !string.IsNullOrWhiteSpace(densityTrim[r]))
{
data.Add(float.Parse(densityTrim[r]));
}
}
}
dataGrid = data.ToArray();
}
}
}

Просмотреть файл

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4b0a1ee15267941c9bae1119500df091
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

Просмотреть файл

@ -1,4 +1,4 @@
using System; using System;
using System.IO; using System.IO;
using UnityEngine; using UnityEngine;
@ -64,6 +64,7 @@ namespace UnityVolumeRendering
VolumeDataset dataset = new VolumeDataset(); VolumeDataset dataset = new VolumeDataset();
dataset.datasetName = Path.GetFileName(filePath); dataset.datasetName = Path.GetFileName(filePath);
dataset.filePath = filePath;
dataset.dimX = dimX; dataset.dimX = dimX;
dataset.dimY = dimY; dataset.dimY = dimY;
dataset.dimZ = dimZ; dataset.dimZ = dimZ;
@ -73,12 +74,12 @@ namespace UnityVolumeRendering
reader.ReadBytes(skipBytes); reader.ReadBytes(skipBytes);
int uDimension = dimX * dimY * dimZ; int uDimension = dimX * dimY * dimZ;
dataset.data = new int[uDimension]; dataset.data = new float[uDimension];
// Read the data/sample values // Read the data/sample values
for (int i = 0; i < uDimension; i++) for (int i = 0; i < uDimension; i++)
{ {
dataset.data[i] = ReadDataValue(reader); dataset.data[i] = (float)ReadDataValue(reader);
} }
Debug.Log("Loaded dataset in range: " + dataset.GetMinDataValue() + " - " + dataset.GetMaxDataValue()); Debug.Log("Loaded dataset in range: " + dataset.GetMinDataValue() + " - " + dataset.GetMaxDataValue());

Просмотреть файл

@ -1,4 +1,4 @@
using UnityEngine; using UnityEngine;
using System.Collections.Generic; using System.Collections.Generic;
using System; using System;

Просмотреть файл

@ -1,4 +1,4 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;

Просмотреть файл

@ -1,4 +1,4 @@
using System.Linq; using System.Linq;
using UnityEngine; using UnityEngine;
namespace UnityVolumeRendering namespace UnityVolumeRendering
@ -17,29 +17,29 @@ namespace UnityVolumeRendering
/// <returns></returns> /// <returns></returns>
public static Texture2D GenerateHistogramTexture(VolumeDataset dataset) public static Texture2D GenerateHistogramTexture(VolumeDataset dataset)
{ {
int minValue = dataset.GetMinDataValue(); float minValue = dataset.GetMinDataValue();
int maxValue = dataset.GetMaxDataValue(); float maxValue = dataset.GetMaxDataValue();
int numValues = maxValue - minValue + 1; float valueRange = maxValue - minValue;
float valRangeRecip = 1.0f / (maxValue - minValue); int numFrequencies = Mathf.Min((int)valueRange, 1024);
int[] frequencies = new int[numFrequencies];
int numSamples = System.Math.Min(numValues, 1024);
int[] values = new int[numSamples];
Color[] cols = new Color[numSamples];
Texture2D texture = new Texture2D(numSamples, 1, TextureFormat.RGBAFloat, false);
int maxFreq = 0; int maxFreq = 0;
float valRangeRecip = 1.0f / (maxValue - minValue);
for (int iData = 0; iData < dataset.data.Length; iData++) for (int iData = 0; iData < dataset.data.Length; iData++)
{ {
int dataValue = dataset.data[iData]; float dataValue = dataset.data[iData];
float tValue = (dataValue - minValue) * valRangeRecip; float tValue = (dataValue - minValue) * valRangeRecip;
int valueIndex = Mathf.RoundToInt((numSamples - 1) * tValue); int freqIndex = (int)(tValue * (numFrequencies - 1));
values[valueIndex] += 1; frequencies[freqIndex] += 1;
maxFreq = System.Math.Max(values[valueIndex], maxFreq); maxFreq = System.Math.Max(frequencies[freqIndex], maxFreq);
} }
for (int iSample = 0; iSample < numSamples; iSample++) Color[] cols = new Color[numFrequencies];
cols[iSample] = new Color(Mathf.Log10((float)values[iSample]) / Mathf.Log10((float)maxFreq), 0.0f, 0.0f, 1.0f); Texture2D texture = new Texture2D(numFrequencies, 1, TextureFormat.RGBAFloat, false);
for (int iSample = 0; iSample < numFrequencies; iSample++)
cols[iSample] = new Color(Mathf.Log10((float)frequencies[iSample]) / Mathf.Log10((float)maxFreq), 0.0f, 0.0f, 1.0f);
texture.SetPixels(cols); texture.SetPixels(cols);
//texture.filterMode = FilterMode.Point; //texture.filterMode = FilterMode.Point;
@ -48,7 +48,6 @@ namespace UnityVolumeRendering
return texture; return texture;
} }
/// <summary> /// <summary>
/// Generates a histogram (but computaion is done on GPU) where: /// Generates a histogram (but computaion is done on GPU) where:
/// X-axis = the data sample (density) value /// X-axis = the data sample (density) value
@ -58,7 +57,8 @@ namespace UnityVolumeRendering
/// <returns></returns> /// <returns></returns>
public static Texture2D GenerateHistogramTextureOnGPU(VolumeDataset dataset) public static Texture2D GenerateHistogramTextureOnGPU(VolumeDataset dataset)
{ {
int numValues = dataset.GetMaxDataValue() - dataset.GetMinDataValue() + 1; double actualBound = dataset.GetMaxDataValue() - dataset.GetMinDataValue() + 1;
int numValues = System.Convert.ToInt32(dataset.GetMaxDataValue() - dataset.GetMinDataValue() + 1); // removed +1
int sampleCount = System.Math.Min(numValues, 256); int sampleCount = System.Math.Min(numValues, 256);
ComputeShader computeHistogram = Resources.Load("ComputeHistogram") as ComputeShader; ComputeShader computeHistogram = Resources.Load("ComputeHistogram") as ComputeShader;
@ -67,7 +67,7 @@ namespace UnityVolumeRendering
ComputeBuffer histogramBuffer = new ComputeBuffer(sampleCount, sizeof(uint) * 1); ComputeBuffer histogramBuffer = new ComputeBuffer(sampleCount, sizeof(uint) * 1);
uint[] histogramData = new uint[sampleCount]; uint[] histogramData = new uint[sampleCount];
Color32 [] histogramCols = new Color32[sampleCount]; Color32[] histogramCols = new Color32[sampleCount];
Texture3D dataTexture = dataset.GetDataTexture(); Texture3D dataTexture = dataset.GetDataTexture();
@ -110,14 +110,14 @@ namespace UnityVolumeRendering
/// <returns></returns> /// <returns></returns>
public static Texture2D Generate2DHistogramTexture(VolumeDataset dataset) public static Texture2D Generate2DHistogramTexture(VolumeDataset dataset)
{ {
int minValue = dataset.GetMinDataValue(); float minValue = dataset.GetMinDataValue();
int maxValue = dataset.GetMaxDataValue(); float maxValue = dataset.GetMaxDataValue();
// Value range of the density values. // Value range of the density values.
int densityValRange = maxValue - minValue + 1; float densityValRange = maxValue - minValue + 1.0f;
float densityRangeRecip = 1.0f / (maxValue - minValue); // reciprocal float densityRangeRecip = 1.0f / (maxValue - minValue); // reciprocal
// Clamp density value samples. // Clamp density value samples.
int numDensitySamples = System.Math.Min(densityValRange, 512); int numDensitySamples = System.Math.Min((int)densityValRange, 512);
int numGradientSamples = 256; int numGradientSamples = 256;
Color[] cols = new Color[numDensitySamples * numGradientSamples]; Color[] cols = new Color[numDensitySamples * numGradientSamples];
@ -127,7 +127,7 @@ namespace UnityVolumeRendering
for (int iCol = 0; iCol < cols.Length; iCol++) for (int iCol = 0; iCol < cols.Length; iCol++)
cols[iCol] = new Color(0.0f, 0.0f, 0.0f, 0.0f); cols[iCol] = new Color(0.0f, 0.0f, 0.0f, 0.0f);
int maxRange = dataset.GetMaxDataValue() - dataset.GetMinDataValue(); float maxRange = dataset.GetMaxDataValue() - dataset.GetMinDataValue();
const float maxNormalisedMagnitude = 1.75f; // sqrt(1^2 + 1^2 + 1^2) = swrt(3) = a bit less than 1.75 const float maxNormalisedMagnitude = 1.75f; // sqrt(1^2 + 1^2 + 1^2) = swrt(3) = a bit less than 1.75
for (int x = 1; x < dataset.dimX - 1; x++) for (int x = 1; x < dataset.dimX - 1; x++)
@ -137,14 +137,14 @@ namespace UnityVolumeRendering
for (int z = 1; z < dataset.dimZ - 1; z++) for (int z = 1; z < dataset.dimZ - 1; z++)
{ {
int iData = x + y * dataset.dimX + z * (dataset.dimX * dataset.dimY); int iData = x + y * dataset.dimX + z * (dataset.dimX * dataset.dimY);
int density = dataset.data[iData]; int density = Mathf.RoundToInt(dataset.data[iData]); // FIXME
int x1 = dataset.data[(x + 1) + y * dataset.dimX + z * (dataset.dimX * dataset.dimY)]; float x1 = dataset.data[(x + 1) + y * dataset.dimX + z * (dataset.dimX * dataset.dimY)];
int x2 = dataset.data[(x - 1) + y * dataset.dimX + z * (dataset.dimX * dataset.dimY)]; float x2 = dataset.data[(x - 1) + y * dataset.dimX + z * (dataset.dimX * dataset.dimY)];
int y1 = dataset.data[x + (y + 1) * dataset.dimX + z * (dataset.dimX * dataset.dimY)]; float y1 = dataset.data[x + (y + 1) * dataset.dimX + z * (dataset.dimX * dataset.dimY)];
int y2 = dataset.data[x + (y - 1) * dataset.dimX + z * (dataset.dimX * dataset.dimY)]; float y2 = dataset.data[x + (y - 1) * dataset.dimX + z * (dataset.dimX * dataset.dimY)];
int z1 = dataset.data[x + y * dataset.dimX + (z + 1) * (dataset.dimX * dataset.dimY)]; float z1 = dataset.data[x + y * dataset.dimX + (z + 1) * (dataset.dimX * dataset.dimY)];
int z2 = dataset.data[x + y * dataset.dimX + (z - 1) * (dataset.dimX * dataset.dimY)]; float z2 = dataset.data[x + y * dataset.dimX + (z - 1) * (dataset.dimX * dataset.dimY)];
// Calculate gradient // Calculate gradient
Vector3 grad = new Vector3((x2 - x1) / (float)maxRange, (y2 - y1) / (float)maxRange, (z2 - z1) / (float)maxRange); Vector3 grad = new Vector3((x2 - x1) / (float)maxRange, (y2 - y1) / (float)maxRange, (z2 - z1) / (float)maxRange);

Просмотреть файл

@ -1,4 +1,5 @@
using System; using System;
using System.IO;
using UnityEngine; using UnityEngine;
namespace UnityVolumeRendering namespace UnityVolumeRendering
@ -9,52 +10,51 @@ namespace UnityVolumeRendering
[Serializable] [Serializable]
public class VolumeDataset : ScriptableObject public class VolumeDataset : ScriptableObject
{ {
public string filePath;
// Flattened 3D array of data sample values. // Flattened 3D array of data sample values.
[SerializeField] [SerializeField]
public int[] data = null; public float[] data;
[SerializeField] [SerializeField]
public int dimX, dimY, dimZ; public int dimX, dimY, dimZ;
[SerializeField] [SerializeField]
public float scaleX = 0.0f, scaleY = 0.0f, scaleZ = 0.0f; public float scaleX = 0.0f, scaleY = 0.0f, scaleZ = 0.0f;
public float volumeScale;
[SerializeField] [SerializeField]
public string datasetName; public string datasetName;
private int minDataValue = int.MaxValue; private float minDataValue = float.MaxValue;
private int maxDataValue = int.MinValue; private float maxDataValue = float.MinValue;
private Texture3D dataTexture = null; private Texture3D dataTexture = null;
private Texture3D gradientTexture = null; private Texture3D gradientTexture = null;
public Texture3D GetDataTexture() public Texture3D GetDataTexture()
{ {
if (dataTexture == null) dataTexture = CreateTextureInternal();
{
dataTexture = CreateTextureInternal();
}
return dataTexture; return dataTexture;
} }
public Texture3D GetGradientTexture() public Texture3D GetGradientTexture()
{ {
if (gradientTexture == null) gradientTexture = CreateGradientTextureInternal();
{
gradientTexture = CreateGradientTextureInternal();
}
return gradientTexture; return gradientTexture;
} }
public int GetMinDataValue() public float GetMinDataValue()
{ {
if (minDataValue == int.MaxValue) if (minDataValue == float.MaxValue)
CalculateValueBounds(); CalculateValueBounds();
return minDataValue; return minDataValue;
} }
public int GetMaxDataValue() public float GetMaxDataValue()
{ {
if (maxDataValue == int.MinValue) if (maxDataValue == float.MinValue)
CalculateValueBounds(); CalculateValueBounds();
return maxDataValue; return maxDataValue;
} }
@ -82,7 +82,7 @@ namespace UnityVolumeRendering
int halfDimX = dimX / 2 + dimX % 2; int halfDimX = dimX / 2 + dimX % 2;
int halfDimY = dimY / 2 + dimY % 2; int halfDimY = dimY / 2 + dimY % 2;
int halfDimZ = dimZ / 2 + dimZ % 2; int halfDimZ = dimZ / 2 + dimZ % 2;
int[] downScaledData = new int[halfDimX * halfDimY * halfDimZ]; float[] downScaledData = new float[halfDimX * halfDimY * halfDimZ];
for (int x = 0; x < halfDimX; x++) for (int x = 0; x < halfDimX; x++)
{ {
@ -90,7 +90,7 @@ namespace UnityVolumeRendering
{ {
for (int z = 0; z < halfDimZ; z++) for (int z = 0; z < halfDimZ; z++)
{ {
downScaledData[x + y * halfDimX + z * (halfDimX * halfDimY)] = Mathf.RoundToInt(GetAvgerageVoxelValues(x * 2, y * 2, z * 2)); downScaledData[x + y * halfDimX + z * (halfDimX * halfDimY)] = Mathf.Round(GetAvgerageVoxelValues(x * 2, y * 2, z * 2));
} }
} }
} }
@ -104,14 +104,17 @@ namespace UnityVolumeRendering
private void CalculateValueBounds() private void CalculateValueBounds()
{ {
minDataValue = int.MaxValue; minDataValue = float.MaxValue;
maxDataValue = int.MinValue; maxDataValue = float.MinValue;
int dim = dimX * dimY * dimZ;
for (int i = 0; i < dim; i++) if (data != null)
{ {
int val = data[i]; for (int i = 0; i < dimX * dimY * dimZ; i++)
minDataValue = Math.Min(minDataValue, val); {
maxDataValue = Math.Max(maxDataValue, val); float val = data[i];
minDataValue = Mathf.Min(minDataValue, val);
maxDataValue = Mathf.Max(maxDataValue, val);
}
} }
} }
@ -121,9 +124,9 @@ namespace UnityVolumeRendering
Texture3D texture = new Texture3D(dimX, dimY, dimZ, texformat, false); Texture3D texture = new Texture3D(dimX, dimY, dimZ, texformat, false);
texture.wrapMode = TextureWrapMode.Clamp; texture.wrapMode = TextureWrapMode.Clamp;
int minValue = GetMinDataValue(); float minValue = GetMinDataValue();
int maxValue = GetMaxDataValue(); float maxValue = GetMaxDataValue();
int maxRange = maxValue - minValue; float maxRange = maxValue - minValue;
bool isHalfFloat = texformat == TextureFormat.RHalf; bool isHalfFloat = texformat == TextureFormat.RHalf;
try try
@ -141,6 +144,7 @@ namespace UnityVolumeRendering
texture.SetPixelData(bytes, 0); texture.SetPixelData(bytes, 0);
} }
catch (OutOfMemoryException ex) catch (OutOfMemoryException ex)
{ {
Debug.LogWarning("Out of memory when creating texture. Using fallback method."); Debug.LogWarning("Out of memory when creating texture. Using fallback method.");
@ -149,7 +153,6 @@ namespace UnityVolumeRendering
for (int z = 0; z < dimZ; z++) for (int z = 0; z < dimZ; z++)
texture.SetPixel(x, y, z, new Color((float)(data[x + y * dimX + z * (dimX * dimY)] - minValue) / maxRange, 0.0f, 0.0f, 0.0f)); texture.SetPixel(x, y, z, new Color((float)(data[x + y * dimX + z * (dimX * dimY)] - minValue) / maxRange, 0.0f, 0.0f, 0.0f));
} }
texture.Apply(); texture.Apply();
return texture; return texture;
} }
@ -160,9 +163,9 @@ namespace UnityVolumeRendering
Texture3D texture = new Texture3D(dimX, dimY, dimZ, texformat, false); Texture3D texture = new Texture3D(dimX, dimY, dimZ, texformat, false);
texture.wrapMode = TextureWrapMode.Clamp; texture.wrapMode = TextureWrapMode.Clamp;
int minValue = GetMinDataValue(); float minValue = GetMinDataValue();
int maxValue = GetMaxDataValue(); float maxValue = GetMaxDataValue();
int maxRange = maxValue - minValue; float maxRange = maxValue - minValue;
Color[] cols; Color[] cols;
try try
@ -181,14 +184,14 @@ namespace UnityVolumeRendering
{ {
int iData = x + y * dimX + z * (dimX * dimY); int iData = x + y * dimX + z * (dimX * dimY);
int x1 = data[Math.Min(x + 1, dimX - 1) + y * dimX + z * (dimX * dimY)] - minValue; float x1 = data[Math.Min(x + 1, dimX - 1) + y * dimX + z * (dimX * dimY)] - minValue;
int x2 = data[Math.Max(x - 1, 0) + y * dimX + z * (dimX * dimY)] - minValue; float x2 = data[Math.Max(x - 1, 0) + y * dimX + z * (dimX * dimY)] - minValue;
int y1 = data[x + Math.Min(y + 1, dimY - 1) * dimX + z * (dimX * dimY)] - minValue; float y1 = data[x + Math.Min(y + 1, dimY - 1) * dimX + z * (dimX * dimY)] - minValue;
int y2 = data[x + Math.Max(y - 1, 0) * dimX + z * (dimX * dimY)] - minValue; float y2 = data[x + Math.Max(y - 1, 0) * dimX + z * (dimX * dimY)] - minValue;
int z1 = data[x + y * dimX + Math.Min(z + 1, dimZ - 1) * (dimX * dimY)] - minValue; float z1 = data[x + y * dimX + Math.Min(z + 1, dimZ - 1) * (dimX * dimY)] - minValue;
int z2 = data[x + y * dimX + Math.Max(z - 1, 0) * (dimX * dimY)] - minValue; float z2 = data[x + y * dimX + Math.Max(z - 1, 0) * (dimX * dimY)] - minValue;
Vector3 grad = new Vector3((x2 - x1) / (float)maxRange, (y2 - y1) / (float)maxRange, (z2 - z1) / (float)maxRange); Vector3 grad = new Vector3((x2 - x1) / maxRange, (y2 - y1) / maxRange, (z2 - z1) / maxRange);
if (cols == null) if (cols == null)
{ {
@ -228,7 +231,7 @@ namespace UnityVolumeRendering
+ GetData(x, y, z + 1) + GetData(x, y + 1, z + 1) + GetData(x + 1, y, z + 1) + GetData(x + 1, y + 1, z + 1)) / 8.0f; + GetData(x, y, z + 1) + GetData(x, y + 1, z + 1) + GetData(x + 1, y, z + 1) + GetData(x + 1, y + 1, z + 1)) / 8.0f;
} }
public int GetData(int x, int y, int z) public float GetData(int x, int y, int z)
{ {
return data[x + y * dimX + z * (dimX * dimY)]; return data[x + y * dimX + z * (dimX * dimY)];
} }

Просмотреть файл

@ -5,5 +5,6 @@ If you run into any issues, don't hesitate to report it to me (through the "Issu
- Create a fork (click the "Fork"-button in the upper right corner) - Create a fork (click the "Fork"-button in the upper right corner)
- Create a feature branch (`git checkout -b branch_name`) - Create a feature branch (`git checkout -b branch_name`)
- Implement your changes - Implement your changes
- (optionally) add your name to [CREDITS.md](CREDITS.md)
- Push - Push
- Create a pull request from the fork: https://help.github.com/en/articles/creating-a-pull-request-from-a-fork - Create a pull request from the fork: https://help.github.com/en/articles/creating-a-pull-request-from-a-fork

16
CREDITS.md Normal file
Просмотреть файл

@ -0,0 +1,16 @@
Contributors:
- [Matias Lavik](https://github.com/mlavik1)
Original project.
- [jasonks2](https://github.com/jasonks2):
Implemented support for PARCHG (.vasp) datasets.
- [denistribouillois](https://github.com/denistribouillois)
Slicing plane improvements. Histogram GPU calculation.
- [Michael Ovens](https://github.com/MichaelOvens)
Image sequence import. Other contributions.
- [Chiara Di Vece](https://github.com/chiaradivece)
GUI for modifying slicing plane positiomn/orientation.
- [btsai-dev](https://github.com/btsai-dev)
Memory leak fix
Feel free to add yourself to this list when contributing to this project.

Просмотреть файл

@ -0,0 +1 @@
This dataset has no license associated with it, but permission to use it has been granted by University of Illinois, Urbana-Champaign. Reference person: https://github.com/jasonks2

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,22 @@
{
"dependencies": {
"com.unity.modules.imageconversion": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.imgui": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
},
"com.unity.modules.jsonserialize": {
"version": "1.0.0",
"depth": 0,
"source": "builtin",
"dependencies": {}
}
}
}

Просмотреть файл

@ -106,17 +106,23 @@ The importer takes the following parameters:
- skipBytes: Number of bytes to skip (offset to where the data begins). This is usually the same as the header size, and will be 0 if there is no header. - skipBytes: Number of bytes to skip (offset to where the data begins). This is usually the same as the header size, and will be 0 if there is no header.
All this info can be added to a ".ini"-file, which the importer will use (if it finds any). See the sample files (in the "DataFiles" folder for an example). All this info can be added to a ".ini"-file, which the importer will use (if it finds any). See the sample files (in the "DataFiles" folder for an example).
# Todo: # Todo:
- Improve 2D Transfer Function editor: Better GUI, more shapes (triangles) - Improve 2D Transfer Function editor: Better GUI, more shapes (triangles)
- Optimise histogram generation - Optimise histogram generation
- Support very large datasets (currently we naively try to create 3D textures with the same dimension as the data) - Support very large datasets (currently we naively try to create 3D textures with the same dimension as the data)
![alt tag](https://github.com/mlavik1/UnityVolumeRendering/blob/master/Screenshots/slices.gif) ![alt tag](Screenshots/slices.gif)
![alt tag](https://github.com/mlavik1/UnityVolumeRendering/blob/master/Screenshots/1.png) ![alt tag](Screenshots/1.png)
![alt tag](https://github.com/mlavik1/UnityVolumeRendering/blob/master/Screenshots/2.png) ![alt tag](Screenshots/2.png)
![alt tag](https://github.com/mlavik1/UnityVolumeRendering/blob/master/Screenshots/3.png) ![alt tag](Screenshots/4.png)
![alt tag](https://github.com/mlavik1/UnityVolumeRendering/blob/master/Screenshots/4.png) ![alt tag](Screenshots/5.png)
![alt tag](https://github.com/mlavik1/UnityVolumeRendering/blob/master/Screenshots/5.png) ![alt tag](Screenshots/6.png)
![alt tag](https://github.com/mlavik1/UnityVolumeRendering/blob/master/Screenshots/6.png) ![alt tag](Screenshots/regions.png)
# Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md) for how to contribute.
Thanks to [everyone who have contributed so far](CREDITS.md).
See ACKNOWLEDGEMENTS.txt for libraries used by this project. See ACKNOWLEDGEMENTS.txt for libraries used by this project.

Двоичные данные
Screenshots/bottom.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 240 KiB

Двоичные данные
Screenshots/dist.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 562 KiB

Двоичные данные
Screenshots/last.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 331 KiB

Двоичные данные
Screenshots/regions.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 775 KiB

Двоичные данные
Screenshots/side.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 591 KiB

Двоичные данные
Screenshots/top.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 396 KiB

Двоичные данные
Screenshots/upper.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.0 MiB

Двоичные данные
Screenshots/vert.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 512 KiB