Runtime Transfer Function editor (#115)
Created a runtime 1D transfer function editor Created a runtime colour picker. Bugfix: You can now open ".ini" files directly. Bugfix: Loading/clearing transfer function will now automatically refresh the volume rendered object. Misc. improvements to runtime file dialog. Packaging script: Read path from argv or raw_input
This commit is contained in:
Родитель
498e5f52b5
Коммит
a7203b765e
|
@ -24,7 +24,7 @@ namespace UnityVolumeRendering
|
|||
fileToImport = filePath;
|
||||
|
||||
if (Path.GetExtension(fileToImport) == ".ini")
|
||||
fileToImport = fileToImport.Replace(".ini", ".raw");
|
||||
fileToImport = fileToImport.Substring(0, fileToImport.Length - 4);
|
||||
|
||||
// Try parse ini file (if available)
|
||||
DatasetIniData initData = DatasetIniReader.ParseIniFile(fileToImport + ".ini");
|
||||
|
@ -43,18 +43,18 @@ namespace UnityVolumeRendering
|
|||
|
||||
private void ImportDataset()
|
||||
{
|
||||
RawDatasetImporter importer = new RawDatasetImporter(fileToImport, dimX, dimY, dimZ, dataFormat, endianness, bytesToSkip);
|
||||
RawDatasetImporter importer = new RawDatasetImporter(fileToImport, dimX, dimY, dimZ, dataFormat, endianness, bytesToSkip);
|
||||
VolumeDataset dataset = importer.Import();
|
||||
|
||||
if (dataset != null)
|
||||
{
|
||||
if (EditorPrefs.GetBool("DownscaleDatasetPrompt"))
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Optional DownScaling",
|
||||
$"Do you want to downscale the dataset? The dataset's dimension is: {dataset.dimX} x {dataset.dimY} x {dataset.dimZ}", "Yes", "No"))
|
||||
{
|
||||
dataset.DownScaleData();
|
||||
}
|
||||
if (EditorPrefs.GetBool("DownscaleDatasetPrompt"))
|
||||
{
|
||||
if (EditorUtility.DisplayDialog("Optional DownScaling",
|
||||
$"Do you want to downscale the dataset? The dataset's dimension is: {dataset.dimX} x {dataset.dimY} x {dataset.dimZ}", "Yes", "No"))
|
||||
{
|
||||
dataset.DownScaleData();
|
||||
}
|
||||
}
|
||||
VolumeRenderedObject obj = VolumeObjectFactory.CreateObject(dataset);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace UnityVolumeRendering
|
|||
{
|
||||
public class SelectionHelper
|
||||
{
|
||||
public static VolumeRenderedObject GetSelectedVolumeObject()
|
||||
public static VolumeRenderedObject GetSelectedVolumeObject(bool autoSelectFirst = false)
|
||||
{
|
||||
foreach (GameObject obj in Selection.gameObjects)
|
||||
{
|
||||
|
@ -13,6 +13,14 @@ namespace UnityVolumeRendering
|
|||
if (volrendobj != null)
|
||||
return volrendobj;
|
||||
}
|
||||
|
||||
VolumeRenderedObject volRendObject = GameObject.FindObjectOfType<VolumeRenderedObject>();
|
||||
if (volRendObject != null)
|
||||
{
|
||||
Selection.objects = new Object[] { volRendObject.gameObject };
|
||||
return volRendObject;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,18 +7,11 @@ namespace UnityVolumeRendering
|
|||
{
|
||||
private TransferFunction tf = null;
|
||||
|
||||
private int movingColPointIndex = -1;
|
||||
private int movingAlphaPointIndex = -1;
|
||||
|
||||
private int selectedColPointIndex = -1;
|
||||
|
||||
private VolumeRenderedObject volRendObject = null;
|
||||
private Texture2D histTex = null;
|
||||
|
||||
private Material tfGUIMat = null;
|
||||
private Material tfPaletteGUIMat = null;
|
||||
private TransferFunctionEditor tfEditor = new TransferFunctionEditor();
|
||||
|
||||
public static void ShowWindow()
|
||||
public static void ShowWindow(VolumeRenderedObject volRendObj)
|
||||
{
|
||||
// Close all (if any) 2D TF editor windows
|
||||
TransferFunction2DEditorWindow[] tf2dWnds = Resources.FindObjectsOfTypeAll<TransferFunction2DEditorWindow>();
|
||||
|
@ -26,6 +19,8 @@ namespace UnityVolumeRendering
|
|||
tf2dWnd.Close();
|
||||
|
||||
TransferFunctionEditorWindow wnd = (TransferFunctionEditorWindow)EditorWindow.GetWindow(typeof(TransferFunctionEditorWindow));
|
||||
if (volRendObj)
|
||||
wnd.volRendObject = volRendObj;
|
||||
wnd.Show();
|
||||
wnd.SetInitialPosition();
|
||||
}
|
||||
|
@ -40,19 +35,7 @@ namespace UnityVolumeRendering
|
|||
|
||||
private void OnEnable()
|
||||
{
|
||||
tfGUIMat = Resources.Load<Material>("TransferFunctionGUIMat");
|
||||
tfPaletteGUIMat = Resources.Load<Material>("TransferFunctionPaletteGUIMat");
|
||||
|
||||
volRendObject = SelectionHelper.GetSelectedVolumeObject();
|
||||
if (volRendObject == null)
|
||||
{
|
||||
volRendObject = GameObject.FindObjectOfType<VolumeRenderedObject>();
|
||||
if (volRendObject != null)
|
||||
Selection.objects = new Object[] { volRendObject.gameObject };
|
||||
}
|
||||
|
||||
if(volRendObject != null)
|
||||
volRendObject.SetTransferFunctionMode(TFRenderMode.TF1D);
|
||||
tfEditor.Initialise();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
|
@ -63,6 +46,7 @@ namespace UnityVolumeRendering
|
|||
|
||||
if (volRendObject == null)
|
||||
return;
|
||||
|
||||
tf = volRendObject.transferFunction;
|
||||
|
||||
Event currentEvent = new Event(Event.current);
|
||||
|
@ -73,143 +57,14 @@ namespace UnityVolumeRendering
|
|||
float contentHeight = contentWidth * 0.5f;
|
||||
|
||||
// Interaction area (slightly larger than the histogram rect)
|
||||
Rect interactRect = new Rect(0.0f, 0.0f, contentWidth, contentHeight);
|
||||
// Histogram rect (histogram view and alpha control points)
|
||||
Rect histRect = new Rect(interactRect.x + 20.0f, interactRect.y + 20.0f, interactRect.width - 40.0f, interactRect.height - 40.0f);
|
||||
// Colour palette rect (colour control points)
|
||||
Rect paletteRect = new Rect(histRect.x, histRect.y + histRect.height + 20, histRect.width, 20.0f);
|
||||
Rect outerRect = new Rect(0.0f, 0.0f, contentWidth, contentHeight);
|
||||
Rect tfEditorRect = new Rect(outerRect.x + 20.0f, outerRect.y + 20.0f, outerRect.width - 40.0f, outerRect.height - 50.0f);
|
||||
|
||||
// TODO: Don't do this every frame
|
||||
tf.GenerateTexture();
|
||||
|
||||
// Create histogram texture
|
||||
if(histTex == null)
|
||||
{
|
||||
if(SystemInfo.supportsComputeShaders)
|
||||
histTex = HistogramTextureGenerator.GenerateHistogramTextureOnGPU(volRendObject.dataset);
|
||||
else
|
||||
histTex = HistogramTextureGenerator.GenerateHistogramTexture(volRendObject.dataset);
|
||||
}
|
||||
|
||||
// Draw histogram
|
||||
tfGUIMat.SetTexture("_TFTex", tf.GetTexture());
|
||||
tfGUIMat.SetTexture("_HistTex", histTex);
|
||||
Graphics.DrawTexture(histRect, tf.GetTexture(), tfGUIMat);
|
||||
|
||||
// Draw colour palette
|
||||
Texture2D tfTexture = tf.GetTexture();
|
||||
tfPaletteGUIMat.SetTexture("_TFTex", tf.GetTexture());
|
||||
Graphics.DrawTexture(new Rect(paletteRect.x, paletteRect.y, paletteRect.width, paletteRect.height), tfTexture, tfPaletteGUIMat);
|
||||
|
||||
// Release selected colour/alpha points if mouse leaves window
|
||||
if (movingAlphaPointIndex != -1 && !interactRect.Contains(currentEvent.mousePosition))
|
||||
movingAlphaPointIndex = -1;
|
||||
if (movingColPointIndex != -1 && !(currentEvent.mousePosition.x >= paletteRect.x && currentEvent.mousePosition.x <= paletteRect.x + paletteRect.width))
|
||||
movingColPointIndex = -1;
|
||||
|
||||
// Mouse down => Move or remove selected colour control point
|
||||
if (currentEvent.type == EventType.MouseDown && paletteRect.Contains(currentEvent.mousePosition))
|
||||
{
|
||||
float mousePos = (currentEvent.mousePosition.x - paletteRect.x) / paletteRect.width;
|
||||
int pointIndex = PickColourControlPoint(mousePos);
|
||||
if (pointIndex != -1)
|
||||
{
|
||||
// Add control point
|
||||
if(currentEvent.button == 0 && !currentEvent.control)
|
||||
{
|
||||
movingColPointIndex = selectedColPointIndex = pointIndex;
|
||||
}
|
||||
// Remove control point
|
||||
else if(currentEvent.button == 1 && currentEvent.control)
|
||||
{
|
||||
tf.colourControlPoints.RemoveAt(pointIndex);
|
||||
currentEvent.type = EventType.Ignore;
|
||||
movingColPointIndex = selectedColPointIndex = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (currentEvent.type == EventType.MouseUp)
|
||||
movingColPointIndex = -1;
|
||||
|
||||
// Mouse down => Move or remove selected alpha control point
|
||||
if (currentEvent.type == EventType.MouseDown)
|
||||
{
|
||||
Vector2 mousePos = new Vector2((currentEvent.mousePosition.x - histRect.x) / histRect.width, 1.0f - (currentEvent.mousePosition.y - histRect.y) / histRect.height);
|
||||
int pointIndex = PickAlphaControlPoint(mousePos);
|
||||
if (pointIndex != -1)
|
||||
{
|
||||
// Add control point
|
||||
if(currentEvent.button == 0 && !currentEvent.control)
|
||||
{
|
||||
movingAlphaPointIndex = pointIndex;
|
||||
}
|
||||
// Remove control point
|
||||
else if(currentEvent.button == 1 && currentEvent.control)
|
||||
{
|
||||
tf.alphaControlPoints.RemoveAt(pointIndex);
|
||||
currentEvent.type = EventType.Ignore;
|
||||
selectedColPointIndex = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move selected alpha control point
|
||||
if (movingAlphaPointIndex != -1)
|
||||
{
|
||||
TFAlphaControlPoint alphaPoint = tf.alphaControlPoints[movingAlphaPointIndex];
|
||||
alphaPoint.dataValue = Mathf.Clamp((currentEvent.mousePosition.x - histRect.x) / histRect.width, 0.0f, 1.0f);
|
||||
alphaPoint.alphaValue = Mathf.Clamp(1.0f - (currentEvent.mousePosition.y - histRect.y) / histRect.height, 0.0f, 1.0f);
|
||||
tf.alphaControlPoints[movingAlphaPointIndex] = alphaPoint;
|
||||
}
|
||||
|
||||
// Move selected colour control point
|
||||
if (movingColPointIndex != -1)
|
||||
{
|
||||
TFColourControlPoint colPoint = tf.colourControlPoints[movingColPointIndex];
|
||||
colPoint.dataValue = Mathf.Clamp((currentEvent.mousePosition.x - paletteRect.x) / paletteRect.width, 0.0f, 1.0f);
|
||||
tf.colourControlPoints[movingColPointIndex] = colPoint;
|
||||
}
|
||||
|
||||
// Draw colour control points
|
||||
for (int iCol = 0; iCol < tf.colourControlPoints.Count; iCol++)
|
||||
{
|
||||
TFColourControlPoint colPoint = tf.colourControlPoints[iCol];
|
||||
Rect ctrlBox = new Rect(histRect.x + histRect.width * colPoint.dataValue, histRect.y + histRect.height + 20, 10, 20);
|
||||
GUI.color = Color.red;
|
||||
GUI.skin.box.fontSize = 6;
|
||||
GUI.Box(ctrlBox, "*");
|
||||
}
|
||||
|
||||
// Draw alpha control points
|
||||
for (int iAlpha = 0; iAlpha < tf.alphaControlPoints.Count; iAlpha++)
|
||||
{
|
||||
const int pointSize = 10;
|
||||
TFAlphaControlPoint alphaPoint = tf.alphaControlPoints[iAlpha];
|
||||
Rect ctrlBox = new Rect(histRect.x + histRect.width * alphaPoint.dataValue - pointSize / 2, histRect.y + (1.0f - alphaPoint.alphaValue) * histRect.height - pointSize / 2, pointSize, pointSize);
|
||||
GUI.color = Color.red;
|
||||
GUI.skin.box.fontSize = 6;
|
||||
GUI.Box(ctrlBox, "*");
|
||||
GUI.color = oldColour;
|
||||
}
|
||||
|
||||
if (currentEvent.type == EventType.MouseUp)
|
||||
{
|
||||
movingColPointIndex = -1;
|
||||
movingAlphaPointIndex = -1;
|
||||
}
|
||||
|
||||
// Add points
|
||||
if (currentEvent.type == EventType.MouseDown && currentEvent.button == 1)
|
||||
{
|
||||
if (histRect.Contains(new Vector2(currentEvent.mousePosition.x, currentEvent.mousePosition.y)))
|
||||
tf.alphaControlPoints.Add(new TFAlphaControlPoint(Mathf.Clamp((currentEvent.mousePosition.x - histRect.x) / histRect.width, 0.0f, 1.0f), Mathf.Clamp(1.0f - (currentEvent.mousePosition.y - histRect.y) / histRect.height, 0.0f, 1.0f)));
|
||||
else
|
||||
tf.colourControlPoints.Add(new TFColourControlPoint(Mathf.Clamp((currentEvent.mousePosition.x - histRect.x) / histRect.width, 0.0f, 1.0f), Random.ColorHSV()));
|
||||
selectedColPointIndex = -1;
|
||||
}
|
||||
tfEditor.SetVolumeObject(volRendObject);
|
||||
tfEditor.DrawOnGUI(tfEditorRect);
|
||||
|
||||
// Save TF
|
||||
if(GUI.Button(new Rect(histRect.x, histRect.y + histRect.height + 50.0f, 70.0f, 30.0f), "Save"))
|
||||
if(GUI.Button(new Rect(tfEditorRect.x, tfEditorRect.y + tfEditorRect.height + 20.0f, 70.0f, 30.0f), "Save"))
|
||||
{
|
||||
string filepath = EditorUtility.SaveFilePanel("Save transfer function", "", "default.tf", "tf");
|
||||
if(filepath != "")
|
||||
|
@ -217,83 +72,45 @@ namespace UnityVolumeRendering
|
|||
}
|
||||
|
||||
// Load TF
|
||||
if(GUI.Button(new Rect(histRect.x + 75.0f, histRect.y + histRect.height + 50.0f, 70.0f, 30.0f), "Load"))
|
||||
if(GUI.Button(new Rect(tfEditorRect.x + 75.0f, tfEditorRect.y + tfEditorRect.height + 20.0f, 70.0f, 30.0f), "Load"))
|
||||
{
|
||||
string filepath = EditorUtility.OpenFilePanel("Save transfer function", "", "tf");
|
||||
if(filepath != "")
|
||||
{
|
||||
TransferFunction newTF = TransferFunctionDatabase.LoadTransferFunction(filepath);
|
||||
if(newTF != null)
|
||||
volRendObject.transferFunction = tf = newTF;
|
||||
{
|
||||
tf = newTF;
|
||||
volRendObject.SetTransferFunction(tf);
|
||||
tfEditor.ClearSelection();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Clear TF
|
||||
if(GUI.Button(new Rect(histRect.x + 150.0f, histRect.y + histRect.height + 50.0f, 70.0f, 30.0f), "Clear"))
|
||||
if(GUI.Button(new Rect(tfEditorRect.x + 150.0f, tfEditorRect.y + tfEditorRect.height + 20.0f, 70.0f, 30.0f), "Clear"))
|
||||
{
|
||||
tf = volRendObject.transferFunction = new TransferFunction();
|
||||
tf = new TransferFunction();
|
||||
tf.alphaControlPoints.Add(new TFAlphaControlPoint(0.2f, 0.0f));
|
||||
tf.alphaControlPoints.Add(new TFAlphaControlPoint(0.8f, 1.0f));
|
||||
tf.colourControlPoints.Add(new TFColourControlPoint(0.5f, new Color(0.469f, 0.354f, 0.223f, 1.0f)));
|
||||
selectedColPointIndex = -1;
|
||||
volRendObject.SetTransferFunction(tf);
|
||||
tfEditor.ClearSelection();
|
||||
}
|
||||
|
||||
// Colour picker
|
||||
if (selectedColPointIndex != -1)
|
||||
Color? selectedColour = tfEditor.GetSelectedColour();
|
||||
if (selectedColour != null)
|
||||
{
|
||||
TFColourControlPoint colPoint = tf.colourControlPoints[selectedColPointIndex];
|
||||
colPoint.colourValue = EditorGUI.ColorField(new Rect(histRect.x + 225, histRect.y + histRect.height + 50, 100.0f, 40.0f), colPoint.colourValue);
|
||||
tf.colourControlPoints[selectedColPointIndex] = colPoint;
|
||||
Color newColour = EditorGUI.ColorField(new Rect(tfEditorRect.x + 225, tfEditorRect.y + tfEditorRect.height + 30, 100.0f, 40.0f), selectedColour.Value);
|
||||
tfEditor.SetSelectedColour(newColour);
|
||||
}
|
||||
|
||||
GUI.skin.label.wordWrap = false;
|
||||
GUI.Label(new Rect(histRect.x, histRect.y + histRect.height + 85.0f, 720.0f, 30.0f), "Left click to select and move a control point. Right click to add a control point, and ctrl + right click to delete.");
|
||||
GUI.Label(new Rect(tfEditorRect.x, tfEditorRect.y + tfEditorRect.height + 55.0f, 720.0f, 50.0f), "Left click to select and move a control point.\nRight click to add a control point, and ctrl + right click to delete.");
|
||||
|
||||
GUI.color = oldColour;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pick the colour control point, nearest to the specified position.
|
||||
/// </summary>
|
||||
/// <param name="maxDistance">Threshold for maximum distance. Points further away than this won't get picked.</param>
|
||||
private int PickColourControlPoint(float position, float maxDistance = 0.03f)
|
||||
{
|
||||
int nearestPointIndex = -1;
|
||||
float nearestDist = 1000.0f;
|
||||
for (int i = 0; i < tf.colourControlPoints.Count; i++)
|
||||
{
|
||||
TFColourControlPoint ctrlPoint = tf.colourControlPoints[i];
|
||||
float dist = Mathf.Abs(ctrlPoint.dataValue - position);
|
||||
if (dist < maxDistance && dist < nearestDist)
|
||||
{
|
||||
nearestPointIndex = i;
|
||||
nearestDist = dist;
|
||||
}
|
||||
}
|
||||
return nearestPointIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pick the alpha control point, nearest to the specified position.
|
||||
/// </summary>
|
||||
/// <param name="maxDistance">Threshold for maximum distance. Points further away than this won't get picked.</param>
|
||||
private int PickAlphaControlPoint(Vector2 position, float maxDistance = 0.05f)
|
||||
{
|
||||
int nearestPointIndex = -1;
|
||||
float nearestDist = 1000.0f;
|
||||
for (int i = 0; i < tf.alphaControlPoints.Count; i++)
|
||||
{
|
||||
TFAlphaControlPoint ctrlPoint = tf.alphaControlPoints[i];
|
||||
Vector2 ctrlPos = new Vector2(ctrlPoint.dataValue, ctrlPoint.alphaValue);
|
||||
float dist = (ctrlPos - position).magnitude;
|
||||
if (dist < maxDistance && dist < nearestDist)
|
||||
{
|
||||
nearestPointIndex = i;
|
||||
nearestDist = dist;
|
||||
}
|
||||
}
|
||||
return nearestPointIndex;
|
||||
}
|
||||
|
||||
private void OnSelectionChange()
|
||||
{
|
||||
VolumeRenderedObject newVolRendObj = Selection.activeGameObject?.GetComponent<VolumeRenderedObject>();
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace UnityVolumeRendering
|
|||
if (GUILayout.Button("Edit transfer function"))
|
||||
{
|
||||
if (tfMode == TFRenderMode.TF1D)
|
||||
TransferFunctionEditorWindow.ShowWindow();
|
||||
TransferFunctionEditorWindow.ShowWindow(volrendObj);
|
||||
else
|
||||
TransferFunction2DEditorWindow.ShowWindow();
|
||||
}
|
||||
|
|
|
@ -243,7 +243,16 @@ namespace UnityVolumeRendering
|
|||
[MenuItem("Volume Rendering/1D Transfer Function")]
|
||||
public static void Show1DTFWindow()
|
||||
{
|
||||
TransferFunctionEditorWindow.ShowWindow();
|
||||
VolumeRenderedObject volRendObj = SelectionHelper.GetSelectedVolumeObject();
|
||||
if (volRendObj != null)
|
||||
{
|
||||
volRendObj.SetTransferFunctionMode(TFRenderMode.TF1D);
|
||||
TransferFunctionEditorWindow.ShowWindow(volRendObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.DisplayDialog("No imported dataset", "You need to import a dataset first", "Ok");
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("Volume Rendering/2D Transfer Function")]
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityVolumeRendering
|
||||
{
|
||||
public class ColourPickerPopup : MonoBehaviour
|
||||
{
|
||||
public Action<Color> callback = null;
|
||||
private Vector3 selectedHSV = new Vector3(0.0f, 1.0f, 1.0f);
|
||||
private Rect windowRect = new Rect(150, 15, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||
private Rect colourBoxRect = new Rect(10, 30, 250, 250);
|
||||
private Rect valueSliderRect = new Rect(280, 30, 20, 250);
|
||||
private int windowID;
|
||||
private Texture2D texture;
|
||||
private Color[] gradientColours = { Color.red, Color.green, Color.blue, Color.red };
|
||||
|
||||
private const int WINDOW_WIDTH = 400;
|
||||
private const int WINDOW_HEIGHT = 400;
|
||||
private const int TEXTURE_WIDTH = 128;
|
||||
private const int TEXTURE_HEIGHT = 128;
|
||||
|
||||
private Vector2 selectedPosition = Vector2.zero;
|
||||
private bool movingPoint = false;
|
||||
|
||||
public Color GetColour()
|
||||
{
|
||||
return Color.HSVToRGB(selectedHSV.x, selectedHSV.y, selectedHSV.z);
|
||||
}
|
||||
|
||||
public void SetColour(Color col)
|
||||
{
|
||||
Color.RGBToHSV(col, out selectedHSV.x, out selectedHSV.y, out selectedHSV.z);
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// Fetch a unique ID for our window (see GUI.Window)
|
||||
windowID = WindowGUID.GetUniqueWindowID();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
windowRect = GUI.Window(windowID, windowRect, UpdateWindow, "Colour picker");
|
||||
}
|
||||
|
||||
private void UpdateWindow(int windowID)
|
||||
{
|
||||
GUI.DragWindow(new Rect(0, 0, 10000, 20));
|
||||
|
||||
GUI.DrawTexture(colourBoxRect, GetTexture());
|
||||
|
||||
Rect ctrlBox = new Rect(selectedPosition.x, selectedPosition.y , 10, 10);
|
||||
GUI.skin.box.fontSize = 6;
|
||||
GUI.Box(ctrlBox, "*");
|
||||
|
||||
if (GUI.Button(new Rect(WINDOW_WIDTH - 100, WINDOW_HEIGHT - 40, 90, 30), "Done"))
|
||||
{
|
||||
CloseBrowser();
|
||||
}
|
||||
|
||||
selectedHSV.z = GUI.VerticalSlider(valueSliderRect, selectedHSV.z, 0.0f, 1.0f);
|
||||
|
||||
Event currentEvent = Event.current;
|
||||
Vector2 mousePos = currentEvent.mousePosition;
|
||||
if (currentEvent.type == EventType.MouseDown && colourBoxRect.Contains(mousePos))
|
||||
movingPoint = true;
|
||||
else if (currentEvent.type == EventType.MouseUp)
|
||||
movingPoint = false;
|
||||
|
||||
if (movingPoint)
|
||||
{
|
||||
Vector2 unitPos = new Vector2((mousePos.x - colourBoxRect.x) / colourBoxRect.width, (mousePos.y - colourBoxRect.y) / colourBoxRect.height);
|
||||
unitPos = (unitPos - new Vector2(0.5f, 0.5f)) * new Vector2(2.0f, -2.0f);
|
||||
if (unitPos.magnitude <= 1.0f)
|
||||
{
|
||||
selectedPosition = mousePos;
|
||||
selectedHSV = GetHSVAtPoint(unitPos, selectedHSV.z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Vector3 GetHSVAtPoint(Vector2 point, float value)
|
||||
{
|
||||
Vector2 a = new Vector2(0.0f, 1.0f);
|
||||
Vector2 b = point;
|
||||
float signedAngle = Vector2.SignedAngle(a, b);
|
||||
float angle = (signedAngle < 0.0f ? signedAngle + 360.0f : signedAngle);
|
||||
float hue = angle / 360.0f;
|
||||
float saturation = point.magnitude;
|
||||
return new Vector3(hue, saturation, value);
|
||||
}
|
||||
|
||||
private Texture2D GetTexture()
|
||||
{
|
||||
if (texture == null)
|
||||
{
|
||||
int textureDimension = TEXTURE_WIDTH * TEXTURE_HEIGHT;
|
||||
Color[] colours = new Color[textureDimension];
|
||||
|
||||
for (int ix = 0; ix < TEXTURE_WIDTH; ix++)
|
||||
{
|
||||
for (int iy = 0; iy < TEXTURE_HEIGHT; iy++)
|
||||
{
|
||||
Vector2 unitPos = new Vector2((float)ix / TEXTURE_WIDTH, (float)iy / TEXTURE_HEIGHT);
|
||||
unitPos = (unitPos - new Vector2(0.5f, 0.5f)) * 2.0f;
|
||||
Color colour;
|
||||
if (unitPos.magnitude <= 1.0f)
|
||||
{
|
||||
Vector3 hsv = GetHSVAtPoint(unitPos, 1.0f);
|
||||
colour = Color.HSVToRGB(hsv.x, hsv.y, hsv.z);
|
||||
}
|
||||
else
|
||||
{
|
||||
colour = Color.clear;
|
||||
}
|
||||
colours[ix + iy * TEXTURE_WIDTH] = colour;
|
||||
}
|
||||
}
|
||||
|
||||
texture = new Texture2D(TEXTURE_WIDTH, TEXTURE_HEIGHT, TextureFormat.RGBAFloat, false);
|
||||
texture.wrapMode = TextureWrapMode.Clamp;
|
||||
texture.SetPixels(colours);
|
||||
texture.Apply();
|
||||
}
|
||||
return texture;
|
||||
}
|
||||
|
||||
private void CloseBrowser()
|
||||
{
|
||||
callback?.Invoke(GetColour());
|
||||
|
||||
GameObject.Destroy(this.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fb9bffc42ab3c9afcbe13a62b234e554
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -79,6 +79,12 @@ namespace UnityVolumeRendering
|
|||
rotation.z = GUILayout.HorizontalSlider(rotation.z, 0.0f, 360.0f);
|
||||
targetObject.transform.rotation = Quaternion.Euler(rotation);
|
||||
|
||||
// Edit transfer function
|
||||
if(GUILayout.Button("Edit transfer function", GUILayout.Width(150.0f)))
|
||||
{
|
||||
RuntimeTransferFunctionEditor.ShowWindow(targetObject);
|
||||
}
|
||||
|
||||
// Load transfer function
|
||||
if(GUILayout.Button("Load transfer function", GUILayout.Width(150.0f)))
|
||||
{
|
||||
|
|
|
@ -21,16 +21,17 @@ namespace UnityVolumeRendering
|
|||
public DialogMode dialogMode = DialogMode.OpenFile;
|
||||
public DialogCallback callback = null;
|
||||
|
||||
public string currentDirectory;
|
||||
private string selectedFile;
|
||||
public string currentDirectory = "";
|
||||
private string selectedFile = "";
|
||||
private string fileName = "";
|
||||
private Vector2 scrollPos = Vector2.zero;
|
||||
private Vector2 dirScrollPos = Vector2.zero;
|
||||
|
||||
private Rect windowRect = new Rect(100, 50, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||
|
||||
private const int LEFT_PANEL_WIDTH = 100;
|
||||
private const int RIGHT_PANEL_WIDTH = 370;
|
||||
private const int WINDOW_WIDTH = 500;
|
||||
private const int LEFT_PANEL_WIDTH = 150;
|
||||
private const int RIGHT_PANEL_WIDTH = 450;
|
||||
private const int WINDOW_WIDTH = 600;
|
||||
private const int WINDOW_HEIGHT = 300;
|
||||
|
||||
private int windowID;
|
||||
|
@ -99,14 +100,6 @@ namespace UnityVolumeRendering
|
|||
GUILayout.BeginVertical(GUILayout.Width(LEFT_PANEL_WIDTH));
|
||||
|
||||
dirScrollPos = GUILayout.BeginScrollView(dirScrollPos);
|
||||
foreach (DriveInfo driveInfo in DriveInfo.GetDrives())
|
||||
{
|
||||
if (GUILayout.Button(driveInfo.Name))
|
||||
{
|
||||
currentDirectory = driveInfo.Name;
|
||||
scrollPos = Vector2.zero;
|
||||
}
|
||||
}
|
||||
|
||||
if (GUILayout.Button("Documents"))
|
||||
{
|
||||
|
@ -118,6 +111,16 @@ namespace UnityVolumeRendering
|
|||
currentDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
|
||||
scrollPos = Vector2.zero;
|
||||
}
|
||||
|
||||
foreach (DriveInfo driveInfo in DriveInfo.GetDrives())
|
||||
{
|
||||
if (GUILayout.Button(driveInfo.Name))
|
||||
{
|
||||
currentDirectory = driveInfo.Name;
|
||||
scrollPos = Vector2.zero;
|
||||
}
|
||||
}
|
||||
|
||||
GUILayout.EndScrollView();
|
||||
|
||||
GUILayout.EndVertical();
|
||||
|
@ -149,6 +152,7 @@ namespace UnityVolumeRendering
|
|||
if (GUILayout.Button(fileInfo.Name))
|
||||
{
|
||||
selectedFile = fileInfo.FullName;
|
||||
fileName = Path.GetFileName(selectedFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -163,17 +167,16 @@ namespace UnityVolumeRendering
|
|||
|
||||
if(dialogMode == DialogMode.OpenFile || dialogMode == DialogMode.SaveFile)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(selectedFile))
|
||||
if (!string.IsNullOrEmpty(selectedFile) || dialogMode == DialogMode.SaveFile)
|
||||
{
|
||||
FileInfo fileInfo = new FileInfo(selectedFile);
|
||||
string fileName = Path.GetFileName(selectedFile);
|
||||
// Show filename textbox
|
||||
fileName = GUILayout.TextField(fileName, GUILayout.Width(RIGHT_PANEL_WIDTH));
|
||||
selectedFile = Path.Combine(fileInfo.Directory.FullName, fileName);
|
||||
if (fileName.Length > 0)
|
||||
selectedFile = Path.Combine(currentDirectory, fileName);
|
||||
GUILayout.FlexibleSpace();
|
||||
// Show button
|
||||
string buttonText = dialogMode == DialogMode.OpenFile ? "Open" : "Save";
|
||||
if (File.Exists(selectedFile) && GUILayout.Button(buttonText))
|
||||
if (!string.IsNullOrEmpty(fileName) && (File.Exists(selectedFile) || dialogMode == DialogMode.SaveFile) && GUILayout.Button(buttonText))
|
||||
{
|
||||
CloseBrowser(false, selectedFile);
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ namespace UnityVolumeRendering
|
|||
// Did the user try to import an .ini-file? Open the corresponding .raw file instead
|
||||
string filePath = result.path;
|
||||
if (System.IO.Path.GetExtension(filePath) == ".ini")
|
||||
filePath = filePath.Replace(".ini", ".raw");
|
||||
filePath = filePath.Substring(0, filePath.Length - 4);
|
||||
|
||||
// Parse .ini file
|
||||
DatasetIniData initData = DatasetIniReader.ParseIniFile(filePath + ".ini");
|
||||
|
|
|
@ -0,0 +1,181 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace UnityVolumeRendering
|
||||
{
|
||||
public class RuntimeTransferFunctionEditor : MonoBehaviour
|
||||
{
|
||||
private static RuntimeTransferFunctionEditor instance = null;
|
||||
private TransferFunction tf = null;
|
||||
private VolumeRenderedObject volRendObject = null;
|
||||
|
||||
private int windowID;
|
||||
private Rect windowRect = new Rect(150, 15, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||
|
||||
private TransferFunctionEditor tfEditor = new TransferFunctionEditor();
|
||||
|
||||
private const int WINDOW_WIDTH = 620;
|
||||
private const int WINDOW_HEIGHT = 400;
|
||||
|
||||
public static void ShowWindow(VolumeRenderedObject volRendObj)
|
||||
{
|
||||
if(instance != null)
|
||||
GameObject.Destroy(instance);
|
||||
|
||||
GameObject obj = new GameObject("RuntimeTransferFunctionEditor");
|
||||
instance = obj.AddComponent<RuntimeTransferFunctionEditor>();
|
||||
instance.volRendObject = volRendObj;
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// Fetch a unique ID for our window (see GUI.Window)
|
||||
windowID = WindowGUID.GetUniqueWindowID();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
tfEditor.Initialise();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// Close window if object has been destroyed
|
||||
if (!volRendObject)
|
||||
CloseWindow();
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
windowRect = GUI.Window(windowID, windowRect, UpdateWindow, "Transfer function");
|
||||
}
|
||||
|
||||
private void UpdateWindow(int windowID)
|
||||
{
|
||||
GUI.DragWindow(new Rect(0, 0, 10000, 20));
|
||||
|
||||
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
|
||||
|
||||
Color oldColour = GUI.color; // Used for setting GUI.color when drawing UI elements
|
||||
|
||||
if (volRendObject == null)
|
||||
return;
|
||||
|
||||
tf = volRendObject.transferFunction;
|
||||
|
||||
float contentWidth = Mathf.Min(WINDOW_WIDTH, (WINDOW_HEIGHT - 100.0f) * 2.0f);
|
||||
float contentHeight = contentWidth * 0.5f;
|
||||
|
||||
Rect outerRect = new Rect(0.0f, 0.0f, contentWidth, contentHeight);
|
||||
Rect tfEditorRect = new Rect(outerRect.x + 20.0f, outerRect.y + 20.0f, outerRect.width - 40.0f, outerRect.height - 50.0f);
|
||||
|
||||
tfEditor.SetVolumeObject(volRendObject);
|
||||
tfEditor.DrawOnGUI(tfEditorRect);
|
||||
|
||||
// Save TF
|
||||
if(GUI.Button(new Rect(tfEditorRect.x, tfEditorRect.y + tfEditorRect.height + 20.0f, 70.0f, 30.0f), "Save"))
|
||||
{
|
||||
RuntimeFileBrowser.ShowSaveFileDialog((RuntimeFileBrowser.DialogResult result) =>
|
||||
{
|
||||
if(!result.cancelled)
|
||||
{
|
||||
TransferFunctionDatabase.SaveTransferFunction(tf, result.path);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Load TF
|
||||
if(GUI.Button(new Rect(tfEditorRect.x + 75.0f, tfEditorRect.y + tfEditorRect.height + 20.0f, 70.0f, 30.0f), "Load"))
|
||||
{
|
||||
RuntimeFileBrowser.ShowOpenFileDialog((RuntimeFileBrowser.DialogResult result) =>
|
||||
{
|
||||
if(!result.cancelled)
|
||||
{
|
||||
TransferFunction newTF = TransferFunctionDatabase.LoadTransferFunction(result.path);
|
||||
if(newTF != null)
|
||||
{
|
||||
tf = newTF;
|
||||
volRendObject.SetTransferFunction(tf);
|
||||
tfEditor.ClearSelection();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// Clear TF
|
||||
if(GUI.Button(new Rect(tfEditorRect.x + 150.0f, tfEditorRect.y + tfEditorRect.height + 20.0f, 70.0f, 30.0f), "Clear"))
|
||||
{
|
||||
tf = new TransferFunction();
|
||||
tf.alphaControlPoints.Add(new TFAlphaControlPoint(0.2f, 0.0f));
|
||||
tf.alphaControlPoints.Add(new TFAlphaControlPoint(0.8f, 1.0f));
|
||||
tf.colourControlPoints.Add(new TFColourControlPoint(0.5f, new Color(0.469f, 0.354f, 0.223f, 1.0f)));
|
||||
volRendObject.SetTransferFunction(tf);
|
||||
tfEditor.ClearSelection();
|
||||
}
|
||||
|
||||
// Colour picker
|
||||
Color? selectedColour = tfEditor.GetSelectedColour();
|
||||
if (selectedColour != null)
|
||||
{
|
||||
Color newColour = GUIUtils.ColourField(new Rect(tfEditorRect.x + 250, tfEditorRect.y + tfEditorRect.height + 20, 80.0f, 30.0f), selectedColour.Value);
|
||||
tfEditor.SetSelectedColour(newColour);
|
||||
}
|
||||
|
||||
GUI.skin.label.wordWrap = false;
|
||||
GUI.Label(new Rect(tfEditorRect.x, tfEditorRect.y + tfEditorRect.height + 55.0f, 720.0f, 50.0f), "Left click to select and move a control point.\nRight click to add a control point, and ctrl + right click to delete.");
|
||||
|
||||
GUI.color = oldColour;
|
||||
|
||||
if (GUI.Button(new Rect(WINDOW_WIDTH - 100, WINDOW_HEIGHT - 40, 90, 30), "Close"))
|
||||
{
|
||||
CloseWindow();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pick the colour control point, nearest to the specified position.
|
||||
/// </summary>
|
||||
/// <param name="maxDistance">Threshold for maximum distance. Points further away than this won't get picked.</param>
|
||||
private int PickColourControlPoint(float position, float maxDistance = 0.03f)
|
||||
{
|
||||
int nearestPointIndex = -1;
|
||||
float nearestDist = 1000.0f;
|
||||
for (int i = 0; i < tf.colourControlPoints.Count; i++)
|
||||
{
|
||||
TFColourControlPoint ctrlPoint = tf.colourControlPoints[i];
|
||||
float dist = Mathf.Abs(ctrlPoint.dataValue - position);
|
||||
if (dist < maxDistance && dist < nearestDist)
|
||||
{
|
||||
nearestPointIndex = i;
|
||||
nearestDist = dist;
|
||||
}
|
||||
}
|
||||
return nearestPointIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pick the alpha control point, nearest to the specified position.
|
||||
/// </summary>
|
||||
/// <param name="maxDistance">Threshold for maximum distance. Points further away than this won't get picked.</param>
|
||||
private int PickAlphaControlPoint(Vector2 position, float maxDistance = 0.05f)
|
||||
{
|
||||
int nearestPointIndex = -1;
|
||||
float nearestDist = 1000.0f;
|
||||
for (int i = 0; i < tf.alphaControlPoints.Count; i++)
|
||||
{
|
||||
TFAlphaControlPoint ctrlPoint = tf.alphaControlPoints[i];
|
||||
Vector2 ctrlPos = new Vector2(ctrlPoint.dataValue, ctrlPoint.alphaValue);
|
||||
float dist = (ctrlPos - position).magnitude;
|
||||
if (dist < maxDistance && dist < nearestDist)
|
||||
{
|
||||
nearestPointIndex = i;
|
||||
nearestDist = dist;
|
||||
}
|
||||
}
|
||||
return nearestPointIndex;
|
||||
}
|
||||
|
||||
private void CloseWindow()
|
||||
{
|
||||
GameObject.Destroy(this.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 05f581cf1454d0be6b36abae59693162
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 8c9a71a3f851b78acaf5caabe4350986
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,43 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace UnityVolumeRendering
|
||||
{
|
||||
public partial class GUIUtils
|
||||
{
|
||||
private static ColourPickerPopup colourPicker = null; // TODO: Not static?
|
||||
|
||||
private static Texture2D previewTexture = null;
|
||||
private static GUIStyle previewStyle = new GUIStyle();
|
||||
|
||||
public static Color ColourField(Rect rect, Color colour)
|
||||
{
|
||||
if (previewTexture == null)
|
||||
{
|
||||
previewTexture = new Texture2D(2, 2, TextureFormat.RGBAFloat, false);
|
||||
}
|
||||
|
||||
if (colourPicker)
|
||||
colour = colourPicker.GetColour();
|
||||
|
||||
Color[] previewCols = { colour, colour, colour, colour };
|
||||
previewTexture.SetPixels(previewCols);
|
||||
previewTexture.Apply();
|
||||
previewStyle.normal.background = previewTexture;
|
||||
previewStyle.alignment = TextAnchor.MiddleCenter;
|
||||
if (GUI.Button(rect, "Colour", previewStyle))
|
||||
{
|
||||
if (colourPicker == null)
|
||||
{
|
||||
GameObject obj = new GameObject();
|
||||
colourPicker = obj.AddComponent<ColourPickerPopup>();
|
||||
}
|
||||
colourPicker.SetColour(colour);
|
||||
}
|
||||
|
||||
if (colourPicker)
|
||||
return colourPicker.GetColour();
|
||||
else
|
||||
return colour;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d7eee4fe67949ef989034674911148da
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -0,0 +1,268 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace UnityVolumeRendering
|
||||
{
|
||||
public class TransferFunctionEditor
|
||||
{
|
||||
private int movingColPointIndex = -1;
|
||||
private int movingAlphaPointIndex = -1;
|
||||
private int selectedColPointIndex = -1;
|
||||
|
||||
private VolumeRenderedObject volRendObject = null;
|
||||
private Texture2D histTex = null;
|
||||
|
||||
private Material tfGUIMat = null;
|
||||
private Material tfPaletteGUIMat = null;
|
||||
|
||||
private bool rightMouseBtnDown = false;
|
||||
|
||||
public void Initialise()
|
||||
{
|
||||
tfGUIMat = Resources.Load<Material>("TransferFunctionGUIMat");
|
||||
tfPaletteGUIMat = Resources.Load<Material>("TransferFunctionPaletteGUIMat");
|
||||
}
|
||||
|
||||
public void SetVolumeObject(VolumeRenderedObject volRendObject)
|
||||
{
|
||||
this.volRendObject = volRendObject;
|
||||
}
|
||||
|
||||
public void DrawOnGUI(Rect rect)
|
||||
{
|
||||
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
|
||||
|
||||
if (volRendObject == null)
|
||||
return;
|
||||
|
||||
TransferFunction tf = volRendObject.transferFunction;
|
||||
|
||||
Event currentEvent = Event.current;
|
||||
|
||||
Color oldColour = GUI.color; // Used for setting GUI.color when drawing UI elements
|
||||
|
||||
float contentWidth = rect.width;
|
||||
float contentHeight = rect.height;
|
||||
|
||||
// Histogram rect (histogram view and alpha control points)
|
||||
Rect histRect = new Rect(rect.x, rect.y, rect.width, rect.height - 40);
|
||||
// Mouse interaction area
|
||||
Rect histMouseRect = new Rect(histRect.x - 20.0f, histRect.y - 20.0f, histRect.width + 40.0f, histRect.height + 40.0f);
|
||||
// Colour palette rect (colour control points)
|
||||
Rect paletteRect = new Rect(histRect.x, histRect.y + histRect.height + 20, histRect.width, 20.0f);
|
||||
|
||||
// TODO: Don't do this every frame
|
||||
tf.GenerateTexture();
|
||||
|
||||
// Create histogram texture
|
||||
if(histTex == null)
|
||||
{
|
||||
if(SystemInfo.supportsComputeShaders)
|
||||
histTex = HistogramTextureGenerator.GenerateHistogramTextureOnGPU(volRendObject.dataset);
|
||||
else
|
||||
histTex = HistogramTextureGenerator.GenerateHistogramTexture(volRendObject.dataset);
|
||||
}
|
||||
|
||||
// Draw histogram
|
||||
tfGUIMat.SetTexture("_TFTex", tf.GetTexture());
|
||||
tfGUIMat.SetTexture("_HistTex", histTex);
|
||||
Graphics.DrawTexture(histRect, tf.GetTexture(), tfGUIMat);
|
||||
|
||||
// Draw colour palette
|
||||
Texture2D tfTexture = tf.GetTexture();
|
||||
tfPaletteGUIMat.SetTexture("_TFTex", tf.GetTexture());
|
||||
Graphics.DrawTexture(new Rect(paletteRect.x, paletteRect.y, paletteRect.width, paletteRect.height), tfTexture, tfPaletteGUIMat);
|
||||
|
||||
// Release selected colour/alpha points if mouse leaves window
|
||||
if (movingAlphaPointIndex != -1 && !histMouseRect.Contains(currentEvent.mousePosition))
|
||||
movingAlphaPointIndex = -1;
|
||||
if (movingColPointIndex != -1 && !(currentEvent.mousePosition.x >= paletteRect.x && currentEvent.mousePosition.x <= paletteRect.x + paletteRect.width))
|
||||
movingColPointIndex = -1;
|
||||
|
||||
// Mouse down => Move or remove selected colour control point
|
||||
if (currentEvent.type == EventType.MouseDown && paletteRect.Contains(currentEvent.mousePosition))
|
||||
{
|
||||
float mousePos = (currentEvent.mousePosition.x - paletteRect.x) / paletteRect.width;
|
||||
int pointIndex = PickColourControlPoint(mousePos);
|
||||
if (pointIndex != -1)
|
||||
{
|
||||
// Add control point
|
||||
if(currentEvent.button == 0 && !currentEvent.control)
|
||||
{
|
||||
movingColPointIndex = selectedColPointIndex = pointIndex;
|
||||
}
|
||||
// Remove control point
|
||||
else if(currentEvent.button == 1 && currentEvent.control)
|
||||
{
|
||||
tf.colourControlPoints.RemoveAt(pointIndex);
|
||||
currentEvent.type = EventType.Ignore;
|
||||
movingColPointIndex = selectedColPointIndex = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (currentEvent.type == EventType.MouseUp)
|
||||
movingColPointIndex = -1;
|
||||
|
||||
// Mouse down => Move or remove selected alpha control point
|
||||
if (currentEvent.type == EventType.MouseDown)
|
||||
{
|
||||
Vector2 mousePos = new Vector2((currentEvent.mousePosition.x - histRect.x) / histRect.width, 1.0f - (currentEvent.mousePosition.y - histRect.y) / histRect.height);
|
||||
int pointIndex = PickAlphaControlPoint(mousePos);
|
||||
if (pointIndex != -1)
|
||||
{
|
||||
// Add control point
|
||||
if(currentEvent.button == 0 && !currentEvent.control)
|
||||
{
|
||||
movingAlphaPointIndex = pointIndex;
|
||||
}
|
||||
// Remove control point
|
||||
else if(currentEvent.button == 1 && currentEvent.control)
|
||||
{
|
||||
tf.alphaControlPoints.RemoveAt(pointIndex);
|
||||
currentEvent.type = EventType.Ignore;
|
||||
selectedColPointIndex = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move selected alpha control point
|
||||
if (movingAlphaPointIndex != -1)
|
||||
{
|
||||
TFAlphaControlPoint alphaPoint = tf.alphaControlPoints[movingAlphaPointIndex];
|
||||
alphaPoint.dataValue = Mathf.Clamp((currentEvent.mousePosition.x - histRect.x) / histRect.width, 0.0f, 1.0f);
|
||||
alphaPoint.alphaValue = Mathf.Clamp(1.0f - (currentEvent.mousePosition.y - histRect.y) / histRect.height, 0.0f, 1.0f);
|
||||
tf.alphaControlPoints[movingAlphaPointIndex] = alphaPoint;
|
||||
}
|
||||
|
||||
// Move selected colour control point
|
||||
if (movingColPointIndex != -1)
|
||||
{
|
||||
TFColourControlPoint colPoint = tf.colourControlPoints[movingColPointIndex];
|
||||
colPoint.dataValue = Mathf.Clamp((currentEvent.mousePosition.x - paletteRect.x) / paletteRect.width, 0.0f, 1.0f);
|
||||
tf.colourControlPoints[movingColPointIndex] = colPoint;
|
||||
}
|
||||
|
||||
// Draw colour control points
|
||||
for (int iCol = 0; iCol < tf.colourControlPoints.Count; iCol++)
|
||||
{
|
||||
TFColourControlPoint colPoint = tf.colourControlPoints[iCol];
|
||||
Rect ctrlBox = new Rect(histRect.x + histRect.width * colPoint.dataValue, histRect.y + histRect.height + 20, 10, 20);
|
||||
GUI.color = Color.red;
|
||||
GUI.skin.box.fontSize = 6;
|
||||
GUI.Box(ctrlBox, "*");
|
||||
}
|
||||
|
||||
// Draw alpha control points
|
||||
for (int iAlpha = 0; iAlpha < tf.alphaControlPoints.Count; iAlpha++)
|
||||
{
|
||||
const int pointSize = 10;
|
||||
TFAlphaControlPoint alphaPoint = tf.alphaControlPoints[iAlpha];
|
||||
Rect ctrlBox = new Rect(histRect.x + histRect.width * alphaPoint.dataValue - pointSize / 2, histRect.y + (1.0f - alphaPoint.alphaValue) * histRect.height - pointSize / 2, pointSize, pointSize);
|
||||
GUI.color = Color.red;
|
||||
GUI.skin.box.fontSize = 6;
|
||||
GUI.Box(ctrlBox, "*");
|
||||
GUI.color = oldColour;
|
||||
}
|
||||
|
||||
if (currentEvent.type == EventType.MouseUp)
|
||||
{
|
||||
movingColPointIndex = -1;
|
||||
movingAlphaPointIndex = -1;
|
||||
}
|
||||
|
||||
// Add points
|
||||
if (currentEvent.type == EventType.MouseDown && currentEvent.button == 1)
|
||||
{
|
||||
rightMouseBtnDown = true;
|
||||
}
|
||||
else if (currentEvent.type == EventType.MouseUp && currentEvent.button == 1 && rightMouseBtnDown)
|
||||
{
|
||||
if (histRect.Contains(new Vector2(currentEvent.mousePosition.x, currentEvent.mousePosition.y)))
|
||||
{
|
||||
tf.alphaControlPoints.Add(new TFAlphaControlPoint(Mathf.Clamp((currentEvent.mousePosition.x - histRect.x) / histRect.width, 0.0f, 1.0f), Mathf.Clamp(1.0f - (currentEvent.mousePosition.y - histRect.y) / histRect.height, 0.0f, 1.0f)));
|
||||
}
|
||||
else
|
||||
{
|
||||
float hue = Random.Range(0.0f, 1.0f);
|
||||
Color newColour = Color.HSVToRGB(hue, 1.0f, 1.0f);
|
||||
tf.colourControlPoints.Add(new TFColourControlPoint(Mathf.Clamp((currentEvent.mousePosition.x - histRect.x) / histRect.width, 0.0f, 1.0f), newColour));
|
||||
}
|
||||
selectedColPointIndex = -1;
|
||||
|
||||
rightMouseBtnDown = false;
|
||||
currentEvent.Use();
|
||||
}
|
||||
|
||||
GUI.color = oldColour;
|
||||
}
|
||||
|
||||
public void ClearSelection()
|
||||
{
|
||||
movingColPointIndex = -1;
|
||||
movingAlphaPointIndex = -1;
|
||||
selectedColPointIndex = -1;
|
||||
}
|
||||
|
||||
public Color? GetSelectedColour()
|
||||
{
|
||||
if (selectedColPointIndex != -1)
|
||||
return volRendObject.transferFunction.colourControlPoints[selectedColPointIndex].colourValue;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public void SetSelectedColour(Color colour)
|
||||
{
|
||||
if (selectedColPointIndex != -1)
|
||||
{
|
||||
TFColourControlPoint colPoint = volRendObject.transferFunction.colourControlPoints[selectedColPointIndex];
|
||||
colPoint.colourValue = colour;
|
||||
volRendObject.transferFunction.colourControlPoints[selectedColPointIndex] = colPoint;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pick the colour control point, nearest to the specified position.
|
||||
/// </summary>
|
||||
/// <param name="maxDistance">Threshold for maximum distance. Points further away than this won't get picked.</param>
|
||||
private int PickColourControlPoint(float position, float maxDistance = 0.03f)
|
||||
{
|
||||
TransferFunction tf = volRendObject.transferFunction;
|
||||
int nearestPointIndex = -1;
|
||||
float nearestDist = 1000.0f;
|
||||
for (int i = 0; i < tf.colourControlPoints.Count; i++)
|
||||
{
|
||||
TFColourControlPoint ctrlPoint = tf.colourControlPoints[i];
|
||||
float dist = Mathf.Abs(ctrlPoint.dataValue - position);
|
||||
if (dist < maxDistance && dist < nearestDist)
|
||||
{
|
||||
nearestPointIndex = i;
|
||||
nearestDist = dist;
|
||||
}
|
||||
}
|
||||
return nearestPointIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pick the alpha control point, nearest to the specified position.
|
||||
/// </summary>
|
||||
/// <param name="maxDistance">Threshold for maximum distance. Points further away than this won't get picked.</param>
|
||||
private int PickAlphaControlPoint(Vector2 position, float maxDistance = 0.05f)
|
||||
{
|
||||
TransferFunction tf = volRendObject.transferFunction;
|
||||
int nearestPointIndex = -1;
|
||||
float nearestDist = 1000.0f;
|
||||
for (int i = 0; i < tf.alphaControlPoints.Count; i++)
|
||||
{
|
||||
TFAlphaControlPoint ctrlPoint = tf.alphaControlPoints[i];
|
||||
Vector2 ctrlPos = new Vector2(ctrlPoint.dataValue, ctrlPoint.alphaValue);
|
||||
float dist = (ctrlPos - position).magnitude;
|
||||
if (dist < maxDistance && dist < nearestDist)
|
||||
{
|
||||
nearestPointIndex = i;
|
||||
nearestDist = dist;
|
||||
}
|
||||
}
|
||||
return nearestPointIndex;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9686cd473128d99a0aa69fa100f03e80
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
|
@ -153,6 +153,12 @@ namespace UnityVolumeRendering
|
|||
}
|
||||
}
|
||||
|
||||
public void SetTransferFunction(TransferFunction tf)
|
||||
{
|
||||
this.transferFunction = tf;
|
||||
UpdateMaterialProperties();
|
||||
}
|
||||
|
||||
private void UpdateMaterialProperties()
|
||||
{
|
||||
bool useGradientTexture = tfRenderMode == TFRenderMode.TF2D || renderMode == RenderMode.IsosurfaceRendering || lightingEnabled;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import os, shutil, errno
|
||||
import os, shutil, errno, sys
|
||||
|
||||
def copy_filedir(src, dst):
|
||||
try:
|
||||
|
@ -8,7 +8,11 @@ def copy_filedir(src, dst):
|
|||
shutil.copy(src, dst)
|
||||
else: raise
|
||||
|
||||
unity_path = "D:/Program Files/UnityEditors/2019.4.35f1/Editor/Unity.exe" # TODO
|
||||
if len(sys.argv) > 1:
|
||||
unity_path = str(sys.argv[1])
|
||||
else:
|
||||
unity_path = raw_input("Enter full filepath of Unity executable:")
|
||||
|
||||
uvr_project_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir)
|
||||
export_project_path = "tmp-package-export"
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче