mirror of
https://github.com/riegera2412/UVtools.git
synced 2026-07-13 03:47:40 +02:00
@@ -623,6 +623,23 @@ namespace UVtools.Core
|
||||
return layers;
|
||||
}
|
||||
|
||||
public Mat GetMergedMatForLayerAtIndex(uint startIndex)
|
||||
{
|
||||
/* This operates backwards because GetSamePositionedLayers returns the highest layer index with the same postion z, not the lowest */
|
||||
var startLayer = this[startIndex];
|
||||
|
||||
Mat layerMat = startLayer.LayerMat;
|
||||
|
||||
for (var curIndex = startIndex + 1; curIndex < LayerCount && (this[curIndex].PositionZ == startLayer.PositionZ); curIndex++)
|
||||
{
|
||||
var nextLayer = this[curIndex].LayerMat;
|
||||
CvInvoke.BitwiseOr(nextLayer, layerMat, layerMat);
|
||||
nextLayer.Dispose();
|
||||
}
|
||||
|
||||
return layerMat;
|
||||
}
|
||||
|
||||
public Rectangle GetBoundingRectangle(OperationProgress progress = null)
|
||||
{
|
||||
if (!_boundingRectangle.IsEmpty || LayerCount == 0 || this[0] is null) return _boundingRectangle;
|
||||
|
||||
@@ -17,6 +17,7 @@ using Emgu.CV;
|
||||
using Emgu.CV.CvEnum;
|
||||
using KdTree;
|
||||
using KdTree.Math;
|
||||
using MoreLinq;
|
||||
using UVtools.Core.Extensions;
|
||||
using UVtools.Core.FileFormats;
|
||||
using UVtools.Core.MeshFormats;
|
||||
@@ -156,12 +157,14 @@ namespace UVtools.Core.Operations
|
||||
float yWidth = (pixelSize.Height > 0 ? pixelSize.Height : 0.035f) * (byte)_quality;
|
||||
var zHeight = SlicerFile.LayerHeight;
|
||||
|
||||
var totalLayerCount = SlicerFile.LayerManager.LayerCount;
|
||||
var distinctLayers = SlicerFile.Where(layer => layer.Index >= LayerIndexStart && layer.Index <= LayerIndexEnd).DistinctBy(layer => layer.PositionZ).ToList();
|
||||
|
||||
/* For the 1st stage, we maintain up to 3 mats, the current layer, the one below us, and the one above us
|
||||
* (below will be null when current layer is 0, above will be null when currentlayer is layercount-1) */
|
||||
/* We init the aboveLayer to the first layer, in the loop coming up we shift above->current->below, so this effectively inits current layer */
|
||||
Mat aboveLayer = null;
|
||||
using (var mat = SlicerFile[LayerIndexStart].LayerMat)
|
||||
using (var mat = SlicerFile.LayerManager.GetMergedMatForLayerAtIndex(distinctLayers[0].Index))
|
||||
{
|
||||
var matRoi = mat.Roi(SlicerFile.BoundingRectangle);
|
||||
|
||||
@@ -184,6 +187,7 @@ namespace UVtools.Core.Operations
|
||||
{
|
||||
aboveLayer = matRoi.Clone(); /* clone and then dispose of the ROI mat, not efficient but keeps the GetPixelPos working and clean */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Mat curLayer = null;
|
||||
@@ -193,18 +197,19 @@ namespace UVtools.Core.Operations
|
||||
var facesToCheck = new[] { Voxelizer.FaceOrientation.Front, Voxelizer.FaceOrientation.Back, Voxelizer.FaceOrientation.Left, Voxelizer.FaceOrientation.Right, Voxelizer.FaceOrientation.Top, Voxelizer.FaceOrientation.Bottom };
|
||||
|
||||
/* Init of other objects that will be used in subsequent stages */
|
||||
var rootFaces = new Voxelizer.UVFace[LayerRangeCount];
|
||||
var layerFaceCounts = new uint[LayerRangeCount];
|
||||
var layerTrees = new KdTree<float, Voxelizer.UVFace>[LayerRangeCount];
|
||||
var rootFaces = new Voxelizer.UVFace[distinctLayers.Count];
|
||||
var layerFaceCounts = new uint[distinctLayers.Count];
|
||||
var layerTrees = new KdTree<float, Voxelizer.UVFace>[distinctLayers.Count];
|
||||
|
||||
progress.Reset("layers", LayerRangeCount);
|
||||
progress.Reset("layers", (uint)distinctLayers.Count);
|
||||
progress.Title = "Stage 1: Generating faces from layers";
|
||||
//progress.ItemCount = LayerRangeCount;
|
||||
|
||||
SlicerFile.LayerManager.GetSamePositionedLayers();
|
||||
|
||||
/* Begin Stage 1, identifying all faces that are visible from outside the model */
|
||||
for (uint treeIndex = 0; treeIndex < LayerRangeCount; treeIndex++)
|
||||
for (uint layerIndex = 0; layerIndex < distinctLayers.Count; layerIndex++)
|
||||
{
|
||||
var layerIndex = LayerIndexStart + treeIndex;
|
||||
Voxelizer.UVFace currentFaceItem = null;
|
||||
|
||||
/* Should contain a list of all found faces on this layer, keyed by the face orientation */
|
||||
@@ -217,9 +222,9 @@ namespace UVtools.Core.Operations
|
||||
curLayer = aboveLayer;
|
||||
|
||||
/* bring in a new aboveLayer if we need to */
|
||||
if (layerIndex < LayerIndexEnd)
|
||||
if (layerIndex < distinctLayers.Count - 1)
|
||||
{
|
||||
using var mat = SlicerFile[layerIndex + 1].LayerMat;
|
||||
using var mat = SlicerFile.LayerManager.GetMergedMatForLayerAtIndex(distinctLayers[(int)layerIndex+1].Index);
|
||||
var matRoi = mat.Roi(SlicerFile.BoundingRectangle);
|
||||
|
||||
if (_flipDirection != Enumerations.FlipDirection.None)
|
||||
@@ -315,15 +320,15 @@ namespace UVtools.Core.Operations
|
||||
{
|
||||
/* This face is disconnected by at least 1 pixel from the chain we've been building */
|
||||
/* Create a UVFace for the current chain and reset to this one */
|
||||
layerFaceCounts[treeIndex]++;
|
||||
layerFaceCounts[layerIndex]++;
|
||||
if (currentFaceItem is null)
|
||||
{
|
||||
rootFaces[treeIndex] = new Voxelizer.UVFace { LayerIndex = treeIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1) };
|
||||
currentFaceItem = rootFaces[treeIndex];
|
||||
rootFaces[layerIndex] = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight};
|
||||
currentFaceItem = rootFaces[layerIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
currentFaceItem.FlatListNext = new Voxelizer.UVFace { LayerIndex = treeIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1) };
|
||||
currentFaceItem.FlatListNext = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
|
||||
currentFaceItem = currentFaceItem.FlatListNext;
|
||||
}
|
||||
//faceTree.Add(new float[] { (float)faceType, startX, startY, layerIndex }, new UVFace() { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1) });
|
||||
@@ -337,15 +342,15 @@ namespace UVtools.Core.Operations
|
||||
{
|
||||
/* this face isn't on the same Y row as previous, therefore it is disconnected. */
|
||||
/* Create a UVFace for the current chain and reset to this one */
|
||||
layerFaceCounts[treeIndex]++;
|
||||
layerFaceCounts[layerIndex]++;
|
||||
if (currentFaceItem is null)
|
||||
{
|
||||
rootFaces[treeIndex] = new Voxelizer.UVFace { LayerIndex = treeIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1) };
|
||||
currentFaceItem = rootFaces[treeIndex];
|
||||
rootFaces[layerIndex] = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
|
||||
currentFaceItem = rootFaces[layerIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
currentFaceItem.FlatListNext = new Voxelizer.UVFace { LayerIndex = treeIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1) };
|
||||
currentFaceItem.FlatListNext = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
|
||||
currentFaceItem = currentFaceItem.FlatListNext;
|
||||
}
|
||||
startY = f.Y;
|
||||
@@ -356,15 +361,15 @@ namespace UVtools.Core.Operations
|
||||
}
|
||||
/* we've gone through all the faces, add the final chain we've been building */
|
||||
/* Create a UVFace for the final chain */
|
||||
layerFaceCounts[treeIndex]++;
|
||||
layerFaceCounts[layerIndex]++;
|
||||
if (currentFaceItem is null)
|
||||
{
|
||||
rootFaces[treeIndex] = new Voxelizer.UVFace { LayerIndex = treeIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1) };
|
||||
currentFaceItem = rootFaces[treeIndex];
|
||||
rootFaces[layerIndex] = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
|
||||
currentFaceItem = rootFaces[layerIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
currentFaceItem.FlatListNext = new Voxelizer.UVFace { LayerIndex = treeIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1) };
|
||||
currentFaceItem.FlatListNext = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
|
||||
currentFaceItem = currentFaceItem.FlatListNext;
|
||||
}
|
||||
}
|
||||
@@ -392,15 +397,15 @@ namespace UVtools.Core.Operations
|
||||
{
|
||||
/* This face is disconnected by at least 1 pixel from the chain we've been building */
|
||||
/* Create a UVFace for the current chain and reset to this one */
|
||||
layerFaceCounts[treeIndex]++;
|
||||
layerFaceCounts[layerIndex]++;
|
||||
if (currentFaceItem is null)
|
||||
{
|
||||
rootFaces[treeIndex] = new Voxelizer.UVFace { LayerIndex = treeIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curY - startY + 1, 1) };
|
||||
currentFaceItem = rootFaces[treeIndex];
|
||||
rootFaces[layerIndex] = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curY - startY + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
|
||||
currentFaceItem = rootFaces[layerIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
currentFaceItem.FlatListNext = new Voxelizer.UVFace { LayerIndex = treeIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curY - startY + 1, 1) };
|
||||
currentFaceItem.FlatListNext = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curY - startY + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
|
||||
currentFaceItem = currentFaceItem.FlatListNext;
|
||||
}
|
||||
startY = f.Y;
|
||||
@@ -411,15 +416,15 @@ namespace UVtools.Core.Operations
|
||||
{
|
||||
/* this face is on a different column, cannot be part of the current chain we're building */
|
||||
/* Create a UVFace for the current chain and reset to this one */
|
||||
layerFaceCounts[treeIndex]++;
|
||||
layerFaceCounts[layerIndex]++;
|
||||
if (currentFaceItem is null)
|
||||
{
|
||||
rootFaces[treeIndex] = new Voxelizer.UVFace { LayerIndex = treeIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curY - startY + 1, 1) };
|
||||
currentFaceItem = rootFaces[treeIndex];
|
||||
rootFaces[layerIndex] = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curY - startY + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
|
||||
currentFaceItem = rootFaces[layerIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
currentFaceItem.FlatListNext = new Voxelizer.UVFace { LayerIndex = treeIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curY - startY + 1, 1) };
|
||||
currentFaceItem.FlatListNext = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curY - startY + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
|
||||
currentFaceItem = currentFaceItem.FlatListNext;
|
||||
}
|
||||
startY = f.Y;
|
||||
@@ -428,15 +433,15 @@ namespace UVtools.Core.Operations
|
||||
curX = f.X;
|
||||
}
|
||||
}
|
||||
layerFaceCounts[treeIndex]++;
|
||||
layerFaceCounts[layerIndex]++;
|
||||
if (currentFaceItem is null)
|
||||
{
|
||||
rootFaces[treeIndex] = new Voxelizer.UVFace { LayerIndex = treeIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curY - startY + 1, 1) };
|
||||
currentFaceItem = rootFaces[treeIndex];
|
||||
rootFaces[layerIndex] = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curY - startY + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
|
||||
currentFaceItem = rootFaces[layerIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
currentFaceItem.FlatListNext = new Voxelizer.UVFace { LayerIndex = treeIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curY - startY + 1, 1) };
|
||||
currentFaceItem.FlatListNext = new Voxelizer.UVFace { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curY - startY + 1, 1), LayerHeight = distinctLayers[(int)layerIndex].LayerHeight };
|
||||
currentFaceItem = currentFaceItem.FlatListNext;
|
||||
}
|
||||
}
|
||||
@@ -456,7 +461,7 @@ namespace UVtools.Core.Operations
|
||||
progress.ProcessedItems = 0;
|
||||
|
||||
/* We build out a 3 dimensional KD tree for each layer, having 1 big KD tree is prohibitive when you get to millions and millions of faces. */
|
||||
Parallel.For(0, LayerRangeCount, layerIndex =>
|
||||
Parallel.For(0, distinctLayers.Count, layerIndex =>
|
||||
{
|
||||
if (progress.Token.IsCancellationRequested) return;
|
||||
|
||||
@@ -490,7 +495,7 @@ namespace UVtools.Core.Operations
|
||||
* Since we don't modify the lists/objects and only connect them via doubly linked list
|
||||
* we can process each layer independant of the others.
|
||||
*/
|
||||
Parallel.For(0, LayerRangeCount, i =>
|
||||
Parallel.For(0, distinctLayers.Count, i =>
|
||||
{
|
||||
if (progress.Token.IsCancellationRequested)
|
||||
{
|
||||
@@ -562,15 +567,15 @@ namespace UVtools.Core.Operations
|
||||
mesh.BeginWrite();
|
||||
|
||||
/* Begin Stage 4, generating triangles and saving to STL */
|
||||
foreach (var tree in layerTrees)
|
||||
{
|
||||
for (var treeIndex = 0; treeIndex < layerTrees.Length; treeIndex++) {
|
||||
var tree = layerTrees[treeIndex];
|
||||
if (tree is null) continue;
|
||||
|
||||
/* only process UVFaces that do not have a parent, these are the "root" faces that couldn't be combined with something above them */
|
||||
foreach (var p in tree.Where(p => p.Value.Parent is null))
|
||||
{
|
||||
/* generate the triangles */
|
||||
foreach (var f in Voxelizer.MakeFacetsForUVFace(p.Value, xWidth, yWidth, zHeight, LayerIndexStart))
|
||||
foreach (var f in Voxelizer.MakeFacetsForUVFace(p.Value, xWidth, yWidth,distinctLayers[treeIndex].PositionZ))
|
||||
{
|
||||
/* write to file */
|
||||
mesh.WriteTriangle(f.p1, f.p2, f.p3, f.normal);
|
||||
|
||||
+39
-506
@@ -31,7 +31,7 @@ namespace UVtools.Core.Voxel
|
||||
public FaceOrientation Type;
|
||||
public uint LayerIndex;
|
||||
public Rectangle FaceRect;
|
||||
|
||||
public float LayerHeight;
|
||||
/* Doubly linked list of UVFaces, used during Stage 3, collapsing the faces vertically.
|
||||
* instead of modifying properties and having to remove items from lists, we keep all faces
|
||||
* and just link parents and children together.
|
||||
@@ -133,18 +133,18 @@ namespace UVtools.Core.Voxel
|
||||
layerBelow ??= curLayer.NewBlank();
|
||||
|
||||
/* anything that is in the current layer but is not in the layer above, by definition has an exposed face */
|
||||
var upperXor = curLayer.NewBlank();
|
||||
CvInvoke.BitwiseXor(curLayer, layerAbove, upperXor);
|
||||
Mat upperSubtract = new Mat();
|
||||
CvInvoke.Subtract(curLayer, layerAbove, upperSubtract);
|
||||
|
||||
/* anything that is in the current layer but is not in the layer below, by definition has an exposed face */
|
||||
var lowerXor = curLayer.NewBlank();
|
||||
CvInvoke.BitwiseXor(curLayer, layerBelow, lowerXor);
|
||||
Mat lowerSubtract = new Mat();
|
||||
CvInvoke.Subtract(curLayer, layerBelow, lowerSubtract);
|
||||
|
||||
/* Or all of these together to get the list of pixels that have exposed face(s) */
|
||||
var voxelLayer = curLayer.NewBlank();
|
||||
CvInvoke.BitwiseOr(onlyContours, voxelLayer, voxelLayer);
|
||||
CvInvoke.BitwiseOr(upperXor, voxelLayer, voxelLayer);
|
||||
CvInvoke.BitwiseOr(lowerXor, voxelLayer, voxelLayer);
|
||||
CvInvoke.BitwiseOr(upperSubtract, voxelLayer, voxelLayer);
|
||||
CvInvoke.BitwiseOr(lowerSubtract, voxelLayer, voxelLayer);
|
||||
|
||||
/* dispoose of the layerAbove/layerBelow if they were allocated here */
|
||||
if (needAboveDispose)
|
||||
@@ -169,480 +169,10 @@ namespace UVtools.Core.Voxel
|
||||
MINECRAFT = 8
|
||||
}
|
||||
|
||||
public unsafe void CreateVoxelMesh(Type meshType, FileFormat file, string filePath, OperationProgress progress, VoxelQuality quality = VoxelQuality.ACCURATE, uint layerStart = 0, uint layerStop = 0)
|
||||
{
|
||||
var layerManager = file.LayerManager;
|
||||
/* Voxelization has 4 overall stages
|
||||
* 1.) Generate all visible faces, this is for each pixel we determine which of its faces are visible from outside the model
|
||||
* 2.) Collapse faces horizontally, this combines faces that are coplanar horizontally into a longer face, this reduces triangles
|
||||
* 3.) Collapse faces that are coplanar and the same size vertically leveraging KD Trees for fast lookups, O(logn) vs O(n) for a normal list
|
||||
* 4.) Generate triangles for faces and write out to STL
|
||||
*/
|
||||
|
||||
/* Basic information for the file, how many layers, how big should each voxel be) */
|
||||
|
||||
if (layerStop == 0)
|
||||
{
|
||||
layerStop = file.LayerCount;
|
||||
}
|
||||
|
||||
uint layerCount = layerStop - layerStart;
|
||||
float xWidth = (float)(layerManager.SlicerFile.Xppmm > 0 ? 1 / layerManager.SlicerFile.Xppmm : 0.035) * (int)quality;
|
||||
float yWidth = (float)(layerManager.SlicerFile.Yppmm > 0 ? 1 / layerManager.SlicerFile.Yppmm : 0.035) * (int)quality;
|
||||
var zHeight = layerManager.SlicerFile.LayerHeight;
|
||||
|
||||
|
||||
/* For the 1st stage, we maintain up to 3 mats, the current layer, the one below us, and the one above us
|
||||
* (below will be null when current layer is 0, above will be null when currentlayer is layercount-1) */
|
||||
/* We init the aboveLayer to the first layer, in the loop coming up we shift above->current->below, so this effectively inits current layer */
|
||||
Mat RoiLayer = layerManager[layerStart].LayerMat.Roi(layerManager.SlicerFile.BoundingRectangle);
|
||||
Mat aboveLayer = RoiLayer.Clone(); /* clone and then dispose of the ROI mat, not effecient but keeps the GetPixelPos working and clean */
|
||||
if ((int)quality > 1)
|
||||
{
|
||||
Mat resize = new Mat();
|
||||
CvInvoke.Resize(aboveLayer, resize, new Size(), 1.0 / (int)quality, 1.0 / (int)quality, Inter.Area);
|
||||
aboveLayer.Dispose();
|
||||
aboveLayer = resize;
|
||||
}
|
||||
RoiLayer.Dispose();
|
||||
Mat curLayer = null;
|
||||
Mat belowLayer = null;
|
||||
|
||||
/* List of faces to process, great for debugging if you are haveing issues with a face of particular orientation. */
|
||||
FaceOrientation[] facesToCheck = new FaceOrientation[] { FaceOrientation.Front, FaceOrientation.Back, FaceOrientation.Left, FaceOrientation.Right, FaceOrientation.Top, FaceOrientation.Bottom };
|
||||
|
||||
/* Init of other objects that will be used in subsequent stages */
|
||||
UVFace[] rootFaces = new UVFace[layerCount];
|
||||
uint[] layerFaceCounts = new uint[layerCount];
|
||||
|
||||
KdTree<float, UVFace>[] layerTrees = new KdTree<float, UVFace>[layerCount];
|
||||
|
||||
progress.Reset();
|
||||
progress.Title = "Generating faces from layers...";
|
||||
progress.ItemCount = layerCount;
|
||||
|
||||
/* Begin Stage 1, identifying all faces that are visible from outside the model */
|
||||
for (uint layerIndex = 0; layerIndex < layerCount; layerIndex++)
|
||||
{
|
||||
UVFace currentFaceItem = null;
|
||||
|
||||
/* Should contain a list of all found faces on this layer, keyed by the face orientation */
|
||||
Dictionary<FaceOrientation, List<Point>> foundFaces = new Dictionary<FaceOrientation, List<Point>>();
|
||||
|
||||
/* move current layer to below */
|
||||
belowLayer = curLayer;
|
||||
|
||||
/* move above layer to us */
|
||||
curLayer = aboveLayer;
|
||||
|
||||
/* bring in a new aboveLayer if we need to */
|
||||
if (layerIndex < layerCount - 1)
|
||||
{
|
||||
RoiLayer = layerManager[layerIndex + layerStart + 1].LayerMat.Roi(layerManager.SlicerFile.BoundingRectangle);
|
||||
aboveLayer = RoiLayer.Clone();
|
||||
|
||||
if ((int)quality > 1)
|
||||
{
|
||||
Mat resize = new Mat();
|
||||
CvInvoke.Resize(aboveLayer, resize, new Size(), 1.0 / (int)quality, 1.0 / (int)quality, Inter.Area);
|
||||
aboveLayer.Dispose();
|
||||
aboveLayer = resize;
|
||||
}
|
||||
|
||||
RoiLayer.Dispose();
|
||||
//CvInvoke.Threshold(aboveLayer, aboveLayer, 1, 255, ThresholdType.Binary);
|
||||
}
|
||||
else
|
||||
{
|
||||
aboveLayer = null;
|
||||
}
|
||||
|
||||
/* get image of pixels to do neighbor checks on */
|
||||
var voxelLayer = BuildVoxelLayerImage(curLayer, aboveLayer, belowLayer);
|
||||
var voxelBytes = voxelLayer.GetBytePointer();
|
||||
|
||||
/* Seems to be faster to parallel on the Y and not the X */
|
||||
Parallel.For(0, curLayer.Height, CoreSettings.ParallelOptions, y =>
|
||||
{
|
||||
/* Collects all the faces found for this thread, will be combined into the main dictionary later */
|
||||
Dictionary<FaceOrientation, List<Point>> threadDict = new Dictionary<FaceOrientation, List<Point>>();
|
||||
for (var x = 0; x < curLayer.Width; x++)
|
||||
{
|
||||
if (voxelBytes[voxelLayer.GetPixelPos(x, y)] == 0) continue;
|
||||
|
||||
var faces = GetOpenFaces(curLayer, x, y, belowLayer, aboveLayer);
|
||||
if (faces != FaceOrientation.None)
|
||||
{
|
||||
foreach (var face in facesToCheck)
|
||||
{
|
||||
if (faces.HasFlag(face))
|
||||
{
|
||||
|
||||
if (!threadDict.ContainsKey(face))
|
||||
{
|
||||
|
||||
threadDict.Add(face, new());
|
||||
}
|
||||
|
||||
threadDict[face].Add(new Point(x, y));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* merge all found faces to main foundFaces dictionary */
|
||||
lock (foundFaces)
|
||||
{
|
||||
foreach (var kvp in threadDict)
|
||||
{
|
||||
if (!foundFaces.ContainsKey(kvp.Key))
|
||||
{
|
||||
foundFaces.Add(kvp.Key, new());
|
||||
}
|
||||
lock (foundFaces[kvp.Key])
|
||||
{
|
||||
foundFaces[kvp.Key].AddRange(kvp.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/* Begin stage 2, horizontal combining of coplanar faces */
|
||||
foreach (var faceType in facesToCheck)
|
||||
{
|
||||
if (foundFaces.ContainsKey(faceType) == false || foundFaces[faceType].Count == 0) continue;
|
||||
|
||||
if (faceType == FaceOrientation.Front || faceType == FaceOrientation.Back || faceType == FaceOrientation.Top || faceType == FaceOrientation.Bottom)
|
||||
{
|
||||
/* sort the faces by coordinate */
|
||||
foundFaces[faceType] = foundFaces[faceType].OrderBy(f => f.Y).ThenBy(f => f.X).ToList();
|
||||
|
||||
var startX = foundFaces[faceType][0].X;
|
||||
var curX = foundFaces[faceType][0].X;
|
||||
var startY = foundFaces[faceType][0].Y;
|
||||
var curY = foundFaces[faceType][0].Y;
|
||||
|
||||
foreach (var f in foundFaces[faceType].Skip(1))
|
||||
{
|
||||
if (f.Y == curY)
|
||||
{
|
||||
/* same row...*/
|
||||
if (f.X == curX + 1)
|
||||
{
|
||||
/* this face is adjecent to the previous, just increase the "width" */
|
||||
curX++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* This face is disconnected by at least 1 pixel from the chain we've been building */
|
||||
/* Create a UVFace for the current chain and reset to this one */
|
||||
layerFaceCounts[layerIndex]++;
|
||||
if (currentFaceItem is null)
|
||||
{
|
||||
rootFaces[layerIndex] = new UVFace() { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1) };
|
||||
currentFaceItem = rootFaces[layerIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
currentFaceItem.FlatListNext = new UVFace() { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1) };
|
||||
currentFaceItem = currentFaceItem.FlatListNext;
|
||||
}
|
||||
//faceTree.Add(new float[] { (float)faceType, startX, startY, layerIndex }, new UVFace() { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1) });
|
||||
/* disconnected */
|
||||
startX = f.X;
|
||||
curX = f.X;
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* this face isn't on the same Y row as previous, therefore it is disconnected. */
|
||||
/* Create a UVFace for the current chain and reset to this one */
|
||||
layerFaceCounts[layerIndex]++;
|
||||
if (currentFaceItem is null)
|
||||
{
|
||||
rootFaces[layerIndex] = new UVFace() { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1) };
|
||||
currentFaceItem = rootFaces[layerIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
currentFaceItem.FlatListNext = new UVFace() { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1) };
|
||||
currentFaceItem = currentFaceItem.FlatListNext;
|
||||
}
|
||||
startY = f.Y;
|
||||
curY = f.Y;
|
||||
startX = f.X;
|
||||
curX = f.X;
|
||||
}
|
||||
}
|
||||
/* we've gone through all the faces, add the final chain we've been building */
|
||||
/* Create a UVFace for the final chain */
|
||||
layerFaceCounts[layerIndex]++;
|
||||
if (currentFaceItem is null)
|
||||
{
|
||||
rootFaces[layerIndex] = new UVFace() { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1) };
|
||||
currentFaceItem = rootFaces[layerIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
currentFaceItem.FlatListNext = new UVFace() { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curX - startX + 1, 1) };
|
||||
currentFaceItem = currentFaceItem.FlatListNext;
|
||||
}
|
||||
}
|
||||
|
||||
if (faceType == FaceOrientation.Left || faceType == FaceOrientation.Right)
|
||||
{
|
||||
/* sort the faces by coordinate */
|
||||
foundFaces[faceType] = foundFaces[faceType].OrderBy(f => f.X).ThenBy(f => f.Y).ToList();
|
||||
|
||||
var startX = foundFaces[faceType][0].X;
|
||||
var curX = foundFaces[faceType][0].X;
|
||||
var startY = foundFaces[faceType][0].Y;
|
||||
var curY = foundFaces[faceType][0].Y;
|
||||
foreach (var f in foundFaces[faceType].Skip(1))
|
||||
{
|
||||
if (f.X == curX)
|
||||
{
|
||||
/* same column...*/
|
||||
if (f.Y == curY + 1)
|
||||
{
|
||||
/* this face is adjecent to the previous, just increase the "width" */
|
||||
curY++;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* This face is disconnected by at least 1 pixel from the chain we've been building */
|
||||
/* Create a UVFace for the current chain and reset to this one */
|
||||
layerFaceCounts[layerIndex]++;
|
||||
if (currentFaceItem is null)
|
||||
{
|
||||
rootFaces[layerIndex] = new UVFace() { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curY - startY + 1, 1) };
|
||||
currentFaceItem = rootFaces[layerIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
currentFaceItem.FlatListNext = new UVFace() { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curY - startY + 1, 1) };
|
||||
currentFaceItem = currentFaceItem.FlatListNext;
|
||||
}
|
||||
startY = f.Y;
|
||||
curY = f.Y;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* this face is on a different column, cannot be part of the current chain we're building */
|
||||
/* Create a UVFace for the current chain and reset to this one */
|
||||
layerFaceCounts[layerIndex]++;
|
||||
if (currentFaceItem is null)
|
||||
{
|
||||
rootFaces[layerIndex] = new UVFace() { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curY - startY + 1, 1) };
|
||||
currentFaceItem = rootFaces[layerIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
currentFaceItem.FlatListNext = new UVFace() { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curY - startY + 1, 1) };
|
||||
currentFaceItem = currentFaceItem.FlatListNext;
|
||||
}
|
||||
startY = f.Y;
|
||||
curY = f.Y;
|
||||
startX = f.X;
|
||||
curX = f.X;
|
||||
}
|
||||
}
|
||||
layerFaceCounts[layerIndex]++;
|
||||
if (currentFaceItem is null)
|
||||
{
|
||||
rootFaces[layerIndex] = new UVFace() { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curY - startY + 1, 1) };
|
||||
currentFaceItem = rootFaces[layerIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
currentFaceItem.FlatListNext = new UVFace() { LayerIndex = layerIndex, Type = faceType, FaceRect = new Rectangle(startX, startY, curY - startY + 1, 1) };
|
||||
currentFaceItem = currentFaceItem.FlatListNext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
progress.LockAndIncrement();
|
||||
|
||||
if (progress.Token.IsCancellationRequested)
|
||||
{
|
||||
Cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
progress.Reset();
|
||||
progress.Title = "Building KD Trees";
|
||||
progress.ItemCount = layerCount;
|
||||
|
||||
/* We build out a 3 dimensional KD tree for each layer, having 1 big KD tree is prohibitive when you get to millions and millions of faces. */
|
||||
Parallel.For(0, layerCount, layerIndex =>
|
||||
{
|
||||
if (progress.Token.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Create the KD tree for the layer, in practice there should never be dups, but just in case, set to skip */
|
||||
layerTrees[layerIndex] = new KdTree<float, UVFace>(3, new FloatMath(), AddDuplicateBehavior.Skip);
|
||||
|
||||
/* Walk the linked list of UVFaces, adding them to the tree */
|
||||
var currentFaceItem = rootFaces[layerIndex];
|
||||
if (currentFaceItem is null) return;
|
||||
while (currentFaceItem.FlatListNext is not null)
|
||||
{
|
||||
layerTrees[layerIndex].Add(new float[] { (float)currentFaceItem.Type, currentFaceItem.FaceRect.X, currentFaceItem.FaceRect.Y }, currentFaceItem);
|
||||
currentFaceItem = currentFaceItem.FlatListNext;
|
||||
}
|
||||
layerTrees[layerIndex].Add(new float[] { (float)currentFaceItem.Type, currentFaceItem.FaceRect.X, currentFaceItem.FaceRect.Y }, currentFaceItem);
|
||||
|
||||
progress.LockAndIncrement();
|
||||
});
|
||||
|
||||
if (progress.Token.IsCancellationRequested)
|
||||
{
|
||||
Cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
progress.Reset();
|
||||
progress.Title = "Collapsing faces...";
|
||||
progress.ItemCount = layerCount;
|
||||
long collapseCount = 0;
|
||||
|
||||
/* Begin Stage 3: Vertical collapse
|
||||
* Since we don't modify the lists/objects and only connect them via doubly linked list
|
||||
* we can process each layer independant of the others.
|
||||
*/
|
||||
Parallel.For(0, layerCount, idx =>
|
||||
{
|
||||
if (progress.Token.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* if no faces on this layer... skip.... needed for empty layers */
|
||||
if (layerTrees[idx] is null) return;
|
||||
|
||||
/* check each point in the current layers tree */
|
||||
foreach (var point in layerTrees[idx])
|
||||
{
|
||||
/* if this point already has a parent, skip */
|
||||
if (point.Value.Parent is not null) continue;
|
||||
|
||||
|
||||
/* deterimine the point below to check.
|
||||
* For front/back/left/right its the same X/Y point and Z is different, and Z is done basically by looking at the layer tree below us
|
||||
* For Top/Bottom its a bit different, the Z stays the same (we query our own layer tree) but the Y coordinate is 1 less */
|
||||
|
||||
float[] pointBelow = null;
|
||||
KdTree<float, UVFace> treeBelow = null;
|
||||
if (point.Value.Type == FaceOrientation.Top || point.Value.Type == FaceOrientation.Bottom)
|
||||
{
|
||||
if (point.Value.Type == FaceOrientation.Top)
|
||||
{
|
||||
pointBelow = new float[] { point.Point[0], point.Point[1], point.Point[2] - 1 };
|
||||
} else
|
||||
{
|
||||
pointBelow = new float[] { point.Point[0], point.Point[1], point.Point[2] - 1 };
|
||||
}
|
||||
treeBelow = layerTrees[idx];
|
||||
}
|
||||
else
|
||||
{
|
||||
pointBelow = new float[] { point.Point[0], point.Point[1], point.Point[2] };
|
||||
if (idx > 0)
|
||||
{
|
||||
treeBelow = layerTrees[idx - 1];
|
||||
}
|
||||
}
|
||||
|
||||
if (treeBelow == null) continue;
|
||||
var faceBelow = treeBelow.FindValueAt(pointBelow);
|
||||
if (faceBelow is not null)
|
||||
{
|
||||
/* if we find a face below us it has to be the same width too */
|
||||
if (point.Value.FaceRect.Width == faceBelow.FaceRect.Width)
|
||||
{
|
||||
/* same coordinate, same width, safe to merge together. Do so by doubly linking the items */
|
||||
point.Value.Parent = faceBelow;
|
||||
faceBelow.Child = point.Value;
|
||||
collapseCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
progress.LockAndIncrement();
|
||||
});
|
||||
|
||||
if (progress.Token.IsCancellationRequested)
|
||||
{
|
||||
Cleanup();
|
||||
return;
|
||||
}
|
||||
|
||||
progress.Reset();
|
||||
progress.Title = "Generating STL...";
|
||||
progress.ItemCount = layerCount;
|
||||
|
||||
using var mesh = meshType.CreateInstance<MeshFile>(filePath, FileMode.Create);
|
||||
mesh.BeginWrite();
|
||||
|
||||
/* Begin Stage 4, generating triangles and saving to STL */
|
||||
foreach (var tree in layerTrees)
|
||||
{
|
||||
if (tree is null) continue;
|
||||
|
||||
/* only process UVFaces that do not have a parent, these are the "root" faces that couldn't be combined with something above them */
|
||||
foreach (var p in tree.Where(p => p.Value.Parent is null))
|
||||
{
|
||||
/* generate the triangles */
|
||||
foreach (var f in MakeFacetsForUVFace(p.Value, xWidth, yWidth, zHeight, layerStart))
|
||||
{
|
||||
/* save to STL file */
|
||||
mesh.WriteTriangle(f.p1, f.p2, f.p3, f.normal);
|
||||
}
|
||||
}
|
||||
|
||||
/* check for cancellation at every layer, and if so, close the STL file properly */
|
||||
if (progress.Token.IsCancellationRequested)
|
||||
{
|
||||
Cleanup();
|
||||
return;
|
||||
}
|
||||
progress.LockAndIncrement();
|
||||
}
|
||||
|
||||
void Cleanup()
|
||||
{
|
||||
/* dispose of everything */
|
||||
for (var x = 0; x < layerTrees.Length; x++)
|
||||
{
|
||||
layerTrees[x] = null;
|
||||
}
|
||||
|
||||
layerTrees = null;
|
||||
|
||||
for (var x = 0; x < rootFaces.Length; x++)
|
||||
{
|
||||
if (rootFaces[x] is not null)
|
||||
{
|
||||
rootFaces[x].FlatListNext = null;
|
||||
}
|
||||
rootFaces[x] = null;
|
||||
}
|
||||
rootFaces = null;
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
mesh.EndWrite();
|
||||
|
||||
}
|
||||
/* CreateVoxelMesh is no longer used, see OperationLayerExportMesh for the logic that used to be here */
|
||||
|
||||
/* NOTE: this took a lot, a lot, a lot, of trial and error, just trust that it generates the correct triangles for a given face ;) */
|
||||
public static IEnumerable<(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 normal)> MakeFacetsForUVFace(UVFace face, float xSize, float ySize, float layerHeight, uint layerStart)
|
||||
public static IEnumerable<(Vector3 p1, Vector3 p2, Vector3 p3, Vector3 normal)> MakeFacetsForUVFace(UVFace face, float xSize, float ySize, float positionZ)
|
||||
{
|
||||
/* triangles need "normal" vectors to show which is the outside of the triangle */
|
||||
/* also, triangle points need to be provided in counter clockwise direction...*/
|
||||
@@ -655,65 +185,68 @@ namespace UVtools.Core.Voxel
|
||||
var FrontNormal = new Vector3(0, -1, 0);
|
||||
|
||||
/* count the "height" of this face, which is == to itself + number of children in its doubly linked list chain */
|
||||
var height = 1;
|
||||
float height = 0;
|
||||
var totalFaceCount = 1;
|
||||
UVFace child = face;
|
||||
while (child.Child is not null)
|
||||
{
|
||||
height++;
|
||||
height += child.LayerHeight;
|
||||
totalFaceCount++;
|
||||
child = child.Child;
|
||||
}
|
||||
face.LayerIndex += layerStart;
|
||||
height += child.LayerHeight;
|
||||
|
||||
if (face.Type == FaceOrientation.Front)
|
||||
{
|
||||
var lowerLeft = new Vector3(face.FaceRect.X * xSize, face.FaceRect.Y * ySize, face.LayerIndex * layerHeight);
|
||||
var lowerRight = new Vector3((face.FaceRect.X + face.FaceRect.Width) * xSize, face.FaceRect.Y * ySize, face.LayerIndex * layerHeight);
|
||||
var upperLeft = new Vector3(lowerLeft.X, lowerLeft.Y, lowerLeft.Z + ((height) * layerHeight));
|
||||
var upperRight = new Vector3(lowerRight.X, lowerRight.Y, lowerRight.Z + ((height) * layerHeight));
|
||||
var lowerLeft = new Vector3(face.FaceRect.X * xSize, face.FaceRect.Y * ySize, positionZ);
|
||||
var lowerRight = new Vector3((face.FaceRect.X + face.FaceRect.Width) * xSize, face.FaceRect.Y * ySize, positionZ);
|
||||
var upperLeft = new Vector3(lowerLeft.X, lowerLeft.Y, lowerLeft.Z + height);
|
||||
var upperRight = new Vector3(lowerRight.X, lowerRight.Y, lowerRight.Z + height);
|
||||
yield return (lowerLeft, lowerRight, upperRight, FrontNormal);
|
||||
yield return (upperRight, upperLeft, lowerLeft, FrontNormal);
|
||||
}
|
||||
else if (face.Type == FaceOrientation.Back)
|
||||
{
|
||||
var lowerRight = new Vector3(face.FaceRect.X * xSize, face.FaceRect.Y * ySize + ySize, face.LayerIndex * layerHeight);
|
||||
var lowerLeft = new Vector3((face.FaceRect.X + face.FaceRect.Width) * xSize, face.FaceRect.Y * ySize + ySize, face.LayerIndex * layerHeight);
|
||||
var upperLeft = new Vector3(lowerLeft.X, lowerLeft.Y, lowerLeft.Z + ((height) * layerHeight));
|
||||
var upperRight = new Vector3(lowerRight.X, lowerRight.Y, lowerRight.Z + ((height) * layerHeight));
|
||||
var lowerRight = new Vector3(face.FaceRect.X * xSize, face.FaceRect.Y * ySize + ySize, positionZ);
|
||||
var lowerLeft = new Vector3((face.FaceRect.X + face.FaceRect.Width) * xSize, face.FaceRect.Y * ySize + ySize, positionZ);
|
||||
var upperLeft = new Vector3(lowerLeft.X, lowerLeft.Y, lowerLeft.Z + height);
|
||||
var upperRight = new Vector3(lowerRight.X, lowerRight.Y, lowerRight.Z + height);
|
||||
yield return (lowerLeft, lowerRight, upperRight, BackNormal);
|
||||
yield return (upperRight, upperLeft, lowerLeft, BackNormal);
|
||||
}
|
||||
else if (face.Type == FaceOrientation.Left)
|
||||
{
|
||||
var lowerLeft = new Vector3(face.FaceRect.X * xSize, (face.FaceRect.Y + face.FaceRect.Width) * ySize, face.LayerIndex * layerHeight);
|
||||
var lowerRight = new Vector3(face.FaceRect.X * xSize, (face.FaceRect.Y) * ySize, face.LayerIndex * layerHeight);
|
||||
var upperLeft = new Vector3(lowerLeft.X, lowerLeft.Y, lowerLeft.Z + ((height) * layerHeight));
|
||||
var upperRight = new Vector3(lowerRight.X, lowerRight.Y, lowerRight.Z + ((height) * layerHeight));
|
||||
var lowerLeft = new Vector3(face.FaceRect.X * xSize, (face.FaceRect.Y + face.FaceRect.Width) * ySize, positionZ);
|
||||
var lowerRight = new Vector3(face.FaceRect.X * xSize, (face.FaceRect.Y) * ySize, positionZ);
|
||||
var upperLeft = new Vector3(lowerLeft.X, lowerLeft.Y, lowerLeft.Z + height);
|
||||
var upperRight = new Vector3(lowerRight.X, lowerRight.Y, lowerRight.Z + height);
|
||||
yield return (lowerLeft, lowerRight, upperRight, LeftNormal);
|
||||
yield return (upperRight, upperLeft, lowerLeft, LeftNormal);
|
||||
}
|
||||
else if (face.Type == FaceOrientation.Right)
|
||||
{
|
||||
var lowerRight = new Vector3(face.FaceRect.X * xSize + xSize, (face.FaceRect.Y + face.FaceRect.Width) * ySize, face.LayerIndex * layerHeight);
|
||||
var lowerLeft = new Vector3(face.FaceRect.X * xSize + xSize, (face.FaceRect.Y) * ySize, face.LayerIndex * layerHeight);
|
||||
var upperLeft = new Vector3(lowerLeft.X, lowerLeft.Y, lowerLeft.Z + ((height) * layerHeight));
|
||||
var upperRight = new Vector3(lowerRight.X, lowerRight.Y, lowerRight.Z + ((height) * layerHeight));
|
||||
var lowerRight = new Vector3(face.FaceRect.X * xSize + xSize, (face.FaceRect.Y + face.FaceRect.Width) * ySize, positionZ);
|
||||
var lowerLeft = new Vector3(face.FaceRect.X * xSize + xSize, (face.FaceRect.Y) * ySize, positionZ);
|
||||
var upperLeft = new Vector3(lowerLeft.X, lowerLeft.Y, lowerLeft.Z + height);
|
||||
var upperRight = new Vector3(lowerRight.X, lowerRight.Y, lowerRight.Z + height);
|
||||
yield return (lowerLeft, lowerRight, upperRight, RightNormal);
|
||||
yield return (upperRight, upperLeft, lowerLeft, RightNormal);
|
||||
}
|
||||
else if (face.Type == FaceOrientation.Top)
|
||||
{
|
||||
var upperLeft = new Vector3(face.FaceRect.X * xSize, face.FaceRect.Y * ySize, face.LayerIndex * layerHeight + layerHeight);
|
||||
var upperRight = new Vector3(face.FaceRect.X * xSize, (face.FaceRect.Y + height) * ySize, face.LayerIndex * layerHeight + layerHeight);
|
||||
var lowerLeft = new Vector3(upperLeft.X + (face.FaceRect.Width * xSize), upperLeft.Y, face.LayerIndex * layerHeight + layerHeight);
|
||||
var lowerRight = new Vector3(upperRight.X + (face.FaceRect.Width * xSize), upperRight.Y, face.LayerIndex * layerHeight + layerHeight);
|
||||
var upperLeft = new Vector3(face.FaceRect.X * xSize, face.FaceRect.Y * ySize, positionZ + face.LayerHeight);
|
||||
var upperRight = new Vector3(face.FaceRect.X * xSize, (face.FaceRect.Y + totalFaceCount) * ySize, positionZ + face.LayerHeight);
|
||||
var lowerLeft = new Vector3(upperLeft.X + (face.FaceRect.Width * xSize), upperLeft.Y, positionZ + face.LayerHeight);
|
||||
var lowerRight = new Vector3(upperRight.X + (face.FaceRect.Width * xSize), upperRight.Y, positionZ + face.LayerHeight);
|
||||
yield return (lowerLeft, lowerRight, upperRight, TopNormal);
|
||||
yield return (upperRight, upperLeft, lowerLeft, TopNormal);
|
||||
}
|
||||
else if (face.Type == FaceOrientation.Bottom)
|
||||
{
|
||||
var upperRight = new Vector3(face.FaceRect.X * xSize, face.FaceRect.Y * ySize, face.LayerIndex * layerHeight);
|
||||
var upperLeft = new Vector3(face.FaceRect.X * xSize, (face.FaceRect.Y + height) * ySize, face.LayerIndex * layerHeight);
|
||||
var lowerLeft = new Vector3(upperLeft.X + (face.FaceRect.Width * xSize), upperLeft.Y, face.LayerIndex * layerHeight);
|
||||
var lowerRight = new Vector3(upperRight.X + (face.FaceRect.Width * xSize), upperRight.Y, face.LayerIndex * layerHeight);
|
||||
var upperRight = new Vector3(face.FaceRect.X * xSize, face.FaceRect.Y * ySize, positionZ);
|
||||
var upperLeft = new Vector3(face.FaceRect.X * xSize, (face.FaceRect.Y + totalFaceCount) * ySize, positionZ);
|
||||
var lowerLeft = new Vector3(upperLeft.X + (face.FaceRect.Width * xSize), upperLeft.Y, positionZ);
|
||||
var lowerRight = new Vector3(upperRight.X + (face.FaceRect.Width * xSize), upperRight.Y, positionZ);
|
||||
yield return (lowerLeft, lowerRight, upperRight, BottomNormal);
|
||||
yield return (upperRight, upperLeft, lowerLeft, BottomNormal);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user