Welcome to mirror list, hosted at ThFree Co, Russian Federation.

Voxelizer.cs « Voxel « UVtools.Core - github.com/sn4k3/UVtools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 40bbbf40c6a01fee6a108e7a89b33fd1498197cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
/*
 *                     GNU AFFERO GENERAL PUBLIC LICENSE
 *                       Version 3, 19 November 2007
 *  Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
 *  Everyone is permitted to copy and distribute verbatim copies
 *  of this license document, but changing it is not allowed.
 */
using Emgu.CV;
using Emgu.CV.CvEnum;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Numerics;
using UVtools.Core.Extensions;

namespace UVtools.Core.Voxel;

public class Voxelizer
{
    public sealed class UVFace
    {
        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.
         * During STL triangle generation, we only draw the 'roots' (faces with no parent) and we count
         * the chain of children for how "high" the face should be. */
        public UVFace? Parent = null;
        public UVFace? Child = null;

        /* This is used to make a linked list of faces, instead of generating a list which requires resize/reallocation/copies. 
         * Particularly useful when you have a model that consists of 49 million visible faces...*/
        public UVFace? FlatListNext = null;
    }

    [Flags]
    public enum FaceOrientation : short
    {
        None = 0,
        Top = 1,
        Bottom = 2,
        Left = 4,
        Right = 8,
        Front = 16,
        Back = 32
    }

    public static FaceOrientation GetOpenFaces(Mat layer, int x, int y, Mat? layerBelow = null, Mat? layerAbove = null)
    {
        var layerSpan = layer.GetDataByteSpan();

        var foundFaces = FaceOrientation.None;
        var pixelPos = layer.GetPixelPos(x, y);
        if (layerSpan[pixelPos] == 0)
        {
            return foundFaces;
        }

        if (layerBelow is null)
        {
            foundFaces |= FaceOrientation.Bottom;
        }
        else
        {
            var belowSpan = layerBelow.GetDataByteSpan();
            if (belowSpan[pixelPos] == 0)
            {
                foundFaces |= FaceOrientation.Bottom;
            }
        }

        if (layerAbove is null)
        {
            foundFaces |= FaceOrientation.Top;
        }
        else
        {
            var aboveSpan = layerAbove.GetDataByteSpan();
            if (aboveSpan[pixelPos] == 0)
            {
                foundFaces |= FaceOrientation.Top;
            }
        }

        if (x == 0 || layerSpan[pixelPos-1] == 0)
        {
            foundFaces |= FaceOrientation.Left;
        }

        if (x == layer.Width - 1 || layerSpan[pixelPos+1] == 0)
        {
            foundFaces |= FaceOrientation.Right;
        }

        if (y == 0 || layerSpan[layer.GetPixelPos(x, y - 1)] == 0)
        {
            foundFaces |= FaceOrientation.Front;
        }

        if (y == layer.Height - 1 || layerSpan[layer.GetPixelPos(x, y + 1)] == 0)
        {
            foundFaces |= FaceOrientation.Back;
        }

        return foundFaces;
    }

    public static Mat BuildVoxelLayerImage(Mat curLayer, Mat? layerAbove = null, Mat? layerBelow = null, ChainApproxMethod contourCompressionMethod = ChainApproxMethod.ChainApproxSimple)
    {
        /* The goal of the VoxelLayerImage is to reduce as much as possible, the number of pixels we need to do 6 direction neighbor checking on */

        /* the outer contours of the current layer should always be checked, they by definition should have an exposed face */
        using var contours = curLayer.FindContours(RetrType.Tree, contourCompressionMethod);
        var onlyContours = curLayer.NewBlank();
        CvInvoke.DrawContours(onlyContours, contours, -1, EmguExtensions.WhiteColor, 1);

        bool needAboveDispose = layerAbove is null;
        bool needBelowDispose = layerBelow is null;

        layerAbove ??= curLayer.NewBlank();
        layerBelow ??= curLayer.NewBlank();

        /* anything that is in the current layer but is not in the layer above, by definition has an exposed face */
        var 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 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(upperSubtract, voxelLayer, voxelLayer);
        CvInvoke.BitwiseOr(lowerSubtract, voxelLayer, voxelLayer);

        /* dispoose of the layerAbove/layerBelow if they were allocated here */
        if (needAboveDispose)
        {
            layerAbove.Dispose();
        }
        if (needBelowDispose)
        {
            layerBelow.Dispose();
        }
        onlyContours.Dispose();

        return voxelLayer;
    }

    /* 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 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...*/

        var LeftNormal = new Vector3(-1, 0, 0);
        var RightNormal = new Vector3(1, 0, 0);
        var TopNormal = new Vector3(0, 0, 1);
        var BottomNormal = new Vector3(0, 0, -1);
        var BackNormal = new Vector3(0, 1, 0);
        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 */
        float height = 0;
        var totalFaceCount = 1;
        UVFace child = face;
        while (child.Child is not null)
        {
            height += child.LayerHeight;
            totalFaceCount++;
            child = child.Child;
        }
        height += child.LayerHeight;

        if (face.Type == FaceOrientation.Front)
        {
            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, 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, 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, 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, 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, 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);
        }
        /*else
        {
            yield break;
        }*/

    }
}