diff options
author | dahlia | 2010-05-06 21:36:27 -0700 |
---|---|---|
committer | dahlia | 2010-05-06 21:36:27 -0700 |
commit | 5d1e9947ed43490fb458af62bd9e8596a816f7b8 (patch) | |
tree | b89886e0faf5bd2194491669ad8a8b3c1ea340a0 /OpenSim/Region/Physics | |
parent | Also remove sale and search flags on god owner change. (diff) | |
download | opensim-SC-5d1e9947ed43490fb458af62bd9e8596a816f7b8.zip opensim-SC-5d1e9947ed43490fb458af62bd9e8596a816f7b8.tar.gz opensim-SC-5d1e9947ed43490fb458af62bd9e8596a816f7b8.tar.bz2 opensim-SC-5d1e9947ed43490fb458af62bd9e8596a816f7b8.tar.xz |
Sculpt meshing refactoring - improves mesh accuracy and UV mapping
Sync with PrimMesher r55
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/Physics/Meshing/SculptMap.cs | 169 | ||||
-rw-r--r-- | OpenSim/Region/Physics/Meshing/SculptMesh.cs | 1280 |
2 files changed, 810 insertions, 639 deletions
diff --git a/OpenSim/Region/Physics/Meshing/SculptMap.cs b/OpenSim/Region/Physics/Meshing/SculptMap.cs new file mode 100644 index 0000000..4d3f82b --- /dev/null +++ b/OpenSim/Region/Physics/Meshing/SculptMap.cs | |||
@@ -0,0 +1,169 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSimulator Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | // to build without references to System.Drawing, comment this out | ||
29 | #define SYSTEM_DRAWING | ||
30 | |||
31 | using System; | ||
32 | using System.Collections.Generic; | ||
33 | using System.Text; | ||
34 | |||
35 | #if SYSTEM_DRAWING | ||
36 | using System.Drawing; | ||
37 | using System.Drawing.Imaging; | ||
38 | |||
39 | namespace PrimMesher | ||
40 | { | ||
41 | public class SculptMap | ||
42 | { | ||
43 | public int width; | ||
44 | public int height; | ||
45 | public byte[] redBytes; | ||
46 | public byte[] greenBytes; | ||
47 | public byte[] blueBytes; | ||
48 | |||
49 | public SculptMap() | ||
50 | { | ||
51 | } | ||
52 | |||
53 | public SculptMap(Bitmap bm, int lod) | ||
54 | { | ||
55 | int bmW = bm.Width; | ||
56 | int bmH = bm.Height; | ||
57 | |||
58 | if (bmW == 0 || bmH == 0) | ||
59 | throw new Exception("SculptMap: bitmap has no data"); | ||
60 | |||
61 | int numLodPixels = lod * 2 * lod * 2; // (32 * 2)^2 = 64^2 pixels for default sculpt map image | ||
62 | |||
63 | width = bmW; | ||
64 | height = bmH; | ||
65 | while (width * height > numLodPixels) | ||
66 | { | ||
67 | width >>= 1; | ||
68 | height >>= 1; | ||
69 | } | ||
70 | |||
71 | width >>= 1; | ||
72 | height >>= 1; | ||
73 | |||
74 | try | ||
75 | { | ||
76 | if (!(bmW == width * 2 && bmH == height * 2)) | ||
77 | bm = ScaleImage(bm, width * 2, height * 2, | ||
78 | System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor); | ||
79 | } | ||
80 | |||
81 | catch (Exception e) | ||
82 | { | ||
83 | throw new Exception("Exception in ScaleImage(): e: " + e.ToString()); | ||
84 | } | ||
85 | |||
86 | |||
87 | int numBytes = (width + 1) * (height + 1); | ||
88 | redBytes = new byte[numBytes]; | ||
89 | greenBytes = new byte[numBytes]; | ||
90 | blueBytes = new byte[numBytes]; | ||
91 | |||
92 | int byteNdx = 0; | ||
93 | |||
94 | try | ||
95 | { | ||
96 | for (int y = 0; y <= height; y++) | ||
97 | { | ||
98 | for (int x = 0; x <= width; x++) | ||
99 | { | ||
100 | int bmY = y < height ? y * 2 : y * 2 - 1; | ||
101 | int bmX = x < width ? x * 2 : x * 2 - 1; | ||
102 | Color c = bm.GetPixel(bmX, bmY); | ||
103 | |||
104 | redBytes[byteNdx] = c.R; | ||
105 | greenBytes[byteNdx] = c.G; | ||
106 | blueBytes[byteNdx] = c.B; | ||
107 | |||
108 | ++byteNdx; | ||
109 | } | ||
110 | } | ||
111 | } | ||
112 | catch (Exception e) | ||
113 | { | ||
114 | throw new Exception("Caught exception processing byte arrays in SculptMap(): e: " + e.ToString()); | ||
115 | } | ||
116 | |||
117 | width++; | ||
118 | height++; | ||
119 | } | ||
120 | |||
121 | public List<List<Coord>> ToRows(bool mirror) | ||
122 | { | ||
123 | int numRows = height; | ||
124 | int numCols = width; | ||
125 | |||
126 | List<List<Coord>> rows = new List<List<Coord>>(numRows); | ||
127 | |||
128 | float pixScale = 1.0f / 255; | ||
129 | |||
130 | int rowNdx, colNdx; | ||
131 | int smNdx = 0; | ||
132 | |||
133 | for (rowNdx = 0; rowNdx < numRows; rowNdx++) | ||
134 | { | ||
135 | List<Coord> row = new List<Coord>(numCols); | ||
136 | for (colNdx = 0; colNdx < numCols; colNdx++) | ||
137 | { | ||
138 | if (mirror) | ||
139 | row.Add(new Coord(-(redBytes[smNdx] * pixScale - 0.5f), (greenBytes[smNdx] * pixScale - 0.5f), blueBytes[smNdx] * pixScale - 0.5f)); | ||
140 | else | ||
141 | row.Add(new Coord(redBytes[smNdx] * pixScale - 0.5f, greenBytes[smNdx] * pixScale - 0.5f, blueBytes[smNdx] * pixScale - 0.5f)); | ||
142 | |||
143 | ++smNdx; | ||
144 | } | ||
145 | rows.Add(row); | ||
146 | } | ||
147 | return rows; | ||
148 | } | ||
149 | |||
150 | private Bitmap ScaleImage(Bitmap srcImage, int destWidth, int destHeight, | ||
151 | System.Drawing.Drawing2D.InterpolationMode interpMode) | ||
152 | { | ||
153 | Bitmap scaledImage = new Bitmap(srcImage, destWidth, destHeight); | ||
154 | scaledImage.SetResolution(96.0f, 96.0f); | ||
155 | |||
156 | Graphics grPhoto = Graphics.FromImage(scaledImage); | ||
157 | grPhoto.InterpolationMode = interpMode; | ||
158 | |||
159 | grPhoto.DrawImage(srcImage, | ||
160 | new Rectangle(0, 0, destWidth, destHeight), | ||
161 | new Rectangle(0, 0, srcImage.Width, srcImage.Height), | ||
162 | GraphicsUnit.Pixel); | ||
163 | |||
164 | grPhoto.Dispose(); | ||
165 | return scaledImage; | ||
166 | } | ||
167 | } | ||
168 | } | ||
169 | #endif | ||
diff --git a/OpenSim/Region/Physics/Meshing/SculptMesh.cs b/OpenSim/Region/Physics/Meshing/SculptMesh.cs index ebc5be6..06606c3 100644 --- a/OpenSim/Region/Physics/Meshing/SculptMesh.cs +++ b/OpenSim/Region/Physics/Meshing/SculptMesh.cs | |||
@@ -1,645 +1,647 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (c) Contributors | 2 | * Copyright (c) Contributors |
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | 3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. |
4 | * | 4 | * |
5 | * Redistribution and use in source and binary forms, with or without | 5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions are met: | 6 | * modification, are permitted provided that the following conditions are met: |
7 | * * Redistributions of source code must retain the above copyright | 7 | * * Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. | 8 | * notice, this list of conditions and the following disclaimer. |
9 | * * Redistributions in binary form must reproduce the above copyright | 9 | * * Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the | 10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. | 11 | * documentation and/or other materials provided with the distribution. |
12 | * * Neither the name of the OpenSimulator Project nor the | 12 | * * Neither the name of the OpenSimulator Project nor the |
13 | * names of its contributors may be used to endorse or promote products | 13 | * names of its contributors may be used to endorse or promote products |
14 | * derived from this software without specific prior written permission. | 14 | * derived from this software without specific prior written permission. |
15 | * | 15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | 16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY |
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | 17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | 19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY |
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | 20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | 22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | */ | 26 | */ |
27 | 27 | ||
28 | // to build without references to System.Drawing, comment this out | 28 | // to build without references to System.Drawing, comment this out |
29 | #define SYSTEM_DRAWING | 29 | #define SYSTEM_DRAWING |
30 | 30 | ||
31 | using System; | 31 | using System; |
32 | using System.Collections.Generic; | 32 | using System.Collections.Generic; |
33 | using System.Text; | 33 | using System.Text; |
34 | using System.IO; | 34 | using System.IO; |
35 | 35 | ||
36 | #if SYSTEM_DRAWING | 36 | #if SYSTEM_DRAWING |
37 | using System.Drawing; | 37 | using System.Drawing; |
38 | using System.Drawing.Imaging; | 38 | using System.Drawing.Imaging; |
39 | #endif | 39 | #endif |
40 | 40 | ||
41 | namespace PrimMesher | 41 | namespace PrimMesher |
42 | { | 42 | { |
43 | 43 | ||
44 | public class SculptMesh | 44 | public class SculptMesh |
45 | { | 45 | { |
46 | public List<Coord> coords; | 46 | public List<Coord> coords; |
47 | public List<Face> faces; | 47 | public List<Face> faces; |
48 | 48 | ||
49 | public List<ViewerFace> viewerFaces; | 49 | public List<ViewerFace> viewerFaces; |
50 | public List<Coord> normals; | 50 | public List<Coord> normals; |
51 | public List<UVCoord> uvs; | 51 | public List<UVCoord> uvs; |
52 | 52 | ||
53 | public enum SculptType { sphere = 1, torus = 2, plane = 3, cylinder = 4 }; | 53 | public enum SculptType { sphere = 1, torus = 2, plane = 3, cylinder = 4 }; |
54 | 54 | ||
55 | #if SYSTEM_DRAWING | 55 | #if SYSTEM_DRAWING |
56 | private Bitmap ScaleImage(Bitmap srcImage, float scale, bool removeAlpha) | 56 | |
57 | { | 57 | public SculptMesh SculptMeshFromFile(string fileName, SculptType sculptType, int lod, bool viewerMode) |
58 | int sourceWidth = srcImage.Width; | 58 | { |
59 | int sourceHeight = srcImage.Height; | 59 | Bitmap bitmap = (Bitmap)Bitmap.FromFile(fileName); |
60 | int sourceX = 0; | 60 | SculptMesh sculptMesh = new SculptMesh(bitmap, sculptType, lod, viewerMode); |
61 | int sourceY = 0; | 61 | bitmap.Dispose(); |
62 | 62 | return sculptMesh; | |
63 | int destX = 0; | 63 | } |
64 | int destY = 0; | 64 | |
65 | int destWidth = (int)(srcImage.Width * scale); | 65 | |
66 | int destHeight = (int)(srcImage.Height * scale); | 66 | public SculptMesh(string fileName, int sculptType, int lod, int viewerMode, int mirror, int invert) |
67 | 67 | { | |
68 | Bitmap scaledImage; | 68 | Bitmap bitmap = (Bitmap)Bitmap.FromFile(fileName); |
69 | 69 | _SculptMesh(bitmap, (SculptType)sculptType, lod, viewerMode != 0, mirror != 0, invert != 0); | |
70 | if (removeAlpha) | 70 | bitmap.Dispose(); |
71 | { | 71 | } |
72 | if (srcImage.PixelFormat == PixelFormat.Format32bppArgb) | 72 | #endif |
73 | for (int y = 0; y < srcImage.Height; y++) | 73 | |
74 | for (int x = 0; x < srcImage.Width; x++) | 74 | /// <summary> |
75 | { | 75 | /// ** Experimental ** May disappear from future versions ** not recommeneded for use in applications |
76 | Color c = srcImage.GetPixel(x, y); | 76 | /// Construct a sculpt mesh from a 2D array of floats |
77 | srcImage.SetPixel(x, y, Color.FromArgb(255, c.R, c.G, c.B)); | 77 | /// </summary> |
78 | } | 78 | /// <param name="zMap"></param> |
79 | 79 | /// <param name="xBegin"></param> | |
80 | scaledImage = new Bitmap(destWidth, destHeight, | 80 | /// <param name="xEnd"></param> |
81 | PixelFormat.Format24bppRgb); | 81 | /// <param name="yBegin"></param> |
82 | } | 82 | /// <param name="yEnd"></param> |
83 | else | 83 | /// <param name="viewerMode"></param> |
84 | scaledImage = new Bitmap(srcImage, destWidth, destHeight); | 84 | public SculptMesh(float[,] zMap, float xBegin, float xEnd, float yBegin, float yEnd, bool viewerMode) |
85 | 85 | { | |
86 | scaledImage.SetResolution(96.0f, 96.0f); | 86 | float xStep, yStep; |
87 | 87 | float uStep, vStep; | |
88 | Graphics grPhoto = Graphics.FromImage(scaledImage); | 88 | |
89 | grPhoto.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Low; | 89 | int numYElements = zMap.GetLength(0); |
90 | 90 | int numXElements = zMap.GetLength(1); | |
91 | grPhoto.DrawImage(srcImage, | 91 | |
92 | new Rectangle(destX, destY, destWidth, destHeight), | 92 | try |
93 | new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight), | 93 | { |
94 | GraphicsUnit.Pixel); | 94 | xStep = (xEnd - xBegin) / (float)(numXElements - 1); |
95 | 95 | yStep = (yEnd - yBegin) / (float)(numYElements - 1); | |
96 | grPhoto.Dispose(); | 96 | |
97 | return scaledImage; | 97 | uStep = 1.0f / (numXElements - 1); |
98 | } | 98 | vStep = 1.0f / (numYElements - 1); |
99 | 99 | } | |
100 | 100 | catch (DivideByZeroException) | |
101 | public SculptMesh SculptMeshFromFile(string fileName, SculptType sculptType, int lod, bool viewerMode) | 101 | { |
102 | { | 102 | return; |
103 | Bitmap bitmap = (Bitmap)Bitmap.FromFile(fileName); | 103 | } |
104 | SculptMesh sculptMesh = new SculptMesh(bitmap, sculptType, lod, viewerMode); | 104 | |
105 | bitmap.Dispose(); | 105 | coords = new List<Coord>(); |
106 | return sculptMesh; | 106 | faces = new List<Face>(); |
107 | } | 107 | normals = new List<Coord>(); |
108 | 108 | uvs = new List<UVCoord>(); | |
109 | public SculptMesh(string fileName, int sculptType, int lod, int viewerMode, int mirror, int invert) | 109 | |
110 | { | 110 | viewerFaces = new List<ViewerFace>(); |
111 | Bitmap bitmap = (Bitmap)Bitmap.FromFile(fileName); | 111 | |
112 | _SculptMesh(bitmap, (SculptType)sculptType, lod, viewerMode != 0, mirror != 0, invert != 0); | 112 | int p1, p2, p3, p4; |
113 | bitmap.Dispose(); | 113 | |
114 | } | 114 | int x, y; |
115 | #endif | 115 | int xStart = 0, yStart = 0; |
116 | 116 | ||
117 | /// <summary> | 117 | for (y = yStart; y < numYElements; y++) |
118 | /// ** Experimental ** May disappear from future versions ** not recommeneded for use in applications | 118 | { |
119 | /// Construct a sculpt mesh from a 2D array of floats | 119 | int rowOffset = y * numXElements; |
120 | /// </summary> | 120 | |
121 | /// <param name="zMap"></param> | 121 | for (x = xStart; x < numXElements; x++) |
122 | /// <param name="xBegin"></param> | 122 | { |
123 | /// <param name="xEnd"></param> | 123 | /* |
124 | /// <param name="yBegin"></param> | 124 | * p1-----p2 |
125 | /// <param name="yEnd"></param> | 125 | * | \ f2 | |
126 | /// <param name="viewerMode"></param> | 126 | * | \ | |
127 | public SculptMesh(float[,] zMap, float xBegin, float xEnd, float yBegin, float yEnd, bool viewerMode) | 127 | * | f1 \| |
128 | { | 128 | * p3-----p4 |
129 | float xStep, yStep; | 129 | */ |
130 | float uStep, vStep; | 130 | |
131 | 131 | p4 = rowOffset + x; | |
132 | int numYElements = zMap.GetLength(0); | 132 | p3 = p4 - 1; |
133 | int numXElements = zMap.GetLength(1); | 133 | |
134 | 134 | p2 = p4 - numXElements; | |
135 | try | 135 | p1 = p3 - numXElements; |
136 | { | 136 | |
137 | xStep = (xEnd - xBegin) / (float)(numXElements - 1); | 137 | Coord c = new Coord(xBegin + x * xStep, yBegin + y * yStep, zMap[y, x]); |
138 | yStep = (yEnd - yBegin) / (float)(numYElements - 1); | 138 | this.coords.Add(c); |
139 | 139 | if (viewerMode) | |
140 | uStep = 1.0f / (numXElements - 1); | 140 | { |
141 | vStep = 1.0f / (numYElements - 1); | 141 | this.normals.Add(new Coord()); |
142 | } | 142 | this.uvs.Add(new UVCoord(uStep * x, 1.0f - vStep * y)); |
143 | catch (DivideByZeroException) | 143 | } |
144 | { | 144 | |
145 | return; | 145 | if (y > 0 && x > 0) |
146 | } | 146 | { |
147 | 147 | Face f1, f2; | |
148 | coords = new List<Coord>(); | 148 | |
149 | faces = new List<Face>(); | 149 | if (viewerMode) |
150 | normals = new List<Coord>(); | 150 | { |
151 | uvs = new List<UVCoord>(); | 151 | f1 = new Face(p1, p4, p3, p1, p4, p3); |
152 | 152 | f1.uv1 = p1; | |
153 | viewerFaces = new List<ViewerFace>(); | 153 | f1.uv2 = p4; |
154 | 154 | f1.uv3 = p3; | |
155 | int p1, p2, p3, p4; | 155 | |
156 | 156 | f2 = new Face(p1, p2, p4, p1, p2, p4); | |
157 | int x, y; | 157 | f2.uv1 = p1; |
158 | int xStart = 0, yStart = 0; | 158 | f2.uv2 = p2; |
159 | 159 | f2.uv3 = p4; | |
160 | for (y = yStart; y < numYElements; y++) | 160 | } |
161 | { | 161 | else |
162 | int rowOffset = y * numXElements; | 162 | { |
163 | 163 | f1 = new Face(p1, p4, p3); | |
164 | for (x = xStart; x < numXElements; x++) | 164 | f2 = new Face(p1, p2, p4); |
165 | { | 165 | } |
166 | |||
167 | this.faces.Add(f1); | ||
168 | this.faces.Add(f2); | ||
169 | } | ||
170 | } | ||
171 | } | ||
172 | |||
173 | if (viewerMode) | ||
174 | calcVertexNormals(SculptType.plane, numXElements, numYElements); | ||
175 | } | ||
176 | |||
177 | #if SYSTEM_DRAWING | ||
178 | public SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode) | ||
179 | { | ||
180 | _SculptMesh(sculptBitmap, sculptType, lod, viewerMode, false, false); | ||
181 | } | ||
182 | |||
183 | public SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode, bool mirror, bool invert) | ||
184 | { | ||
185 | _SculptMesh(sculptBitmap, sculptType, lod, viewerMode, mirror, invert); | ||
186 | } | ||
187 | #endif | ||
188 | |||
189 | public SculptMesh(List<List<Coord>> rows, SculptType sculptType, bool viewerMode, bool mirror, bool invert) | ||
190 | { | ||
191 | _SculptMesh(rows, sculptType, viewerMode, mirror, invert); | ||
192 | } | ||
193 | |||
194 | #if SYSTEM_DRAWING | ||
195 | /// <summary> | ||
196 | /// converts a bitmap to a list of lists of coords, while scaling the image. | ||
197 | /// the scaling is done in floating point so as to allow for reduced vertex position | ||
198 | /// quantization as the position will be averaged between pixel values. this routine will | ||
199 | /// likely fail if the bitmap width and height are not powers of 2. | ||
200 | /// </summary> | ||
201 | /// <param name="bitmap"></param> | ||
202 | /// <param name="scale"></param> | ||
203 | /// <param name="mirror"></param> | ||
204 | /// <returns></returns> | ||
205 | private List<List<Coord>> bitmap2Coords(Bitmap bitmap, int scale, bool mirror) | ||
206 | { | ||
207 | int numRows = bitmap.Height / scale; | ||
208 | int numCols = bitmap.Width / scale; | ||
209 | List<List<Coord>> rows = new List<List<Coord>>(numRows); | ||
210 | |||
211 | float pixScale = 1.0f / (scale * scale); | ||
212 | pixScale /= 255; | ||
213 | |||
214 | int imageX, imageY = 0; | ||
215 | |||
216 | int rowNdx, colNdx; | ||
217 | |||
218 | for (rowNdx = 0; rowNdx < numRows; rowNdx++) | ||
219 | { | ||
220 | List<Coord> row = new List<Coord>(numCols); | ||
221 | for (colNdx = 0; colNdx < numCols; colNdx++) | ||
222 | { | ||
223 | imageX = colNdx * scale; | ||
224 | int imageYStart = rowNdx * scale; | ||
225 | int imageYEnd = imageYStart + scale; | ||
226 | int imageXEnd = imageX + scale; | ||
227 | float rSum = 0.0f; | ||
228 | float gSum = 0.0f; | ||
229 | float bSum = 0.0f; | ||
230 | for (; imageX < imageXEnd; imageX++) | ||
231 | { | ||
232 | for (imageY = imageYStart; imageY < imageYEnd; imageY++) | ||
233 | { | ||
234 | Color c = bitmap.GetPixel(imageX, imageY); | ||
235 | if (c.A != 255) | ||
236 | { | ||
237 | bitmap.SetPixel(imageX, imageY, Color.FromArgb(255, c.R, c.G, c.B)); | ||
238 | c = bitmap.GetPixel(imageX, imageY); | ||
239 | } | ||
240 | rSum += c.R; | ||
241 | gSum += c.G; | ||
242 | bSum += c.B; | ||
243 | } | ||
244 | } | ||
245 | if (mirror) | ||
246 | row.Add(new Coord(-(rSum * pixScale - 0.5f), gSum * pixScale - 0.5f, bSum * pixScale - 0.5f)); | ||
247 | else | ||
248 | row.Add(new Coord(rSum * pixScale - 0.5f, gSum * pixScale - 0.5f, bSum * pixScale - 0.5f)); | ||
249 | |||
250 | } | ||
251 | rows.Add(row); | ||
252 | } | ||
253 | return rows; | ||
254 | } | ||
255 | |||
256 | private List<List<Coord>> bitmap2CoordsSampled(Bitmap bitmap, int scale, bool mirror) | ||
257 | { | ||
258 | int numRows = bitmap.Height / scale; | ||
259 | int numCols = bitmap.Width / scale; | ||
260 | List<List<Coord>> rows = new List<List<Coord>>(numRows); | ||
261 | |||
262 | float pixScale = 1.0f / 256.0f; | ||
263 | |||
264 | int imageX, imageY = 0; | ||
265 | |||
266 | int rowNdx, colNdx; | ||
267 | |||
268 | for (rowNdx = 0; rowNdx <= numRows; rowNdx++) | ||
269 | { | ||
270 | List<Coord> row = new List<Coord>(numCols); | ||
271 | imageY = rowNdx * scale; | ||
272 | if (rowNdx == numRows) imageY--; | ||
273 | for (colNdx = 0; colNdx <= numCols; colNdx++) | ||
274 | { | ||
275 | imageX = colNdx * scale; | ||
276 | if (colNdx == numCols) imageX--; | ||
277 | |||
278 | Color c = bitmap.GetPixel(imageX, imageY); | ||
279 | if (c.A != 255) | ||
280 | { | ||
281 | bitmap.SetPixel(imageX, imageY, Color.FromArgb(255, c.R, c.G, c.B)); | ||
282 | c = bitmap.GetPixel(imageX, imageY); | ||
283 | } | ||
284 | |||
285 | if (mirror) | ||
286 | row.Add(new Coord(-(c.R * pixScale - 0.5f), c.G * pixScale - 0.5f, c.B * pixScale - 0.5f)); | ||
287 | else | ||
288 | row.Add(new Coord(c.R * pixScale - 0.5f, c.G * pixScale - 0.5f, c.B * pixScale - 0.5f)); | ||
289 | |||
290 | } | ||
291 | rows.Add(row); | ||
292 | } | ||
293 | return rows; | ||
294 | } | ||
295 | |||
296 | |||
297 | void _SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode, bool mirror, bool invert) | ||
298 | { | ||
299 | _SculptMesh(new SculptMap(sculptBitmap, lod).ToRows(mirror), sculptType, viewerMode, mirror, invert); | ||
300 | } | ||
301 | #endif | ||
302 | |||
303 | void _SculptMesh(List<List<Coord>> rows, SculptType sculptType, bool viewerMode, bool mirror, bool invert) | ||
304 | { | ||
305 | coords = new List<Coord>(); | ||
306 | faces = new List<Face>(); | ||
307 | normals = new List<Coord>(); | ||
308 | uvs = new List<UVCoord>(); | ||
309 | |||
310 | sculptType = (SculptType)(((int)sculptType) & 0x07); | ||
311 | |||
312 | if (mirror) | ||
313 | if (sculptType == SculptType.plane) | ||
314 | invert = !invert; | ||
315 | |||
316 | viewerFaces = new List<ViewerFace>(); | ||
317 | |||
318 | int width = rows[0].Count; | ||
319 | |||
320 | int p1, p2, p3, p4; | ||
321 | |||
322 | int imageX, imageY; | ||
323 | |||
324 | if (sculptType != SculptType.plane) | ||
325 | { | ||
326 | if (rows.Count % 2 == 0) | ||
327 | { | ||
328 | for (int rowNdx = 0; rowNdx < rows.Count; rowNdx++) | ||
329 | rows[rowNdx].Add(rows[rowNdx][0]); | ||
330 | } | ||
331 | else | ||
332 | { | ||
333 | int lastIndex = rows[0].Count - 1; | ||
334 | |||
335 | for (int i = 0; i < rows.Count; i++) | ||
336 | rows[i][0] = rows[i][lastIndex]; | ||
337 | } | ||
338 | } | ||
339 | |||
340 | Coord topPole = rows[0][width / 2]; | ||
341 | Coord bottomPole = rows[rows.Count - 1][width / 2]; | ||
342 | |||
343 | if (sculptType == SculptType.sphere) | ||
344 | { | ||
345 | if (rows.Count % 2 == 0) | ||
346 | { | ||
347 | int count = rows[0].Count; | ||
348 | List<Coord> topPoleRow = new List<Coord>(count); | ||
349 | List<Coord> bottomPoleRow = new List<Coord>(count); | ||
350 | |||
351 | for (int i = 0; i < count; i++) | ||
352 | { | ||
353 | topPoleRow.Add(topPole); | ||
354 | bottomPoleRow.Add(bottomPole); | ||
355 | } | ||
356 | rows.Insert(0, topPoleRow); | ||
357 | rows.Add(bottomPoleRow); | ||
358 | } | ||
359 | else | ||
360 | { | ||
361 | int count = rows[0].Count; | ||
362 | |||
363 | List<Coord> topPoleRow = rows[0]; | ||
364 | List<Coord> bottomPoleRow = rows[rows.Count - 1]; | ||
365 | |||
366 | for (int i = 0; i < count; i++) | ||
367 | { | ||
368 | topPoleRow[i] = topPole; | ||
369 | bottomPoleRow[i] = bottomPole; | ||
370 | } | ||
371 | } | ||
372 | } | ||
373 | |||
374 | if (sculptType == SculptType.torus) | ||
375 | rows.Add(rows[0]); | ||
376 | |||
377 | int coordsDown = rows.Count; | ||
378 | int coordsAcross = rows[0].Count; | ||
379 | int lastColumn = coordsAcross - 1; | ||
380 | |||
381 | float widthUnit = 1.0f / (coordsAcross - 1); | ||
382 | float heightUnit = 1.0f / (coordsDown - 1); | ||
383 | |||
384 | for (imageY = 0; imageY < coordsDown; imageY++) | ||
385 | { | ||
386 | int rowOffset = imageY * coordsAcross; | ||
387 | |||
388 | for (imageX = 0; imageX < coordsAcross; imageX++) | ||
389 | { | ||
166 | /* | 390 | /* |
167 | * p1-----p2 | 391 | * p1-----p2 |
168 | * | \ f2 | | 392 | * | \ f2 | |
169 | * | \ | | 393 | * | \ | |
170 | * | f1 \| | 394 | * | f1 \| |
171 | * p3-----p4 | 395 | * p3-----p4 |
172 | */ | 396 | */ |
173 | 397 | ||
174 | p4 = rowOffset + x; | 398 | p4 = rowOffset + imageX; |
175 | p3 = p4 - 1; | 399 | p3 = p4 - 1; |
176 | 400 | ||
177 | p2 = p4 - numXElements; | 401 | p2 = p4 - coordsAcross; |
178 | p1 = p3 - numXElements; | 402 | p1 = p3 - coordsAcross; |
179 | 403 | ||
180 | Coord c = new Coord(xBegin + x * xStep, yBegin + y * yStep, zMap[y, x]); | 404 | this.coords.Add(rows[imageY][imageX]); |
181 | this.coords.Add(c); | 405 | if (viewerMode) |
182 | if (viewerMode) | 406 | { |
183 | { | 407 | this.normals.Add(new Coord()); |
184 | this.normals.Add(new Coord()); | 408 | this.uvs.Add(new UVCoord(widthUnit * imageX, heightUnit * imageY)); |
185 | this.uvs.Add(new UVCoord(uStep * x, 1.0f - vStep * y)); | 409 | } |
186 | } | 410 | |
187 | 411 | if (imageY > 0 && imageX > 0) | |
188 | if (y > 0 && x > 0) | 412 | { |
189 | { | 413 | Face f1, f2; |
190 | Face f1, f2; | 414 | |
191 | 415 | if (viewerMode) | |
192 | if (viewerMode) | 416 | { |
193 | { | 417 | if (invert) |
194 | f1 = new Face(p1, p4, p3, p1, p4, p3); | 418 | { |
195 | f1.uv1 = p1; | 419 | f1 = new Face(p1, p4, p3, p1, p4, p3); |
196 | f1.uv2 = p4; | 420 | f1.uv1 = p1; |
197 | f1.uv3 = p3; | 421 | f1.uv2 = p4; |
198 | 422 | f1.uv3 = p3; | |
199 | f2 = new Face(p1, p2, p4, p1, p2, p4); | 423 | |
200 | f2.uv1 = p1; | 424 | f2 = new Face(p1, p2, p4, p1, p2, p4); |
201 | f2.uv2 = p2; | 425 | f2.uv1 = p1; |
202 | f2.uv3 = p4; | 426 | f2.uv2 = p2; |
203 | } | 427 | f2.uv3 = p4; |
204 | else | 428 | } |
205 | { | 429 | else |
206 | f1 = new Face(p1, p4, p3); | 430 | { |
207 | f2 = new Face(p1, p2, p4); | 431 | f1 = new Face(p1, p3, p4, p1, p3, p4); |
208 | } | 432 | f1.uv1 = p1; |
209 | 433 | f1.uv2 = p3; | |
210 | this.faces.Add(f1); | 434 | f1.uv3 = p4; |
211 | this.faces.Add(f2); | 435 | |
212 | } | 436 | f2 = new Face(p1, p4, p2, p1, p4, p2); |
213 | } | 437 | f2.uv1 = p1; |
214 | } | 438 | f2.uv2 = p4; |
215 | 439 | f2.uv3 = p2; | |
216 | if (viewerMode) | 440 | } |
217 | calcVertexNormals(SculptType.plane, numXElements, numYElements); | 441 | } |
218 | } | 442 | else |
219 | 443 | { | |
220 | #if SYSTEM_DRAWING | 444 | if (invert) |
221 | public SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode) | 445 | { |
222 | { | 446 | f1 = new Face(p1, p4, p3); |
223 | _SculptMesh(sculptBitmap, sculptType, lod, viewerMode, false, false); | 447 | f2 = new Face(p1, p2, p4); |
224 | } | 448 | } |
225 | 449 | else | |
226 | public SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode, bool mirror, bool invert) | 450 | { |
227 | { | 451 | f1 = new Face(p1, p3, p4); |
228 | _SculptMesh(sculptBitmap, sculptType, lod, viewerMode, mirror, invert); | 452 | f2 = new Face(p1, p4, p2); |
229 | } | 453 | } |
230 | #endif | 454 | } |
231 | 455 | ||
232 | public SculptMesh(List<List<Coord>> rows, SculptType sculptType, bool viewerMode, bool mirror, bool invert) | 456 | this.faces.Add(f1); |
233 | { | 457 | this.faces.Add(f2); |
234 | _SculptMesh(rows, sculptType, viewerMode, mirror, invert); | 458 | } |
235 | } | 459 | } |
236 | 460 | } | |
237 | #if SYSTEM_DRAWING | 461 | |
238 | /// <summary> | 462 | if (viewerMode) |
239 | /// converts a bitmap to a list of lists of coords, while scaling the image. | 463 | calcVertexNormals(sculptType, coordsAcross, coordsDown); |
240 | /// the scaling is done in floating point so as to allow for reduced vertex position | 464 | } |
241 | /// quantization as the position will be averaged between pixel values. this routine will | 465 | |
242 | /// likely fail if the bitmap width and height are not powers of 2. | 466 | /// <summary> |
243 | /// </summary> | 467 | /// Duplicates a SculptMesh object. All object properties are copied by value, including lists. |
244 | /// <param name="bitmap"></param> | 468 | /// </summary> |
245 | /// <param name="scale"></param> | 469 | /// <returns></returns> |
246 | /// <param name="mirror"></param> | 470 | public SculptMesh Copy() |
247 | /// <returns></returns> | 471 | { |
248 | private List<List<Coord>> bitmap2Coords(Bitmap bitmap, int scale, bool mirror) | 472 | return new SculptMesh(this); |
249 | { | 473 | } |
250 | int numRows = bitmap.Height / scale; | 474 | |
251 | int numCols = bitmap.Width / scale; | 475 | public SculptMesh(SculptMesh sm) |
252 | List<List<Coord>> rows = new List<List<Coord>>(numRows); | 476 | { |
253 | 477 | coords = new List<Coord>(sm.coords); | |
254 | float pixScale = 1.0f / (scale * scale); | 478 | faces = new List<Face>(sm.faces); |
255 | pixScale /= 255; | 479 | viewerFaces = new List<ViewerFace>(sm.viewerFaces); |
256 | 480 | normals = new List<Coord>(sm.normals); | |
257 | int imageX, imageY = 0; | 481 | uvs = new List<UVCoord>(sm.uvs); |
258 | 482 | } | |
259 | int rowNdx, colNdx; | 483 | |
260 | 484 | private void calcVertexNormals(SculptType sculptType, int xSize, int ySize) | |
261 | for (rowNdx = 0; rowNdx < numRows; rowNdx++) | 485 | { // compute vertex normals by summing all the surface normals of all the triangles sharing |
262 | { | 486 | // each vertex and then normalizing |
263 | List<Coord> row = new List<Coord>(numCols); | 487 | int numFaces = this.faces.Count; |
264 | for (colNdx = 0; colNdx < numCols; colNdx++) | 488 | for (int i = 0; i < numFaces; i++) |
265 | { | 489 | { |
266 | imageX = colNdx * scale; | 490 | Face face = this.faces[i]; |
267 | int imageYStart = rowNdx * scale; | 491 | Coord surfaceNormal = face.SurfaceNormal(this.coords); |
268 | int imageYEnd = imageYStart + scale; | 492 | this.normals[face.n1] += surfaceNormal; |
269 | int imageXEnd = imageX + scale; | 493 | this.normals[face.n2] += surfaceNormal; |
270 | float rSum = 0.0f; | 494 | this.normals[face.n3] += surfaceNormal; |
271 | float gSum = 0.0f; | 495 | } |
272 | float bSum = 0.0f; | 496 | |
273 | for (; imageX < imageXEnd; imageX++) | 497 | int numNormals = this.normals.Count; |
274 | { | 498 | for (int i = 0; i < numNormals; i++) |
275 | for (imageY = imageYStart; imageY < imageYEnd; imageY++) | 499 | this.normals[i] = this.normals[i].Normalize(); |
276 | { | 500 | |
277 | Color c = bitmap.GetPixel(imageX, imageY); | 501 | if (sculptType != SculptType.plane) |
278 | if (c.A != 255) | 502 | { // blend the vertex normals at the cylinder seam |
279 | { | 503 | for (int y = 0; y < ySize; y++) |
280 | bitmap.SetPixel(imageX, imageY, Color.FromArgb(255, c.R, c.G, c.B)); | 504 | { |
281 | c = bitmap.GetPixel(imageX, imageY); | 505 | int rowOffset = y * xSize; |
282 | } | 506 | |
283 | rSum += c.R; | 507 | this.normals[rowOffset] = this.normals[rowOffset + xSize - 1] = (this.normals[rowOffset] + this.normals[rowOffset + xSize - 1]).Normalize(); |
284 | gSum += c.G; | 508 | } |
285 | bSum += c.B; | 509 | } |
286 | } | 510 | |
287 | } | 511 | foreach (Face face in this.faces) |
288 | if (mirror) | 512 | { |
289 | row.Add(new Coord(-(rSum * pixScale - 0.5f), gSum * pixScale - 0.5f, bSum * pixScale - 0.5f)); | 513 | ViewerFace vf = new ViewerFace(0); |
290 | else | 514 | vf.v1 = this.coords[face.v1]; |
291 | row.Add(new Coord(rSum * pixScale - 0.5f, gSum * pixScale - 0.5f, bSum * pixScale - 0.5f)); | 515 | vf.v2 = this.coords[face.v2]; |
292 | 516 | vf.v3 = this.coords[face.v3]; | |
293 | } | 517 | |
294 | rows.Add(row); | 518 | vf.coordIndex1 = face.v1; |
295 | } | 519 | vf.coordIndex2 = face.v2; |
296 | return rows; | 520 | vf.coordIndex3 = face.v3; |
297 | } | 521 | |
298 | 522 | vf.n1 = this.normals[face.n1]; | |
299 | 523 | vf.n2 = this.normals[face.n2]; | |
300 | void _SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode, bool mirror, bool invert) | 524 | vf.n3 = this.normals[face.n3]; |
301 | { | 525 | |
302 | coords = new List<Coord>(); | 526 | vf.uv1 = this.uvs[face.uv1]; |
303 | faces = new List<Face>(); | 527 | vf.uv2 = this.uvs[face.uv2]; |
304 | normals = new List<Coord>(); | 528 | vf.uv3 = this.uvs[face.uv3]; |
305 | uvs = new List<UVCoord>(); | 529 | |
306 | 530 | this.viewerFaces.Add(vf); | |
307 | sculptType = (SculptType)(((int)sculptType) & 0x07); | 531 | } |
308 | 532 | } | |
309 | if (mirror) | 533 | |
310 | if (sculptType == SculptType.plane) | 534 | /// <summary> |
311 | invert = !invert; | 535 | /// Adds a value to each XYZ vertex coordinate in the mesh |
312 | 536 | /// </summary> | |
313 | float sculptBitmapLod = (float)Math.Sqrt(sculptBitmap.Width * sculptBitmap.Height); | 537 | /// <param name="x"></param> |
314 | 538 | /// <param name="y"></param> | |
315 | float sourceScaleFactor = (float)(lod) / sculptBitmapLod; | 539 | /// <param name="z"></param> |
316 | 540 | public void AddPos(float x, float y, float z) | |
317 | float fScale = 1.0f / sourceScaleFactor; | 541 | { |
318 | 542 | int i; | |
319 | int iScale = (int)fScale; | 543 | int numVerts = this.coords.Count; |
320 | if (iScale < 1) iScale = 1; | 544 | Coord vert; |
321 | if (iScale > 2 && iScale % 2 == 0) | 545 | |
322 | _SculptMesh(bitmap2Coords(ScaleImage(sculptBitmap, 64.0f / sculptBitmapLod, true), 64 / lod, mirror), sculptType, viewerMode, mirror, invert); | 546 | for (i = 0; i < numVerts; i++) |
323 | else | 547 | { |
324 | _SculptMesh(bitmap2Coords(sculptBitmap, iScale, mirror), sculptType, viewerMode, mirror, invert); | 548 | vert = this.coords[i]; |
325 | } | 549 | vert.X += x; |
326 | #endif | 550 | vert.Y += y; |
327 | 551 | vert.Z += z; | |
328 | 552 | this.coords[i] = vert; | |
329 | void _SculptMesh(List<List<Coord>> rows, SculptType sculptType, bool viewerMode, bool mirror, bool invert) | 553 | } |
330 | { | 554 | |
331 | coords = new List<Coord>(); | 555 | if (this.viewerFaces != null) |
332 | faces = new List<Face>(); | 556 | { |
333 | normals = new List<Coord>(); | 557 | int numViewerFaces = this.viewerFaces.Count; |
334 | uvs = new List<UVCoord>(); | 558 | |
335 | 559 | for (i = 0; i < numViewerFaces; i++) | |
336 | sculptType = (SculptType)(((int)sculptType) & 0x07); | 560 | { |
337 | 561 | ViewerFace v = this.viewerFaces[i]; | |
338 | if (mirror) | 562 | v.AddPos(x, y, z); |
339 | if (sculptType == SculptType.plane) | 563 | this.viewerFaces[i] = v; |
340 | invert = !invert; | 564 | } |
341 | 565 | } | |
342 | viewerFaces = new List<ViewerFace>(); | 566 | } |
343 | 567 | ||
344 | int width = rows[0].Count; | 568 | /// <summary> |
345 | 569 | /// Rotates the mesh | |
346 | int p1, p2, p3, p4; | 570 | /// </summary> |
347 | 571 | /// <param name="q"></param> | |
348 | int imageX, imageY; | 572 | public void AddRot(Quat q) |
349 | 573 | { | |
350 | if (sculptType != SculptType.plane) | 574 | int i; |
351 | { | 575 | int numVerts = this.coords.Count; |
352 | for (int rowNdx = 0; rowNdx < rows.Count; rowNdx++) | 576 | |
353 | rows[rowNdx].Add(rows[rowNdx][0]); | 577 | for (i = 0; i < numVerts; i++) |
354 | } | 578 | this.coords[i] *= q; |
355 | 579 | ||
356 | Coord topPole = rows[0][width / 2]; | 580 | int numNormals = this.normals.Count; |
357 | Coord bottomPole = rows[rows.Count - 1][width / 2]; | 581 | for (i = 0; i < numNormals; i++) |
358 | 582 | this.normals[i] *= q; | |
359 | if (sculptType == SculptType.sphere) | 583 | |
360 | { | 584 | if (this.viewerFaces != null) |
361 | int count = rows[0].Count; | 585 | { |
362 | List<Coord> topPoleRow = new List<Coord>(count); | 586 | int numViewerFaces = this.viewerFaces.Count; |
363 | List<Coord> bottomPoleRow = new List<Coord>(count); | 587 | |
364 | 588 | for (i = 0; i < numViewerFaces; i++) | |
365 | for (int i = 0; i < count; i++) | 589 | { |
366 | { | 590 | ViewerFace v = this.viewerFaces[i]; |
367 | topPoleRow.Add(topPole); | 591 | v.v1 *= q; |
368 | bottomPoleRow.Add(bottomPole); | 592 | v.v2 *= q; |
369 | } | 593 | v.v3 *= q; |
370 | rows.Insert(0, topPoleRow); | 594 | |
371 | rows.Add(bottomPoleRow); | 595 | v.n1 *= q; |
372 | } | 596 | v.n2 *= q; |
373 | else if (sculptType == SculptType.torus) | 597 | v.n3 *= q; |
374 | rows.Add(rows[0]); | 598 | |
375 | 599 | this.viewerFaces[i] = v; | |
376 | int coordsDown = rows.Count; | 600 | } |
377 | int coordsAcross = rows[0].Count; | 601 | } |
378 | 602 | } | |
379 | float widthUnit = 1.0f / (coordsAcross - 1); | 603 | |
380 | float heightUnit = 1.0f / (coordsDown - 1); | 604 | public void Scale(float x, float y, float z) |
381 | 605 | { | |
382 | for (imageY = 0; imageY < coordsDown; imageY++) | 606 | int i; |
383 | { | 607 | int numVerts = this.coords.Count; |
384 | int rowOffset = imageY * coordsAcross; | 608 | |
385 | 609 | Coord m = new Coord(x, y, z); | |
386 | for (imageX = 0; imageX < coordsAcross; imageX++) | 610 | for (i = 0; i < numVerts; i++) |
387 | { | 611 | this.coords[i] *= m; |
388 | /* | 612 | |
389 | * p1-----p2 | 613 | if (this.viewerFaces != null) |
390 | * | \ f2 | | 614 | { |
391 | * | \ | | 615 | int numViewerFaces = this.viewerFaces.Count; |
392 | * | f1 \| | 616 | for (i = 0; i < numViewerFaces; i++) |
393 | * p3-----p4 | 617 | { |
394 | */ | 618 | ViewerFace v = this.viewerFaces[i]; |
395 | 619 | v.v1 *= m; | |
396 | p4 = rowOffset + imageX; | 620 | v.v2 *= m; |
397 | p3 = p4 - 1; | 621 | v.v3 *= m; |
398 | 622 | this.viewerFaces[i] = v; | |
399 | p2 = p4 - coordsAcross; | 623 | } |
400 | p1 = p3 - coordsAcross; | 624 | } |
401 | 625 | } | |
402 | this.coords.Add(rows[imageY][imageX]); | 626 | |
403 | if (viewerMode) | 627 | public void DumpRaw(String path, String name, String title) |
404 | { | 628 | { |
405 | this.normals.Add(new Coord()); | 629 | if (path == null) |
406 | this.uvs.Add(new UVCoord(widthUnit * imageX, heightUnit * imageY)); | 630 | return; |
407 | } | 631 | String fileName = name + "_" + title + ".raw"; |
408 | 632 | String completePath = System.IO.Path.Combine(path, fileName); | |
409 | if (imageY > 0 && imageX > 0) | 633 | StreamWriter sw = new StreamWriter(completePath); |
410 | { | 634 | |
411 | Face f1, f2; | 635 | for (int i = 0; i < this.faces.Count; i++) |
412 | 636 | { | |
413 | if (viewerMode) | 637 | string s = this.coords[this.faces[i].v1].ToString(); |
414 | { | 638 | s += " " + this.coords[this.faces[i].v2].ToString(); |
415 | if (invert) | 639 | s += " " + this.coords[this.faces[i].v3].ToString(); |
416 | { | 640 | |
417 | f1 = new Face(p1, p4, p3, p1, p4, p3); | 641 | sw.WriteLine(s); |
418 | f1.uv1 = p1; | 642 | } |
419 | f1.uv2 = p4; | 643 | |
420 | f1.uv3 = p3; | 644 | sw.Close(); |
421 | 645 | } | |
422 | f2 = new Face(p1, p2, p4, p1, p2, p4); | 646 | } |
423 | f2.uv1 = p1; | 647 | } |
424 | f2.uv2 = p2; | ||
425 | f2.uv3 = p4; | ||
426 | } | ||
427 | else | ||
428 | { | ||
429 | f1 = new Face(p1, p3, p4, p1, p3, p4); | ||
430 | f1.uv1 = p1; | ||
431 | f1.uv2 = p3; | ||
432 | f1.uv3 = p4; | ||
433 | |||
434 | f2 = new Face(p1, p4, p2, p1, p4, p2); | ||
435 | f2.uv1 = p1; | ||
436 | f2.uv2 = p4; | ||
437 | f2.uv3 = p2; | ||
438 | } | ||
439 | } | ||
440 | else | ||
441 | { | ||
442 | if (invert) | ||
443 | { | ||
444 | f1 = new Face(p1, p4, p3); | ||
445 | f2 = new Face(p1, p2, p4); | ||
446 | } | ||
447 | else | ||
448 | { | ||
449 | f1 = new Face(p1, p3, p4); | ||
450 | f2 = new Face(p1, p4, p2); | ||
451 | } | ||
452 | } | ||
453 | |||
454 | this.faces.Add(f1); | ||
455 | this.faces.Add(f2); | ||
456 | } | ||
457 | } | ||
458 | } | ||
459 | |||
460 | if (viewerMode) | ||
461 | calcVertexNormals(sculptType, coordsAcross, coordsDown); | ||
462 | } | ||
463 | |||
464 | /// <summary> | ||
465 | /// Duplicates a SculptMesh object. All object properties are copied by value, including lists. | ||
466 | /// </summary> | ||
467 | /// <returns></returns> | ||
468 | public SculptMesh Copy() | ||
469 | { | ||
470 | return new SculptMesh(this); | ||
471 | } | ||
472 | |||
473 | public SculptMesh(SculptMesh sm) | ||
474 | { | ||
475 | coords = new List<Coord>(sm.coords); | ||
476 | faces = new List<Face>(sm.faces); | ||
477 | viewerFaces = new List<ViewerFace>(sm.viewerFaces); | ||
478 | normals = new List<Coord>(sm.normals); | ||
479 | uvs = new List<UVCoord>(sm.uvs); | ||
480 | } | ||
481 | |||
482 | private void calcVertexNormals(SculptType sculptType, int xSize, int ySize) | ||
483 | { // compute vertex normals by summing all the surface normals of all the triangles sharing | ||
484 | // each vertex and then normalizing | ||
485 | int numFaces = this.faces.Count; | ||
486 | for (int i = 0; i < numFaces; i++) | ||
487 | { | ||
488 | Face face = this.faces[i]; | ||
489 | Coord surfaceNormal = face.SurfaceNormal(this.coords); | ||
490 | this.normals[face.n1] += surfaceNormal; | ||
491 | this.normals[face.n2] += surfaceNormal; | ||
492 | this.normals[face.n3] += surfaceNormal; | ||
493 | } | ||
494 | |||
495 | int numNormals = this.normals.Count; | ||
496 | for (int i = 0; i < numNormals; i++) | ||
497 | this.normals[i] = this.normals[i].Normalize(); | ||
498 | |||
499 | if (sculptType != SculptType.plane) | ||
500 | { // blend the vertex normals at the cylinder seam | ||
501 | for (int y = 0; y < ySize; y++) | ||
502 | { | ||
503 | int rowOffset = y * xSize; | ||
504 | |||
505 | this.normals[rowOffset] = this.normals[rowOffset + xSize - 1] = (this.normals[rowOffset] + this.normals[rowOffset + xSize - 1]).Normalize(); | ||
506 | } | ||
507 | } | ||
508 | |||
509 | foreach (Face face in this.faces) | ||
510 | { | ||
511 | ViewerFace vf = new ViewerFace(0); | ||
512 | vf.v1 = this.coords[face.v1]; | ||
513 | vf.v2 = this.coords[face.v2]; | ||
514 | vf.v3 = this.coords[face.v3]; | ||
515 | |||
516 | vf.coordIndex1 = face.v1; | ||
517 | vf.coordIndex2 = face.v2; | ||
518 | vf.coordIndex3 = face.v3; | ||
519 | |||
520 | vf.n1 = this.normals[face.n1]; | ||
521 | vf.n2 = this.normals[face.n2]; | ||
522 | vf.n3 = this.normals[face.n3]; | ||
523 | |||
524 | vf.uv1 = this.uvs[face.uv1]; | ||
525 | vf.uv2 = this.uvs[face.uv2]; | ||
526 | vf.uv3 = this.uvs[face.uv3]; | ||
527 | |||
528 | this.viewerFaces.Add(vf); | ||
529 | } | ||
530 | } | ||
531 | |||
532 | /// <summary> | ||
533 | /// Adds a value to each XYZ vertex coordinate in the mesh | ||
534 | /// </summary> | ||
535 | /// <param name="x"></param> | ||
536 | /// <param name="y"></param> | ||
537 | /// <param name="z"></param> | ||
538 | public void AddPos(float x, float y, float z) | ||
539 | { | ||
540 | int i; | ||
541 | int numVerts = this.coords.Count; | ||
542 | Coord vert; | ||
543 | |||
544 | for (i = 0; i < numVerts; i++) | ||
545 | { | ||
546 | vert = this.coords[i]; | ||
547 | vert.X += x; | ||
548 | vert.Y += y; | ||
549 | vert.Z += z; | ||
550 | this.coords[i] = vert; | ||
551 | } | ||
552 | |||
553 | if (this.viewerFaces != null) | ||
554 | { | ||
555 | int numViewerFaces = this.viewerFaces.Count; | ||
556 | |||
557 | for (i = 0; i < numViewerFaces; i++) | ||
558 | { | ||
559 | ViewerFace v = this.viewerFaces[i]; | ||
560 | v.AddPos(x, y, z); | ||
561 | this.viewerFaces[i] = v; | ||
562 | } | ||
563 | } | ||
564 | } | ||
565 | |||
566 | /// <summary> | ||
567 | /// Rotates the mesh | ||
568 | /// </summary> | ||
569 | /// <param name="q"></param> | ||
570 | public void AddRot(Quat q) | ||
571 | { | ||
572 | int i; | ||
573 | int numVerts = this.coords.Count; | ||
574 | |||
575 | for (i = 0; i < numVerts; i++) | ||
576 | this.coords[i] *= q; | ||
577 | |||
578 | int numNormals = this.normals.Count; | ||
579 | for (i = 0; i < numNormals; i++) | ||
580 | this.normals[i] *= q; | ||
581 | |||
582 | if (this.viewerFaces != null) | ||
583 | { | ||
584 | int numViewerFaces = this.viewerFaces.Count; | ||
585 | |||
586 | for (i = 0; i < numViewerFaces; i++) | ||
587 | { | ||
588 | ViewerFace v = this.viewerFaces[i]; | ||
589 | v.v1 *= q; | ||
590 | v.v2 *= q; | ||
591 | v.v3 *= q; | ||
592 | |||
593 | v.n1 *= q; | ||
594 | v.n2 *= q; | ||
595 | v.n3 *= q; | ||
596 | |||
597 | this.viewerFaces[i] = v; | ||
598 | } | ||
599 | } | ||
600 | } | ||
601 | |||
602 | public void Scale(float x, float y, float z) | ||
603 | { | ||
604 | int i; | ||
605 | int numVerts = this.coords.Count; | ||
606 | |||
607 | Coord m = new Coord(x, y, z); | ||
608 | for (i = 0; i < numVerts; i++) | ||
609 | this.coords[i] *= m; | ||
610 | |||
611 | if (this.viewerFaces != null) | ||
612 | { | ||
613 | int numViewerFaces = this.viewerFaces.Count; | ||
614 | for (i = 0; i < numViewerFaces; i++) | ||
615 | { | ||
616 | ViewerFace v = this.viewerFaces[i]; | ||
617 | v.v1 *= m; | ||
618 | v.v2 *= m; | ||
619 | v.v3 *= m; | ||
620 | this.viewerFaces[i] = v; | ||
621 | } | ||
622 | } | ||
623 | } | ||
624 | |||
625 | public void DumpRaw(String path, String name, String title) | ||
626 | { | ||
627 | if (path == null) | ||
628 | return; | ||
629 | String fileName = name + "_" + title + ".raw"; | ||
630 | String completePath = System.IO.Path.Combine(path, fileName); | ||
631 | StreamWriter sw = new StreamWriter(completePath); | ||
632 | |||
633 | for (int i = 0; i < this.faces.Count; i++) | ||
634 | { | ||
635 | string s = this.coords[this.faces[i].v1].ToString(); | ||
636 | s += " " + this.coords[this.faces[i].v2].ToString(); | ||
637 | s += " " + this.coords[this.faces[i].v3].ToString(); | ||
638 | |||
639 | sw.WriteLine(s); | ||
640 | } | ||
641 | |||
642 | sw.Close(); | ||
643 | } | ||
644 | } | ||
645 | } | ||