diff options
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/Physics/Meshing/Extruder.cs | 472 | ||||
-rw-r--r-- | OpenSim/Region/Physics/Meshing/HelperTypes.cs | 14 | ||||
-rw-r--r-- | OpenSim/Region/Physics/Meshing/Mesh.cs | 20 | ||||
-rw-r--r-- | OpenSim/Region/Physics/Meshing/Meshmerizer.cs | 1761 | ||||
-rw-r--r-- | OpenSim/Region/Physics/Meshing/PrimMesher.cs | 163 | ||||
-rw-r--r-- | OpenSim/Region/Physics/Meshing/SculptMesh.cs | 523 | ||||
-rw-r--r-- | OpenSim/Region/Physics/Meshing/SimpleHull.cs | 394 | ||||
-rw-r--r-- | OpenSim/Region/Physics/Meshing/Simplex.cs | 220 |
8 files changed, 525 insertions, 3042 deletions
diff --git a/OpenSim/Region/Physics/Meshing/Extruder.cs b/OpenSim/Region/Physics/Meshing/Extruder.cs deleted file mode 100644 index 1fc65e3..0000000 --- a/OpenSim/Region/Physics/Meshing/Extruder.cs +++ /dev/null | |||
@@ -1,472 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
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 OpenSim 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 | //#define SPAM | ||
28 | |||
29 | using OpenMetaverse; | ||
30 | using OpenSim.Region.Physics.Manager; | ||
31 | |||
32 | namespace OpenSim.Region.Physics.Meshing | ||
33 | { | ||
34 | internal class Extruder | ||
35 | { | ||
36 | //public float startParameter; | ||
37 | //public float stopParameter; | ||
38 | public PhysicsVector size; | ||
39 | |||
40 | public float taperTopFactorX = 1f; | ||
41 | public float taperTopFactorY = 1f; | ||
42 | public float taperBotFactorX = 1f; | ||
43 | public float taperBotFactorY = 1f; | ||
44 | |||
45 | public float pushX = 0f; | ||
46 | public float pushY = 0f; | ||
47 | |||
48 | // twist amount in radians. NOT DEGREES. | ||
49 | public float twistTop = 0; | ||
50 | public float twistBot = 0; | ||
51 | public float twistMid = 0; | ||
52 | public float pathScaleX = 1.0f; | ||
53 | public float pathScaleY = 0.5f; | ||
54 | public float skew = 0.0f; | ||
55 | public float radius = 0.0f; | ||
56 | public float revolutions = 1.0f; | ||
57 | |||
58 | public float pathCutBegin = 0.0f; | ||
59 | public float pathCutEnd = 1.0f; | ||
60 | |||
61 | public ushort pathBegin = 0; | ||
62 | public ushort pathEnd = 0; | ||
63 | |||
64 | public float pathTaperX = 0.0f; | ||
65 | public float pathTaperY = 0.0f; | ||
66 | |||
67 | /// <summary> | ||
68 | /// Creates an extrusion of a profile along a linear path. Used to create prim types box, cylinder, and prism. | ||
69 | /// </summary> | ||
70 | /// <param name="m"></param> | ||
71 | /// <returns>A mesh of the extruded shape</returns> | ||
72 | public Mesh ExtrudeLinearPath(Mesh m) | ||
73 | { | ||
74 | Mesh result = new Mesh(); | ||
75 | |||
76 | Mesh newLayer; | ||
77 | Mesh lastLayer = null; | ||
78 | |||
79 | int step = 0; | ||
80 | int steps = 1; | ||
81 | |||
82 | float twistTotal = twistTop - twistBot; | ||
83 | // if the profile has a lot of twist, add more layers otherwise the layers may overlap | ||
84 | // and the resulting mesh may be quite inaccurate. This method is arbitrary and may not | ||
85 | // accurately match the viewer | ||
86 | float twistTotalAbs = System.Math.Abs(twistTotal); | ||
87 | if (twistTotalAbs > 0.01) | ||
88 | steps += (int)(twistTotalAbs * 3.66f); // dahlia's magic number ;) | ||
89 | |||
90 | #if SPAM | ||
91 | System.Console.WriteLine("ExtrudeLinearPath: twistTotalAbs: " + twistTotalAbs.ToString() + " steps: " + steps.ToString()); | ||
92 | #endif | ||
93 | |||
94 | double percentOfPathMultiplier = 1.0 / steps; | ||
95 | |||
96 | float start = -0.5f; | ||
97 | |||
98 | float stepSize = 1.0f / (float)steps; | ||
99 | |||
100 | float xProfileScale = 1.0f; | ||
101 | float yProfileScale = 1.0f; | ||
102 | |||
103 | float xOffset = 0.0f; | ||
104 | float yOffset = 0.0f; | ||
105 | float zOffset = start; | ||
106 | |||
107 | float xOffsetStepIncrement = pushX / steps; | ||
108 | float yOffsetStepIncrement = pushY / steps; | ||
109 | |||
110 | #if SPAM | ||
111 | System.Console.WriteLine("Extruder: twistTop: " + twistTop.ToString() + " twistbot: " + twistBot.ToString() + " twisttotal: " + twistTotal.ToString()); | ||
112 | System.Console.WriteLine("Extruder: taperBotFactorX: " + taperBotFactorX.ToString() + " taperBotFactorY: " + taperBotFactorY.ToString() | ||
113 | + " taperTopFactorX: " + taperTopFactorX.ToString() + " taperTopFactorY: " + taperTopFactorY.ToString()); | ||
114 | System.Console.WriteLine("Extruder: PathScaleX: " + pathScaleX.ToString() + " pathScaleY: " + pathScaleY.ToString()); | ||
115 | #endif | ||
116 | |||
117 | //float percentOfPath = 0.0f; | ||
118 | float percentOfPath = (float)pathBegin * 2.0e-5f; | ||
119 | zOffset += percentOfPath; | ||
120 | bool done = false; | ||
121 | do // loop through the length of the path and add the layers | ||
122 | { | ||
123 | newLayer = m.Clone(); | ||
124 | |||
125 | if (taperBotFactorX < 1.0f) | ||
126 | xProfileScale = 1.0f - (1.0f - percentOfPath) * (1.0f - taperBotFactorX); | ||
127 | else if (taperTopFactorX < 1.0f) | ||
128 | xProfileScale = 1.0f - percentOfPath * (1.0f - taperTopFactorX); | ||
129 | else xProfileScale = 1.0f; | ||
130 | |||
131 | if (taperBotFactorY < 1.0f) | ||
132 | yProfileScale = 1.0f - (1.0f - percentOfPath) * (1.0f - taperBotFactorY); | ||
133 | else if (taperTopFactorY < 1.0f) | ||
134 | yProfileScale = 1.0f - percentOfPath * (1.0f - taperTopFactorY); | ||
135 | else yProfileScale = 1.0f; | ||
136 | |||
137 | #if SPAM | ||
138 | //System.Console.WriteLine("xProfileScale: " + xProfileScale.ToString() + " yProfileScale: " + yProfileScale.ToString()); | ||
139 | #endif | ||
140 | Vertex vTemp = new Vertex(0.0f, 0.0f, 0.0f); | ||
141 | |||
142 | // apply the taper to the profile before any rotations | ||
143 | if (xProfileScale != 1.0f || yProfileScale != 1.0f) | ||
144 | { | ||
145 | foreach (Vertex v in newLayer.vertices) | ||
146 | { | ||
147 | if (v != null) | ||
148 | { | ||
149 | v.X *= xProfileScale; | ||
150 | v.Y *= yProfileScale; | ||
151 | } | ||
152 | } | ||
153 | } | ||
154 | |||
155 | |||
156 | float twist = twistBot + (twistTotal * (float)percentOfPath); | ||
157 | #if SPAM | ||
158 | System.Console.WriteLine("Extruder: percentOfPath: " + percentOfPath.ToString() + " zOffset: " + zOffset.ToString() | ||
159 | + " xProfileScale: " + xProfileScale.ToString() + " yProfileScale: " + yProfileScale.ToString()); | ||
160 | #endif | ||
161 | |||
162 | // apply twist rotation to the profile layer and position the layer in the prim | ||
163 | |||
164 | Quaternion profileRot = Quaternion.CreateFromAxisAngle(new Vector3(0.0f, 0.0f, 1.0f), twist); | ||
165 | foreach (Vertex v in newLayer.vertices) | ||
166 | { | ||
167 | if (v != null) | ||
168 | { | ||
169 | vTemp = v * profileRot; | ||
170 | v.X = vTemp.X + xOffset; | ||
171 | v.Y = vTemp.Y + yOffset; | ||
172 | v.Z = vTemp.Z + zOffset; | ||
173 | } | ||
174 | } | ||
175 | |||
176 | if (step == 0) // the first layer, invert normals | ||
177 | { | ||
178 | foreach (Triangle t in newLayer.triangles) | ||
179 | { | ||
180 | t.invertNormal(); | ||
181 | } | ||
182 | } | ||
183 | |||
184 | result.Append(newLayer); | ||
185 | |||
186 | int iLastNull = 0; | ||
187 | |||
188 | if (lastLayer != null) | ||
189 | { | ||
190 | int i, count = newLayer.vertices.Count; | ||
191 | |||
192 | for (i = 0; i < count; i++) | ||
193 | { | ||
194 | int iNext = (i + 1); | ||
195 | |||
196 | if (lastLayer.vertices[i] == null) // cant make a simplex here | ||
197 | { | ||
198 | iLastNull = i + 1; | ||
199 | } | ||
200 | else | ||
201 | { | ||
202 | if (i == count - 1) // End of list | ||
203 | iNext = iLastNull; | ||
204 | |||
205 | if (lastLayer.vertices[iNext] == null) // Null means wrap to begin of last segment | ||
206 | iNext = iLastNull; | ||
207 | |||
208 | result.Add(new Triangle(newLayer.vertices[i], lastLayer.vertices[i], newLayer.vertices[iNext])); | ||
209 | result.Add(new Triangle(newLayer.vertices[iNext], lastLayer.vertices[i], lastLayer.vertices[iNext])); | ||
210 | } | ||
211 | } | ||
212 | } | ||
213 | lastLayer = newLayer; | ||
214 | |||
215 | // calc the step for the next interation of the loop | ||
216 | |||
217 | if (step < steps) | ||
218 | { | ||
219 | step++; | ||
220 | percentOfPath += (float)percentOfPathMultiplier; | ||
221 | |||
222 | xOffset += xOffsetStepIncrement; | ||
223 | yOffset += yOffsetStepIncrement; | ||
224 | zOffset += stepSize; | ||
225 | |||
226 | if (percentOfPath > 1.0f - (float)pathEnd * 2.0e-5f) | ||
227 | done = true; | ||
228 | } | ||
229 | else done = true; | ||
230 | |||
231 | } while (!done); // loop until all the layers in the path are completed | ||
232 | |||
233 | // scale the mesh to the desired size | ||
234 | float xScale = size.X; | ||
235 | float yScale = size.Y; | ||
236 | float zScale = size.Z; | ||
237 | |||
238 | foreach (Vertex v in result.vertices) | ||
239 | { | ||
240 | if (v != null) | ||
241 | { | ||
242 | v.X *= xScale; | ||
243 | v.Y *= yScale; | ||
244 | v.Z *= zScale; | ||
245 | } | ||
246 | } | ||
247 | |||
248 | return result; | ||
249 | } | ||
250 | |||
251 | /// <summary> | ||
252 | /// Extrudes a shape around a circular path. Used to create prim types torus, ring, and tube. | ||
253 | /// </summary> | ||
254 | /// <param name="m"></param> | ||
255 | /// <returns>a mesh of the extruded shape</returns> | ||
256 | public Mesh ExtrudeCircularPath(Mesh m) | ||
257 | { | ||
258 | Mesh result = new Mesh(); | ||
259 | |||
260 | Mesh newLayer; | ||
261 | Mesh lastLayer = null; | ||
262 | |||
263 | int step; | ||
264 | int steps = 24; | ||
265 | |||
266 | float twistTotal = twistTop - twistBot; | ||
267 | // if the profile has a lot of twist, add more layers otherwise the layers may overlap | ||
268 | // and the resulting mesh may be quite inaccurate. This method is arbitrary and doesn't | ||
269 | // accurately match the viewer | ||
270 | if (System.Math.Abs(twistTotal) > (float)System.Math.PI * 1.5f) steps *= 2; | ||
271 | if (System.Math.Abs(twistTotal) > (float)System.Math.PI * 3.0f) steps *= 2; | ||
272 | |||
273 | // double percentOfPathMultiplier = 1.0 / steps; | ||
274 | // double angleStepMultiplier = System.Math.PI * 2.0 / steps; | ||
275 | |||
276 | float yPathScale = pathScaleY * 0.5f; | ||
277 | float pathLength = pathCutEnd - pathCutBegin; | ||
278 | float totalSkew = skew * 2.0f * pathLength; | ||
279 | float skewStart = (-skew) + pathCutBegin * 2.0f * skew; | ||
280 | |||
281 | // It's not quite clear what pushY (Y top shear) does, but subtracting it from the start and end | ||
282 | // angles appears to approximate it's effects on path cut. Likewise, adding it to the angle used | ||
283 | // to calculate the sine for generating the path radius appears to approximate it's effects there | ||
284 | // too, but there are some subtle differences in the radius which are noticeable as the prim size | ||
285 | // increases and it may affect megaprims quite a bit. The effect of the Y top shear parameter on | ||
286 | // the meshes generated with this technique appear nearly identical in shape to the same prims when | ||
287 | // displayed by the viewer. | ||
288 | |||
289 | |||
290 | float startAngle = (float)(System.Math.PI * 2.0 * pathCutBegin * revolutions) - pushY * 0.9f; | ||
291 | float endAngle = (float)(System.Math.PI * 2.0 * pathCutEnd * revolutions) - pushY * 0.9f; | ||
292 | float stepSize = (float)0.2617993878; // 2*PI / 24 segments per revolution | ||
293 | |||
294 | step = (int)(startAngle / stepSize); | ||
295 | float angle = startAngle; | ||
296 | |||
297 | float xProfileScale = 1.0f; | ||
298 | float yProfileScale = 1.0f; | ||
299 | |||
300 | |||
301 | #if SPAM | ||
302 | System.Console.WriteLine("Extruder: twistTop: " + twistTop.ToString() + " twistbot: " + twistBot.ToString() + " twisttotal: " + twistTotal.ToString()); | ||
303 | System.Console.WriteLine("Extruder: startAngle: " + startAngle.ToString() + " endAngle: " + endAngle.ToString() + " step: " + step.ToString()); | ||
304 | System.Console.WriteLine("Extruder: taperBotFactorX: " + taperBotFactorX.ToString() + " taperBotFactorY: " + taperBotFactorY.ToString() | ||
305 | + " taperTopFactorX: " + taperTopFactorX.ToString() + " taperTopFactorY: " + taperTopFactorY.ToString()); | ||
306 | System.Console.WriteLine("Extruder: PathScaleX: " + pathScaleX.ToString() + " pathScaleY: " + pathScaleY.ToString()); | ||
307 | #endif | ||
308 | |||
309 | bool done = false; | ||
310 | do // loop through the length of the path and add the layers | ||
311 | { | ||
312 | newLayer = m.Clone(); | ||
313 | |||
314 | float percentOfPath = (angle - startAngle) / (endAngle - startAngle); // endAngle should always be larger than startAngle | ||
315 | |||
316 | if (pathTaperX > 0.001f) // can't really compare to 0.0f as the value passed is never exactly zero | ||
317 | xProfileScale = 1.0f - percentOfPath * pathTaperX; | ||
318 | else if (pathTaperX < -0.001f) | ||
319 | xProfileScale = 1.0f + (1.0f - percentOfPath) * pathTaperX; | ||
320 | else xProfileScale = 1.0f; | ||
321 | |||
322 | if (pathTaperY > 0.001f) | ||
323 | yProfileScale = 1.0f - percentOfPath * pathTaperY; | ||
324 | else if (pathTaperY < -0.001f) | ||
325 | yProfileScale = 1.0f + (1.0f - percentOfPath) * pathTaperY; | ||
326 | else yProfileScale = 1.0f; | ||
327 | |||
328 | #if SPAM | ||
329 | //System.Console.WriteLine("xProfileScale: " + xProfileScale.ToString() + " yProfileScale: " + yProfileScale.ToString()); | ||
330 | #endif | ||
331 | Vertex vTemp = new Vertex(0.0f, 0.0f, 0.0f); | ||
332 | |||
333 | // apply the taper to the profile before any rotations | ||
334 | if (xProfileScale != 1.0f || yProfileScale != 1.0f) | ||
335 | { | ||
336 | foreach (Vertex v in newLayer.vertices) | ||
337 | { | ||
338 | if (v != null) | ||
339 | { | ||
340 | v.X *= xProfileScale; | ||
341 | v.Y *= yProfileScale; | ||
342 | } | ||
343 | } | ||
344 | } | ||
345 | |||
346 | float radiusScale; | ||
347 | |||
348 | if (radius > 0.001f) | ||
349 | radiusScale = 1.0f - radius * percentOfPath; | ||
350 | else if (radius < 0.001f) | ||
351 | radiusScale = 1.0f + radius * (1.0f - percentOfPath); | ||
352 | else | ||
353 | radiusScale = 1.0f; | ||
354 | |||
355 | #if SPAM | ||
356 | System.Console.WriteLine("Extruder: angle: " + angle.ToString() + " percentOfPath: " + percentOfPath.ToString() | ||
357 | + " radius: " + radius.ToString() + " radiusScale: " + radiusScale.ToString() | ||
358 | + " xProfileScale: " + xProfileScale.ToString() + " yProfileScale: " + yProfileScale.ToString()); | ||
359 | #endif | ||
360 | |||
361 | float twist = twistBot + (twistTotal * (float)percentOfPath); | ||
362 | |||
363 | float xOffset; | ||
364 | float yOffset; | ||
365 | float zOffset; | ||
366 | |||
367 | xOffset = 0.5f * (skewStart + totalSkew * (float)percentOfPath); | ||
368 | xOffset += (float) System.Math.Sin(angle) * pushX * 0.45f; | ||
369 | yOffset = (float)(System.Math.Cos(angle) * (0.5f - yPathScale)) * radiusScale; | ||
370 | zOffset = (float)(System.Math.Sin(angle + pushY * 0.9f) * (0.5f - yPathScale)) * radiusScale; | ||
371 | |||
372 | // next apply twist rotation to the profile layer | ||
373 | if (twistTotal != 0.0f || twistBot != 0.0f) | ||
374 | { | ||
375 | Quaternion profileRot = new Quaternion(new Vector3(0.0f, 0.0f, 1.0f), twist); | ||
376 | foreach (Vertex v in newLayer.vertices) | ||
377 | { | ||
378 | if (v != null) | ||
379 | { | ||
380 | vTemp = v * profileRot; | ||
381 | v.X = vTemp.X; | ||
382 | v.Y = vTemp.Y; | ||
383 | v.Z = vTemp.Z; | ||
384 | } | ||
385 | } | ||
386 | } | ||
387 | |||
388 | // now orient the rotation of the profile layer relative to it's position on the path | ||
389 | // adding pushY to the angle used to generate the quat appears to approximate the viewer | ||
390 | Quaternion layerRot = Quaternion.CreateFromAxisAngle(new Vector3(1.0f, 0.0f, 0.0f), (float)angle + pushY * 0.9f); | ||
391 | foreach (Vertex v in newLayer.vertices) | ||
392 | { | ||
393 | if (v != null) | ||
394 | { | ||
395 | vTemp = v * layerRot; | ||
396 | v.X = vTemp.X + xOffset; | ||
397 | v.Y = vTemp.Y + yOffset; | ||
398 | v.Z = vTemp.Z + zOffset; | ||
399 | } | ||
400 | } | ||
401 | |||
402 | if (angle == startAngle) // the first layer, invert normals | ||
403 | { | ||
404 | foreach (Triangle t in newLayer.triangles) | ||
405 | { | ||
406 | t.invertNormal(); | ||
407 | } | ||
408 | } | ||
409 | |||
410 | result.Append(newLayer); | ||
411 | |||
412 | int iLastNull = 0; | ||
413 | |||
414 | if (lastLayer != null) | ||
415 | { | ||
416 | int i, count = newLayer.vertices.Count; | ||
417 | |||
418 | for (i = 0; i < count; i++) | ||
419 | { | ||
420 | int iNext = (i + 1); | ||
421 | |||
422 | if (lastLayer.vertices[i] == null) // cant make a simplex here | ||
423 | { | ||
424 | iLastNull = i + 1; | ||
425 | } | ||
426 | else | ||
427 | { | ||
428 | if (i == count - 1) // End of list | ||
429 | iNext = iLastNull; | ||
430 | |||
431 | if (lastLayer.vertices[iNext] == null) // Null means wrap to begin of last segment | ||
432 | iNext = iLastNull; | ||
433 | |||
434 | result.Add(new Triangle(newLayer.vertices[i], lastLayer.vertices[i], newLayer.vertices[iNext])); | ||
435 | result.Add(new Triangle(newLayer.vertices[iNext], lastLayer.vertices[i], lastLayer.vertices[iNext])); | ||
436 | } | ||
437 | } | ||
438 | } | ||
439 | lastLayer = newLayer; | ||
440 | |||
441 | // calc the angle for the next interation of the loop | ||
442 | if (angle >= endAngle) | ||
443 | { | ||
444 | done = true; | ||
445 | } | ||
446 | else | ||
447 | { | ||
448 | angle = stepSize * ++step; | ||
449 | if (angle > endAngle) | ||
450 | angle = endAngle; | ||
451 | } | ||
452 | } while (!done); // loop until all the layers in the path are completed | ||
453 | |||
454 | // scale the mesh to the desired size | ||
455 | float xScale = size.X; | ||
456 | float yScale = size.Y; | ||
457 | float zScale = size.Z; | ||
458 | |||
459 | foreach (Vertex v in result.vertices) | ||
460 | { | ||
461 | if (v != null) | ||
462 | { | ||
463 | v.X *= xScale; | ||
464 | v.Y *= yScale; | ||
465 | v.Z *= zScale; | ||
466 | } | ||
467 | } | ||
468 | |||
469 | return result; | ||
470 | } | ||
471 | } | ||
472 | } | ||
diff --git a/OpenSim/Region/Physics/Meshing/HelperTypes.cs b/OpenSim/Region/Physics/Meshing/HelperTypes.cs index 7491782..7198cae 100644 --- a/OpenSim/Region/Physics/Meshing/HelperTypes.cs +++ b/OpenSim/Region/Physics/Meshing/HelperTypes.cs | |||
@@ -356,20 +356,6 @@ public class Triangle | |||
356 | radius_square = (float) (rx*rx + ry*ry); | 356 | radius_square = (float) (rx*rx + ry*ry); |
357 | } | 357 | } |
358 | 358 | ||
359 | public List<Simplex> GetSimplices() | ||
360 | { | ||
361 | List<Simplex> result = new List<Simplex>(); | ||
362 | Simplex s1 = new Simplex(v1, v2); | ||
363 | Simplex s2 = new Simplex(v2, v3); | ||
364 | Simplex s3 = new Simplex(v3, v1); | ||
365 | |||
366 | result.Add(s1); | ||
367 | result.Add(s2); | ||
368 | result.Add(s3); | ||
369 | |||
370 | return result; | ||
371 | } | ||
372 | |||
373 | public override String ToString() | 359 | public override String ToString() |
374 | { | 360 | { |
375 | NumberFormatInfo nfi = new NumberFormatInfo(); | 361 | NumberFormatInfo nfi = new NumberFormatInfo(); |
diff --git a/OpenSim/Region/Physics/Meshing/Mesh.cs b/OpenSim/Region/Physics/Meshing/Mesh.cs index 583b485..5a565ff 100644 --- a/OpenSim/Region/Physics/Meshing/Mesh.cs +++ b/OpenSim/Region/Physics/Meshing/Mesh.cs | |||
@@ -115,26 +115,6 @@ namespace OpenSim.Region.Physics.Meshing | |||
115 | vertices.Remove(v); | 115 | vertices.Remove(v); |
116 | } | 116 | } |
117 | 117 | ||
118 | public void RemoveTrianglesOutside(SimpleHull hull) | ||
119 | { | ||
120 | int i; | ||
121 | |||
122 | for (i = 0; i < triangles.Count; i++) | ||
123 | { | ||
124 | Triangle t = triangles[i]; | ||
125 | Vertex v1 = t.v1; | ||
126 | Vertex v2 = t.v2; | ||
127 | Vertex v3 = t.v3; | ||
128 | PhysicsVector m = v1 + v2 + v3; | ||
129 | m /= 3.0f; | ||
130 | if (!hull.IsPointIn(new Vertex(m))) | ||
131 | { | ||
132 | triangles.RemoveAt(i); | ||
133 | i--; | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | |||
138 | public void Add(List<Vertex> lv) | 118 | public void Add(List<Vertex> lv) |
139 | { | 119 | { |
140 | foreach (Vertex v in lv) | 120 | foreach (Vertex v in lv) |
diff --git a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs index 6955aa0..a65d0f4 100644 --- a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs +++ b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs | |||
@@ -31,6 +31,9 @@ using System.Collections.Generic; | |||
31 | using OpenSim.Framework; | 31 | using OpenSim.Framework; |
32 | using OpenSim.Region.Physics.Manager; | 32 | using OpenSim.Region.Physics.Manager; |
33 | using OpenMetaverse; | 33 | using OpenMetaverse; |
34 | using OpenMetaverse.Imaging; | ||
35 | using System.Drawing; | ||
36 | using System.Drawing.Imaging; | ||
34 | using PrimMesher; | 37 | using PrimMesher; |
35 | 38 | ||
36 | namespace OpenSim.Region.Physics.Meshing | 39 | namespace OpenSim.Region.Physics.Meshing |
@@ -54,8 +57,6 @@ namespace OpenSim.Region.Physics.Meshing | |||
54 | 57 | ||
55 | public class Meshmerizer : IMesher | 58 | public class Meshmerizer : IMesher |
56 | { | 59 | { |
57 | private bool usePrimMesher = true; | ||
58 | |||
59 | //private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); | 60 | //private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); |
60 | 61 | ||
61 | // Setting baseDir to a path will enable the dumping of raw files | 62 | // Setting baseDir to a path will enable the dumping of raw files |
@@ -65,277 +66,9 @@ namespace OpenSim.Region.Physics.Meshing | |||
65 | #else | 66 | #else |
66 | private const string baseDir = null; //"rawFiles"; | 67 | private const string baseDir = null; //"rawFiles"; |
67 | #endif | 68 | #endif |
68 | private const float DEG_TO_RAD = 0.01745329238f; | ||
69 | 69 | ||
70 | private float minSizeForComplexMesh = 0.2f; // prims with all dimensions smaller than this will have a bounding box mesh | 70 | private float minSizeForComplexMesh = 0.2f; // prims with all dimensions smaller than this will have a bounding box mesh |
71 | 71 | ||
72 | // private static void IntersectionParameterPD(PhysicsVector p1, PhysicsVector r1, PhysicsVector p2, | ||
73 | // PhysicsVector r2, ref float lambda, ref float mu) | ||
74 | // { | ||
75 | // p1, p2, points on the straight | ||
76 | // r1, r2, directional vectors of the straight. Not necessarily of length 1! | ||
77 | // note, that l, m can be scaled such, that the range 0..1 is mapped to the area between two points, | ||
78 | // thus allowing to decide whether an intersection is between two points | ||
79 | |||
80 | // float r1x = r1.X; | ||
81 | // float r1y = r1.Y; | ||
82 | // float r2x = r2.X; | ||
83 | // float r2y = r2.Y; | ||
84 | // | ||
85 | // float denom = r1y * r2x - r1x * r2y; | ||
86 | // | ||
87 | // if (denom == 0.0) | ||
88 | // { | ||
89 | // lambda = Single.NaN; | ||
90 | // mu = Single.NaN; | ||
91 | // return; | ||
92 | // } | ||
93 | // | ||
94 | // float p1x = p1.X; | ||
95 | // float p1y = p1.Y; | ||
96 | // float p2x = p2.X; | ||
97 | // float p2y = p2.Y; | ||
98 | // lambda = (-p2x * r2y + p1x * r2y + (p2y - p1y) * r2x) / denom; | ||
99 | // mu = (-p2x * r1y + p1x * r1y + (p2y - p1y) * r1x) / denom; | ||
100 | // } | ||
101 | |||
102 | private static List<Triangle> FindInfluencedTriangles(List<Triangle> triangles, Vertex v) | ||
103 | { | ||
104 | List<Triangle> influenced = new List<Triangle>(); | ||
105 | foreach (Triangle t in triangles) | ||
106 | { | ||
107 | if (t.isInCircle(v.X, v.Y)) | ||
108 | { | ||
109 | influenced.Add(t); | ||
110 | } | ||
111 | } | ||
112 | return influenced; | ||
113 | } | ||
114 | |||
115 | private static void InsertVertices(List<Vertex> vertices, int usedForSeed, List<Triangle> triangles) | ||
116 | { | ||
117 | // This is a variant of the delaunay algorithm | ||
118 | // each time a new vertex is inserted, all triangles that are influenced by it are deleted | ||
119 | // and replaced by new ones including the new vertex | ||
120 | // It is not very time efficient but easy to implement. | ||
121 | |||
122 | int iCurrentVertex; | ||
123 | int iMaxVertex = vertices.Count; | ||
124 | for (iCurrentVertex = usedForSeed; iCurrentVertex < iMaxVertex; iCurrentVertex++) | ||
125 | { | ||
126 | // Background: A triangle mesh fulfills the delaunay condition if (iff!) | ||
127 | // each circumlocutory circle (i.e. the circle that touches all three corners) | ||
128 | // of each triangle is empty of other vertices. | ||
129 | // Obviously a single (seeding) triangle fulfills this condition. | ||
130 | // If we now add one vertex, we need to reconstruct all triangles, that | ||
131 | // do not fulfill this condition with respect to the new triangle | ||
132 | |||
133 | // Find the triangles that are influenced by the new vertex | ||
134 | Vertex v = vertices[iCurrentVertex]; | ||
135 | if (v == null) | ||
136 | continue; // Null is polygon stop marker. Ignore it | ||
137 | List<Triangle> influencedTriangles = FindInfluencedTriangles(triangles, v); | ||
138 | |||
139 | List<Simplex> simplices = new List<Simplex>(); | ||
140 | |||
141 | // Reconstruction phase. First step, dissolve each triangle into it's simplices, | ||
142 | // i.e. it's "border lines" | ||
143 | // Goal is to find "inner" borders and delete them, while the hull gets conserved. | ||
144 | // Inner borders are special in the way that they always come twice, which is how we detect them | ||
145 | foreach (Triangle t in influencedTriangles) | ||
146 | { | ||
147 | List<Simplex> newSimplices = t.GetSimplices(); | ||
148 | simplices.AddRange(newSimplices); | ||
149 | triangles.Remove(t); | ||
150 | } | ||
151 | // Now sort the simplices. That will make identical ones reside side by side in the list | ||
152 | simplices.Sort(); | ||
153 | |||
154 | // Look for duplicate simplices here. | ||
155 | // Remember, they are directly side by side in the list right now, | ||
156 | // So we only check directly neighbours | ||
157 | int iSimplex; | ||
158 | List<Simplex> innerSimplices = new List<Simplex>(); | ||
159 | for (iSimplex = 1; iSimplex < simplices.Count; iSimplex++) // Startindex=1, so we can refer backwards | ||
160 | { | ||
161 | if (simplices[iSimplex - 1].CompareTo(simplices[iSimplex]) == 0) | ||
162 | { | ||
163 | innerSimplices.Add(simplices[iSimplex - 1]); | ||
164 | innerSimplices.Add(simplices[iSimplex]); | ||
165 | } | ||
166 | } | ||
167 | |||
168 | foreach (Simplex s in innerSimplices) | ||
169 | { | ||
170 | simplices.Remove(s); | ||
171 | } | ||
172 | |||
173 | // each simplex still in the list belongs to the hull of the region in question | ||
174 | // The new vertex (yes, we still deal with verices here :-)) forms a triangle | ||
175 | // with each of these simplices. Build the new triangles and add them to the list | ||
176 | foreach (Simplex s in simplices) | ||
177 | { | ||
178 | Triangle t = new Triangle(s.v1, s.v2, vertices[iCurrentVertex]); | ||
179 | if (!t.isDegraded()) | ||
180 | { | ||
181 | triangles.Add(t); | ||
182 | } | ||
183 | } | ||
184 | } | ||
185 | } | ||
186 | |||
187 | private static SimpleHull BuildHoleHull(PrimitiveBaseShape pbs, ProfileShape pshape, HollowShape hshape, UInt16 hollowFactor) | ||
188 | { | ||
189 | // Tackle HollowShape.Same | ||
190 | float fhollowFactor = (float)hollowFactor; | ||
191 | |||
192 | switch (pshape) | ||
193 | { | ||
194 | case ProfileShape.Square: | ||
195 | if (hshape == HollowShape.Same) | ||
196 | hshape= HollowShape.Square; | ||
197 | break; | ||
198 | case ProfileShape.EquilateralTriangle: | ||
199 | fhollowFactor = ((float)hollowFactor / 1.9f); | ||
200 | if (hshape == HollowShape.Same) | ||
201 | { | ||
202 | hshape = HollowShape.Triangle; | ||
203 | } | ||
204 | |||
205 | break; | ||
206 | |||
207 | case ProfileShape.HalfCircle: | ||
208 | case ProfileShape.Circle: | ||
209 | if (pbs.PathCurve == (byte)Extrusion.Straight) | ||
210 | { | ||
211 | if (hshape == HollowShape.Same) | ||
212 | { | ||
213 | hshape = HollowShape.Circle; | ||
214 | } | ||
215 | } | ||
216 | break; | ||
217 | |||
218 | |||
219 | default: | ||
220 | if (hshape == HollowShape.Same) | ||
221 | hshape= HollowShape.Square; | ||
222 | break; | ||
223 | } | ||
224 | |||
225 | |||
226 | SimpleHull holeHull = null; | ||
227 | |||
228 | if (hshape == HollowShape.Square) | ||
229 | { | ||
230 | float hollowFactorF = (float)fhollowFactor / (float)50000; | ||
231 | Vertex IMM; | ||
232 | Vertex IPM; | ||
233 | Vertex IPP; | ||
234 | Vertex IMP; | ||
235 | |||
236 | if (pshape == ProfileShape.Circle) | ||
237 | { // square cutout in cylinder is 45 degress rotated | ||
238 | IMM = new Vertex(0.0f, -0.707f * hollowFactorF, 0.0f); | ||
239 | IPM = new Vertex(0.707f * hollowFactorF, 0.0f, 0.0f); | ||
240 | IPP = new Vertex(0.0f, 0.707f * hollowFactorF, 0.0f); | ||
241 | IMP = new Vertex(-0.707f * hollowFactorF, 0.0f, 0.0f); | ||
242 | } | ||
243 | else if (pshape == ProfileShape.EquilateralTriangle) | ||
244 | { | ||
245 | IMM = new Vertex(0.0f, -0.667f * hollowFactorF, 0.0f); | ||
246 | IPM = new Vertex(0.667f * hollowFactorF, 0.0f, 0.0f); | ||
247 | IPP = new Vertex(0.0f, 0.667f * hollowFactorF, 0.0f); | ||
248 | IMP = new Vertex(-0.667f * hollowFactorF, 0.0f, 0.0f); | ||
249 | } | ||
250 | else | ||
251 | { | ||
252 | IMM = new Vertex(-0.5f * hollowFactorF, -0.5f * hollowFactorF, 0.0f); | ||
253 | IPM = new Vertex(+0.5f * hollowFactorF, -0.5f * hollowFactorF, 0.0f); | ||
254 | IPP = new Vertex(+0.5f * hollowFactorF, +0.5f * hollowFactorF, 0.0f); | ||
255 | IMP = new Vertex(-0.5f * hollowFactorF, +0.5f * hollowFactorF, 0.0f); | ||
256 | } | ||
257 | |||
258 | holeHull = new SimpleHull(); | ||
259 | |||
260 | holeHull.AddVertex(IMM); | ||
261 | holeHull.AddVertex(IMP); | ||
262 | holeHull.AddVertex(IPP); | ||
263 | holeHull.AddVertex(IPM); | ||
264 | } | ||
265 | //if (hshape == HollowShape.Circle && pbs.PathCurve == (byte)Extrusion.Straight) | ||
266 | if (hshape == HollowShape.Circle) | ||
267 | { | ||
268 | float hollowFactorF = (float)fhollowFactor / (float)50000; | ||
269 | |||
270 | //Counter clockwise around the quadrants | ||
271 | holeHull = new SimpleHull(); | ||
272 | |||
273 | holeHull.AddVertex(new Vertex(0.353553f * hollowFactorF, 0.353553f * hollowFactorF, 0.0f)); // 45 degrees | ||
274 | holeHull.AddVertex(new Vertex(0.433013f * hollowFactorF, 0.250000f * hollowFactorF, 0.0f)); // 30 degrees | ||
275 | holeHull.AddVertex(new Vertex(0.482963f * hollowFactorF, 0.129410f * hollowFactorF, 0.0f)); // 15 degrees | ||
276 | holeHull.AddVertex(new Vertex(0.500000f * hollowFactorF, 0.000000f * hollowFactorF, 0.0f)); // 0 degrees | ||
277 | holeHull.AddVertex(new Vertex(0.482963f * hollowFactorF, -0.129410f * hollowFactorF, 0.0f)); // 345 degrees | ||
278 | holeHull.AddVertex(new Vertex(0.433013f * hollowFactorF, -0.250000f * hollowFactorF, 0.0f)); // 330 degrees | ||
279 | holeHull.AddVertex(new Vertex(0.353553f * hollowFactorF, -0.353553f * hollowFactorF, 0.0f)); // 315 degrees | ||
280 | holeHull.AddVertex(new Vertex(0.250000f * hollowFactorF, -0.433013f * hollowFactorF, 0.0f)); // 300 degrees | ||
281 | holeHull.AddVertex(new Vertex(0.129410f * hollowFactorF, -0.482963f * hollowFactorF, 0.0f)); // 285 degrees | ||
282 | holeHull.AddVertex(new Vertex(0.000000f * hollowFactorF, -0.500000f * hollowFactorF, 0.0f)); // 270 degrees | ||
283 | holeHull.AddVertex(new Vertex(-0.129410f * hollowFactorF, -0.482963f * hollowFactorF, 0.0f)); // 255 degrees | ||
284 | holeHull.AddVertex(new Vertex(-0.250000f * hollowFactorF, -0.433013f * hollowFactorF, 0.0f)); // 240 degrees | ||
285 | holeHull.AddVertex(new Vertex(-0.353553f * hollowFactorF, -0.353553f * hollowFactorF, 0.0f)); // 225 degrees | ||
286 | holeHull.AddVertex(new Vertex(-0.433013f * hollowFactorF, -0.250000f * hollowFactorF, 0.0f)); // 210 degrees | ||
287 | holeHull.AddVertex(new Vertex(-0.482963f * hollowFactorF, -0.129410f * hollowFactorF, 0.0f)); // 195 degrees | ||
288 | holeHull.AddVertex(new Vertex(-0.500000f * hollowFactorF, 0.000000f * hollowFactorF, 0.0f)); // 180 degrees | ||
289 | holeHull.AddVertex(new Vertex(-0.482963f * hollowFactorF, 0.129410f * hollowFactorF, 0.0f)); // 165 degrees | ||
290 | holeHull.AddVertex(new Vertex(-0.433013f * hollowFactorF, 0.250000f * hollowFactorF, 0.0f)); // 150 degrees | ||
291 | holeHull.AddVertex(new Vertex(-0.353553f * hollowFactorF, 0.353553f * hollowFactorF, 0.0f)); // 135 degrees | ||
292 | holeHull.AddVertex(new Vertex(-0.250000f * hollowFactorF, 0.433013f * hollowFactorF, 0.0f)); // 120 degrees | ||
293 | holeHull.AddVertex(new Vertex(-0.129410f * hollowFactorF, 0.482963f * hollowFactorF, 0.0f)); // 105 degrees | ||
294 | holeHull.AddVertex(new Vertex(0.000000f * hollowFactorF, 0.500000f * hollowFactorF, 0.0f)); // 90 degrees | ||
295 | holeHull.AddVertex(new Vertex(0.129410f * hollowFactorF, 0.482963f * hollowFactorF, 0.0f)); // 75 degrees | ||
296 | holeHull.AddVertex(new Vertex(0.250000f * hollowFactorF, 0.433013f * hollowFactorF, 0.0f)); // 60 degrees | ||
297 | holeHull.AddVertex(new Vertex(0.353553f * hollowFactorF, 0.353553f * hollowFactorF, 0.0f)); // 45 degrees | ||
298 | |||
299 | } | ||
300 | if (hshape == HollowShape.Triangle) | ||
301 | { | ||
302 | float hollowFactorF = (float)fhollowFactor / (float)50000; | ||
303 | Vertex IMM; | ||
304 | Vertex IPM; | ||
305 | Vertex IPP; | ||
306 | |||
307 | if (pshape == ProfileShape.Square) | ||
308 | { | ||
309 | // corner points are at 345, 105, and 225 degrees for the triangle within a box | ||
310 | |||
311 | //IMM = new Vertex(((float)Math.Cos(345.0 * DEG_TO_RAD) * 0.5f) * hollowFactorF, ((float)Math.Sin(345.0 * DEG_TO_RAD) * 0.5f) * hollowFactorF, 0.0f); | ||
312 | //IPM = new Vertex(((float)Math.Cos(105.0 * DEG_TO_RAD) * 0.5f) * hollowFactorF, ((float)Math.Sin(105.0 * DEG_TO_RAD) * 0.5f) * hollowFactorF, 0.0f); | ||
313 | //IPP = new Vertex(((float)Math.Cos(225.0 * DEG_TO_RAD) * 0.5f) * hollowFactorF, ((float)Math.Sin(225.0 * DEG_TO_RAD) * 0.5f) * hollowFactorF, 0.0f); | ||
314 | |||
315 | // hard coded here for speed, the equations are in the commented out lines above | ||
316 | IMM = new Vertex(0.48296f * hollowFactorF, -0.12941f * hollowFactorF, 0.0f); | ||
317 | IPM = new Vertex(-0.12941f * hollowFactorF, 0.48296f * hollowFactorF, 0.0f); | ||
318 | IPP = new Vertex(-0.35355f * hollowFactorF, -0.35355f * hollowFactorF, 0.0f); | ||
319 | } | ||
320 | else | ||
321 | { | ||
322 | IMM = new Vertex(-0.25f * hollowFactorF, -0.45f * hollowFactorF, 0.0f); | ||
323 | IPM = new Vertex(+0.5f * hollowFactorF, +0f * hollowFactorF, 0.0f); | ||
324 | IPP = new Vertex(-0.25f * hollowFactorF, +0.45f * hollowFactorF, 0.0f); | ||
325 | } | ||
326 | |||
327 | holeHull = new SimpleHull(); | ||
328 | |||
329 | holeHull.AddVertex(IMM); | ||
330 | holeHull.AddVertex(IPP); | ||
331 | holeHull.AddVertex(IPM); | ||
332 | |||
333 | } | ||
334 | |||
335 | return holeHull; | ||
336 | |||
337 | |||
338 | } | ||
339 | 72 | ||
340 | /// <summary> | 73 | /// <summary> |
341 | /// creates a simple box mesh of the specified size | 74 | /// creates a simple box mesh of the specified size |
@@ -420,1364 +153,197 @@ namespace OpenSim.Region.Physics.Meshing | |||
420 | return CreateSimpleBoxMesh(minX, maxX, minY, maxY, minZ, maxZ); | 153 | return CreateSimpleBoxMesh(minX, maxX, minY, maxY, minZ, maxZ); |
421 | } | 154 | } |
422 | 155 | ||
423 | 156 | private void ReportPrimError(string message, string primName, PrimMesh primMesh) | |
424 | private static Mesh CreateBoxMesh(String primName, PrimitiveBaseShape primShape, PhysicsVector size) | ||
425 | // Builds the z (+ and -) surfaces of a box shaped prim | ||
426 | { | ||
427 | UInt16 hollowFactor = primShape.ProfileHollow; | ||
428 | UInt16 profileBegin = primShape.ProfileBegin; | ||
429 | UInt16 profileEnd = primShape.ProfileEnd; | ||
430 | UInt16 taperX = primShape.PathScaleX; | ||
431 | UInt16 taperY = primShape.PathScaleY; | ||
432 | UInt16 pathShearX = primShape.PathShearX; | ||
433 | UInt16 pathShearY = primShape.PathShearY; | ||
434 | |||
435 | #if SPAM | ||
436 | reportPrimParams("[BOX] " + primName, primShape); | ||
437 | #endif | ||
438 | |||
439 | // Procedure: This is based on the fact that the upper (plus) and lower (minus) Z-surface | ||
440 | // of a block are basically the same | ||
441 | // They may be warped differently but the shape is identical | ||
442 | // So we only create one surface as a model and derive both plus and minus surface of the block from it | ||
443 | // This is done in a model space where the block spans from -.5 to +.5 in X and Y | ||
444 | // The mapping to Scene space is done later during the "extrusion" phase | ||
445 | |||
446 | // Base | ||
447 | Vertex MM = new Vertex(-0.5f, -0.5f, 0.0f); | ||
448 | Vertex PM = new Vertex(+0.5f, -0.5f, 0.0f); | ||
449 | Vertex PP = new Vertex(+0.5f, +0.5f, 0.0f); | ||
450 | Vertex MP = new Vertex(-0.5f, +0.5f, 0.0f); | ||
451 | |||
452 | SimpleHull outerHull = new SimpleHull(); | ||
453 | |||
454 | outerHull.AddVertex(PP); | ||
455 | outerHull.AddVertex(MP); | ||
456 | outerHull.AddVertex(MM); | ||
457 | outerHull.AddVertex(PM); | ||
458 | |||
459 | // Deal with cuts now | ||
460 | if ((profileBegin != 0) || (profileEnd != 0)) | ||
461 | { | ||
462 | double fProfileBeginAngle = profileBegin / 50000.0*360.0; | ||
463 | // In degree, for easier debugging and understanding | ||
464 | fProfileBeginAngle -= (90.0 + 45.0); // for some reasons, the SL client counts from the corner -X/-Y | ||
465 | double fProfileEndAngle = 360.0 - profileEnd / 50000.0*360.0; // Pathend comes as complement to 1.0 | ||
466 | fProfileEndAngle -= (90.0 + 45.0); | ||
467 | |||
468 | // avoid some problem angles until the hull subtraction routine is fixed | ||
469 | if ((fProfileBeginAngle + 45.0f) % 90.0f == 0.0f) | ||
470 | fProfileBeginAngle += 5.0f; | ||
471 | if ((fProfileEndAngle + 45.0f) % 90.0f == 0.0f) | ||
472 | fProfileEndAngle -= 5.0f; | ||
473 | if (fProfileBeginAngle % 90.0f == 0.0f) | ||
474 | fProfileBeginAngle += 1.0f; | ||
475 | if (fProfileEndAngle % 90.0f == 0.0f) | ||
476 | fProfileEndAngle -= 1.0f; | ||
477 | |||
478 | if (fProfileBeginAngle < fProfileEndAngle) | ||
479 | fProfileEndAngle -= 360.0; | ||
480 | |||
481 | #if SPAM | ||
482 | Console.WriteLine("Meshmerizer: fProfileBeginAngle: " + fProfileBeginAngle.ToString() + " fProfileEndAngle: " + fProfileEndAngle.ToString()); | ||
483 | #endif | ||
484 | |||
485 | // Note, that we don't want to cut out a triangle, even if this is a | ||
486 | // good approximation for small cuts. Indeed we want to cut out an arc | ||
487 | // and we approximate this arc by a polygon chain | ||
488 | // Also note, that these vectors are of length 1.0 and thus their endpoints lay outside the model space | ||
489 | // So it can easily be subtracted from the outer hull | ||
490 | int iSteps = (int) (((fProfileBeginAngle - fProfileEndAngle)/45.0) + .5); | ||
491 | // how many steps do we need with approximately 45 degree | ||
492 | double dStepWidth = (fProfileBeginAngle - fProfileEndAngle)/iSteps; | ||
493 | |||
494 | Vertex origin = new Vertex(0.0f, 0.0f, 0.0f); | ||
495 | |||
496 | // Note the sequence of vertices here. It's important to have the other rotational sense than in outerHull | ||
497 | SimpleHull cutHull = new SimpleHull(); | ||
498 | cutHull.AddVertex(origin); | ||
499 | for (int i = 0; i < iSteps; i++) | ||
500 | { | ||
501 | double angle = fProfileBeginAngle - i*dStepWidth; // we count against the angle orientation!!!! | ||
502 | Vertex v = Vertex.FromAngle(angle*Math.PI/180.0); | ||
503 | cutHull.AddVertex(v); | ||
504 | } | ||
505 | Vertex legEnd = Vertex.FromAngle(fProfileEndAngle*Math.PI/180.0); | ||
506 | // Calculated separately to avoid errors | ||
507 | cutHull.AddVertex(legEnd); | ||
508 | |||
509 | //m_log.DebugFormat("Starting cutting of the hollow shape from the prim {1}", 0, primName); | ||
510 | SimpleHull cuttedHull = SimpleHull.SubtractHull(outerHull, cutHull); | ||
511 | |||
512 | outerHull = cuttedHull; | ||
513 | } | ||
514 | |||
515 | // Deal with the hole here | ||
516 | if (hollowFactor > 0) | ||
517 | { | ||
518 | if (hollowFactor < 1000) | ||
519 | hollowFactor = 1000; // some sane minimum for our beloved SimpleHull routines | ||
520 | |||
521 | SimpleHull holeHull = BuildHoleHull(primShape, primShape.ProfileShape, primShape.HollowShape, hollowFactor); | ||
522 | if (holeHull != null) | ||
523 | { | ||
524 | SimpleHull hollowedHull = SimpleHull.SubtractHull(outerHull, holeHull); | ||
525 | |||
526 | outerHull = hollowedHull; | ||
527 | } | ||
528 | } | ||
529 | |||
530 | Mesh m = new Mesh(); | ||
531 | |||
532 | Vertex Seed1 = new Vertex(0.0f, -10.0f, 0.0f); | ||
533 | Vertex Seed2 = new Vertex(-10.0f, 10.0f, 0.0f); | ||
534 | Vertex Seed3 = new Vertex(10.0f, 10.0f, 0.0f); | ||
535 | |||
536 | m.Add(Seed1); | ||
537 | m.Add(Seed2); | ||
538 | m.Add(Seed3); | ||
539 | |||
540 | m.Add(new Triangle(Seed1, Seed2, Seed3)); | ||
541 | m.Add(outerHull.getVertices()); | ||
542 | |||
543 | InsertVertices(m.vertices, 3, m.triangles); | ||
544 | m.DumpRaw(baseDir, primName, "Proto first Mesh"); | ||
545 | |||
546 | m.Remove(Seed1); | ||
547 | m.Remove(Seed2); | ||
548 | m.Remove(Seed3); | ||
549 | m.DumpRaw(baseDir, primName, "Proto seeds removed"); | ||
550 | |||
551 | m.RemoveTrianglesOutside(outerHull); | ||
552 | m.DumpRaw(baseDir, primName, "Proto outsides removed"); | ||
553 | |||
554 | foreach (Triangle t in m.triangles) | ||
555 | { | ||
556 | PhysicsVector n = t.getNormal(); | ||
557 | if (n.Z < 0.0) | ||
558 | t.invertNormal(); | ||
559 | } | ||
560 | |||
561 | Extruder extr = new Extruder(); | ||
562 | |||
563 | extr.size = size; | ||
564 | |||
565 | if (taperX != 100) | ||
566 | { | ||
567 | if (taperX > 100) | ||
568 | { | ||
569 | extr.taperTopFactorX = 1.0f - ((float)(taperX - 100) / 100); | ||
570 | } | ||
571 | else | ||
572 | { | ||
573 | extr.taperBotFactorX = 1.0f - ((100 - (float)taperX) / 100); | ||
574 | } | ||
575 | |||
576 | } | ||
577 | |||
578 | if (taperY != 100) | ||
579 | { | ||
580 | if (taperY > 100) | ||
581 | { | ||
582 | extr.taperTopFactorY = 1.0f - ((float)(taperY - 100) / 100); | ||
583 | } | ||
584 | else | ||
585 | { | ||
586 | extr.taperBotFactorY = 1.0f - ((100 - (float)taperY) / 100); | ||
587 | } | ||
588 | } | ||
589 | |||
590 | if (pathShearX != 0) | ||
591 | { | ||
592 | if (pathShearX > 50) | ||
593 | { | ||
594 | // Complimentary byte. Negative values wrap around the byte. Positive values go up to 50 | ||
595 | extr.pushX = (((float)(256 - pathShearX) / 100) * -1f); | ||
596 | } | ||
597 | else | ||
598 | { | ||
599 | extr.pushX = (float)pathShearX / 100; | ||
600 | } | ||
601 | } | ||
602 | |||
603 | if (pathShearY != 0) | ||
604 | { | ||
605 | if (pathShearY > 50) | ||
606 | { | ||
607 | // Complimentary byte. Negative values wrap around the byte. Positive values go up to 50 | ||
608 | extr.pushY = (((float)(256 - pathShearY) / 100) * -1f); | ||
609 | } | ||
610 | else | ||
611 | { | ||
612 | extr.pushY = (float)pathShearY / 100; | ||
613 | } | ||
614 | } | ||
615 | |||
616 | extr.twistTop = (float)primShape.PathTwist * (float)Math.PI * 0.01f; | ||
617 | extr.twistBot = (float)primShape.PathTwistBegin * (float)Math.PI * 0.01f; | ||
618 | extr.pathBegin = primShape.PathBegin; | ||
619 | extr.pathEnd = primShape.PathEnd; | ||
620 | |||
621 | Mesh result = extr.ExtrudeLinearPath(m); | ||
622 | result.DumpRaw(baseDir, primName, "Z extruded"); | ||
623 | #if SPAM | ||
624 | int vCount = 0; | ||
625 | |||
626 | foreach (Vertex v in result.vertices) | ||
627 | if (v != null) | ||
628 | vCount++; | ||
629 | System.Console.WriteLine("Mesh vertex count: " + vCount.ToString()); | ||
630 | #endif | ||
631 | return result; | ||
632 | } | ||
633 | |||
634 | private static Mesh CreateCylinderMesh(String primName, PrimitiveBaseShape primShape, PhysicsVector size) | ||
635 | // Builds the z (+ and -) surfaces of a box shaped prim | ||
636 | { | 157 | { |
158 | Console.WriteLine(message); | ||
159 | Console.WriteLine("\nPrim Name: " + primName); | ||
160 | Console.WriteLine("****** PrimMesh Parameters ******\n" + primMesh.ParamsToDisplayString()); | ||
637 | 161 | ||
638 | UInt16 hollowFactor = primShape.ProfileHollow; | ||
639 | UInt16 profileBegin = primShape.ProfileBegin; | ||
640 | UInt16 profileEnd = primShape.ProfileEnd; | ||
641 | UInt16 taperX = primShape.PathScaleX; | ||
642 | UInt16 taperY = primShape.PathScaleY; | ||
643 | UInt16 pathShearX = primShape.PathShearX; | ||
644 | UInt16 pathShearY = primShape.PathShearY; | ||
645 | |||
646 | #if SPAM | ||
647 | reportPrimParams("[CYLINDER] " + primName, primShape); | ||
648 | #endif | ||
649 | |||
650 | |||
651 | // Procedure: This is based on the fact that the upper (plus) and lower (minus) Z-surface | ||
652 | // of a block are basically the same | ||
653 | // They may be warped differently but the shape is identical | ||
654 | // So we only create one surface as a model and derive both plus and minus surface of the block from it | ||
655 | // This is done in a model space where the block spans from -.5 to +.5 in X and Y | ||
656 | // The mapping to Scene space is done later during the "extrusion" phase | ||
657 | |||
658 | // Base | ||
659 | |||
660 | SimpleHull outerHull = new SimpleHull(); | ||
661 | |||
662 | // counter-clockwise around the quadrants, start at 45 degrees | ||
663 | |||
664 | outerHull.AddVertex(new Vertex(0.353553f, 0.353553f, 0.0f)); // 45 degrees | ||
665 | outerHull.AddVertex(new Vertex(0.250000f, 0.433013f, 0.0f)); // 60 degrees | ||
666 | outerHull.AddVertex(new Vertex(0.129410f, 0.482963f, 0.0f)); // 75 degrees | ||
667 | outerHull.AddVertex(new Vertex(0.000000f, 0.500000f, 0.0f)); // 90 degrees | ||
668 | outerHull.AddVertex(new Vertex(-0.129410f, 0.482963f, 0.0f)); // 105 degrees | ||
669 | outerHull.AddVertex(new Vertex(-0.250000f, 0.433013f, 0.0f)); // 120 degrees | ||
670 | outerHull.AddVertex(new Vertex(-0.353553f, 0.353553f, 0.0f)); // 135 degrees | ||
671 | outerHull.AddVertex(new Vertex(-0.433013f, 0.250000f, 0.0f)); // 150 degrees | ||
672 | outerHull.AddVertex(new Vertex(-0.482963f, 0.129410f, 0.0f)); // 165 degrees | ||
673 | outerHull.AddVertex(new Vertex(-0.500000f, 0.000000f, 0.0f)); // 180 degrees | ||
674 | outerHull.AddVertex(new Vertex(-0.482963f, -0.129410f, 0.0f)); // 195 degrees | ||
675 | outerHull.AddVertex(new Vertex(-0.433013f, -0.250000f, 0.0f)); // 210 degrees | ||
676 | outerHull.AddVertex(new Vertex(-0.353553f, -0.353553f, 0.0f)); // 225 degrees | ||
677 | outerHull.AddVertex(new Vertex(-0.250000f, -0.433013f, 0.0f)); // 240 degrees | ||
678 | outerHull.AddVertex(new Vertex(-0.129410f, -0.482963f, 0.0f)); // 255 degrees | ||
679 | outerHull.AddVertex(new Vertex(0.000000f, -0.500000f, 0.0f)); // 270 degrees | ||
680 | outerHull.AddVertex(new Vertex(0.129410f, -0.482963f, 0.0f)); // 285 degrees | ||
681 | outerHull.AddVertex(new Vertex(0.250000f, -0.433013f, 0.0f)); // 300 degrees | ||
682 | outerHull.AddVertex(new Vertex(0.353553f, -0.353553f, 0.0f)); // 315 degrees | ||
683 | outerHull.AddVertex(new Vertex(0.433013f, -0.250000f, 0.0f)); // 330 degrees | ||
684 | outerHull.AddVertex(new Vertex(0.482963f, -0.129410f, 0.0f)); // 345 degrees | ||
685 | outerHull.AddVertex(new Vertex(0.500000f, 0.000000f, 0.0f)); // 0 degrees | ||
686 | outerHull.AddVertex(new Vertex(0.482963f, 0.129410f, 0.0f)); // 15 degrees | ||
687 | outerHull.AddVertex(new Vertex(0.433013f, 0.250000f, 0.0f)); // 30 degrees | ||
688 | |||
689 | |||
690 | |||
691 | // Deal with cuts now | ||
692 | if ((profileBegin != 0) || (profileEnd != 0)) | ||
693 | { | ||
694 | double fProfileBeginAngle = profileBegin / 50000.0 * 360.0; | ||
695 | // In degree, for easier debugging and understanding | ||
696 | double fProfileEndAngle = 360.0 - profileEnd / 50000.0 * 360.0; // Pathend comes as complement to 1.0 | ||
697 | |||
698 | #if SPAM | ||
699 | Console.WriteLine("Extruder: Cylinder fProfileBeginAngle: " + fProfileBeginAngle.ToString() + " fProfileEndAngle: " + fProfileEndAngle.ToString()); | ||
700 | #endif | ||
701 | if (fProfileBeginAngle > 270.0f && fProfileBeginAngle < 271.8f) // a problem angle for the hull subtract routine :( | ||
702 | fProfileBeginAngle = 271.8f; // workaround - use the smaller slice | ||
703 | |||
704 | if (fProfileBeginAngle < fProfileEndAngle) | ||
705 | fProfileEndAngle -= 360.0; | ||
706 | #if SPAM | ||
707 | Console.WriteLine("Extruder: Cylinder fProfileBeginAngle: " + fProfileBeginAngle.ToString() + " fProfileEndAngle: " + fProfileEndAngle.ToString()); | ||
708 | #endif | ||
709 | |||
710 | // Note, that we don't want to cut out a triangle, even if this is a | ||
711 | // good approximation for small cuts. Indeed we want to cut out an arc | ||
712 | // and we approximate this arc by a polygon chain | ||
713 | // Also note, that these vectors are of length 1.0 and thus their endpoints lay outside the model space | ||
714 | // So it can easily be subtracted from the outer hull | ||
715 | int iSteps = (int)(((fProfileBeginAngle - fProfileEndAngle) / 45.0) + .5); | ||
716 | // how many steps do we need with approximately 45 degree | ||
717 | double dStepWidth = (fProfileBeginAngle - fProfileEndAngle) / iSteps; | ||
718 | |||
719 | Vertex origin = new Vertex(0.0f, 0.0f, 0.0f); | ||
720 | |||
721 | // Note the sequence of vertices here. It's important to have the other rotational sense than in outerHull | ||
722 | SimpleHull cutHull = new SimpleHull(); | ||
723 | cutHull.AddVertex(origin); | ||
724 | for (int i = 0; i < iSteps; i++) | ||
725 | { | ||
726 | double angle = fProfileBeginAngle - i * dStepWidth; // we count against the angle orientation!!!! | ||
727 | Vertex v = Vertex.FromAngle(angle * Math.PI / 180.0); | ||
728 | cutHull.AddVertex(v); | ||
729 | } | ||
730 | Vertex legEnd = Vertex.FromAngle(fProfileEndAngle * Math.PI / 180.0); | ||
731 | // Calculated separately to avoid errors | ||
732 | cutHull.AddVertex(legEnd); | ||
733 | |||
734 | SimpleHull cuttedHull = SimpleHull.SubtractHull(outerHull, cutHull); | ||
735 | |||
736 | outerHull = cuttedHull; | ||
737 | } | ||
738 | |||
739 | // Deal with the hole here | ||
740 | if (hollowFactor > 0) | ||
741 | { | ||
742 | if (hollowFactor < 1000) | ||
743 | hollowFactor = 1000; // some sane minimum for our beloved SimpleHull routines | ||
744 | |||
745 | SimpleHull holeHull = BuildHoleHull(primShape, primShape.ProfileShape, primShape.HollowShape, hollowFactor); | ||
746 | if (holeHull != null) | ||
747 | { | ||
748 | SimpleHull hollowedHull = SimpleHull.SubtractHull(outerHull, holeHull); | ||
749 | |||
750 | outerHull = hollowedHull; | ||
751 | } | ||
752 | } | ||
753 | |||
754 | Mesh m = new Mesh(); | ||
755 | |||
756 | Vertex Seed1 = new Vertex(0.0f, -10.0f, 0.0f); | ||
757 | Vertex Seed2 = new Vertex(-10.0f, 10.0f, 0.0f); | ||
758 | Vertex Seed3 = new Vertex(10.0f, 10.0f, 0.0f); | ||
759 | |||
760 | m.Add(Seed1); | ||
761 | m.Add(Seed2); | ||
762 | m.Add(Seed3); | ||
763 | |||
764 | m.Add(new Triangle(Seed1, Seed2, Seed3)); | ||
765 | m.Add(outerHull.getVertices()); | ||
766 | |||
767 | InsertVertices(m.vertices, 3, m.triangles); | ||
768 | m.DumpRaw(baseDir, primName, "Proto first Mesh"); | ||
769 | |||
770 | m.Remove(Seed1); | ||
771 | m.Remove(Seed2); | ||
772 | m.Remove(Seed3); | ||
773 | m.DumpRaw(baseDir, primName, "Proto seeds removed"); | ||
774 | |||
775 | m.RemoveTrianglesOutside(outerHull); | ||
776 | m.DumpRaw(baseDir, primName, "Proto outsides removed"); | ||
777 | |||
778 | foreach (Triangle t in m.triangles) | ||
779 | { | ||
780 | PhysicsVector n = t.getNormal(); | ||
781 | if (n.Z < 0.0) | ||
782 | t.invertNormal(); | ||
783 | } | ||
784 | |||
785 | Extruder extr = new Extruder(); | ||
786 | |||
787 | extr.size = size; | ||
788 | |||
789 | if (taperX != 100) | ||
790 | { | ||
791 | if (taperX > 100) | ||
792 | { | ||
793 | extr.taperTopFactorX = 1.0f - ((float)(taperX - 100) / 100); } | ||
794 | else | ||
795 | { | ||
796 | extr.taperBotFactorX = 1.0f - ((100 - (float)taperX) / 100); | ||
797 | } | ||
798 | |||
799 | } | ||
800 | |||
801 | if (taperY != 100) | ||
802 | { | ||
803 | if (taperY > 100) | ||
804 | { | ||
805 | extr.taperTopFactorY = 1.0f - ((float)(taperY - 100) / 100); | ||
806 | } | ||
807 | else | ||
808 | { | ||
809 | extr.taperBotFactorY = 1.0f - ((100 - (float)taperY) / 100); | ||
810 | } | ||
811 | } | ||
812 | |||
813 | if (pathShearX != 0) | ||
814 | { | ||
815 | if (pathShearX > 50) | ||
816 | { | ||
817 | // Complimentary byte. Negative values wrap around the byte. Positive values go up to 50 | ||
818 | extr.pushX = (((float)(256 - pathShearX) / 100) * -1f); | ||
819 | } | ||
820 | else | ||
821 | { | ||
822 | extr.pushX = (float)pathShearX / 100; | ||
823 | } | ||
824 | } | ||
825 | |||
826 | if (pathShearY != 0) | ||
827 | { | ||
828 | if (pathShearY > 50) | ||
829 | { | ||
830 | // Complimentary byte. Negative values wrap around the byte. Positive values go up to 50 | ||
831 | extr.pushY = (((float)(256 - pathShearY) / 100) * -1f); | ||
832 | } | ||
833 | else | ||
834 | { | ||
835 | extr.pushY = (float)pathShearY / 100; | ||
836 | } | ||
837 | |||
838 | } | ||
839 | |||
840 | extr.twistTop = (float)primShape.PathTwist * (float)Math.PI * 0.01f; | ||
841 | extr.twistBot = (float)primShape.PathTwistBegin * (float)Math.PI * 0.01f; | ||
842 | extr.pathBegin = primShape.PathBegin; | ||
843 | extr.pathEnd = primShape.PathEnd; | ||
844 | |||
845 | Mesh result = extr.ExtrudeLinearPath(m); | ||
846 | result.DumpRaw(baseDir, primName, "Z extruded"); | ||
847 | #if SPAM | ||
848 | int vCount = 0; | ||
849 | |||
850 | foreach (Vertex v in result.vertices) | ||
851 | if (v != null) | ||
852 | vCount++; | ||
853 | System.Console.WriteLine("Mesh vertex count: " + vCount.ToString()); | ||
854 | #endif | ||
855 | return result; | ||
856 | } | 162 | } |
857 | 163 | ||
858 | private static Mesh CreatePrismMesh(String primName, PrimitiveBaseShape primShape, PhysicsVector size) | 164 | public Mesh CreateMeshFromPrimMesher(string primName, PrimitiveBaseShape primShape, PhysicsVector size, float lod) |
859 | // Builds the z (+ and -) surfaces of a box shaped prim | ||
860 | { | 165 | { |
861 | UInt16 hollowFactor = primShape.ProfileHollow; | 166 | Mesh mesh = new Mesh(); |
862 | UInt16 profileBegin = primShape.ProfileBegin; | 167 | PrimMesh primMesh; |
863 | UInt16 profileEnd = primShape.ProfileEnd; | 168 | PrimMesher.SculptMesh sculptMesh; |
864 | UInt16 taperX = primShape.PathScaleX; | ||
865 | UInt16 taperY = primShape.PathScaleY; | ||
866 | UInt16 pathShearX = primShape.PathShearX; | ||
867 | UInt16 pathShearY = primShape.PathShearY; | ||
868 | |||
869 | |||
870 | #if SPAM | ||
871 | reportPrimParams("[PRISM] " + primName, primShape); | ||
872 | #endif | ||
873 | // Procedure: This is based on the fact that the upper (plus) and lower (minus) Z-surface | ||
874 | // of a block are basically the same | ||
875 | // They may be warped differently but the shape is identical | ||
876 | // So we only create one surface as a model and derive both plus and minus surface of the block from it | ||
877 | // This is done in a model space where the block spans from -.5 to +.5 in X and Y | ||
878 | // The mapping to Scene space is done later during the "extrusion" phase | ||
879 | |||
880 | // Base | ||
881 | Vertex MM = new Vertex(-0.25f, -0.45f, 0.0f); | ||
882 | Vertex PM = new Vertex(+0.5f, 0f, 0.0f); | ||
883 | Vertex PP = new Vertex(-0.25f, +0.45f, 0.0f); | ||
884 | |||
885 | |||
886 | SimpleHull outerHull = new SimpleHull(); | ||
887 | |||
888 | outerHull.AddVertex(PP); | ||
889 | outerHull.AddVertex(MM); | ||
890 | outerHull.AddVertex(PM); | ||
891 | |||
892 | // Deal with cuts now | ||
893 | if ((profileBegin != 0) || (profileEnd != 0)) | ||
894 | { | ||
895 | double fProfileBeginAngle = profileBegin / 50000.0 * 360.0; | ||
896 | // In degree, for easier debugging and understanding | ||
897 | double fProfileEndAngle = 360.0 - profileEnd / 50000.0 * 360.0; // Pathend comes as complement to 1.0 | ||
898 | |||
899 | if (fProfileBeginAngle < fProfileEndAngle) | ||
900 | fProfileEndAngle -= 360.0; | ||
901 | |||
902 | // Note, that we don't want to cut out a triangle, even if this is a | ||
903 | // good approximation for small cuts. Indeed we want to cut out an arc | ||
904 | // and we approximate this arc by a polygon chain | ||
905 | // Also note, that these vectors are of length 1.0 and thus their endpoints lay outside the model space | ||
906 | // So it can easily be subtracted from the outer hull | ||
907 | int iSteps = (int)(((fProfileBeginAngle - fProfileEndAngle) / 45.0) + .5); | ||
908 | // how many steps do we need with approximately 45 degree | ||
909 | double dStepWidth = (fProfileBeginAngle - fProfileEndAngle) / iSteps; | ||
910 | |||
911 | Vertex origin = new Vertex(0.0f, 0.0f, 0.0f); | ||
912 | |||
913 | // Note the sequence of vertices here. It's important to have the other rotational sense than in outerHull | ||
914 | SimpleHull cutHull = new SimpleHull(); | ||
915 | cutHull.AddVertex(origin); | ||
916 | for (int i = 0; i < iSteps; i++) | ||
917 | { | ||
918 | double angle = fProfileBeginAngle - i * dStepWidth; // we count against the angle orientation!!!! | ||
919 | Vertex v = Vertex.FromAngle(angle * Math.PI / 180.0); | ||
920 | cutHull.AddVertex(v); | ||
921 | } | ||
922 | Vertex legEnd = Vertex.FromAngle(fProfileEndAngle * Math.PI / 180.0); | ||
923 | // Calculated separately to avoid errors | ||
924 | cutHull.AddVertex(legEnd); | ||
925 | |||
926 | SimpleHull cuttedHull = SimpleHull.SubtractHull(outerHull, cutHull); | ||
927 | |||
928 | outerHull = cuttedHull; | ||
929 | } | ||
930 | |||
931 | // Deal with the hole here | ||
932 | if (hollowFactor > 0) | ||
933 | { | ||
934 | if (hollowFactor < 1000) | ||
935 | hollowFactor = 1000; // some sane minimum for our beloved SimpleHull routines | ||
936 | |||
937 | SimpleHull holeHull = BuildHoleHull(primShape, primShape.ProfileShape, primShape.HollowShape, hollowFactor); | ||
938 | if (holeHull != null) | ||
939 | { | ||
940 | SimpleHull hollowedHull = SimpleHull.SubtractHull(outerHull, holeHull); | ||
941 | |||
942 | outerHull = hollowedHull; | ||
943 | } | ||
944 | } | ||
945 | |||
946 | Mesh m = new Mesh(); | ||
947 | |||
948 | Vertex Seed1 = new Vertex(0.0f, -10.0f, 0.0f); | ||
949 | Vertex Seed2 = new Vertex(-10.0f, 10.0f, 0.0f); | ||
950 | Vertex Seed3 = new Vertex(10.0f, 10.0f, 0.0f); | ||
951 | |||
952 | m.Add(Seed1); | ||
953 | m.Add(Seed2); | ||
954 | m.Add(Seed3); | ||
955 | |||
956 | m.Add(new Triangle(Seed1, Seed2, Seed3)); | ||
957 | m.Add(outerHull.getVertices()); | ||
958 | |||
959 | InsertVertices(m.vertices, 3, m.triangles); | ||
960 | m.DumpRaw(baseDir, primName, "Proto first Mesh"); | ||
961 | |||
962 | m.Remove(Seed1); | ||
963 | m.Remove(Seed2); | ||
964 | m.Remove(Seed3); | ||
965 | m.DumpRaw(baseDir, primName, "Proto seeds removed"); | ||
966 | |||
967 | m.RemoveTrianglesOutside(outerHull); | ||
968 | m.DumpRaw(baseDir, primName, "Proto outsides removed"); | ||
969 | |||
970 | foreach (Triangle t in m.triangles) | ||
971 | { | ||
972 | PhysicsVector n = t.getNormal(); | ||
973 | if (n.Z < 0.0) | ||
974 | t.invertNormal(); | ||
975 | } | ||
976 | 169 | ||
977 | Extruder extr = new Extruder(); | 170 | List<Coord> coords; |
171 | List<Face> faces; | ||
978 | 172 | ||
979 | extr.size = size; | 173 | Image idata = null; |
980 | 174 | ||
981 | if (taperX != 100) | 175 | if (primShape.SculptEntry) |
982 | { | 176 | { |
983 | if (taperX > 100) | 177 | if (primShape.SculptData.Length == 0) |
984 | { | 178 | return null; |
985 | extr.taperTopFactorX = 1.0f - ((float)(taperX - 100) / 100); | ||
986 | } | ||
987 | else | ||
988 | { | ||
989 | extr.taperBotFactorX = 1.0f - ((100 - (float)taperX) / 100); | ||
990 | } | ||
991 | |||
992 | } | ||
993 | 179 | ||
994 | if (taperY != 100) | 180 | try |
995 | { | ||
996 | if (taperY > 100) | ||
997 | { | 181 | { |
998 | extr.taperTopFactorY = 1.0f - ((float)(taperY - 100) / 100); | 182 | ManagedImage managedImage; // we never use this |
183 | OpenJPEG.DecodeToImage(primShape.SculptData, out managedImage, out idata); | ||
999 | } | 184 | } |
1000 | else | 185 | catch (Exception) |
1001 | { | 186 | { |
1002 | extr.taperBotFactorY = 1.0f - ((100 - (float)taperY) / 100); | 187 | System.Console.WriteLine("[PHYSICS]: Unable to generate a Sculpty physics proxy. Sculpty texture decode failed!"); |
188 | return null; | ||
1003 | } | 189 | } |
1004 | } | ||
1005 | 190 | ||
1006 | if (pathShearX != 0) | ||
1007 | { | ||
1008 | if (pathShearX > 50) | ||
1009 | { | ||
1010 | // Complimentary byte. Negative values wrap around the byte. Positive values go up to 50 | ||
1011 | extr.pushX = (((float)(256 - pathShearX) / 100) * -1f); | ||
1012 | } | ||
1013 | else | ||
1014 | { | ||
1015 | extr.pushX = (float)pathShearX / 100; | ||
1016 | } | ||
1017 | } | ||
1018 | 191 | ||
1019 | if (pathShearY != 0) | 192 | PrimMesher.SculptMesh.SculptType sculptType; |
1020 | { | 193 | switch ((OpenMetaverse.SculptType)primShape.SculptType) |
1021 | if (pathShearY > 50) | ||
1022 | { | ||
1023 | // Complimentary byte. Negative values wrap around the byte. Positive values go up to 50 | ||
1024 | extr.pushY = (((float)(256 - pathShearY) / 100) * -1f); | ||
1025 | } | ||
1026 | else | ||
1027 | { | 194 | { |
1028 | extr.pushY = (float)pathShearY / 100; | 195 | case OpenMetaverse.SculptType.Cylinder: |
196 | sculptType = PrimMesher.SculptMesh.SculptType.cylinder; | ||
197 | break; | ||
198 | case OpenMetaverse.SculptType.Plane: | ||
199 | sculptType = PrimMesher.SculptMesh.SculptType.plane; | ||
200 | break; | ||
201 | case OpenMetaverse.SculptType.Torus: | ||
202 | sculptType = PrimMesher.SculptMesh.SculptType.torus; | ||
203 | break; | ||
204 | case OpenMetaverse.SculptType.Sphere: | ||
205 | default: | ||
206 | sculptType = PrimMesher.SculptMesh.SculptType.sphere; | ||
207 | break; | ||
1029 | } | 208 | } |
1030 | } | 209 | sculptMesh = new PrimMesher.SculptMesh((Bitmap)idata, sculptType, (int)lod, false); |
1031 | |||
1032 | extr.twistTop = (float)primShape.PathTwist * (float)Math.PI * 0.01f; | ||
1033 | extr.twistBot = (float)primShape.PathTwistBegin * (float)Math.PI * 0.01f; | ||
1034 | extr.pathBegin = primShape.PathBegin; | ||
1035 | extr.pathEnd = primShape.PathEnd; | ||
1036 | |||
1037 | Mesh result = extr.ExtrudeLinearPath(m); | ||
1038 | result.DumpRaw(baseDir, primName, "Z extruded"); | ||
1039 | #if SPAM | ||
1040 | int vCount = 0; | ||
1041 | |||
1042 | foreach (Vertex v in result.vertices) | ||
1043 | if (v != null) | ||
1044 | vCount++; | ||
1045 | System.Console.WriteLine("Mesh vertex count: " + vCount.ToString()); | ||
1046 | #endif | ||
1047 | return result; | ||
1048 | } | ||
1049 | |||
1050 | /// <summary> | ||
1051 | /// builds an icosahedral geodesic sphere - used as default in place of problem meshes | ||
1052 | /// </summary> | ||
1053 | /// <param name="primName"></param> | ||
1054 | /// <param name="primShape"></param> | ||
1055 | /// <param name="size"></param> | ||
1056 | /// <returns></returns> | ||
1057 | private static Mesh CreateSphereMesh(String primName, PrimitiveBaseShape primShape, PhysicsVector size) | ||
1058 | { | ||
1059 | // Builds an icosahedral geodesic sphere | ||
1060 | // based on an article by Paul Bourke | ||
1061 | // http://local.wasp.uwa.edu.au/~pbourke/ | ||
1062 | // articles: | ||
1063 | // http://local.wasp.uwa.edu.au/~pbourke/geometry/polygonmesh/ | ||
1064 | // and | ||
1065 | // http://local.wasp.uwa.edu.au/~pbourke/geometry/polyhedra/index.html | ||
1066 | |||
1067 | // Still have more to do here. | ||
1068 | |||
1069 | Mesh m = new Mesh(); | ||
1070 | |||
1071 | #if SPAM | ||
1072 | reportPrimParams("[SPHERE] " + primName, primShape); | ||
1073 | #endif | ||
1074 | |||
1075 | float LOD = 0.2f; | ||
1076 | float diameter = 0.5f;// Our object will result in -0.5 to 0.5 | ||
1077 | float sq5 = (float) Math.Sqrt(5.0); | ||
1078 | float phi = (1 + sq5) * 0.5f; | ||
1079 | float rat = (float) Math.Sqrt(10f + (2f * sq5)) / (4f * phi); | ||
1080 | float a = (diameter / rat) * 0.5f; | ||
1081 | float b = (diameter / rat) / (2.0f * phi); | ||
1082 | |||
1083 | |||
1084 | // 12 Icosahedron vertexes | ||
1085 | Vertex v1 = new Vertex(0f, b, -a); | ||
1086 | Vertex v2 = new Vertex(b, a, 0f); | ||
1087 | Vertex v3 = new Vertex(-b, a, 0f); | ||
1088 | Vertex v4 = new Vertex(0f, b, a); | ||
1089 | Vertex v5 = new Vertex(0f, -b, a); | ||
1090 | Vertex v6 = new Vertex(-a, 0f, b); | ||
1091 | Vertex v7 = new Vertex(0f, -b, -a); | ||
1092 | Vertex v8 = new Vertex(a, 0f, -b); | ||
1093 | Vertex v9 = new Vertex(a, 0f, b); | ||
1094 | Vertex v10 = new Vertex(-a, 0f, -b); | ||
1095 | Vertex v11 = new Vertex(b, -a, 0); | ||
1096 | Vertex v12 = new Vertex(-b, -a, 0); | ||
1097 | |||
1098 | |||
1099 | |||
1100 | // Base Faces of the Icosahedron (20) | ||
1101 | SphereLODTriangle(v1, v2, v3, diameter, LOD, m); | ||
1102 | SphereLODTriangle(v4, v3, v2, diameter, LOD, m); | ||
1103 | SphereLODTriangle(v4, v5, v6, diameter, LOD, m); | ||
1104 | SphereLODTriangle(v4, v9, v5, diameter, LOD, m); | ||
1105 | SphereLODTriangle(v1, v7, v8, diameter, LOD, m); | ||
1106 | SphereLODTriangle(v1, v10, v7, diameter, LOD, m); | ||
1107 | SphereLODTriangle(v5, v11, v12, diameter, LOD, m); | ||
1108 | SphereLODTriangle(v7, v12, v11, diameter, LOD, m); | ||
1109 | SphereLODTriangle(v3, v6, v10, diameter, LOD, m); | ||
1110 | SphereLODTriangle(v12, v10, v6, diameter, LOD, m); | ||
1111 | SphereLODTriangle(v2, v8, v9, diameter, LOD, m); | ||
1112 | SphereLODTriangle(v11, v9, v8, diameter, LOD, m); | ||
1113 | SphereLODTriangle(v4, v6, v3, diameter, LOD, m); | ||
1114 | SphereLODTriangle(v4, v2, v9, diameter, LOD, m); | ||
1115 | SphereLODTriangle(v1, v3, v10, diameter, LOD, m); | ||
1116 | SphereLODTriangle(v1, v8, v2, diameter, LOD, m); | ||
1117 | SphereLODTriangle(v7, v10, v12, diameter, LOD, m); | ||
1118 | SphereLODTriangle(v7, v11, v8, diameter, LOD, m); | ||
1119 | SphereLODTriangle(v5, v12, v6, diameter, LOD, m); | ||
1120 | SphereLODTriangle(v5, v9, v11, diameter, LOD, m); | ||
1121 | |||
1122 | // Scale the mesh based on our prim scale | ||
1123 | foreach (Vertex v in m.vertices) | ||
1124 | { | ||
1125 | v.X *= size.X; | ||
1126 | v.Y *= size.Y; | ||
1127 | v.Z *= size.Z; | ||
1128 | } | ||
1129 | |||
1130 | // This was built with the normals pointing inside.. | ||
1131 | // therefore we have to invert the normals | ||
1132 | foreach (Triangle t in m.triangles) | ||
1133 | { | ||
1134 | t.invertNormal(); | ||
1135 | } | ||
1136 | // Dump the faces for visualization in blender. | ||
1137 | m.DumpRaw(baseDir, primName, "Icosahedron"); | ||
1138 | #if SPAM | ||
1139 | int vCount = 0; | ||
1140 | |||
1141 | foreach (Vertex v in m.vertices) | ||
1142 | if (v != null) | ||
1143 | vCount++; | ||
1144 | System.Console.WriteLine("Mesh vertex count: " + vCount.ToString()); | ||
1145 | #endif | ||
1146 | |||
1147 | return m; | ||
1148 | } | ||
1149 | private SculptMesh CreateSculptMesh(string primName, PrimitiveBaseShape primShape, PhysicsVector size, float lod) | ||
1150 | { | ||
1151 | |||
1152 | #if SPAM | ||
1153 | reportPrimParams("[SCULPT] " + primName, primShape); | ||
1154 | #endif | ||
1155 | |||
1156 | SculptMesh sm = new SculptMesh(primShape.SculptData, lod); | ||
1157 | // Scale the mesh based on our prim scale | ||
1158 | foreach (Vertex v in sm.vertices) | ||
1159 | { | ||
1160 | v.X *= 0.5f; | ||
1161 | v.Y *= 0.5f; | ||
1162 | v.Z *= 0.5f; | ||
1163 | v.X *= size.X; | ||
1164 | v.Y *= size.Y; | ||
1165 | v.Z *= size.Z; | ||
1166 | } | ||
1167 | // This was built with the normals pointing inside.. | ||
1168 | // therefore we have to invert the normals | ||
1169 | foreach (Triangle t in sm.triangles) | ||
1170 | { | ||
1171 | t.invertNormal(); | ||
1172 | } | ||
1173 | sm.DumpRaw(baseDir, primName, "Sculpt"); | ||
1174 | return sm; | ||
1175 | |||
1176 | } | ||
1177 | |||
1178 | /// <summary> | ||
1179 | /// Creates a mesh for prim types torus, ring, tube, and sphere | ||
1180 | /// </summary> | ||
1181 | /// <param name="primName"></param> | ||
1182 | /// <param name="primShape"></param> | ||
1183 | /// <param name="size"></param> | ||
1184 | /// <returns></returns> | ||
1185 | private static Mesh CreateCircularPathMesh(String primName, PrimitiveBaseShape primShape, PhysicsVector size) | ||
1186 | { | ||
1187 | |||
1188 | UInt16 hollowFactor = primShape.ProfileHollow; | ||
1189 | UInt16 profileBegin = primShape.ProfileBegin; | ||
1190 | UInt16 profileEnd = primShape.ProfileEnd; | ||
1191 | UInt16 pathShearX = primShape.PathShearX; | ||
1192 | UInt16 pathShearY = primShape.PathShearY; | ||
1193 | HollowShape hollowShape = primShape.HollowShape; | ||
1194 | |||
1195 | #if SPAM | ||
1196 | reportPrimParams("[CIRCULAR PATH PRIM] " + primName, primShape); | ||
1197 | Console.WriteLine("pathTwist: " + primShape.PathTwist.ToString() + " pathTwistBegin: " + primShape.PathTwistBegin.ToString()); | ||
1198 | Console.WriteLine("primShape.ProfileCurve & 0x07: " + Convert.ToString(primShape.ProfileCurve & 0x07)); | ||
1199 | |||
1200 | #endif | ||
1201 | |||
1202 | SimpleHull outerHull = new SimpleHull(); | ||
1203 | |||
1204 | if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.Circle) | ||
1205 | { | ||
1206 | #if SPAM | ||
1207 | Console.WriteLine("Meshmerizer thinks " + primName + " is a TORUS"); | ||
1208 | #endif | ||
1209 | if (hollowShape == HollowShape.Same) | ||
1210 | hollowShape = HollowShape.Circle; | ||
1211 | |||
1212 | // build the profile shape | ||
1213 | // counter-clockwise around the quadrants, start at 45 degrees | ||
1214 | |||
1215 | outerHull.AddVertex(new Vertex(0.353553f, 0.353553f, 0.0f)); // 45 degrees | ||
1216 | outerHull.AddVertex(new Vertex(0.250000f, 0.433013f, 0.0f)); // 60 degrees | ||
1217 | outerHull.AddVertex(new Vertex(0.129410f, 0.482963f, 0.0f)); // 75 degrees | ||
1218 | outerHull.AddVertex(new Vertex(0.000000f, 0.500000f, 0.0f)); // 90 degrees | ||
1219 | outerHull.AddVertex(new Vertex(-0.129410f, 0.482963f, 0.0f)); // 105 degrees | ||
1220 | outerHull.AddVertex(new Vertex(-0.250000f, 0.433013f, 0.0f)); // 120 degrees | ||
1221 | outerHull.AddVertex(new Vertex(-0.353553f, 0.353553f, 0.0f)); // 135 degrees | ||
1222 | outerHull.AddVertex(new Vertex(-0.433013f, 0.250000f, 0.0f)); // 150 degrees | ||
1223 | outerHull.AddVertex(new Vertex(-0.482963f, 0.129410f, 0.0f)); // 165 degrees | ||
1224 | outerHull.AddVertex(new Vertex(-0.500000f, 0.000000f, 0.0f)); // 180 degrees | ||
1225 | outerHull.AddVertex(new Vertex(-0.482963f, -0.129410f, 0.0f)); // 195 degrees | ||
1226 | outerHull.AddVertex(new Vertex(-0.433013f, -0.250000f, 0.0f)); // 210 degrees | ||
1227 | outerHull.AddVertex(new Vertex(-0.353553f, -0.353553f, 0.0f)); // 225 degrees | ||
1228 | outerHull.AddVertex(new Vertex(-0.250000f, -0.433013f, 0.0f)); // 240 degrees | ||
1229 | outerHull.AddVertex(new Vertex(-0.129410f, -0.482963f, 0.0f)); // 255 degrees | ||
1230 | outerHull.AddVertex(new Vertex(0.000000f, -0.500000f, 0.0f)); // 270 degrees | ||
1231 | outerHull.AddVertex(new Vertex(0.129410f, -0.482963f, 0.0f)); // 285 degrees | ||
1232 | outerHull.AddVertex(new Vertex(0.250000f, -0.433013f, 0.0f)); // 300 degrees | ||
1233 | outerHull.AddVertex(new Vertex(0.353553f, -0.353553f, 0.0f)); // 315 degrees | ||
1234 | outerHull.AddVertex(new Vertex(0.433013f, -0.250000f, 0.0f)); // 330 degrees | ||
1235 | outerHull.AddVertex(new Vertex(0.482963f, -0.129410f, 0.0f)); // 345 degrees | ||
1236 | outerHull.AddVertex(new Vertex(0.500000f, 0.000000f, 0.0f)); // 0 degrees | ||
1237 | outerHull.AddVertex(new Vertex(0.482963f, 0.129410f, 0.0f)); // 15 degrees | ||
1238 | outerHull.AddVertex(new Vertex(0.433013f, 0.250000f, 0.0f)); // 30 degrees | ||
1239 | } | ||
1240 | |||
1241 | else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.Square) // a ring | ||
1242 | { | ||
1243 | #if SPAM | ||
1244 | Console.WriteLine("Meshmerizer thinks " + primName + " is a TUBE"); | ||
1245 | #endif | ||
1246 | if (hollowShape == HollowShape.Same) | ||
1247 | hollowShape = HollowShape.Square; | ||
1248 | 210 | ||
1249 | outerHull.AddVertex(new Vertex(+0.5f, +0.5f, 0.0f)); | 211 | idata.Dispose(); |
1250 | outerHull.AddVertex(new Vertex(-0.5f, +0.5f, 0.0f)); | ||
1251 | outerHull.AddVertex(new Vertex(-0.5f, -0.5f, 0.0f)); | ||
1252 | outerHull.AddVertex(new Vertex(+0.5f, -0.5f, 0.0f)); | ||
1253 | } | ||
1254 | 212 | ||
1255 | else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle) | 213 | sculptMesh.DumpRaw(baseDir, primName, "primMesh"); |
1256 | { | ||
1257 | #if SPAM | ||
1258 | Console.WriteLine("Meshmerizer thinks " + primName + " is a RING"); | ||
1259 | #endif | ||
1260 | if (hollowShape == HollowShape.Same) | ||
1261 | hollowShape = HollowShape.Triangle; | ||
1262 | 214 | ||
1263 | outerHull.AddVertex(new Vertex(+0.255f, -0.375f, 0.0f)); | 215 | sculptMesh.Scale(size.X, size.Y, size.Z); |
1264 | outerHull.AddVertex(new Vertex(+0.25f, +0.375f, 0.0f)); | ||
1265 | outerHull.AddVertex(new Vertex(-0.5f, +0.0f, 0.0f)); | ||
1266 | 216 | ||
217 | coords = sculptMesh.coords; | ||
218 | faces = sculptMesh.faces; | ||
1267 | } | 219 | } |
1268 | 220 | ||
1269 | else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle) | 221 | else |
1270 | { | 222 | { |
1271 | #if SPAM | 223 | float pathShearX = primShape.PathShearX < 128 ? (float)primShape.PathShearX * 0.01f : (float)(primShape.PathShearX - 256) * 0.01f; |
1272 | Console.WriteLine("Meshmerizer thinks " + primName + " is a SPHERE"); | 224 | float pathShearY = primShape.PathShearY < 128 ? (float)primShape.PathShearY * 0.01f : (float)(primShape.PathShearY - 256) * 0.01f; |
1273 | #endif | 225 | float pathBegin = (float)primShape.PathBegin * 2.0e-5f; |
1274 | 226 | float pathEnd = 1.0f - (float)primShape.PathEnd * 2.0e-5f; | |
1275 | // sanity check here... some spheres have inverted normals which can trap avatars | 227 | float pathScaleX = (float)(primShape.PathScaleX - 100) * 0.01f; |
1276 | // so for now if the shape parameters are such that this may happen, revert to the | 228 | float pathScaleY = (float)(primShape.PathScaleY - 100) * 0.01f; |
1277 | // geodesic sphere mesh.. the threshold is arbitrary as it seems any twist on a sphere | ||
1278 | // will create some inverted normals | ||
1279 | if ( | ||
1280 | (System.Math.Abs(primShape.PathTwist - primShape.PathTwistBegin) > 65) | ||
1281 | || (primShape.PathBegin == 0 | ||
1282 | && primShape.PathEnd == 0 | ||
1283 | && primShape.PathTwist == 0 | ||
1284 | && primShape.PathTwistBegin == 0 | ||
1285 | && primShape.ProfileBegin == 0 | ||
1286 | && primShape.ProfileEnd == 0 | ||
1287 | && hollowFactor == 0 | ||
1288 | ) // simple sphere, revert to geodesic shape | ||
1289 | |||
1290 | ) | ||
1291 | { | ||
1292 | #if SPAM | ||
1293 | System.Console.WriteLine("reverting to geodesic sphere for prim: " + primName); | ||
1294 | #endif | ||
1295 | return CreateSphereMesh(primName, primShape, size); | ||
1296 | } | ||
1297 | 229 | ||
1298 | if (hollowFactor == 0) | 230 | float profileBegin = (float)primShape.ProfileBegin * 2.0e-5f; |
1299 | { | 231 | float profileEnd = 1.0f - (float)primShape.ProfileEnd * 2.0e-5f; |
1300 | // the hull triangulator is happier with a minimal hollow | 232 | float profileHollow = (float)primShape.ProfileHollow * 2.0e-5f; |
1301 | hollowFactor = 2000; | ||
1302 | } | ||
1303 | 233 | ||
1304 | if (hollowShape == HollowShape.Same) | 234 | int sides = 4; |
1305 | hollowShape = HollowShape.Circle; | 235 | if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle) |
1306 | 236 | sides = 3; | |
1307 | outerHull.AddVertex(new Vertex(0.250000f, 0.433013f, 0.0f)); // 60 degrees | 237 | else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.Circle) |
1308 | outerHull.AddVertex(new Vertex(0.129410f, 0.482963f, 0.0f)); // 75 degrees | 238 | sides = 24; |
1309 | outerHull.AddVertex(new Vertex(0.000000f, 0.500000f, 0.0f)); // 90 degrees | 239 | else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle) |
1310 | outerHull.AddVertex(new Vertex(-0.129410f, 0.482963f, 0.0f)); // 105 degrees | 240 | { // half circle, prim is a sphere |
1311 | outerHull.AddVertex(new Vertex(-0.250000f, 0.433013f, 0.0f)); // 120 degrees | 241 | sides = 24; |
1312 | outerHull.AddVertex(new Vertex(-0.353553f, 0.353553f, 0.0f)); // 135 degrees | ||
1313 | outerHull.AddVertex(new Vertex(-0.433013f, 0.250000f, 0.0f)); // 150 degrees | ||
1314 | outerHull.AddVertex(new Vertex(-0.482963f, 0.129410f, 0.0f)); // 165 degrees | ||
1315 | outerHull.AddVertex(new Vertex(-0.500000f, 0.000000f, 0.0f)); // 180 degrees | ||
1316 | |||
1317 | outerHull.AddVertex(new Vertex(0.500000f, 0.000000f, 0.0f)); // 0 degrees | ||
1318 | outerHull.AddVertex(new Vertex(0.482963f, 0.129410f, 0.0f)); // 15 degrees | ||
1319 | outerHull.AddVertex(new Vertex(0.433013f, 0.250000f, 0.0f)); // 30 degrees | ||
1320 | outerHull.AddVertex(new Vertex(0.353553f, 0.353553f, 0.0f)); // 45 degrees | ||
1321 | } | ||
1322 | 242 | ||
1323 | // Deal with cuts now | 243 | profileBegin = 0.5f * profileBegin + 0.5f; |
1324 | if ((profileBegin != 0) || (profileEnd != 0)) | 244 | profileEnd = 0.5f * profileEnd + 0.5f; |
1325 | { | ||
1326 | double fProfileBeginAngle = profileBegin / 50000.0 * 360.0; | ||
1327 | // In degree, for easier debugging and understanding | ||
1328 | double fProfileEndAngle = 360.0 - profileEnd / 50000.0 * 360.0; // Pathend comes as complement to 1.0 | ||
1329 | |||
1330 | if (fProfileBeginAngle < fProfileEndAngle) | ||
1331 | fProfileEndAngle -= 360.0; | ||
1332 | |||
1333 | if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle) | ||
1334 | { // dimpled sphere uses profile cut but since it's a half circle the angles are smaller | ||
1335 | fProfileBeginAngle = 0.0036f * (float)primShape.ProfileBegin; | ||
1336 | fProfileEndAngle = 180.0f - 0.0036f * (float)primShape.ProfileEnd; | ||
1337 | if (fProfileBeginAngle < fProfileEndAngle) | ||
1338 | fProfileEndAngle -= 360.0f; | ||
1339 | // a cut starting at 0 degrees with a hollow causes an infinite loop so move the start angle | ||
1340 | // past it into the empty part of the circle to avoid this condition | ||
1341 | if (fProfileBeginAngle == 0.0f) fProfileBeginAngle = -10.0f; | ||
1342 | 245 | ||
1343 | #if SPAM | ||
1344 | Console.WriteLine("Sphere dimple: fProfileBeginAngle: " + fProfileBeginAngle.ToString() + " fProfileEndAngle: " + fProfileEndAngle.ToString()); | ||
1345 | #endif | ||
1346 | } | ||
1347 | else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.Square) | ||
1348 | { // tube profile cut is offset 45 degrees from other prim types | ||
1349 | fProfileBeginAngle += 45.0f; | ||
1350 | fProfileEndAngle += 45.0f; | ||
1351 | if (fProfileBeginAngle < fProfileEndAngle) | ||
1352 | fProfileEndAngle -= 360.0; | ||
1353 | } | ||
1354 | else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle) | ||
1355 | { // ring profile cut is offset 180 degrees from other prim types | ||
1356 | fProfileBeginAngle += 180.0f; | ||
1357 | fProfileEndAngle += 180.0f; | ||
1358 | if (fProfileBeginAngle < fProfileEndAngle) | ||
1359 | fProfileEndAngle -= 360.0; | ||
1360 | } | 246 | } |
1361 | 247 | ||
1362 | // Note, that we don't want to cut out a triangle, even if this is a | 248 | int hollowSides = sides; |
1363 | // good approximation for small cuts. Indeed we want to cut out an arc | 249 | if (primShape.HollowShape == HollowShape.Circle) |
1364 | // and we approximate this arc by a polygon chain | 250 | hollowSides = 24; |
1365 | // Also note, that these vectors are of length 1.0 and thus their endpoints lay outside the model space | 251 | else if (primShape.HollowShape == HollowShape.Square) |
1366 | // So it can easily be subtracted from the outer hull | 252 | hollowSides = 4; |
1367 | int iSteps = (int)(((fProfileBeginAngle - fProfileEndAngle) / 45.0) + .5); | 253 | else if (primShape.HollowShape == HollowShape.Triangle) |
1368 | // how many steps do we need with approximately 45 degree | 254 | hollowSides = 3; |
1369 | double dStepWidth = (fProfileBeginAngle - fProfileEndAngle) / iSteps; | ||
1370 | |||
1371 | Vertex origin = new Vertex(0.0f, 0.0f, 0.0f); | ||
1372 | |||
1373 | // Note the sequence of vertices here. It's important to have the other rotational sense than in outerHull | ||
1374 | SimpleHull cutHull = new SimpleHull(); | ||
1375 | cutHull.AddVertex(origin); | ||
1376 | for (int i = 0; i < iSteps; i++) | ||
1377 | { | ||
1378 | double angle = fProfileBeginAngle - i * dStepWidth; // we count against the angle orientation!!!! | ||
1379 | Vertex v = Vertex.FromAngle(angle * Math.PI / 180.0); | ||
1380 | cutHull.AddVertex(v); | ||
1381 | } | ||
1382 | Vertex legEnd = Vertex.FromAngle(fProfileEndAngle * Math.PI / 180.0); | ||
1383 | // Calculated separately to avoid errors | ||
1384 | cutHull.AddVertex(legEnd); | ||
1385 | 255 | ||
1386 | // m_log.DebugFormat("Starting cutting of the hollow shape from the prim {1}", 0, primName); | 256 | primMesh = new PrimMesh(sides, profileBegin, profileEnd, profileHollow, hollowSides); |
1387 | SimpleHull cuttedHull = SimpleHull.SubtractHull(outerHull, cutHull); | ||
1388 | 257 | ||
1389 | if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.Circle) | 258 | primMesh.topShearX = pathShearX; |
1390 | { | 259 | primMesh.topShearY = pathShearY; |
1391 | Quaternion zFlip = Quaternion.CreateFromAxisAngle(new Vector3(0.0f, 0.0f, 1.0f), (float)Math.PI); | 260 | primMesh.pathCutBegin = pathBegin; |
1392 | Vertex vTmp = new Vertex(0.0f, 0.0f, 0.0f); | 261 | primMesh.pathCutEnd = pathEnd; |
1393 | foreach (Vertex v in cuttedHull.getVertices()) | ||
1394 | if (v != null) | ||
1395 | { | ||
1396 | vTmp = v * zFlip; | ||
1397 | v.X = vTmp.X; | ||
1398 | v.Y = vTmp.Y; | ||
1399 | v.Z = vTmp.Z; | ||
1400 | } | ||
1401 | } | ||
1402 | |||
1403 | outerHull = cuttedHull; | ||
1404 | } | ||
1405 | 262 | ||
1406 | // Deal with the hole here | 263 | if (primShape.PathCurve == (byte)Extrusion.Straight) |
1407 | if (hollowFactor > 0) | ||
1408 | { | ||
1409 | SimpleHull holeHull; | ||
1410 | |||
1411 | if (hollowShape == HollowShape.Triangle) | ||
1412 | { | 264 | { |
1413 | holeHull = new SimpleHull(); | 265 | primMesh.twistBegin = primShape.PathTwistBegin * 18 / 10; |
266 | primMesh.twistEnd = primShape.PathTwist * 18 / 10; | ||
267 | primMesh.taperX = pathScaleX; | ||
268 | primMesh.taperY = pathScaleY; | ||
1414 | 269 | ||
1415 | float hollowFactorF = (float)hollowFactor * 2.0e-5f; | 270 | if (profileBegin < 0.0f || profileBegin >= profileEnd || profileEnd > 1.0f) |
1416 | |||
1417 | if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle) | ||
1418 | { | 271 | { |
1419 | holeHull.AddVertex(new Vertex(+0.125f * hollowFactorF, -0.1875f * hollowFactorF, 0.0f)); | 272 | ReportPrimError("*** CORRUPT PRIM!! ***", primName, primMesh); |
1420 | holeHull.AddVertex(new Vertex(-0.25f * hollowFactorF, -0f * hollowFactorF, 0.0f)); | 273 | if (profileBegin < 0.0f) profileBegin = 0.0f; |
1421 | holeHull.AddVertex(new Vertex(+0.125f * hollowFactorF, +0.1875f * hollowFactorF, 0.0f)); | 274 | if (profileEnd > 1.0f) profileEnd = 1.0f; |
1422 | } | 275 | } |
1423 | else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle) | 276 | #if SPAM |
277 | Console.WriteLine("****** PrimMesh Parameters (Linear) ******\n" + primMesh.ParamsToDisplayString()); | ||
278 | #endif | ||
279 | try | ||
1424 | { | 280 | { |
1425 | holeHull.AddVertex(new Vertex(-0.500000f * hollowFactorF, 0.000000f * hollowFactorF, 0.0f)); // 180 degrees | 281 | primMesh.ExtrudeLinear(); |
1426 | holeHull.AddVertex(new Vertex(-0.250000f * hollowFactorF, 0.433013f * hollowFactorF, 0.0f)); // 120 degrees | ||
1427 | holeHull.AddVertex(new Vertex(0.250000f * hollowFactorF, 0.433013f * hollowFactorF, 0.0f)); // 60 degrees | ||
1428 | holeHull.AddVertex(new Vertex(0.500000f * hollowFactorF, 0.000000f * hollowFactorF, 0.0f)); // 0 degrees | ||
1429 | } | 282 | } |
1430 | else | 283 | catch (Exception ex) |
1431 | { | 284 | { |
1432 | holeHull.AddVertex(new Vertex(+0.25f * hollowFactorF, -0.45f * hollowFactorF, 0.0f)); | 285 | ReportPrimError("Extrusion failure: exception: " + ex.ToString(), primName, primMesh); |
1433 | holeHull.AddVertex(new Vertex(-0.5f * hollowFactorF, -0f * hollowFactorF, 0.0f)); | 286 | return null; |
1434 | holeHull.AddVertex(new Vertex(+0.25f * hollowFactorF, +0.45f * hollowFactorF, 0.0f)); | ||
1435 | } | 287 | } |
1436 | } | 288 | } |
1437 | else if (hollowShape == HollowShape.Square && (primShape.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle) | ||
1438 | { | ||
1439 | holeHull = new SimpleHull(); | ||
1440 | |||
1441 | float hollowFactorF = (float)hollowFactor * 2.0e-5f; | ||
1442 | |||
1443 | holeHull.AddVertex(new Vertex(-0.707f * hollowFactorF, 0.0f, 0.0f)); // 180 degrees | ||
1444 | holeHull.AddVertex(new Vertex(0.0f, 0.707f * hollowFactorF, 0.0f)); // 120 degrees | ||
1445 | holeHull.AddVertex(new Vertex(0.707f * hollowFactorF, 0.0f, 0.0f)); // 60 degrees | ||
1446 | } | ||
1447 | else | 289 | else |
1448 | { | 290 | { |
1449 | holeHull = BuildHoleHull(primShape, primShape.ProfileShape, hollowShape, hollowFactor); | 291 | primMesh.holeSizeX = (200 - primShape.PathScaleX) * 0.01f; |
1450 | } | 292 | primMesh.holeSizeY = (200 - primShape.PathScaleY) * 0.01f; |
1451 | 293 | primMesh.radius = 0.01f * primShape.PathRadiusOffset; | |
1452 | if (holeHull != null) | 294 | primMesh.revolutions = 1.0f + 0.015f * primShape.PathRevolutions; |
1453 | { | 295 | primMesh.skew = 0.01f * primShape.PathSkew; |
1454 | SimpleHull hollowedHull = SimpleHull.SubtractHull(outerHull, holeHull); | 296 | primMesh.twistBegin = primShape.PathTwistBegin * 36 / 10; |
1455 | 297 | primMesh.twistEnd = primShape.PathTwist * 36 / 10; | |
1456 | outerHull = hollowedHull; | 298 | primMesh.taperX = primShape.PathTaperX * 0.01f; |
1457 | } | 299 | primMesh.taperY = primShape.PathTaperY * 0.01f; |
1458 | } | 300 | |
1459 | 301 | if (profileBegin < 0.0f || profileBegin >= profileEnd || profileEnd > 1.0f) | |
1460 | Mesh m = new Mesh(); | 302 | { |
1461 | 303 | ReportPrimError("*** CORRUPT PRIM!! ***", primName, primMesh); | |
1462 | Vertex Seed1 = new Vertex(0.0f, -10.0f, 0.0f); | 304 | if (profileBegin < 0.0f) profileBegin = 0.0f; |
1463 | Vertex Seed2 = new Vertex(-10.0f, 10.0f, 0.0f); | 305 | if (profileEnd > 1.0f) profileEnd = 1.0f; |
1464 | Vertex Seed3 = new Vertex(10.0f, 10.0f, 0.0f); | 306 | } |
1465 | |||
1466 | m.Add(Seed1); | ||
1467 | m.Add(Seed2); | ||
1468 | m.Add(Seed3); | ||
1469 | |||
1470 | m.Add(new Triangle(Seed1, Seed2, Seed3)); | ||
1471 | m.Add(outerHull.getVertices()); | ||
1472 | |||
1473 | InsertVertices(m.vertices, 3, m.triangles); | ||
1474 | m.DumpRaw(baseDir, primName, "Proto first Mesh"); | ||
1475 | |||
1476 | m.Remove(Seed1); | ||
1477 | m.Remove(Seed2); | ||
1478 | m.Remove(Seed3); | ||
1479 | m.DumpRaw(baseDir, primName, "Proto seeds removed"); | ||
1480 | |||
1481 | m.RemoveTrianglesOutside(outerHull); | ||
1482 | m.DumpRaw(baseDir, primName, "Proto outsides removed"); | ||
1483 | |||
1484 | foreach (Triangle t in m.triangles) | ||
1485 | t.invertNormal(); | ||
1486 | |||
1487 | |||
1488 | float skew = primShape.PathSkew * 0.01f; | ||
1489 | float pathScaleX = (float)(200 - primShape.PathScaleX) * 0.01f; | ||
1490 | float pathScaleY = (float)(200 - primShape.PathScaleY) * 0.01f; | ||
1491 | float profileXComp = pathScaleX * (1.0f - Math.Abs(skew)); | ||
1492 | |||
1493 | #if SPAM | ||
1494 | //Console.WriteLine("primShape.PathScaleX: " + primShape.PathScaleX.ToString() + " primShape.PathScaleY: " + primShape.PathScaleY.ToString()); | ||
1495 | //Console.WriteLine("primShape.PathSkew: " + primShape.PathSkew.ToString() + " primShape.PathRadiusOffset: " + primShape.PathRadiusOffset.ToString() + " primShape.pathRevolutions: " + primShape.PathRevolutions.ToString()); | ||
1496 | Console.WriteLine("PathScaleX: " + pathScaleX.ToString() + " pathScaleY: " + pathScaleY.ToString()); | ||
1497 | Console.WriteLine("skew: " + skew.ToString() + " profileXComp: " + profileXComp.ToString()); | ||
1498 | #endif | ||
1499 | |||
1500 | foreach (Vertex v in m.vertices) | ||
1501 | if (v != null) | ||
1502 | { | ||
1503 | v.X *= profileXComp; | ||
1504 | v.Y *= pathScaleY; | ||
1505 | } | ||
1506 | |||
1507 | Extruder extr = new Extruder(); | ||
1508 | |||
1509 | extr.size = size; | ||
1510 | extr.pathScaleX = pathScaleX; | ||
1511 | extr.pathScaleY = pathScaleY; | ||
1512 | extr.pathCutBegin = 0.00002f * primShape.PathBegin; | ||
1513 | extr.pathCutEnd = 0.00002f * (50000 - primShape.PathEnd); | ||
1514 | extr.pathBegin = primShape.PathBegin; | ||
1515 | extr.pathEnd = primShape.PathEnd; | ||
1516 | extr.skew = skew; | ||
1517 | extr.revolutions = 1.0f + (float)primShape.PathRevolutions * 3.0f / 200.0f; | ||
1518 | extr.pathTaperX = 0.01f * (float)primShape.PathTaperX; | ||
1519 | extr.pathTaperY = 0.01f * (float)primShape.PathTaperY; | ||
1520 | |||
1521 | extr.radius = 0.01f * (float)primShape.PathRadiusOffset; | ||
1522 | |||
1523 | #if SPAM | 307 | #if SPAM |
1524 | //System.Console.WriteLine("primShape.PathBegin: " + primShape.PathBegin.ToString() + " primShape.PathEnd: " + primShape.PathEnd.ToString()); | 308 | Console.WriteLine("****** PrimMesh Parameters (Circular) ******\n" + primMesh.ParamsToDisplayString()); |
1525 | System.Console.WriteLine("extr.pathCutBegin: " + extr.pathCutBegin.ToString() + " extr.pathCutEnd: " + extr.pathCutEnd.ToString()); | ||
1526 | System.Console.WriteLine("extr.revolutions: " + extr.revolutions.ToString()); | ||
1527 | |||
1528 | //System.Console.WriteLine("primShape.PathTaperX: " + primShape.PathTaperX.ToString()); | ||
1529 | //System.Console.WriteLine("primShape.PathTaperY: " + primShape.PathTaperY.ToString()); | ||
1530 | |||
1531 | |||
1532 | //System.Console.WriteLine("primShape.PathRadiusOffset: " + primShape.PathRadiusOffset.ToString()); | ||
1533 | #endif | 309 | #endif |
1534 | 310 | try | |
1535 | 311 | { | |
1536 | 312 | primMesh.ExtrudeCircular(); | |
1537 | 313 | } | |
1538 | if (pathShearX != 0) | 314 | catch (Exception ex) |
1539 | { | 315 | { |
1540 | if (pathShearX > 50) | 316 | ReportPrimError("Extrusion failure: exception: " + ex.ToString(), primName, primMesh); |
1541 | { | 317 | return null; |
1542 | // Complimentary byte. Negative values wrap around the byte. Positive values go up to 50 | 318 | } |
1543 | extr.pushX = (((float)(256 - pathShearX) / 100) * -1f); | ||
1544 | } | ||
1545 | else | ||
1546 | { | ||
1547 | extr.pushX = (float)pathShearX / 100; | ||
1548 | } | ||
1549 | } | ||
1550 | |||
1551 | if (pathShearY != 0) | ||
1552 | { | ||
1553 | if (pathShearY > 50) | ||
1554 | { | ||
1555 | // Complimentary byte. Negative values wrap around the byte. Positive values go up to 50 | ||
1556 | extr.pushY = (((float)(256 - pathShearY) / 100) * -1f); | ||
1557 | } | ||
1558 | else | ||
1559 | { | ||
1560 | extr.pushY = (float)pathShearY / 100; | ||
1561 | } | 319 | } |
1562 | 320 | ||
1563 | } | 321 | primMesh.DumpRaw(baseDir, primName, "primMesh"); |
1564 | |||
1565 | extr.twistTop = (float)primShape.PathTwist * (float)Math.PI * 0.02f; | ||
1566 | extr.twistBot = (float)primShape.PathTwistBegin * (float)Math.PI * 0.02f; | ||
1567 | |||
1568 | Mesh result = extr.ExtrudeCircularPath(m); | ||
1569 | result.DumpRaw(baseDir, primName, "Z extruded"); | ||
1570 | |||
1571 | #if SPAM | ||
1572 | int vCount = 0; | ||
1573 | |||
1574 | foreach (Vertex v in result.vertices) | ||
1575 | { | ||
1576 | if (v != null) | ||
1577 | vCount++; | ||
1578 | } | ||
1579 | |||
1580 | System.Console.WriteLine("Mesh vertex count: " + vCount.ToString()); | ||
1581 | #endif | ||
1582 | |||
1583 | return result; | ||
1584 | } | ||
1585 | |||
1586 | public static Vertex midUnitRadialPoint(Vertex a, Vertex b, float radius) | ||
1587 | { | ||
1588 | Vertex midpoint = new Vertex(a + b) * 0.5f; | ||
1589 | return (midpoint.normalize() * radius); | ||
1590 | } | ||
1591 | |||
1592 | public static void SphereLODTriangle(Vertex a, Vertex b, Vertex c, float diameter, float LOD, Mesh m) | ||
1593 | { | ||
1594 | Vertex aa = a - b; | ||
1595 | Vertex ba = b - c; | ||
1596 | Vertex da = c - a; | ||
1597 | |||
1598 | if (((aa.length() < LOD) && (ba.length() < LOD) && (da.length() < LOD))) | ||
1599 | { | ||
1600 | // We don't want duplicate verticies. Duplicates cause the scale algorithm to produce a spikeball | ||
1601 | // spikes are novel, but we want ellipsoids. | ||
1602 | |||
1603 | if (!m.vertices.Contains(a)) | ||
1604 | m.Add(a); | ||
1605 | if (!m.vertices.Contains(b)) | ||
1606 | m.Add(b); | ||
1607 | if (!m.vertices.Contains(c)) | ||
1608 | m.Add(c); | ||
1609 | |||
1610 | // Add the triangle to the mesh | ||
1611 | Triangle t = new Triangle(a, b, c); | ||
1612 | m.Add(t); | ||
1613 | } | ||
1614 | else | ||
1615 | { | ||
1616 | Vertex ab = midUnitRadialPoint(a, b, diameter); | ||
1617 | Vertex bc = midUnitRadialPoint(b, c, diameter); | ||
1618 | Vertex ca = midUnitRadialPoint(c, a, diameter); | ||
1619 | |||
1620 | // Recursive! Splits the triangle up into 4 smaller triangles | ||
1621 | SphereLODTriangle(a, ab, ca, diameter, LOD, m); | ||
1622 | SphereLODTriangle(ab, b, bc, diameter, LOD, m); | ||
1623 | SphereLODTriangle(ca, bc, c, diameter, LOD, m); | ||
1624 | SphereLODTriangle(ab, bc, ca, diameter, LOD, m); | ||
1625 | |||
1626 | } | ||
1627 | } | ||
1628 | |||
1629 | private void ReportPrimError(string message, string primName, PrimMesh primMesh) | ||
1630 | { | ||
1631 | Console.WriteLine(message); | ||
1632 | Console.WriteLine("\nPrim Name: " + primName); | ||
1633 | Console.WriteLine("****** PrimMesh Parameters (Linear) ******\n" + primMesh.ParamsToDisplayString()); | ||
1634 | |||
1635 | } | ||
1636 | |||
1637 | public Mesh CreateMeshFromPrimMesher(string primName, PrimitiveBaseShape primShape, PhysicsVector size, float lod) | ||
1638 | { | ||
1639 | Mesh mesh = new Mesh(); | ||
1640 | |||
1641 | float pathShearX = primShape.PathShearX < 128 ? (float)primShape.PathShearX * 0.01f : (float)(primShape.PathShearX - 256) * 0.01f; | ||
1642 | float pathShearY = primShape.PathShearY < 128 ? (float)primShape.PathShearY * 0.01f : (float)(primShape.PathShearY - 256) * 0.01f; | ||
1643 | float pathBegin = (float)primShape.PathBegin * 2.0e-5f; | ||
1644 | float pathEnd = 1.0f - (float)primShape.PathEnd * 2.0e-5f; | ||
1645 | float pathScaleX = (float)(primShape.PathScaleX - 100) * 0.01f; | ||
1646 | float pathScaleY = (float)(primShape.PathScaleY - 100) * 0.01f; | ||
1647 | |||
1648 | float profileBegin = (float)primShape.ProfileBegin * 2.0e-5f; | ||
1649 | float profileEnd = 1.0f - (float)primShape.ProfileEnd * 2.0e-5f; | ||
1650 | float profileHollow = (float)primShape.ProfileHollow * 2.0e-5f; | ||
1651 | |||
1652 | int sides = 4; | ||
1653 | if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle) | ||
1654 | sides = 3; | ||
1655 | else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.Circle) | ||
1656 | sides = 24; | ||
1657 | else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle) | ||
1658 | { // half circle, prim is a sphere | ||
1659 | sides = 24; | ||
1660 | |||
1661 | profileBegin = 0.5f * profileBegin + 0.5f; | ||
1662 | profileEnd = 0.5f * profileEnd + 0.5f; | ||
1663 | |||
1664 | } | ||
1665 | |||
1666 | int hollowSides = sides; | ||
1667 | if (primShape.HollowShape == HollowShape.Circle) | ||
1668 | hollowSides = 24; | ||
1669 | else if (primShape.HollowShape == HollowShape.Square) | ||
1670 | hollowSides = 4; | ||
1671 | else if (primShape.HollowShape == HollowShape.Triangle) | ||
1672 | hollowSides = 3; | ||
1673 | 322 | ||
1674 | PrimMesh primMesh = new PrimMesh(sides, profileBegin, profileEnd, profileHollow, hollowSides); | 323 | primMesh.Scale(size.X, size.Y, size.Z); |
1675 | 324 | ||
1676 | primMesh.topShearX = pathShearX; | 325 | coords = primMesh.coords; |
1677 | primMesh.topShearY = pathShearY; | 326 | faces = primMesh.faces; |
1678 | primMesh.pathCutBegin = pathBegin; | ||
1679 | primMesh.pathCutEnd = pathEnd; | ||
1680 | 327 | ||
1681 | if (primShape.PathCurve == (byte)Extrusion.Straight) | ||
1682 | { | ||
1683 | primMesh.twistBegin = primShape.PathTwistBegin * 18 / 10; | ||
1684 | primMesh.twistEnd = primShape.PathTwist * 18 / 10; | ||
1685 | primMesh.taperX = pathScaleX; | ||
1686 | primMesh.taperY = pathScaleY; | ||
1687 | |||
1688 | if (profileBegin < 0.0f || profileBegin >= profileEnd || profileEnd > 1.0f) | ||
1689 | { | ||
1690 | ReportPrimError("*** CORRUPT PRIM!! ***", primName, primMesh); | ||
1691 | if (profileBegin < 0.0f) profileBegin = 0.0f; | ||
1692 | if (profileEnd > 1.0f) profileEnd = 1.0f; | ||
1693 | } | ||
1694 | #if SPAM | ||
1695 | Console.WriteLine("****** PrimMesh Parameters (Linear) ******\n" + primMesh.ParamsToDisplayString()); | ||
1696 | #endif | ||
1697 | try | ||
1698 | { | ||
1699 | primMesh.ExtrudeLinear(); | ||
1700 | } | ||
1701 | catch (Exception ex) | ||
1702 | { | ||
1703 | ReportPrimError("Extrusion failure: exception: " + ex.ToString(), primName, primMesh); | ||
1704 | return null; | ||
1705 | } | ||
1706 | } | 328 | } |
1707 | else | ||
1708 | { | ||
1709 | primMesh.holeSizeX = (200 - primShape.PathScaleX) * 0.01f; | ||
1710 | primMesh.holeSizeY = (200 - primShape.PathScaleY) * 0.01f; | ||
1711 | primMesh.radius = 0.01f * primShape.PathRadiusOffset; | ||
1712 | primMesh.revolutions = 1.0f + 0.015f * primShape.PathRevolutions; | ||
1713 | primMesh.skew = 0.01f * primShape.PathSkew; | ||
1714 | primMesh.twistBegin = primShape.PathTwistBegin * 36 / 10; | ||
1715 | primMesh.twistEnd = primShape.PathTwist * 36 / 10; | ||
1716 | primMesh.taperX = primShape.PathTaperX * 0.01f; | ||
1717 | primMesh.taperY = primShape.PathTaperY * 0.01f; | ||
1718 | |||
1719 | if (profileBegin < 0.0f || profileBegin >= profileEnd || profileEnd > 1.0f) | ||
1720 | { | ||
1721 | ReportPrimError("*** CORRUPT PRIM!! ***", primName, primMesh); | ||
1722 | if (profileBegin < 0.0f) profileBegin = 0.0f; | ||
1723 | if (profileEnd > 1.0f) profileEnd = 1.0f; | ||
1724 | } | ||
1725 | #if SPAM | ||
1726 | Console.WriteLine("****** PrimMesh Parameters (Circular) ******\n" + primMesh.ParamsToDisplayString()); | ||
1727 | #endif | ||
1728 | try | ||
1729 | { | ||
1730 | primMesh.ExtrudeCircular(); | ||
1731 | } | ||
1732 | catch (Exception ex) | ||
1733 | { | ||
1734 | ReportPrimError("Extrusion failure: exception: " + ex.ToString(), primName, primMesh); | ||
1735 | return null; | ||
1736 | } | ||
1737 | } | ||
1738 | |||
1739 | primMesh.DumpRaw(baseDir, primName, "primMesh"); | ||
1740 | 329 | ||
1741 | primMesh.Scale(size.X, size.Y, size.Z); | ||
1742 | 330 | ||
1743 | int numCoords = primMesh.coords.Count; | 331 | int numCoords = coords.Count; |
1744 | int numFaces = primMesh.faces.Count; | 332 | int numFaces = faces.Count; |
1745 | 333 | ||
1746 | List<Coord> coords = primMesh.coords; | ||
1747 | for (int i = 0; i < numCoords; i++) | 334 | for (int i = 0; i < numCoords; i++) |
1748 | { | 335 | { |
1749 | Coord c = coords[i]; | 336 | Coord c = coords[i]; |
1750 | mesh.vertices.Add(new Vertex(c.X, c.Y, c.Z)); | 337 | mesh.vertices.Add(new Vertex(c.X, c.Y, c.Z)); |
1751 | } | 338 | } |
1752 | 339 | ||
1753 | List<Face> faces = primMesh.faces; | ||
1754 | List<Vertex> vertices = mesh.vertices; | 340 | List<Vertex> vertices = mesh.vertices; |
1755 | |||
1756 | for (int i = 0; i < numFaces; i++) | 341 | for (int i = 0; i < numFaces; i++) |
1757 | { | 342 | { |
1758 | Face f = faces[i]; | 343 | Face f = faces[i]; |
1759 | mesh.triangles.Add(new Triangle(vertices[f.v1], vertices[f.v2], vertices[f.v3])); | 344 | mesh.triangles.Add(new Triangle(vertices[f.v1], vertices[f.v2], vertices[f.v3])); |
1760 | } | 345 | } |
1761 | 346 | ||
1762 | //for (int i = 0; i < numFaces; i++) | ||
1763 | //{ | ||
1764 | // Face f = primMesh.faces[i]; | ||
1765 | // Coord vert = primMesh.coords[f.v1]; | ||
1766 | // Vertex v1 = new Vertex(vert.X, vert.Y, vert.Z); | ||
1767 | // mesh.vertices.Add(v1); | ||
1768 | // vert = primMesh.coords[f.v2]; | ||
1769 | // Vertex v2 = new Vertex(vert.X, vert.Y, vert.Z); | ||
1770 | // mesh.vertices.Add(v2); | ||
1771 | // vert = primMesh.coords[f.v3]; | ||
1772 | // Vertex v3 = new Vertex(vert.X, vert.Y, vert.Z); | ||
1773 | // mesh.vertices.Add(v3); | ||
1774 | // mesh.triangles.Add(new Triangle(v1, v2, v3)); | ||
1775 | //} | ||
1776 | |||
1777 | //mesh.DumpRaw(baseDir, primName, "Mesh"); | ||
1778 | |||
1779 | //mesh.primMesh = primMesh; | ||
1780 | |||
1781 | return mesh; | 347 | return mesh; |
1782 | } | 348 | } |
1783 | 349 | ||
@@ -1794,83 +360,16 @@ namespace OpenSim.Region.Physics.Meshing | |||
1794 | if (size.Y < 0.01f) size.Y = 0.01f; | 360 | if (size.Y < 0.01f) size.Y = 0.01f; |
1795 | if (size.Z < 0.01f) size.Z = 0.01f; | 361 | if (size.Z < 0.01f) size.Z = 0.01f; |
1796 | 362 | ||
1797 | #if SPAM | 363 | mesh = CreateMeshFromPrimMesher(primName, primShape, size, lod); |
1798 | reportPrimParams(primName, primShape); | ||
1799 | #endif | ||
1800 | |||
1801 | if (primShape.SculptEntry && primShape.SculptType != (byte)0 && primShape.SculptData.Length > 0) | ||
1802 | { | ||
1803 | SculptMesh smesh = CreateSculptMesh(primName, primShape, size, lod); | ||
1804 | mesh = (Mesh)smesh; | ||
1805 | } | ||
1806 | |||
1807 | else if (usePrimMesher) | ||
1808 | { | ||
1809 | mesh = CreateMeshFromPrimMesher(primName, primShape, size, lod); | ||
1810 | } | ||
1811 | else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.Square) | ||
1812 | { | ||
1813 | if (primShape.PathCurve == (byte)Extrusion.Straight) | ||
1814 | { // its a box | ||
1815 | mesh = CreateBoxMesh(primName, primShape, size); | ||
1816 | //mesh = CreateMeshFromPrimMesher(primName, primShape, size, lod); | ||
1817 | } | ||
1818 | else if (primShape.PathCurve == (byte)Extrusion.Curve1) | ||
1819 | { // tube | ||
1820 | // do a cylinder for now | ||
1821 | mesh = CreateCylinderMesh(primName, primShape, size); | ||
1822 | //mesh = CreateMeshFromPrimMesher(primName, primShape, size, lod); | ||
1823 | } | ||
1824 | } | ||
1825 | else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.Circle) | ||
1826 | { | ||
1827 | if (primShape.PathCurve == (byte)Extrusion.Straight) | ||
1828 | { | ||
1829 | mesh = CreateCylinderMesh(primName, primShape, size); | ||
1830 | //mesh = CreateMeshFromPrimMesher(primName, primShape, size, lod); | ||
1831 | } | ||
1832 | |||
1833 | // ProfileCurve seems to combine hole shape and profile curve so we need to only compare against the lower 3 bits | ||
1834 | else if (primShape.PathCurve == (byte) Extrusion.Curve1) | ||
1835 | { // dahlia's favorite, a torus :) | ||
1836 | mesh = CreateCircularPathMesh(primName, primShape, size); | ||
1837 | //mesh = CreateMeshFromPrimMesher(primName, primShape, size, lod);\ | ||
1838 | } | ||
1839 | } | ||
1840 | else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle) | ||
1841 | { | ||
1842 | if (primShape.PathCurve == (byte)Extrusion.Curve1 || primShape.PathCurve == (byte) Extrusion.Curve2) | ||
1843 | { | ||
1844 | //mesh = CreateSphereMesh(primName, primShape, size); | ||
1845 | mesh = CreateCircularPathMesh(primName, primShape, size); | ||
1846 | //mesh = CreateMeshFromPrimMesher(primName, primShape, size, lod); | ||
1847 | } | ||
1848 | } | ||
1849 | else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle) | ||
1850 | { | ||
1851 | if (primShape.PathCurve == (byte)Extrusion.Straight) | ||
1852 | { | ||
1853 | mesh = CreatePrismMesh(primName, primShape, size); | ||
1854 | //mesh = CreateMeshFromPrimMesher(primName, primShape, size, lod); | ||
1855 | } | ||
1856 | else if (primShape.PathCurve == (byte) Extrusion.Curve1) | ||
1857 | { // a ring - do a cylinder for now | ||
1858 | //mesh = CreateCylinderMesh(primName, primShape, size); | ||
1859 | mesh = CreateCircularPathMesh(primName, primShape, size); | ||
1860 | //mesh = CreateMeshFromPrimMesher(primName, primShape, size, lod); | ||
1861 | } | ||
1862 | } | ||
1863 | else // just do a box | ||
1864 | { | ||
1865 | mesh = CreateBoxMesh(primName, primShape, size); | ||
1866 | } | ||
1867 | 364 | ||
1868 | if (mesh != null) | 365 | if (mesh != null) |
1869 | { | 366 | { |
1870 | if ((!isPhysical) && size.X < minSizeForComplexMesh && size.Y < minSizeForComplexMesh && size.Z < minSizeForComplexMesh) | 367 | if ((!isPhysical) && size.X < minSizeForComplexMesh && size.Y < minSizeForComplexMesh && size.Z < minSizeForComplexMesh) |
1871 | { | 368 | { |
1872 | #if SPAM | 369 | #if SPAM |
1873 | Console.WriteLine("Meshmerizer: prim " + primName + " has a size of " + size.ToString() + " which is below threshold of " + minSizeForComplexMesh.ToString() + " - creating simple bounding box" ); | 370 | Console.WriteLine("Meshmerizer: prim " + primName + " has a size of " + size.ToString() + " which is below threshold of " + |
371 | |||
372 | minSizeForComplexMesh.ToString() + " - creating simple bounding box" ); | ||
1874 | #endif | 373 | #endif |
1875 | mesh = CreateBoundingBoxMesh(mesh); | 374 | mesh = CreateBoundingBoxMesh(mesh); |
1876 | mesh.DumpRaw(baseDir, primName, "Z extruded"); | 375 | mesh.DumpRaw(baseDir, primName, "Z extruded"); |
@@ -1884,6 +383,8 @@ namespace OpenSim.Region.Physics.Meshing | |||
1884 | return mesh; | 383 | return mesh; |
1885 | } | 384 | } |
1886 | 385 | ||
386 | |||
387 | |||
1887 | #if SPAM | 388 | #if SPAM |
1888 | // please dont comment this out until I'm done with this module - dahlia | 389 | // please dont comment this out until I'm done with this module - dahlia |
1889 | private static void reportPrimParams(string name, PrimitiveBaseShape primShape) | 390 | private static void reportPrimParams(string name, PrimitiveBaseShape primShape) |
diff --git a/OpenSim/Region/Physics/Meshing/PrimMesher.cs b/OpenSim/Region/Physics/Meshing/PrimMesher.cs index 282bbd5..d79a480 100644 --- a/OpenSim/Region/Physics/Meshing/PrimMesher.cs +++ b/OpenSim/Region/Physics/Meshing/PrimMesher.cs | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (c) Contributors, http://opensimulator.org/ | 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 |
@@ -243,10 +243,11 @@ namespace PrimMesher | |||
243 | public int n2; | 243 | public int n2; |
244 | public int n3; | 244 | public int n3; |
245 | 245 | ||
246 | //// UVs | 246 | // uvs |
247 | //public int uv1; | 247 | public int uv1; |
248 | //public int uv2; | 248 | public int uv2; |
249 | //public int uv3; | 249 | public int uv3; |
250 | |||
250 | 251 | ||
251 | public Face(int v1, int v2, int v3) | 252 | public Face(int v1, int v2, int v3) |
252 | { | 253 | { |
@@ -260,9 +261,10 @@ namespace PrimMesher | |||
260 | this.n2 = 0; | 261 | this.n2 = 0; |
261 | this.n3 = 0; | 262 | this.n3 = 0; |
262 | 263 | ||
263 | //this.uv1 = 0; | 264 | this.uv1 = 0; |
264 | //this.uv2 = 0; | 265 | this.uv2 = 0; |
265 | //this.uv3 = 0; | 266 | this.uv3 = 0; |
267 | |||
266 | } | 268 | } |
267 | 269 | ||
268 | public Face(int v1, int v2, int v3, int n1, int n2, int n3) | 270 | public Face(int v1, int v2, int v3, int n1, int n2, int n3) |
@@ -277,9 +279,21 @@ namespace PrimMesher | |||
277 | this.n2 = n2; | 279 | this.n2 = n2; |
278 | this.n3 = n3; | 280 | this.n3 = n3; |
279 | 281 | ||
280 | //this.uv1 = 0; | 282 | this.uv1 = 0; |
281 | //this.uv2 = 0; | 283 | this.uv2 = 0; |
282 | //this.uv3 = 0; | 284 | this.uv3 = 0; |
285 | } | ||
286 | |||
287 | public Coord SurfaceNormal(List<Coord> coordList) | ||
288 | { | ||
289 | Coord c1 = coordList[this.v1]; | ||
290 | Coord c2 = coordList[this.v2]; | ||
291 | Coord c3 = coordList[this.v3]; | ||
292 | |||
293 | Coord edge1 = new Coord(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z); | ||
294 | Coord edge2 = new Coord(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z); | ||
295 | |||
296 | return Coord.Cross(edge1, edge2).Normalize(); | ||
283 | } | 297 | } |
284 | } | 298 | } |
285 | 299 | ||
@@ -560,7 +574,7 @@ namespace PrimMesher | |||
560 | /// <summary> | 574 | /// <summary> |
561 | /// generates a profile for extrusion | 575 | /// generates a profile for extrusion |
562 | /// </summary> | 576 | /// </summary> |
563 | public class Profile | 577 | internal class Profile |
564 | { | 578 | { |
565 | private const float twoPi = 2.0f * (float)Math.PI; | 579 | private const float twoPi = 2.0f * (float)Math.PI; |
566 | 580 | ||
@@ -569,6 +583,7 @@ namespace PrimMesher | |||
569 | internal List<Coord> vertexNormals; | 583 | internal List<Coord> vertexNormals; |
570 | internal List<float> us; | 584 | internal List<float> us; |
571 | internal List<UVCoord> faceUVs; | 585 | internal List<UVCoord> faceUVs; |
586 | internal List<int> faceNumbers; | ||
572 | 587 | ||
573 | internal Coord faceNormal = new Coord(0.0f, 0.0f, 1.0f); | 588 | internal Coord faceNormal = new Coord(0.0f, 0.0f, 1.0f); |
574 | internal Coord cutNormal1 = new Coord(); | 589 | internal Coord cutNormal1 = new Coord(); |
@@ -578,6 +593,8 @@ namespace PrimMesher | |||
578 | internal int numHollowVerts = 0; | 593 | internal int numHollowVerts = 0; |
579 | 594 | ||
580 | internal bool calcVertexNormals = false; | 595 | internal bool calcVertexNormals = false; |
596 | internal int bottomFaceNumber = 0; | ||
597 | internal int numPrimFaces = 0; | ||
581 | 598 | ||
582 | internal Profile() | 599 | internal Profile() |
583 | { | 600 | { |
@@ -586,9 +603,10 @@ namespace PrimMesher | |||
586 | this.vertexNormals = new List<Coord>(); | 603 | this.vertexNormals = new List<Coord>(); |
587 | this.us = new List<float>(); | 604 | this.us = new List<float>(); |
588 | this.faceUVs = new List<UVCoord>(); | 605 | this.faceUVs = new List<UVCoord>(); |
606 | this.faceNumbers = new List<int>(); | ||
589 | } | 607 | } |
590 | 608 | ||
591 | public Profile(int sides, float profileStart, float profileEnd, float hollow, int hollowSides, bool createFaces, bool calcVertexNormals) | 609 | internal Profile(int sides, float profileStart, float profileEnd, float hollow, int hollowSides, bool createFaces, bool calcVertexNormals) |
592 | { | 610 | { |
593 | this.calcVertexNormals = calcVertexNormals; | 611 | this.calcVertexNormals = calcVertexNormals; |
594 | this.coords = new List<Coord>(); | 612 | this.coords = new List<Coord>(); |
@@ -596,6 +614,8 @@ namespace PrimMesher | |||
596 | this.vertexNormals = new List<Coord>(); | 614 | this.vertexNormals = new List<Coord>(); |
597 | this.us = new List<float>(); | 615 | this.us = new List<float>(); |
598 | this.faceUVs = new List<UVCoord>(); | 616 | this.faceUVs = new List<UVCoord>(); |
617 | this.faceNumbers = new List<int>(); | ||
618 | |||
599 | Coord center = new Coord(0.0f, 0.0f, 0.0f); | 619 | Coord center = new Coord(0.0f, 0.0f, 0.0f); |
600 | 620 | ||
601 | List<Coord> hollowCoords = new List<Coord>(); | 621 | List<Coord> hollowCoords = new List<Coord>(); |
@@ -674,7 +694,7 @@ namespace PrimMesher | |||
674 | hollowCoords.Add(newVert); | 694 | hollowCoords.Add(newVert); |
675 | if (this.calcVertexNormals) | 695 | if (this.calcVertexNormals) |
676 | { | 696 | { |
677 | if (sides < 5) | 697 | if (hollowSides < 5) |
678 | hollowNormals.Add(hollowAngles.normals[i].Invert()); | 698 | hollowNormals.Add(hollowAngles.normals[i].Invert()); |
679 | else | 699 | else |
680 | hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f)); | 700 | hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f)); |
@@ -689,7 +709,7 @@ namespace PrimMesher | |||
689 | 709 | ||
690 | for (int i = 0; i < numAngles; i++) | 710 | for (int i = 0; i < numAngles; i++) |
691 | { | 711 | { |
692 | //int iNext = i == numAngles ? i + 1 : 0; | 712 | int iNext = i == numAngles ? i + 1 : 0; |
693 | angle = angles.angles[i]; | 713 | angle = angles.angles[i]; |
694 | newVert.X = angle.X * xScale; | 714 | newVert.X = angle.X * xScale; |
695 | newVert.Y = angle.Y * yScale; | 715 | newVert.Y = angle.Y * yScale; |
@@ -884,21 +904,46 @@ namespace PrimMesher | |||
884 | hollowNormals = null; | 904 | hollowNormals = null; |
885 | hollowUs = null; | 905 | hollowUs = null; |
886 | 906 | ||
907 | if (calcVertexNormals) | ||
908 | { // calculate prim face numbers | ||
909 | // I know it's ugly but so is the whole concept of prim face numbers | ||
910 | int faceNum = 1; | ||
911 | int startVert = hasProfileCut && !hasHollow ? 1 : 0; | ||
912 | if (startVert > 0) | ||
913 | this.faceNumbers.Add(0); | ||
914 | for (int i = 0; i < numOuterVerts; i++) | ||
915 | this.faceNumbers.Add(sides < 5 ? faceNum++ : faceNum); | ||
916 | if (sides > 4) | ||
917 | faceNum++; | ||
918 | if (hasProfileCut) | ||
919 | this.faceNumbers.Add(0); | ||
920 | for (int i = 0; i < numHollowVerts; i++) | ||
921 | this.faceNumbers.Add(faceNum++); | ||
922 | this.bottomFaceNumber = faceNum++; | ||
923 | if (hasHollow && hasProfileCut) | ||
924 | this.faceNumbers.Add(faceNum++); | ||
925 | for (int i = 0; i < this.faceNumbers.Count; i++) | ||
926 | if (this.faceNumbers[i] == 0) | ||
927 | this.faceNumbers[i] = faceNum++; | ||
928 | |||
929 | this.numPrimFaces = faceNum; | ||
930 | } | ||
931 | |||
887 | } | 932 | } |
888 | 933 | ||
889 | public void MakeFaceUVs() | 934 | internal void MakeFaceUVs() |
890 | { | 935 | { |
891 | this.faceUVs = new List<UVCoord>(); | 936 | this.faceUVs = new List<UVCoord>(); |
892 | foreach (Coord c in this.coords) | 937 | foreach (Coord c in this.coords) |
893 | this.faceUVs.Add(new UVCoord(1.0f - (0.5f + c.X), 1.0f - (0.5f - c.Y))); | 938 | this.faceUVs.Add(new UVCoord(1.0f - (0.5f + c.X), 1.0f - (0.5f - c.Y))); |
894 | } | 939 | } |
895 | 940 | ||
896 | public Profile Clone() | 941 | internal Profile Clone() |
897 | { | 942 | { |
898 | return this.Clone(true); | 943 | return this.Clone(true); |
899 | } | 944 | } |
900 | 945 | ||
901 | public Profile Clone(bool needFaces) | 946 | internal Profile Clone(bool needFaces) |
902 | { | 947 | { |
903 | Profile clone = new Profile(); | 948 | Profile clone = new Profile(); |
904 | 949 | ||
@@ -914,6 +959,7 @@ namespace PrimMesher | |||
914 | clone.cutNormal1 = this.cutNormal1; | 959 | clone.cutNormal1 = this.cutNormal1; |
915 | clone.cutNormal2 = this.cutNormal2; | 960 | clone.cutNormal2 = this.cutNormal2; |
916 | clone.us.AddRange(this.us); | 961 | clone.us.AddRange(this.us); |
962 | clone.faceNumbers.AddRange(this.faceNumbers); | ||
917 | } | 963 | } |
918 | clone.numOuterVerts = this.numOuterVerts; | 964 | clone.numOuterVerts = this.numOuterVerts; |
919 | clone.numHollowVerts = this.numHollowVerts; | 965 | clone.numHollowVerts = this.numHollowVerts; |
@@ -921,12 +967,12 @@ namespace PrimMesher | |||
921 | return clone; | 967 | return clone; |
922 | } | 968 | } |
923 | 969 | ||
924 | public void AddPos(Coord v) | 970 | internal void AddPos(Coord v) |
925 | { | 971 | { |
926 | this.AddPos(v.X, v.Y, v.Z); | 972 | this.AddPos(v.X, v.Y, v.Z); |
927 | } | 973 | } |
928 | 974 | ||
929 | public void AddPos(float x, float y, float z) | 975 | internal void AddPos(float x, float y, float z) |
930 | { | 976 | { |
931 | int i; | 977 | int i; |
932 | int numVerts = this.coords.Count; | 978 | int numVerts = this.coords.Count; |
@@ -942,7 +988,7 @@ namespace PrimMesher | |||
942 | } | 988 | } |
943 | } | 989 | } |
944 | 990 | ||
945 | public void AddRot(Quat q) | 991 | internal void AddRot(Quat q) |
946 | { | 992 | { |
947 | int i; | 993 | int i; |
948 | int numVerts = this.coords.Count; | 994 | int numVerts = this.coords.Count; |
@@ -963,7 +1009,7 @@ namespace PrimMesher | |||
963 | } | 1009 | } |
964 | } | 1010 | } |
965 | 1011 | ||
966 | public void Scale(float x, float y) | 1012 | internal void Scale(float x, float y) |
967 | { | 1013 | { |
968 | int i; | 1014 | int i; |
969 | int numVerts = this.coords.Count; | 1015 | int numVerts = this.coords.Count; |
@@ -981,7 +1027,7 @@ namespace PrimMesher | |||
981 | /// <summary> | 1027 | /// <summary> |
982 | /// Changes order of the vertex indices and negates the center vertex normal. Does not alter vertex normals of radial vertices | 1028 | /// Changes order of the vertex indices and negates the center vertex normal. Does not alter vertex normals of radial vertices |
983 | /// </summary> | 1029 | /// </summary> |
984 | public void FlipNormals() | 1030 | internal void FlipNormals() |
985 | { | 1031 | { |
986 | int i; | 1032 | int i; |
987 | int numFaces = this.faces.Count; | 1033 | int numFaces = this.faces.Count; |
@@ -1021,7 +1067,7 @@ namespace PrimMesher | |||
1021 | } | 1067 | } |
1022 | } | 1068 | } |
1023 | 1069 | ||
1024 | public void AddValue2FaceVertexIndices(int num) | 1070 | internal void AddValue2FaceVertexIndices(int num) |
1025 | { | 1071 | { |
1026 | int numFaces = this.faces.Count; | 1072 | int numFaces = this.faces.Count; |
1027 | Face tmpFace; | 1073 | Face tmpFace; |
@@ -1036,7 +1082,7 @@ namespace PrimMesher | |||
1036 | } | 1082 | } |
1037 | } | 1083 | } |
1038 | 1084 | ||
1039 | public void AddValue2FaceNormalIndices(int num) | 1085 | internal void AddValue2FaceNormalIndices(int num) |
1040 | { | 1086 | { |
1041 | if (this.calcVertexNormals) | 1087 | if (this.calcVertexNormals) |
1042 | { | 1088 | { |
@@ -1054,7 +1100,7 @@ namespace PrimMesher | |||
1054 | } | 1100 | } |
1055 | } | 1101 | } |
1056 | 1102 | ||
1057 | public void DumpRaw(String path, String name, String title) | 1103 | internal void DumpRaw(String path, String name, String title) |
1058 | { | 1104 | { |
1059 | if (path == null) | 1105 | if (path == null) |
1060 | return; | 1106 | return; |
@@ -1113,6 +1159,12 @@ namespace PrimMesher | |||
1113 | private bool normalsProcessed = false; | 1159 | private bool normalsProcessed = false; |
1114 | public bool viewerMode = false; | 1160 | public bool viewerMode = false; |
1115 | 1161 | ||
1162 | public int numPrimFaces = 0; | ||
1163 | |||
1164 | /// <summary> | ||
1165 | /// Human readable string representation of the parameters used to create a mesh. | ||
1166 | /// </summary> | ||
1167 | /// <returns></returns> | ||
1116 | public string ParamsToDisplayString() | 1168 | public string ParamsToDisplayString() |
1117 | { | 1169 | { |
1118 | string s = ""; | 1170 | string s = ""; |
@@ -1141,7 +1193,14 @@ namespace PrimMesher | |||
1141 | return s; | 1193 | return s; |
1142 | } | 1194 | } |
1143 | 1195 | ||
1144 | 1196 | /// <summary> | |
1197 | /// Constructs a PrimMesh object and creates the profile for extrusion. | ||
1198 | /// </summary> | ||
1199 | /// <param name="sides"></param> | ||
1200 | /// <param name="profileStart"></param> | ||
1201 | /// <param name="profileEnd"></param> | ||
1202 | /// <param name="hollow"></param> | ||
1203 | /// <param name="hollowSides"></param> | ||
1145 | public PrimMesh(int sides, float profileStart, float profileEnd, float hollow, int hollowSides) | 1204 | public PrimMesh(int sides, float profileStart, float profileEnd, float hollow, int hollowSides) |
1146 | { | 1205 | { |
1147 | this.coords = new List<Coord>(); | 1206 | this.coords = new List<Coord>(); |
@@ -1174,6 +1233,9 @@ namespace PrimMesher | |||
1174 | this.hasHollow = (this.hollow > 0.001f); | 1233 | this.hasHollow = (this.hollow > 0.001f); |
1175 | } | 1234 | } |
1176 | 1235 | ||
1236 | /// <summary> | ||
1237 | /// Extrudes a profile along a straight line path. Used for prim types box, cylinder, and prism. | ||
1238 | /// </summary> | ||
1177 | public void ExtrudeLinear() | 1239 | public void ExtrudeLinear() |
1178 | { | 1240 | { |
1179 | this.coords = new List<Coord>(); | 1241 | this.coords = new List<Coord>(); |
@@ -1248,6 +1310,7 @@ namespace PrimMesher | |||
1248 | hollow *= 1.414f; | 1310 | hollow *= 1.414f; |
1249 | 1311 | ||
1250 | Profile profile = new Profile(this.sides, this.profileStart, this.profileEnd, hollow, this.hollowSides, true, calcVertexNormals); | 1312 | Profile profile = new Profile(this.sides, this.profileStart, this.profileEnd, hollow, this.hollowSides, true, calcVertexNormals); |
1313 | this.numPrimFaces = profile.numPrimFaces; | ||
1251 | 1314 | ||
1252 | int cut1Vert = -1; | 1315 | int cut1Vert = -1; |
1253 | int cut2Vert = -1; | 1316 | int cut2Vert = -1; |
@@ -1398,7 +1461,7 @@ namespace PrimMesher | |||
1398 | if (u2 < 0.1f) | 1461 | if (u2 < 0.1f) |
1399 | u2 = 1.0f; | 1462 | u2 = 1.0f; |
1400 | 1463 | ||
1401 | newViewerFace2.primFaceNumber = newViewerFace1.primFaceNumber = whichVert + 1; | 1464 | //newViewerFace2.primFaceNumber = newViewerFace1.primFaceNumber = whichVert + 1; |
1402 | } | 1465 | } |
1403 | 1466 | ||
1404 | newViewerFace1.uv1.U = u1; | 1467 | newViewerFace1.uv1.U = u1; |
@@ -1462,6 +1525,8 @@ namespace PrimMesher | |||
1462 | } | 1525 | } |
1463 | } | 1526 | } |
1464 | 1527 | ||
1528 | newViewerFace2.primFaceNumber = newViewerFace1.primFaceNumber = newLayer.faceNumbers[whichVert]; | ||
1529 | |||
1465 | this.viewerFaces.Add(newViewerFace1); | 1530 | this.viewerFaces.Add(newViewerFace1); |
1466 | this.viewerFaces.Add(newViewerFace2); | 1531 | this.viewerFaces.Add(newViewerFace2); |
1467 | 1532 | ||
@@ -1492,7 +1557,7 @@ namespace PrimMesher | |||
1492 | // add the top faces to the viewerFaces list here | 1557 | // add the top faces to the viewerFaces list here |
1493 | Coord faceNormal = newLayer.faceNormal; | 1558 | Coord faceNormal = newLayer.faceNormal; |
1494 | ViewerFace newViewerFace = new ViewerFace(); | 1559 | ViewerFace newViewerFace = new ViewerFace(); |
1495 | newViewerFace.primFaceNumber = 0; | 1560 | newViewerFace.primFaceNumber = newLayer.bottomFaceNumber; |
1496 | foreach (Face face in newLayer.faces) | 1561 | foreach (Face face in newLayer.faces) |
1497 | { | 1562 | { |
1498 | newViewerFace.v1 = newLayer.coords[face.v1 - coordsLen]; | 1563 | newViewerFace.v1 = newLayer.coords[face.v1 - coordsLen]; |
@@ -1513,6 +1578,9 @@ namespace PrimMesher | |||
1513 | } | 1578 | } |
1514 | } | 1579 | } |
1515 | 1580 | ||
1581 | /// <summary> | ||
1582 | /// Extrude a profile into a circular path prim mesh. Used for prim types torus, tube, and ring. | ||
1583 | /// </summary> | ||
1516 | public void ExtrudeCircular() | 1584 | public void ExtrudeCircular() |
1517 | { | 1585 | { |
1518 | this.coords = new List<Coord>(); | 1586 | this.coords = new List<Coord>(); |
@@ -1615,6 +1683,7 @@ namespace PrimMesher | |||
1615 | needEndFaces = true; | 1683 | needEndFaces = true; |
1616 | 1684 | ||
1617 | Profile profile = new Profile(this.sides, this.profileStart, this.profileEnd, hollow, this.hollowSides, needEndFaces, calcVertexNormals); | 1685 | Profile profile = new Profile(this.sides, this.profileStart, this.profileEnd, hollow, this.hollowSides, needEndFaces, calcVertexNormals); |
1686 | this.numPrimFaces = profile.numPrimFaces; | ||
1618 | 1687 | ||
1619 | int cut1Vert = -1; | 1688 | int cut1Vert = -1; |
1620 | int cut2Vert = -1; | 1689 | int cut2Vert = -1; |
@@ -1787,7 +1856,7 @@ namespace PrimMesher | |||
1787 | if (u2 < 0.1f) | 1856 | if (u2 < 0.1f) |
1788 | u2 = 1.0f; | 1857 | u2 = 1.0f; |
1789 | 1858 | ||
1790 | newViewerFace2.primFaceNumber = newViewerFace1.primFaceNumber = whichVert + 1; | 1859 | //newViewerFace2.primFaceNumber = newViewerFace1.primFaceNumber = whichVert + 1; |
1791 | } | 1860 | } |
1792 | 1861 | ||
1793 | newViewerFace1.uv1.U = u1; | 1862 | newViewerFace1.uv1.U = u1; |
@@ -1865,6 +1934,7 @@ namespace PrimMesher | |||
1865 | } | 1934 | } |
1866 | } | 1935 | } |
1867 | 1936 | ||
1937 | newViewerFace1.primFaceNumber = newViewerFace2.primFaceNumber = newLayer.faceNumbers[whichVert]; | ||
1868 | this.viewerFaces.Add(newViewerFace1); | 1938 | this.viewerFaces.Add(newViewerFace1); |
1869 | this.viewerFaces.Add(newViewerFace2); | 1939 | this.viewerFaces.Add(newViewerFace2); |
1870 | 1940 | ||
@@ -1894,7 +1964,7 @@ namespace PrimMesher | |||
1894 | // add the bottom faces to the viewerFaces list here | 1964 | // add the bottom faces to the viewerFaces list here |
1895 | Coord faceNormal = newLayer.faceNormal; | 1965 | Coord faceNormal = newLayer.faceNormal; |
1896 | ViewerFace newViewerFace = new ViewerFace(); | 1966 | ViewerFace newViewerFace = new ViewerFace(); |
1897 | newViewerFace.primFaceNumber = 0; | 1967 | newViewerFace.primFaceNumber = newLayer.bottomFaceNumber; |
1898 | foreach (Face face in newLayer.faces) | 1968 | foreach (Face face in newLayer.faces) |
1899 | { | 1969 | { |
1900 | newViewerFace.v1 = newLayer.coords[face.v1 - coordsLen]; | 1970 | newViewerFace.v1 = newLayer.coords[face.v1 - coordsLen]; |
@@ -1932,6 +2002,11 @@ namespace PrimMesher | |||
1932 | return SurfaceNormal(this.coords[face.v1], this.coords[face.v2], this.coords[face.v3]); | 2002 | return SurfaceNormal(this.coords[face.v1], this.coords[face.v2], this.coords[face.v3]); |
1933 | } | 2003 | } |
1934 | 2004 | ||
2005 | /// <summary> | ||
2006 | /// Calculate the surface normal for a face in the list of faces | ||
2007 | /// </summary> | ||
2008 | /// <param name="faceIndex"></param> | ||
2009 | /// <returns></returns> | ||
1935 | public Coord SurfaceNormal(int faceIndex) | 2010 | public Coord SurfaceNormal(int faceIndex) |
1936 | { | 2011 | { |
1937 | int numFaces = this.faces.Count; | 2012 | int numFaces = this.faces.Count; |
@@ -1941,6 +2016,9 @@ namespace PrimMesher | |||
1941 | return SurfaceNormal(this.faces[faceIndex]); | 2016 | return SurfaceNormal(this.faces[faceIndex]); |
1942 | } | 2017 | } |
1943 | 2018 | ||
2019 | /// <summary> | ||
2020 | /// Calculate surface normals for all of the faces in the list of faces in this mesh | ||
2021 | /// </summary> | ||
1944 | public void CalcNormals() | 2022 | public void CalcNormals() |
1945 | { | 2023 | { |
1946 | if (normalsProcessed) | 2024 | if (normalsProcessed) |
@@ -1968,6 +2046,12 @@ namespace PrimMesher | |||
1968 | } | 2046 | } |
1969 | } | 2047 | } |
1970 | 2048 | ||
2049 | /// <summary> | ||
2050 | /// Adds a value to each XYZ vertex coordinate in the mesh | ||
2051 | /// </summary> | ||
2052 | /// <param name="x"></param> | ||
2053 | /// <param name="y"></param> | ||
2054 | /// <param name="z"></param> | ||
1971 | public void AddPos(float x, float y, float z) | 2055 | public void AddPos(float x, float y, float z) |
1972 | { | 2056 | { |
1973 | int i; | 2057 | int i; |
@@ -1984,9 +2068,12 @@ namespace PrimMesher | |||
1984 | } | 2068 | } |
1985 | } | 2069 | } |
1986 | 2070 | ||
2071 | /// <summary> | ||
2072 | /// Rotates the mesh | ||
2073 | /// </summary> | ||
2074 | /// <param name="q"></param> | ||
1987 | public void AddRot(Quat q) | 2075 | public void AddRot(Quat q) |
1988 | { | 2076 | { |
1989 | Console.WriteLine("AddRot(" + q.ToString() + ")"); | ||
1990 | int i; | 2077 | int i; |
1991 | int numVerts = this.coords.Count; | 2078 | int numVerts = this.coords.Count; |
1992 | 2079 | ||
@@ -2020,6 +2107,12 @@ namespace PrimMesher | |||
2020 | 2107 | ||
2021 | } | 2108 | } |
2022 | 2109 | ||
2110 | /// <summary> | ||
2111 | /// Scales the mesh | ||
2112 | /// </summary> | ||
2113 | /// <param name="x"></param> | ||
2114 | /// <param name="y"></param> | ||
2115 | /// <param name="z"></param> | ||
2023 | public void Scale(float x, float y, float z) | 2116 | public void Scale(float x, float y, float z) |
2024 | { | 2117 | { |
2025 | int i; | 2118 | int i; |
@@ -2046,6 +2139,12 @@ namespace PrimMesher | |||
2046 | 2139 | ||
2047 | } | 2140 | } |
2048 | 2141 | ||
2142 | /// <summary> | ||
2143 | /// Dumps the mesh to a Blender compatible "Raw" format file | ||
2144 | /// </summary> | ||
2145 | /// <param name="path"></param> | ||
2146 | /// <param name="name"></param> | ||
2147 | /// <param name="title"></param> | ||
2049 | public void DumpRaw(String path, String name, String title) | 2148 | public void DumpRaw(String path, String name, String title) |
2050 | { | 2149 | { |
2051 | if (path == null) | 2150 | if (path == null) |
diff --git a/OpenSim/Region/Physics/Meshing/SculptMesh.cs b/OpenSim/Region/Physics/Meshing/SculptMesh.cs index 0dc7ef2..826030b 100644 --- a/OpenSim/Region/Physics/Meshing/SculptMesh.cs +++ b/OpenSim/Region/Physics/Meshing/SculptMesh.cs | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (c) Contributors, http://opensimulator.org/ | 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 |
@@ -27,314 +27,317 @@ | |||
27 | 27 | ||
28 | using System; | 28 | using System; |
29 | using System.Collections.Generic; | 29 | using System.Collections.Generic; |
30 | using System.Text; | ||
31 | using System.IO; | ||
30 | using System.Drawing; | 32 | using System.Drawing; |
31 | using System.Drawing.Imaging; | 33 | using System.Drawing.Imaging; |
32 | using System.Text; | ||
33 | using OpenMetaverse.Imaging; | ||
34 | 34 | ||
35 | namespace OpenSim.Region.Physics.Meshing | 35 | namespace PrimMesher |
36 | { | 36 | { |
37 | // This functionality based on the XNA SculptPreview by John Hurliman. | ||
38 | public class SculptMesh : Mesh | ||
39 | { | ||
40 | Image idata = null; | ||
41 | Bitmap bLOD = null; | ||
42 | Bitmap bBitmap = null; | ||
43 | 37 | ||
44 | Vertex northpole = new Vertex(0, 0, 0); | 38 | public class SculptMesh |
45 | Vertex southpole = new Vertex(0, 0, 0); | 39 | { |
40 | public List<Coord> coords; | ||
41 | public List<Face> faces; | ||
46 | 42 | ||
47 | private int lod = 32; | 43 | public List<ViewerFace> viewerFaces; |
48 | private const float RANGE = 128.0f; | 44 | public List<Coord> normals; |
45 | public List<UVCoord> uvs; | ||
49 | 46 | ||
50 | public SculptMesh(byte[] jpegData, float _lod) | 47 | public enum SculptType { sphere = 1, torus = 2, plane = 3, cylinder = 4 }; |
48 | private const float pixScale = 0.00390625f; // 1.0 / 256 | ||
49 | |||
50 | private Bitmap ScaleImage(Bitmap srcImage, float scale) | ||
51 | { | 51 | { |
52 | if (_lod == 2f || _lod == 4f || _lod == 8f || _lod == 16f || _lod == 32f || _lod == 64f) | 52 | int sourceWidth = srcImage.Width; |
53 | lod = (int)_lod; | 53 | int sourceHeight = srcImage.Height; |
54 | int sourceX = 0; | ||
55 | int sourceY = 0; | ||
56 | |||
57 | int destX = 0; | ||
58 | int destY = 0; | ||
59 | int destWidth = (int)(sourceWidth * scale); | ||
60 | int destHeight = (int)(sourceHeight * scale); | ||
61 | |||
62 | Bitmap scaledImage = new Bitmap(destWidth, destHeight, | ||
63 | PixelFormat.Format24bppRgb); | ||
64 | scaledImage.SetResolution(srcImage.HorizontalResolution, | ||
65 | srcImage.VerticalResolution); | ||
66 | |||
67 | Graphics grPhoto = Graphics.FromImage(scaledImage); | ||
68 | grPhoto.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Bilinear; | ||
69 | |||
70 | grPhoto.DrawImage(srcImage, | ||
71 | new Rectangle(destX, destY, destWidth, destHeight), | ||
72 | new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight), | ||
73 | GraphicsUnit.Pixel); | ||
74 | |||
75 | grPhoto.Dispose(); | ||
76 | return scaledImage; | ||
77 | } | ||
54 | 78 | ||
55 | try | 79 | public SculptMesh SculptMeshFromFile(string fileName, SculptType sculptType, int lod, bool viewerMode) |
56 | { | 80 | { |
57 | ManagedImage managedImage; // we never use this | 81 | Bitmap bitmap = (Bitmap)Bitmap.FromFile(fileName); |
58 | OpenJPEG.DecodeToImage(jpegData, out managedImage, out idata); | 82 | SculptMesh sculptMesh = new SculptMesh(bitmap, sculptType, lod, viewerMode); |
59 | //int i = 0; | 83 | bitmap.Dispose(); |
60 | //i = i / i; | 84 | return sculptMesh; |
61 | } | 85 | } |
62 | catch (Exception) | ||
63 | { | ||
64 | System.Console.WriteLine("[PHYSICS]: Unable to generate a Sculpty physics proxy. Sculpty texture decode failed!"); | ||
65 | return; | ||
66 | } | ||
67 | 86 | ||
68 | if (idata != null) | 87 | public SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode) |
69 | { | 88 | { |
70 | bBitmap = new Bitmap(idata); | 89 | coords = new List<Coord>(); |
71 | if (bBitmap.Width == bBitmap.Height) | 90 | faces = new List<Face>(); |
72 | { | 91 | normals = new List<Coord>(); |
73 | DoLOD(); | 92 | uvs = new List<UVCoord>(); |
74 | 93 | ||
75 | LoadPoles(); | 94 | float sourceScaleFactor = (float)lod / (float)Math.Max(sculptBitmap.Width, sculptBitmap.Height); |
95 | bool scaleSourceImage = sourceScaleFactor < 1.0f ? true : false; | ||
76 | 96 | ||
77 | processSculptTexture(); | 97 | Bitmap bitmap; |
98 | if (scaleSourceImage) | ||
99 | bitmap = ScaleImage(sculptBitmap, sourceScaleFactor); | ||
100 | else | ||
101 | bitmap = sculptBitmap; | ||
78 | 102 | ||
79 | bLOD.Dispose(); | 103 | viewerFaces = new List<ViewerFace>(); |
80 | bBitmap.Dispose(); | ||
81 | idata.Dispose(); | ||
82 | } | ||
83 | } | ||
84 | } | ||
85 | |||
86 | private Vertex ColorToVertex(Color input) | ||
87 | { | ||
88 | return new Vertex( | ||
89 | ((float)input.R - 128) / RANGE, | ||
90 | ((float)input.G - 128) / RANGE, | ||
91 | ((float)input.B - 128) / RANGE); | ||
92 | } | ||
93 | |||
94 | private void LoadPoles() | ||
95 | { | ||
96 | northpole = new Vertex(0, 0, 0); | ||
97 | for (int x = 0; x < bLOD.Width; x++) | ||
98 | { | ||
99 | northpole += ColorToVertex(GetPixel(0, 0)); | ||
100 | } | ||
101 | northpole /= bLOD.Width; | ||
102 | 104 | ||
103 | southpole = new Vertex(0, 0, 0); | 105 | int width = bitmap.Width; |
104 | for (int x = 0; x < bLOD.Width; x++) | 106 | int height = bitmap.Height; |
105 | { | ||
106 | //System.Console.WriteLine("Height: " + bLOD.Height.ToString()); | ||
107 | southpole += ColorToVertex(GetPixel(bLOD.Height - 1, (bLOD.Height - 1))); | ||
108 | } | ||
109 | southpole /= bBitmap.Width; | ||
110 | } | ||
111 | 107 | ||
112 | private Color GetPixel(int x, int y) | 108 | float widthUnit = 1.0f / width; |
113 | { | 109 | float heightUnit = 1.0f / (height - 1); |
114 | return bLOD.GetPixel(x, y); | ||
115 | } | ||
116 | 110 | ||
117 | public int LOD | 111 | int p1, p2, p3, p4; |
118 | { | 112 | Color color; |
119 | get | 113 | float x, y, z; |
120 | { | ||
121 | return (int)Math.Log(Scale, 2); | ||
122 | } | ||
123 | set | ||
124 | { | ||
125 | int power = value; | ||
126 | if (power == 0) | ||
127 | power = 6; | ||
128 | if (power < 2) | ||
129 | power = 2; | ||
130 | if (power > 9) | ||
131 | power = 9; | ||
132 | int t = (int)Math.Pow(2, power); | ||
133 | if (t != Scale) | ||
134 | { | ||
135 | lod = t; | ||
136 | } | ||
137 | } | ||
138 | } | ||
139 | 114 | ||
140 | public int Scale | 115 | int imageX, imageY; |
141 | { | ||
142 | get | ||
143 | { | ||
144 | return lod; | ||
145 | } | ||
146 | } | ||
147 | |||
148 | private void DoLOD() | ||
149 | { | ||
150 | int x_max = Math.Min(Scale, bBitmap.Width); | ||
151 | int y_max = Math.Min(Scale, bBitmap.Height); | ||
152 | if (bBitmap.Width == x_max && bBitmap.Height == y_max) | ||
153 | bLOD = bBitmap; | ||
154 | 116 | ||
155 | else if (bLOD == null || x_max != bLOD.Width || y_max != bLOD.Height)//don't resize if you don't need to. | 117 | if (sculptType == SculptType.sphere) |
156 | { | 118 | { // average the top and bottom row pixel values so the resulting vertices appear to converge |
157 | System.Drawing.Bitmap tile = new System.Drawing.Bitmap(bBitmap.Width * 2, bBitmap.Height, PixelFormat.Format24bppRgb); | 119 | int lastRow = height - 1; |
158 | System.Drawing.Bitmap tile_LOD = new System.Drawing.Bitmap(x_max * 2, y_max, PixelFormat.Format24bppRgb); | 120 | int r1 = 0, g1 = 0, b1 = 0; |
159 | 121 | int r2 = 0, g2 = 0, b2 = 0; | |
160 | bLOD = new System.Drawing.Bitmap(x_max, y_max, PixelFormat.Format24bppRgb); | 122 | for (imageX = 0; imageX < width; imageX++) |
161 | bLOD.SetResolution(bBitmap.HorizontalResolution, bBitmap.VerticalResolution); | 123 | { |
162 | 124 | Color c1 = bitmap.GetPixel(imageX, 0); | |
163 | System.Drawing.Graphics grPhoto = System.Drawing.Graphics.FromImage(tile); | 125 | Color c2 = bitmap.GetPixel(imageX, lastRow); |
164 | grPhoto.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; | ||
165 | |||
166 | grPhoto.DrawImage(bBitmap, | ||
167 | new System.Drawing.Rectangle(0, 0, bBitmap.Width / 2, bBitmap.Height), | ||
168 | new System.Drawing.Rectangle(bBitmap.Width / 2, 0, bBitmap.Width / 2, bBitmap.Height), | ||
169 | System.Drawing.GraphicsUnit.Pixel); | ||
170 | |||
171 | grPhoto.DrawImage(bBitmap, | ||
172 | new System.Drawing.Rectangle((3 * bBitmap.Width) / 2, 0, bBitmap.Width / 2, bBitmap.Height), | ||
173 | new System.Drawing.Rectangle(0, 0, bBitmap.Width / 2, bBitmap.Height), | ||
174 | System.Drawing.GraphicsUnit.Pixel); | ||
175 | |||
176 | grPhoto.DrawImage(bBitmap, | ||
177 | new System.Drawing.Rectangle(bBitmap.Width / 2, 0, bBitmap.Width, bBitmap.Height), | ||
178 | new System.Drawing.Rectangle(0, 0, bBitmap.Width, bBitmap.Height), | ||
179 | System.Drawing.GraphicsUnit.Pixel); | ||
180 | |||
181 | grPhoto = System.Drawing.Graphics.FromImage(tile_LOD); | ||
182 | //grPhoto.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear; | ||
183 | grPhoto.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Bilinear; | ||
184 | |||
185 | grPhoto.DrawImage(tile, | ||
186 | new System.Drawing.Rectangle(0, 0, tile_LOD.Width, tile_LOD.Height), | ||
187 | new System.Drawing.Rectangle(0, 0, tile.Width, tile.Height), | ||
188 | System.Drawing.GraphicsUnit.Pixel); | ||
189 | |||
190 | grPhoto = System.Drawing.Graphics.FromImage(bLOD); | ||
191 | grPhoto.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor; | ||
192 | |||
193 | grPhoto.DrawImage(tile_LOD, | ||
194 | new System.Drawing.Rectangle(0, 0, bLOD.Width, bLOD.Height), | ||
195 | new System.Drawing.Rectangle(tile_LOD.Width / 4, 0, tile_LOD.Width / 2, tile_LOD.Height), | ||
196 | System.Drawing.GraphicsUnit.Pixel); | ||
197 | |||
198 | grPhoto.Dispose(); | ||
199 | tile_LOD.Dispose(); | ||
200 | tile.Dispose(); | ||
201 | } | ||
202 | 126 | ||
203 | } | 127 | r1 += c1.R; |
204 | 128 | g1 += c1.G; | |
205 | public void clearStuff() | 129 | b1 += c1.B; |
206 | { | ||
207 | this.triangles.Clear(); | ||
208 | this.vertices.Clear(); | ||
209 | //normals = new float[0]; | ||
210 | } | ||
211 | |||
212 | public void processSculptTexture() | ||
213 | { | ||
214 | int x_max = Math.Min(Scale, bBitmap.Width); | ||
215 | int y_max = Math.Min(Scale, bBitmap.Height); | ||
216 | 130 | ||
217 | int COLUMNS = x_max + 1; | 131 | r2 += c2.R; |
132 | g2 += c2.G; | ||
133 | b2 += c2.B; | ||
134 | } | ||
218 | 135 | ||
219 | Vertex[] sVertices = new Vertex[COLUMNS * y_max]; | 136 | Color newC1 = Color.FromArgb(r1 / width, g1 / width, b1 / width); |
220 | //float[] indices = new float[COLUMNS * (y_max - 1) * 6]; | 137 | Color newC2 = Color.FromArgb(r2 / width, g2 / width, b2 / width); |
221 | 138 | ||
222 | for (int y = 0; y < y_max; y++) | 139 | for (imageX = 0; imageX < width; imageX++) |
223 | { | ||
224 | for (int x = 0; x < x_max; x++) | ||
225 | { | 140 | { |
226 | // Create the vertex | 141 | bitmap.SetPixel(imageX, 0, newC1); |
227 | Vertex v1 = new Vertex(0,0,0); | 142 | bitmap.SetPixel(imageX, lastRow, newC2); |
143 | } | ||
144 | } | ||
228 | 145 | ||
229 | // Create a vertex position from the RGB channels in the current pixel | ||
230 | // int ypos = y * bLOD.Width; | ||
231 | 146 | ||
147 | int pixelsAcross = sculptType == SculptType.plane ? width : width + 1; | ||
148 | int pixelsDown = sculptType == SculptType.sphere || sculptType == SculptType.cylinder ? height + 1 : height; | ||
232 | 149 | ||
233 | if (y == 0) | 150 | for (imageY = 0; imageY < pixelsDown; imageY++) |
234 | { | 151 | { |
235 | v1 = northpole; | 152 | int rowOffset = imageY * width; |
236 | } | 153 | |
237 | else if (y == y_max - 1) | 154 | for (imageX = 0; imageX < pixelsAcross; imageX++) |
155 | { | ||
156 | /* | ||
157 | * p1-----p2 | ||
158 | * | \ f2 | | ||
159 | * | \ | | ||
160 | * | f1 \| | ||
161 | * p3-----p4 | ||
162 | */ | ||
163 | |||
164 | if (imageX < width) | ||
238 | { | 165 | { |
239 | v1 = southpole; | 166 | p4 = rowOffset + imageX; |
167 | p3 = p4 - 1; | ||
240 | } | 168 | } |
241 | else | 169 | else |
242 | { | 170 | { |
243 | v1 = ColorToVertex(GetPixel(x, y)); | 171 | p4 = rowOffset; // wrap around to beginning |
172 | p3 = rowOffset + imageX - 1; | ||
244 | } | 173 | } |
245 | 174 | ||
246 | // Add the vertex for use later | 175 | p2 = p4 - width; |
247 | if (!vertices.Contains(v1)) | 176 | p1 = p3 - width; |
248 | Add(v1); | ||
249 | 177 | ||
250 | sVertices[y * COLUMNS + x] = v1; | 178 | color = bitmap.GetPixel(imageX == width ? 0 : imageX, imageY == height ? height - 1 : imageY); |
251 | //System.Console.WriteLine("adding: " + v1.ToString()); | ||
252 | } | ||
253 | //Vertex tempVertex = vertices[y * COLUMNS]; | ||
254 | // sVertices[y * COLUMNS + x_max] = tempVertex; | ||
255 | } | ||
256 | 179 | ||
257 | // Create the Triangles | 180 | x = (color.R - 128) * pixScale; |
258 | //int i = 0; | 181 | y = (color.G - 128) * pixScale; |
182 | z = (color.B - 128) * pixScale; | ||
259 | 183 | ||
260 | for (int y = 0; y < y_max - 1; y++) | 184 | Coord c = new Coord(x, y, z); |
261 | { | 185 | this.coords.Add(c); |
262 | int x; | 186 | if (viewerMode) |
263 | |||
264 | for (x = 0; x < x_max; x++) | ||
265 | { | ||
266 | Vertex vt11 = sVertices[(y * COLUMNS + x)]; | ||
267 | Vertex vt12 = sVertices[(y * COLUMNS + (x + 1))]; | ||
268 | Vertex vt13 = sVertices[((y + 1) * COLUMNS + (x + 1))]; | ||
269 | if (vt11 != null && vt12 != null && vt13 != null) | ||
270 | { | 187 | { |
271 | if (vt11 != vt12 && vt11 != vt13 && vt12 != vt13) | 188 | this.normals.Add(new Coord()); |
272 | { | 189 | this.uvs.Add(new UVCoord(widthUnit * imageX, heightUnit * imageY)); |
273 | Triangle tri1 = new Triangle(vt11, vt12, vt13); | ||
274 | //indices[i++] = (ushort)(y * COLUMNS + x); | ||
275 | //indices[i++] = (ushort)(y * COLUMNS + (x + 1)); | ||
276 | //indices[i++] = (ushort)((y + 1) * COLUMNS + (x + 1)); | ||
277 | Add(tri1); | ||
278 | } | ||
279 | } | 190 | } |
280 | 191 | ||
281 | Vertex vt21 = sVertices[(y * COLUMNS + x)]; | 192 | if (imageY > 0 && imageX > 0) |
282 | Vertex vt22 = sVertices[((y + 1) * COLUMNS + (x + 1))]; | ||
283 | Vertex vt23 = sVertices[((y + 1) * COLUMNS + x)]; | ||
284 | if (vt21 != null && vt22 != null && vt23 != null) | ||
285 | { | 193 | { |
286 | if (vt21.Equals(vt22, 0.022f) || vt21.Equals(vt23, 0.022f) || vt22.Equals(vt23, 0.022f)) | 194 | Face f1, f2; |
195 | |||
196 | if (viewerMode) | ||
287 | { | 197 | { |
198 | f1 = new Face(p1, p3, p4, p1, p3, p4); | ||
199 | f1.uv1 = p1; | ||
200 | f1.uv2 = p3; | ||
201 | f1.uv3 = p4; | ||
202 | |||
203 | f2 = new Face(p1, p4, p2, p1, p4, p2); | ||
204 | f2.uv1 = p1; | ||
205 | f2.uv2 = p4; | ||
206 | f2.uv3 = p2; | ||
288 | } | 207 | } |
289 | else | 208 | else |
290 | { | 209 | { |
291 | Triangle tri2 = new Triangle(vt21, vt22, vt23); | 210 | f1 = new Face(p1, p3, p4); |
292 | //indices[i++] = (ushort)(y * COLUMNS + x); | 211 | f2 = new Face(p1, p4, p2); |
293 | //indices[i++] = (ushort)((y + 1) * COLUMNS + (x + 1)); | ||
294 | //indices[i++] = (ushort)((y + 1) * COLUMNS + x); | ||
295 | Add(tri2); | ||
296 | } | 212 | } |
213 | |||
214 | this.faces.Add(f1); | ||
215 | this.faces.Add(f2); | ||
297 | } | 216 | } |
217 | } | ||
218 | } | ||
219 | |||
220 | if (scaleSourceImage) | ||
221 | bitmap.Dispose(); | ||
222 | |||
223 | if (viewerMode) | ||
224 | { // compute vertex normals by summing all the surface normals of all the triangles sharing | ||
225 | // each vertex and then normalizing | ||
226 | int numFaces = this.faces.Count; | ||
227 | for (int i = 0; i < numFaces; i++) | ||
228 | { | ||
229 | Face face = this.faces[i]; | ||
230 | Coord surfaceNormal = face.SurfaceNormal(this.coords); | ||
231 | this.normals[face.v1] += surfaceNormal; | ||
232 | this.normals[face.v2] += surfaceNormal; | ||
233 | this.normals[face.v3] += surfaceNormal; | ||
234 | } | ||
298 | 235 | ||
236 | int numCoords = this.coords.Count; | ||
237 | for (int i = 0; i < numCoords; i++) | ||
238 | this.coords[i].Normalize(); | ||
239 | |||
240 | if (sculptType != SculptType.plane) | ||
241 | { // blend the vertex normals at the cylinder seam | ||
242 | pixelsAcross = width + 1; | ||
243 | for (imageY = 0; imageY < height; imageY++) | ||
244 | { | ||
245 | int rowOffset = imageY * pixelsAcross; | ||
246 | |||
247 | this.normals[rowOffset] = this.normals[rowOffset + width - 1] = (this.normals[rowOffset] + this.normals[rowOffset + width - 1]).Normalize(); | ||
248 | } | ||
299 | } | 249 | } |
300 | //Vertex vt31 = sVertices[(y * x_max + x)]; | ||
301 | //Vertex vt32 = sVertices[(y * x_max + 0)]; | ||
302 | //Vertex vt33 = sVertices[((y + 1) * x_max + 0)]; | ||
303 | //if (vt31 != null && vt32 != null && vt33 != null) | ||
304 | //{ | ||
305 | //if (vt31.Equals(vt32, 0.022f) || vt31.Equals(vt33, 0.022f) || vt32.Equals(vt33, 0.022f)) | ||
306 | //{ | ||
307 | //} | ||
308 | //else | ||
309 | //{ | ||
310 | //Triangle tri3 = new Triangle(vt31, vt32, vt33); | ||
311 | // Wrap the last cell in the row around | ||
312 | //indices[i++] = (ushort)(y * x_max + x); //a | ||
313 | //indices[i++] = (ushort)(y * x_max + 0); //b | ||
314 | //indices[i++] = (ushort)((y + 1) * x_max + 0); //c | ||
315 | //Add(tri3); | ||
316 | // } | ||
317 | //} | ||
318 | |||
319 | //Vertex vt41 = sVertices[(y * x_max + x)]; | ||
320 | //Vertex vt42 = sVertices[((y + 1) * x_max + 0)]; | ||
321 | //Vertex vt43 = sVertices[((y + 1) * x_max + x)]; | ||
322 | //if (vt41 != null && vt42 != null && vt43 != null) | ||
323 | //{ | ||
324 | //if (vt41.Equals(vt42, 0.022f) || vt31.Equals(vt43, 0.022f) || vt32.Equals(vt43, 0.022f)) | ||
325 | //{ | ||
326 | //} | ||
327 | // else | ||
328 | // { | ||
329 | //Triangle tri4 = new Triangle(vt41, vt42, vt43); | ||
330 | //indices[i++] = (ushort)(y * x_max + x); //a | ||
331 | //indices[i++] = (ushort)((y + 1) * x_max + 0); //b | ||
332 | //indices[i++] = (ushort)((y + 1) * x_max + x); //c | ||
333 | //Add(tri4); | ||
334 | //} | ||
335 | //} | ||
336 | 250 | ||
251 | foreach (Face face in this.faces) | ||
252 | { | ||
253 | ViewerFace vf = new ViewerFace(0); | ||
254 | vf.v1 = this.coords[face.v1]; | ||
255 | vf.v2 = this.coords[face.v2]; | ||
256 | vf.v3 = this.coords[face.v3]; | ||
257 | |||
258 | vf.n1 = this.normals[face.n1]; | ||
259 | vf.n2 = this.normals[face.n2]; | ||
260 | vf.n3 = this.normals[face.n3]; | ||
261 | |||
262 | vf.uv1 = this.uvs[face.uv1]; | ||
263 | vf.uv2 = this.uvs[face.uv2]; | ||
264 | vf.uv3 = this.uvs[face.uv3]; | ||
265 | |||
266 | this.viewerFaces.Add(vf); | ||
267 | } | ||
337 | } | 268 | } |
338 | } | 269 | } |
270 | |||
271 | public void AddRot(Quat q) | ||
272 | { | ||
273 | int i; | ||
274 | int numVerts = this.coords.Count; | ||
275 | |||
276 | for (i = 0; i < numVerts; i++) | ||
277 | this.coords[i] *= q; | ||
278 | |||
279 | if (this.viewerFaces != null) | ||
280 | { | ||
281 | int numViewerFaces = this.viewerFaces.Count; | ||
282 | |||
283 | for (i = 0; i < numViewerFaces; i++) | ||
284 | { | ||
285 | ViewerFace v = this.viewerFaces[i]; | ||
286 | v.v1 *= q; | ||
287 | v.v2 *= q; | ||
288 | v.v3 *= q; | ||
289 | |||
290 | v.n1 *= q; | ||
291 | v.n2 *= q; | ||
292 | v.n3 *= q; | ||
293 | |||
294 | this.viewerFaces[i] = v; | ||
295 | } | ||
296 | } | ||
297 | } | ||
298 | |||
299 | public void Scale(float x, float y, float z) | ||
300 | { | ||
301 | int i; | ||
302 | int numVerts = this.coords.Count; | ||
303 | //Coord vert; | ||
304 | |||
305 | Coord m = new Coord(x, y, z); | ||
306 | for (i = 0; i < numVerts; i++) | ||
307 | this.coords[i] *= m; | ||
308 | |||
309 | if (this.viewerFaces != null) | ||
310 | { | ||
311 | int numViewerFaces = this.viewerFaces.Count; | ||
312 | for (i = 0; i < numViewerFaces; i++) | ||
313 | { | ||
314 | ViewerFace v = this.viewerFaces[i]; | ||
315 | v.v1 *= m; | ||
316 | v.v2 *= m; | ||
317 | v.v3 *= m; | ||
318 | this.viewerFaces[i] = v; | ||
319 | } | ||
320 | } | ||
321 | } | ||
322 | |||
323 | public void DumpRaw(String path, String name, String title) | ||
324 | { | ||
325 | if (path == null) | ||
326 | return; | ||
327 | String fileName = name + "_" + title + ".raw"; | ||
328 | String completePath = Path.Combine(path, fileName); | ||
329 | StreamWriter sw = new StreamWriter(completePath); | ||
330 | |||
331 | for (int i = 0; i < this.faces.Count; i++) | ||
332 | { | ||
333 | string s = this.coords[this.faces[i].v1].ToString(); | ||
334 | s += " " + this.coords[this.faces[i].v2].ToString(); | ||
335 | s += " " + this.coords[this.faces[i].v3].ToString(); | ||
336 | |||
337 | sw.WriteLine(s); | ||
338 | } | ||
339 | |||
340 | sw.Close(); | ||
341 | } | ||
339 | } | 342 | } |
340 | } | 343 | } |
diff --git a/OpenSim/Region/Physics/Meshing/SimpleHull.cs b/OpenSim/Region/Physics/Meshing/SimpleHull.cs deleted file mode 100644 index 5eeadae..0000000 --- a/OpenSim/Region/Physics/Meshing/SimpleHull.cs +++ /dev/null | |||
@@ -1,394 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
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 OpenSim 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 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using OpenSim.Region.Physics.Manager; | ||
31 | |||
32 | namespace OpenSim.Region.Physics.Meshing | ||
33 | { | ||
34 | // A simple hull is a set of vertices building up to simplices that border a region | ||
35 | // The word simple referes to the fact, that this class assumes, that all simplices | ||
36 | // do not intersect | ||
37 | // Simple hulls can be added and subtracted. | ||
38 | // Vertices can be checked to lie inside a hull | ||
39 | // Also note, that the sequence of the vertices is important and defines if the region that | ||
40 | // is defined by the hull lies inside or outside the simplex chain | ||
41 | public class SimpleHull | ||
42 | { | ||
43 | //private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); | ||
44 | |||
45 | private List<Vertex> vertices = new List<Vertex>(); | ||
46 | private List<Vertex> holeVertices = new List<Vertex>(); // Only used, when the hull is hollow | ||
47 | |||
48 | // Adds a vertex to the end of the list | ||
49 | public void AddVertex(Vertex v) | ||
50 | { | ||
51 | vertices.Add(v); | ||
52 | } | ||
53 | |||
54 | public override String ToString() | ||
55 | { | ||
56 | String result = String.Empty; | ||
57 | foreach (Vertex v in vertices) | ||
58 | { | ||
59 | result += "b:" + v.ToString() + "\n"; | ||
60 | } | ||
61 | |||
62 | return result; | ||
63 | } | ||
64 | |||
65 | |||
66 | public List<Vertex> getVertices() | ||
67 | { | ||
68 | List<Vertex> newVertices = new List<Vertex>(); | ||
69 | |||
70 | newVertices.AddRange(vertices); | ||
71 | newVertices.Add(null); | ||
72 | newVertices.AddRange(holeVertices); | ||
73 | |||
74 | return newVertices; | ||
75 | } | ||
76 | |||
77 | public SimpleHull Clone() | ||
78 | { | ||
79 | SimpleHull result = new SimpleHull(); | ||
80 | foreach (Vertex v in vertices) | ||
81 | { | ||
82 | result.AddVertex(v.Clone()); | ||
83 | } | ||
84 | |||
85 | foreach (Vertex v in holeVertices) | ||
86 | { | ||
87 | result.holeVertices.Add(v.Clone()); | ||
88 | } | ||
89 | |||
90 | return result; | ||
91 | } | ||
92 | |||
93 | public bool IsPointIn(Vertex v1) | ||
94 | { | ||
95 | int iCounter = 0; | ||
96 | List<Simplex> simplices = buildSimplexList(); | ||
97 | foreach (Simplex s in simplices) | ||
98 | { | ||
99 | // Send a ray along the positive X-Direction | ||
100 | // Note, that this direction must correlate with the "below" interpretation | ||
101 | // of handling for the special cases below | ||
102 | PhysicsVector intersection = s.RayIntersect(v1, new PhysicsVector(1.0f, 0.0f, 0.0f), true); | ||
103 | |||
104 | if (intersection == null) | ||
105 | continue; // No intersection. Done. More tests to follow otherwise | ||
106 | |||
107 | // Did we hit the end of a simplex? | ||
108 | // Then this can be one of two special cases: | ||
109 | // 1. we go through a border exactly at a joint | ||
110 | // 2. we have just marginally touched a corner | ||
111 | // 3. we can slide along a border | ||
112 | // Solution: If the other vertex is "below" the ray, we don't count it | ||
113 | // Thus corners pointing down are counted twice, corners pointing up are not counted | ||
114 | // borders are counted once | ||
115 | if (intersection.IsIdentical(s.v1, 0.001f)) | ||
116 | { | ||
117 | if (s.v2.Y < v1.Y) | ||
118 | continue; | ||
119 | } | ||
120 | // Do this for the other vertex two | ||
121 | if (intersection.IsIdentical(s.v2, 0.001f)) | ||
122 | { | ||
123 | if (s.v1.Y < v1.Y) | ||
124 | continue; | ||
125 | } | ||
126 | iCounter++; | ||
127 | } | ||
128 | |||
129 | return iCounter%2 == 1; // Point is inside if the number of intersections is odd | ||
130 | } | ||
131 | |||
132 | public bool containsPointsFrom(SimpleHull otherHull) | ||
133 | { | ||
134 | foreach (Vertex v in otherHull.vertices) | ||
135 | { | ||
136 | if (IsPointIn(v)) | ||
137 | return true; | ||
138 | } | ||
139 | |||
140 | return false; | ||
141 | } | ||
142 | |||
143 | |||
144 | private List<Simplex> buildSimplexList() | ||
145 | { | ||
146 | List<Simplex> result = new List<Simplex>(); | ||
147 | |||
148 | // Not asserted but assumed: at least three vertices | ||
149 | for (int i = 0; i < vertices.Count - 1; i++) | ||
150 | { | ||
151 | Simplex s = new Simplex(vertices[i], vertices[i + 1]); | ||
152 | result.Add(s); | ||
153 | } | ||
154 | Simplex s1 = new Simplex(vertices[vertices.Count - 1], vertices[0]); | ||
155 | result.Add(s1); | ||
156 | |||
157 | if (holeVertices.Count == 0) | ||
158 | return result; | ||
159 | |||
160 | // Same here. At least three vertices in hole assumed | ||
161 | for (int i = 0; i < holeVertices.Count - 1; i++) | ||
162 | { | ||
163 | Simplex s = new Simplex(holeVertices[i], holeVertices[i + 1]); | ||
164 | result.Add(s); | ||
165 | } | ||
166 | |||
167 | s1 = new Simplex(holeVertices[holeVertices.Count - 1], holeVertices[0]); | ||
168 | result.Add(s1); | ||
169 | return result; | ||
170 | } | ||
171 | |||
172 | // TODO: unused | ||
173 | // private bool InsertVertex(Vertex v, int iAfter) | ||
174 | // { | ||
175 | // vertices.Insert(iAfter + 1, v); | ||
176 | // return true; | ||
177 | // } | ||
178 | |||
179 | private Vertex getNextVertex(Vertex currentVertex) | ||
180 | { | ||
181 | int iCurrentIndex; | ||
182 | iCurrentIndex = vertices.IndexOf(currentVertex); | ||
183 | |||
184 | // Error handling for iCurrentIndex==-1 should go here (and probably never will) | ||
185 | |||
186 | iCurrentIndex++; | ||
187 | if (iCurrentIndex == vertices.Count) | ||
188 | iCurrentIndex = 0; | ||
189 | |||
190 | return vertices[iCurrentIndex]; | ||
191 | } | ||
192 | |||
193 | public Vertex FindVertex(Vertex vBase, float tolerance) | ||
194 | { | ||
195 | foreach (Vertex v in vertices) | ||
196 | { | ||
197 | if (v.IsIdentical(vBase, tolerance)) | ||
198 | return v; | ||
199 | } | ||
200 | |||
201 | return null; | ||
202 | } | ||
203 | |||
204 | public void FindIntersection(Simplex s, ref Vertex Intersection, ref Vertex nextVertex) | ||
205 | { | ||
206 | Vertex bestIntersection = null; | ||
207 | float distToV1 = Single.PositiveInfinity; | ||
208 | Simplex bestIntersectingSimplex = null; | ||
209 | |||
210 | List<Simplex> simple = buildSimplexList(); | ||
211 | foreach (Simplex sTest in simple) | ||
212 | { | ||
213 | PhysicsVector vvTemp = Simplex.Intersect(sTest, s, -.001f, -.001f, 0.999f, .999f); | ||
214 | |||
215 | Vertex vTemp = null; | ||
216 | if (vvTemp != null) | ||
217 | vTemp = new Vertex(vvTemp); | ||
218 | |||
219 | if (vTemp != null) | ||
220 | { | ||
221 | PhysicsVector diff = (s.v1 - vTemp); | ||
222 | float distTemp = diff.length(); | ||
223 | |||
224 | if (bestIntersection == null || distTemp < distToV1) | ||
225 | { | ||
226 | bestIntersection = vTemp; | ||
227 | distToV1 = distTemp; | ||
228 | bestIntersectingSimplex = sTest; | ||
229 | } | ||
230 | } | ||
231 | } | ||
232 | |||
233 | Intersection = bestIntersection; | ||
234 | if (bestIntersectingSimplex != null) | ||
235 | nextVertex = bestIntersectingSimplex.v2; | ||
236 | else | ||
237 | nextVertex = null; | ||
238 | } | ||
239 | |||
240 | |||
241 | public static SimpleHull SubtractHull(SimpleHull baseHull, SimpleHull otherHull) | ||
242 | { | ||
243 | SimpleHull baseHullClone = baseHull.Clone(); | ||
244 | SimpleHull otherHullClone = otherHull.Clone(); | ||
245 | bool intersects = false; | ||
246 | |||
247 | //m_log.Debug("State before intersection detection"); | ||
248 | //m_log.DebugFormat("The baseHull is:\n{1}", 0, baseHullClone.ToString()); | ||
249 | //m_log.DebugFormat("The otherHull is:\n{1}", 0, otherHullClone.ToString()); | ||
250 | |||
251 | { | ||
252 | int iBase, iOther; | ||
253 | |||
254 | // Insert into baseHull | ||
255 | for (iBase = 0; iBase < baseHullClone.vertices.Count; iBase++) | ||
256 | { | ||
257 | int iBaseNext = (iBase + 1)%baseHullClone.vertices.Count; | ||
258 | Simplex sBase = new Simplex(baseHullClone.vertices[iBase], baseHullClone.vertices[iBaseNext]); | ||
259 | |||
260 | for (iOther = 0; iOther < otherHullClone.vertices.Count; iOther++) | ||
261 | { | ||
262 | int iOtherNext = (iOther + 1)%otherHullClone.vertices.Count; | ||
263 | Simplex sOther = | ||
264 | new Simplex(otherHullClone.vertices[iOther], otherHullClone.vertices[iOtherNext]); | ||
265 | |||
266 | PhysicsVector intersect = Simplex.Intersect(sBase, sOther, 0.001f, -.001f, 0.999f, 1.001f); | ||
267 | if (intersect != null) | ||
268 | { | ||
269 | Vertex vIntersect = new Vertex(intersect); | ||
270 | baseHullClone.vertices.Insert(iBase + 1, vIntersect); | ||
271 | sBase.v2 = vIntersect; | ||
272 | intersects = true; | ||
273 | } | ||
274 | } | ||
275 | } | ||
276 | } | ||
277 | |||
278 | //m_log.Debug("State after intersection detection for the base hull"); | ||
279 | //m_log.DebugFormat("The baseHull is:\n{1}", 0, baseHullClone.ToString()); | ||
280 | |||
281 | { | ||
282 | int iOther, iBase; | ||
283 | |||
284 | // Insert into otherHull | ||
285 | for (iOther = 0; iOther < otherHullClone.vertices.Count; iOther++) | ||
286 | { | ||
287 | int iOtherNext = (iOther + 1)%otherHullClone.vertices.Count; | ||
288 | Simplex sOther = new Simplex(otherHullClone.vertices[iOther], otherHullClone.vertices[iOtherNext]); | ||
289 | |||
290 | for (iBase = 0; iBase < baseHullClone.vertices.Count; iBase++) | ||
291 | { | ||
292 | int iBaseNext = (iBase + 1)%baseHullClone.vertices.Count; | ||
293 | Simplex sBase = new Simplex(baseHullClone.vertices[iBase], baseHullClone.vertices[iBaseNext]); | ||
294 | |||
295 | PhysicsVector intersect = Simplex.Intersect(sBase, sOther, -.001f, 0.001f, 1.001f, 0.999f); | ||
296 | if (intersect != null) | ||
297 | { | ||
298 | Vertex vIntersect = new Vertex(intersect); | ||
299 | otherHullClone.vertices.Insert(iOther + 1, vIntersect); | ||
300 | sOther.v2 = vIntersect; | ||
301 | intersects = true; | ||
302 | } | ||
303 | } | ||
304 | } | ||
305 | } | ||
306 | |||
307 | //m_log.Debug("State after intersection detection for the base hull"); | ||
308 | //m_log.DebugFormat("The otherHull is:\n{1}", 0, otherHullClone.ToString()); | ||
309 | |||
310 | bool otherIsInBase = baseHullClone.containsPointsFrom(otherHullClone); | ||
311 | if (!intersects && otherIsInBase) | ||
312 | { | ||
313 | // We have a hole here | ||
314 | baseHullClone.holeVertices = otherHullClone.vertices; | ||
315 | return baseHullClone; | ||
316 | } | ||
317 | |||
318 | SimpleHull result = new SimpleHull(); | ||
319 | |||
320 | // Find a good starting Simplex from baseHull | ||
321 | // A good starting simplex is one that is outside otherHull | ||
322 | // Such a simplex must exist, otherwise the result will be empty | ||
323 | Vertex baseStartVertex = null; | ||
324 | { | ||
325 | int iBase; | ||
326 | for (iBase = 0; iBase < baseHullClone.vertices.Count; iBase++) | ||
327 | { | ||
328 | int iBaseNext = (iBase + 1)%baseHullClone.vertices.Count; | ||
329 | Vertex center = new Vertex((baseHullClone.vertices[iBase] + baseHullClone.vertices[iBaseNext])/2.0f); | ||
330 | bool isOutside = !otherHullClone.IsPointIn(center); | ||
331 | if (isOutside) | ||
332 | { | ||
333 | baseStartVertex = baseHullClone.vertices[iBaseNext]; | ||
334 | break; | ||
335 | } | ||
336 | } | ||
337 | } | ||
338 | |||
339 | |||
340 | if (baseStartVertex == null) // i.e. no simplex fulfilled the "outside" condition. | ||
341 | // In otherwords, subtractHull completely embraces baseHull | ||
342 | { | ||
343 | return result; | ||
344 | } | ||
345 | |||
346 | // The simplex that *starts* with baseStartVertex is outside the cutting hull, | ||
347 | // so we can start our walk with the next vertex without loosing a branch | ||
348 | Vertex V1 = baseStartVertex; | ||
349 | bool onBase = true; | ||
350 | |||
351 | // And here is how we do the magic :-) | ||
352 | // Start on the base hull. | ||
353 | // Walk the vertices in the positive direction | ||
354 | // For each vertex check, whether it is a vertex shared with the other hull | ||
355 | // if this is the case, switch over to walking the other vertex list. | ||
356 | // Note: The other hull *must* go backwards to our starting point (via several orther vertices) | ||
357 | // Thus it is important that the cutting hull has the inverse directional sense than the | ||
358 | // base hull!!!!!!!!! (means if base goes CW around it's center cutting hull must go CCW) | ||
359 | |||
360 | bool done = false; | ||
361 | while (!done) | ||
362 | { | ||
363 | result.AddVertex(V1); | ||
364 | Vertex nextVertex = null; | ||
365 | if (onBase) | ||
366 | { | ||
367 | nextVertex = otherHullClone.FindVertex(V1, 0.001f); | ||
368 | } | ||
369 | else | ||
370 | { | ||
371 | nextVertex = baseHullClone.FindVertex(V1, 0.001f); | ||
372 | } | ||
373 | |||
374 | if (nextVertex != null) // A node that represents an intersection | ||
375 | { | ||
376 | V1 = nextVertex; // Needed to find the next vertex on the other hull | ||
377 | onBase = !onBase; | ||
378 | } | ||
379 | |||
380 | if (onBase) | ||
381 | V1 = baseHullClone.getNextVertex(V1); | ||
382 | else | ||
383 | V1 = otherHullClone.getNextVertex(V1); | ||
384 | |||
385 | if (V1 == baseStartVertex) | ||
386 | done = true; | ||
387 | } | ||
388 | |||
389 | //m_log.DebugFormat("The resulting Hull is:\n{1}", 0, result.ToString()); | ||
390 | |||
391 | return result; | ||
392 | } | ||
393 | } | ||
394 | } | ||
diff --git a/OpenSim/Region/Physics/Meshing/Simplex.cs b/OpenSim/Region/Physics/Meshing/Simplex.cs deleted file mode 100644 index aeeef11..0000000 --- a/OpenSim/Region/Physics/Meshing/Simplex.cs +++ /dev/null | |||
@@ -1,220 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
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 OpenSim 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 | using System; | ||
29 | using OpenSim.Region.Physics.Manager; | ||
30 | |||
31 | namespace OpenSim.Region.Physics.Meshing | ||
32 | { | ||
33 | // A simplex is a section of a straight line. | ||
34 | // It is defined by its endpoints, i.e. by two vertices | ||
35 | // Operation on vertices are | ||
36 | public class Simplex : IComparable<Simplex> | ||
37 | { | ||
38 | public Vertex v1; | ||
39 | public Vertex v2; | ||
40 | |||
41 | public Simplex(Vertex _v1, Vertex _v2) | ||
42 | { | ||
43 | v1 = _v1; | ||
44 | v2 = _v2; | ||
45 | } | ||
46 | |||
47 | public int CompareTo(Simplex other) | ||
48 | { | ||
49 | Vertex lv1, lv2, ov1, ov2, temp; | ||
50 | |||
51 | lv1 = v1; | ||
52 | lv2 = v2; | ||
53 | ov1 = other.v1; | ||
54 | ov2 = other.v2; | ||
55 | |||
56 | if (lv1 > lv2) | ||
57 | { | ||
58 | temp = lv1; | ||
59 | lv1 = lv2; | ||
60 | lv2 = temp; | ||
61 | } | ||
62 | |||
63 | if (ov1 > ov2) | ||
64 | { | ||
65 | temp = ov1; | ||
66 | ov1 = ov2; | ||
67 | ov2 = temp; | ||
68 | } | ||
69 | |||
70 | if (lv1 > ov1) | ||
71 | { | ||
72 | return 1; | ||
73 | } | ||
74 | if (lv1 < ov1) | ||
75 | { | ||
76 | return -1; | ||
77 | } | ||
78 | |||
79 | if (lv2 > ov2) | ||
80 | { | ||
81 | return 1; | ||
82 | } | ||
83 | if (lv2 < ov2) | ||
84 | { | ||
85 | return -1; | ||
86 | } | ||
87 | |||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | private static void intersectParameter(PhysicsVector p1, PhysicsVector r1, PhysicsVector p2, PhysicsVector r2, | ||
92 | ref float lambda, ref float mu) | ||
93 | { | ||
94 | // Intersects two straights | ||
95 | // p1, p2, points on the straight | ||
96 | // r1, r2, directional vectors of the straight. Not necessarily of length 1! | ||
97 | // note, that l, m can be scaled such, that the range 0..1 is mapped to the area between two points, | ||
98 | // thus allowing to decide whether an intersection is between two points | ||
99 | |||
100 | float r1x = r1.X; | ||
101 | float r1y = r1.Y; | ||
102 | float r2x = r2.X; | ||
103 | float r2y = r2.Y; | ||
104 | |||
105 | float denom = r1y*r2x - r1x*r2y; | ||
106 | |||
107 | float p1x = p1.X; | ||
108 | float p1y = p1.Y; | ||
109 | float p2x = p2.X; | ||
110 | float p2y = p2.Y; | ||
111 | |||
112 | float z1 = -p2x*r2y + p1x*r2y + (p2y - p1y)*r2x; | ||
113 | float z2 = -p2x*r1y + p1x*r1y + (p2y - p1y)*r1x; | ||
114 | |||
115 | if (denom == 0.0f) // Means the straights are parallel. Either no intersection or an infinite number of them | ||
116 | { | ||
117 | if (z1 == 0.0f) | ||
118 | { | ||
119 | // Means they are identical -> many, many intersections | ||
120 | lambda = Single.NaN; | ||
121 | mu = Single.NaN; | ||
122 | } | ||
123 | else | ||
124 | { | ||
125 | lambda = Single.PositiveInfinity; | ||
126 | mu = Single.PositiveInfinity; | ||
127 | } | ||
128 | return; | ||
129 | } | ||
130 | |||
131 | |||
132 | lambda = z1/denom; | ||
133 | mu = z2/denom; | ||
134 | } | ||
135 | |||
136 | |||
137 | // Intersects the simplex with another one. | ||
138 | // the borders are used to deal with float inaccuracies | ||
139 | // As a rule of thumb, the borders are | ||
140 | // lowerBorder1 : 0.0 | ||
141 | // lowerBorder2 : 0.0 | ||
142 | // upperBorder1 : 1.0 | ||
143 | // upperBorder2 : 1.0 | ||
144 | // Set these to values near the given parameters (e.g. 0.001 instead of 1 to exclude simplex starts safely, or to -0.001 to include them safely) | ||
145 | public static PhysicsVector Intersect( | ||
146 | Simplex s1, | ||
147 | Simplex s2, | ||
148 | float lowerBorder1, | ||
149 | float lowerBorder2, | ||
150 | float upperBorder1, | ||
151 | float upperBorder2) | ||
152 | { | ||
153 | PhysicsVector firstSimplexDirection = s1.v2 - s1.v1; | ||
154 | PhysicsVector secondSimplexDirection = s2.v2 - s2.v1; | ||
155 | |||
156 | float lambda = 0.0f; | ||
157 | float mu = 0.0f; | ||
158 | |||
159 | // Give us the parameters of an intersection. This subroutine does *not* take the constraints | ||
160 | // (intersection must be between v1 and v2 and it must be in the positive direction of the ray) | ||
161 | // into account. We do that afterwards. | ||
162 | intersectParameter(s1.v1, firstSimplexDirection, s2.v1, secondSimplexDirection, ref lambda, ref mu); | ||
163 | |||
164 | if (Single.IsInfinity(lambda)) // Special case. No intersection at all. directions parallel. | ||
165 | return null; | ||
166 | |||
167 | if (Single.IsNaN(lambda)) // Special case. many, many intersections. | ||
168 | return null; | ||
169 | |||
170 | if (lambda > upperBorder1) // We're behind v2 | ||
171 | return null; | ||
172 | |||
173 | if (lambda < lowerBorder1) | ||
174 | return null; | ||
175 | |||
176 | if (mu < lowerBorder2) // outside simplex 2 | ||
177 | return null; | ||
178 | |||
179 | if (mu > upperBorder2) // outside simplex 2 | ||
180 | return null; | ||
181 | |||
182 | return s1.v1 + lambda*firstSimplexDirection; | ||
183 | } | ||
184 | |||
185 | // Intersects the simplex with a ray. The ray is defined as all p=origin + lambda*direction | ||
186 | // where lambda >= 0 | ||
187 | public PhysicsVector RayIntersect(Vertex origin, PhysicsVector direction, bool bEndsIncluded) | ||
188 | { | ||
189 | PhysicsVector simplexDirection = v2 - v1; | ||
190 | |||
191 | float lambda = 0.0f; | ||
192 | float mu = 0.0f; | ||
193 | |||
194 | // Give us the parameters of an intersection. This subroutine does *not* take the constraints | ||
195 | // (intersection must be between v1 and v2 and it must be in the positive direction of the ray) | ||
196 | // into account. We do that afterwards. | ||
197 | intersectParameter(v1, simplexDirection, origin, direction, ref lambda, ref mu); | ||
198 | |||
199 | if (Single.IsInfinity(lambda)) // Special case. No intersection at all. directions parallel. | ||
200 | return null; | ||
201 | |||
202 | if (Single.IsNaN(lambda)) // Special case. many, many intersections. | ||
203 | return null; | ||
204 | |||
205 | if (mu < 0.0) // We're on the wrong side of the ray | ||
206 | return null; | ||
207 | |||
208 | if (lambda > 1.0) // We're behind v2 | ||
209 | return null; | ||
210 | |||
211 | if (lambda == 1.0 && !bEndsIncluded) | ||
212 | return null; // The end of the simplices are not included | ||
213 | |||
214 | if (lambda < 0.0f) // we're before v1; | ||
215 | return null; | ||
216 | |||
217 | return v1 + lambda*simplexDirection; | ||
218 | } | ||
219 | } | ||
220 | } | ||