aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/Meshing/SculptMesh.cs
diff options
context:
space:
mode:
authordahlia2010-05-06 21:36:27 -0700
committerdahlia2010-05-06 21:36:27 -0700
commit5d1e9947ed43490fb458af62bd9e8596a816f7b8 (patch)
treeb89886e0faf5bd2194491669ad8a8b3c1ea340a0 /OpenSim/Region/Physics/Meshing/SculptMesh.cs
parentAlso remove sale and search flags on god owner change. (diff)
downloadopensim-SC_OLD-5d1e9947ed43490fb458af62bd9e8596a816f7b8.zip
opensim-SC_OLD-5d1e9947ed43490fb458af62bd9e8596a816f7b8.tar.gz
opensim-SC_OLD-5d1e9947ed43490fb458af62bd9e8596a816f7b8.tar.bz2
opensim-SC_OLD-5d1e9947ed43490fb458af62bd9e8596a816f7b8.tar.xz
Sculpt meshing refactoring - improves mesh accuracy and UV mapping
Sync with PrimMesher r55
Diffstat (limited to 'OpenSim/Region/Physics/Meshing/SculptMesh.cs')
-rw-r--r--OpenSim/Region/Physics/Meshing/SculptMesh.cs1280
1 files changed, 641 insertions, 639 deletions
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
31using System; 31using System;
32using System.Collections.Generic; 32using System.Collections.Generic;
33using System.Text; 33using System.Text;
34using System.IO; 34using System.IO;
35 35
36#if SYSTEM_DRAWING 36#if SYSTEM_DRAWING
37using System.Drawing; 37using System.Drawing;
38using System.Drawing.Imaging; 38using System.Drawing.Imaging;
39#endif 39#endif
40 40
41namespace PrimMesher 41namespace 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}