diff options
Diffstat (limited to 'OpenSim/Region/Physics/Meshing/Meshmerizer.cs')
-rw-r--r-- | OpenSim/Region/Physics/Meshing/Meshmerizer.cs | 393 |
1 files changed, 393 insertions, 0 deletions
diff --git a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs new file mode 100644 index 0000000..da4ee58 --- /dev/null +++ b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs | |||
@@ -0,0 +1,393 @@ | |||
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 | |||
29 | using System; | ||
30 | using System.IO; | ||
31 | using System.Globalization; | ||
32 | using System.Diagnostics; | ||
33 | using System.Collections.Generic; | ||
34 | using System.Runtime.InteropServices; | ||
35 | using OpenSim.Framework; | ||
36 | using OpenSim.Framework.Console; | ||
37 | using OpenSim.Region.Physics.Manager; | ||
38 | |||
39 | namespace OpenSim.Region.Physics.Meshing | ||
40 | { | ||
41 | |||
42 | public class MeshmerizerPlugin : IMeshingPlugin | ||
43 | { | ||
44 | public MeshmerizerPlugin() | ||
45 | { | ||
46 | } | ||
47 | |||
48 | public string GetName() | ||
49 | { | ||
50 | return "Meshmerizer"; | ||
51 | } | ||
52 | |||
53 | public IMesher GetMesher() | ||
54 | { | ||
55 | return new Meshmerizer(); | ||
56 | } | ||
57 | } | ||
58 | |||
59 | public class Meshmerizer : IMesher | ||
60 | { | ||
61 | // Setting baseDir to a path will enable the dumping of raw files | ||
62 | // raw files can be imported by blender so a visual inspection of the results can be done | ||
63 | // const string baseDir = "rawFiles"; | ||
64 | const string baseDir = null; | ||
65 | |||
66 | static void IntersectionParameterPD(PhysicsVector p1, PhysicsVector r1, PhysicsVector p2, PhysicsVector r2, ref float lambda, ref float mu) | ||
67 | { | ||
68 | // p1, p2, points on the straight | ||
69 | // r1, r2, directional vectors of the straight. Not necessarily of length 1! | ||
70 | // note, that l, m can be scaled such, that the range 0..1 is mapped to the area between two points, | ||
71 | // thus allowing to decide whether an intersection is between two points | ||
72 | |||
73 | float r1x = r1.X; | ||
74 | float r1y = r1.Y; | ||
75 | float r2x = r2.X; | ||
76 | float r2y = r2.Y; | ||
77 | |||
78 | float denom = r1y*r2x - r1x*r2y; | ||
79 | |||
80 | if (denom == 0.0) | ||
81 | { | ||
82 | lambda = Single.NaN; | ||
83 | mu = Single.NaN; | ||
84 | return; | ||
85 | } | ||
86 | |||
87 | float p1x = p1.X; | ||
88 | float p1y = p1.Y; | ||
89 | float p2x = p2.X; | ||
90 | float p2y = p2.Y; | ||
91 | lambda = (-p2x * r2y + p1x * r2y + (p2y - p1y) * r2x) / denom; | ||
92 | mu = (-p2x * r1y + p1x * r1y + (p2y - p1y) * r1x) / denom; | ||
93 | |||
94 | } | ||
95 | |||
96 | private static List<Triangle> FindInfluencedTriangles(List<Triangle> triangles, Vertex v) | ||
97 | { | ||
98 | List<Triangle> influenced = new List<Triangle>(); | ||
99 | foreach (Triangle t in triangles) | ||
100 | { | ||
101 | if (t.isInCircle(v.X, v.Y)) | ||
102 | { | ||
103 | influenced.Add(t); | ||
104 | } | ||
105 | } | ||
106 | return influenced; | ||
107 | } | ||
108 | |||
109 | |||
110 | private static void InsertVertices(List<Vertex> vertices, int usedForSeed, List<Triangle> triangles) | ||
111 | { | ||
112 | // This is a variant of the delaunay algorithm | ||
113 | // each time a new vertex is inserted, all triangles that are influenced by it are deleted | ||
114 | // and replaced by new ones including the new vertex | ||
115 | // It is not very time efficient but easy to implement. | ||
116 | |||
117 | int iCurrentVertex; | ||
118 | int iMaxVertex = vertices.Count; | ||
119 | for (iCurrentVertex = usedForSeed; iCurrentVertex < iMaxVertex; iCurrentVertex++) | ||
120 | { | ||
121 | // Background: A triangle mesh fulfills the delaunay condition if (iff!) | ||
122 | // each circumlocutory circle (i.e. the circle that touches all three corners) | ||
123 | // of each triangle is empty of other vertices. | ||
124 | // Obviously a single (seeding) triangle fulfills this condition. | ||
125 | // If we now add one vertex, we need to reconstruct all triangles, that | ||
126 | // do not fulfill this condition with respect to the new triangle | ||
127 | |||
128 | // Find the triangles that are influenced by the new vertex | ||
129 | Vertex v=vertices[iCurrentVertex]; | ||
130 | if (v == null) | ||
131 | continue; // Null is polygon stop marker. Ignore it | ||
132 | List<Triangle> influencedTriangles=FindInfluencedTriangles(triangles, v); | ||
133 | |||
134 | List<Simplex> simplices = new List<Simplex>(); | ||
135 | |||
136 | // Reconstruction phase. First step, dissolve each triangle into it's simplices, | ||
137 | // i.e. it's "border lines" | ||
138 | // Goal is to find "inner" borders and delete them, while the hull gets conserved. | ||
139 | // Inner borders are special in the way that they always come twice, which is how we detect them | ||
140 | foreach (Triangle t in influencedTriangles) | ||
141 | { | ||
142 | List<Simplex> newSimplices = t.GetSimplices(); | ||
143 | simplices.AddRange(newSimplices); | ||
144 | triangles.Remove(t); | ||
145 | } | ||
146 | // Now sort the simplices. That will make identical ones reside side by side in the list | ||
147 | simplices.Sort(); | ||
148 | |||
149 | // Look for duplicate simplices here. | ||
150 | // Remember, they are directly side by side in the list right now, | ||
151 | // So we only check directly neighbours | ||
152 | int iSimplex; | ||
153 | List<Simplex> innerSimplices = new List<Simplex>(); | ||
154 | for (iSimplex = 1; iSimplex < simplices.Count; iSimplex++) // Startindex=1, so we can refer backwards | ||
155 | { | ||
156 | if (simplices[iSimplex - 1].CompareTo(simplices[iSimplex]) == 0) | ||
157 | { | ||
158 | innerSimplices.Add(simplices[iSimplex - 1]); | ||
159 | innerSimplices.Add(simplices[iSimplex]); | ||
160 | } | ||
161 | } | ||
162 | |||
163 | foreach (Simplex s in innerSimplices) | ||
164 | { | ||
165 | simplices.Remove(s); | ||
166 | } | ||
167 | |||
168 | // each simplex still in the list belongs to the hull of the region in question | ||
169 | // The new vertex (yes, we still deal with verices here :-) ) forms a triangle | ||
170 | // with each of these simplices. Build the new triangles and add them to the list | ||
171 | foreach (Simplex s in simplices) | ||
172 | { | ||
173 | Triangle t = new Triangle(s.v1, s.v2, vertices[iCurrentVertex]); | ||
174 | if (!t.isDegraded()) | ||
175 | { | ||
176 | triangles.Add(t); | ||
177 | } | ||
178 | } | ||
179 | } | ||
180 | |||
181 | } | ||
182 | |||
183 | |||
184 | static Mesh CreateBoxMesh(String primName, PrimitiveBaseShape primShape, PhysicsVector size) | ||
185 | // Builds the z (+ and -) surfaces of a box shaped prim | ||
186 | { | ||
187 | UInt16 hollowFactor = primShape.ProfileHollow; | ||
188 | UInt16 profileBegin = primShape.ProfileBegin; | ||
189 | UInt16 profileEnd = primShape.ProfileEnd; | ||
190 | |||
191 | // Procedure: This is based on the fact that the upper (plus) and lower (minus) Z-surface | ||
192 | // of a block are basically the same | ||
193 | // They may be warped differently but the shape is identical | ||
194 | // So we only create one surface as a model and derive both plus and minus surface of the block from it | ||
195 | // This is done in a model space where the block spans from -.5 to +.5 in X and Y | ||
196 | // The mapping to Scene space is done later during the "extrusion" phase | ||
197 | |||
198 | // Base | ||
199 | Vertex MM = new Vertex(-0.5f, -0.5f, 0.0f); | ||
200 | Vertex PM = new Vertex(+0.5f, -0.5f, 0.0f); | ||
201 | Vertex MP = new Vertex(-0.5f, +0.5f, 0.0f); | ||
202 | Vertex PP = new Vertex(+0.5f, +0.5f, 0.0f); | ||
203 | |||
204 | Meshing.SimpleHull outerHull = new SimpleHull(); | ||
205 | outerHull.AddVertex(MM); | ||
206 | outerHull.AddVertex(PM); | ||
207 | outerHull.AddVertex(PP); | ||
208 | outerHull.AddVertex(MP); | ||
209 | |||
210 | // Deal with cuts now | ||
211 | if ((profileBegin != 0) || (profileEnd != 0)) | ||
212 | { | ||
213 | double fProfileBeginAngle = profileBegin / 50000.0 * 360.0; // In degree, for easier debugging and understanding | ||
214 | fProfileBeginAngle -= (90.0 + 45.0); // for some reasons, the SL client counts from the corner -X/-Y | ||
215 | double fProfileEndAngle = 360.0 - profileEnd / 50000.0 * 360.0; // Pathend comes as complement to 1.0 | ||
216 | fProfileEndAngle -= (90.0 + 45.0); | ||
217 | if (fProfileBeginAngle < fProfileEndAngle) | ||
218 | fProfileEndAngle -= 360.0; | ||
219 | |||
220 | // Note, that we don't want to cut out a triangle, even if this is a | ||
221 | // good approximation for small cuts. Indeed we want to cut out an arc | ||
222 | // and we approximate this arc by a polygon chain | ||
223 | // Also note, that these vectors are of length 1.0 and thus their endpoints lay outside the model space | ||
224 | // So it can easily be subtracted from the outer hull | ||
225 | int iSteps = (int)(((fProfileBeginAngle - fProfileEndAngle) / 45.0) + .5); // how many steps do we need with approximately 45 degree | ||
226 | double dStepWidth=(fProfileBeginAngle-fProfileEndAngle)/iSteps; | ||
227 | |||
228 | Vertex origin = new Vertex(0.0f, 0.0f, 0.0f); | ||
229 | |||
230 | // Note the sequence of vertices here. It's important to have the other rotational sense than in outerHull | ||
231 | SimpleHull cutHull = new SimpleHull(); | ||
232 | cutHull.AddVertex(origin); | ||
233 | for (int i=0; i<iSteps; i++) { | ||
234 | double angle=fProfileBeginAngle-i*dStepWidth; // we count against the angle orientation!!!! | ||
235 | Vertex v = Vertex.FromAngle(angle * Math.PI / 180.0); | ||
236 | cutHull.AddVertex(v); | ||
237 | } | ||
238 | Vertex legEnd = Vertex.FromAngle(fProfileEndAngle * Math.PI / 180.0); // Calculated separately to avoid errors | ||
239 | cutHull.AddVertex(legEnd); | ||
240 | |||
241 | MainLog.Instance.Debug("Starting cutting of the hollow shape from the prim {1}", 0, primName); | ||
242 | SimpleHull cuttedHull = SimpleHull.SubtractHull(outerHull, cutHull); | ||
243 | |||
244 | outerHull = cuttedHull; | ||
245 | } | ||
246 | |||
247 | // Deal with the hole here | ||
248 | if (hollowFactor > 0) | ||
249 | { | ||
250 | float hollowFactorF = (float) hollowFactor/(float) 50000; | ||
251 | Vertex IMM = new Vertex(-0.5f * hollowFactorF, -0.5f * hollowFactorF, 0.0f); | ||
252 | Vertex IPM = new Vertex(+0.5f * hollowFactorF, -0.5f * hollowFactorF, 0.0f); | ||
253 | Vertex IMP = new Vertex(-0.5f * hollowFactorF, +0.5f * hollowFactorF, 0.0f); | ||
254 | Vertex IPP = new Vertex(+0.5f * hollowFactorF, +0.5f * hollowFactorF, 0.0f); | ||
255 | |||
256 | SimpleHull holeHull = new SimpleHull(); | ||
257 | |||
258 | holeHull.AddVertex(IMM); | ||
259 | holeHull.AddVertex(IMP); | ||
260 | holeHull.AddVertex(IPP); | ||
261 | holeHull.AddVertex(IPM); | ||
262 | |||
263 | SimpleHull hollowedHull = SimpleHull.SubtractHull(outerHull, holeHull); | ||
264 | |||
265 | outerHull = hollowedHull; | ||
266 | |||
267 | } | ||
268 | |||
269 | Mesh m = new Mesh(); | ||
270 | |||
271 | Vertex Seed1 = new Vertex(0.0f, -10.0f, 0.0f); | ||
272 | Vertex Seed2 = new Vertex(-10.0f, 10.0f, 0.0f); | ||
273 | Vertex Seed3 = new Vertex(10.0f, 10.0f, 0.0f); | ||
274 | |||
275 | m.Add(Seed1); | ||
276 | m.Add(Seed2); | ||
277 | m.Add(Seed3); | ||
278 | |||
279 | m.Add(new Triangle(Seed1, Seed2, Seed3)); | ||
280 | m.Add(outerHull.getVertices()); | ||
281 | |||
282 | InsertVertices(m.vertices, 3, m.triangles); | ||
283 | m.DumpRaw(baseDir, primName, "Proto first Mesh"); | ||
284 | |||
285 | m.Remove(Seed1); | ||
286 | m.Remove(Seed2); | ||
287 | m.Remove(Seed3); | ||
288 | m.DumpRaw(baseDir, primName, "Proto seeds removed"); | ||
289 | |||
290 | m.RemoveTrianglesOutside(outerHull); | ||
291 | m.DumpRaw(baseDir, primName, "Proto outsides removed"); | ||
292 | |||
293 | foreach (Triangle t in m.triangles) | ||
294 | { | ||
295 | PhysicsVector n = t.getNormal(); | ||
296 | if (n.Z < 0.0) | ||
297 | t.invertNormal(); | ||
298 | } | ||
299 | |||
300 | Extruder extr = new Extruder(); | ||
301 | |||
302 | extr.size = size; | ||
303 | |||
304 | Mesh result = extr.Extrude(m); | ||
305 | result.DumpRaw(baseDir, primName, "Z extruded"); | ||
306 | return result; | ||
307 | } | ||
308 | |||
309 | public static void CalcNormals(Mesh mesh) | ||
310 | { | ||
311 | int iTriangles = mesh.triangles.Count; | ||
312 | |||
313 | mesh.normals = new float[iTriangles*3]; | ||
314 | |||
315 | int i = 0; | ||
316 | foreach (Triangle t in mesh.triangles) | ||
317 | { | ||
318 | float ux, uy, uz; | ||
319 | float vx, vy, vz; | ||
320 | float wx, wy, wz; | ||
321 | |||
322 | ux = t.v1.X; | ||
323 | uy = t.v1.Y; | ||
324 | uz = t.v1.Z; | ||
325 | |||
326 | vx = t.v2.X; | ||
327 | vy = t.v2.Y; | ||
328 | vz = t.v2.Z; | ||
329 | |||
330 | wx = t.v3.X; | ||
331 | wy = t.v3.Y; | ||
332 | wz = t.v3.Z; | ||
333 | |||
334 | |||
335 | // Vectors for edges | ||
336 | float e1x, e1y, e1z; | ||
337 | float e2x, e2y, e2z; | ||
338 | |||
339 | e1x = ux - vx; | ||
340 | e1y = uy - vy; | ||
341 | e1z = uz - vz; | ||
342 | |||
343 | e2x = ux - wx; | ||
344 | e2y = uy - wy; | ||
345 | e2z = uz - wz; | ||
346 | |||
347 | |||
348 | // Cross product for normal | ||
349 | float nx, ny, nz; | ||
350 | nx = e1y*e2z - e1z*e2y; | ||
351 | ny = e1z*e2x - e1x*e2z; | ||
352 | nz = e1x*e2y - e1y*e2x; | ||
353 | |||
354 | // Length | ||
355 | float l = (float) Math.Sqrt(nx*nx + ny*ny + nz*nz); | ||
356 | |||
357 | // Normalized "normal" | ||
358 | nx /= l; | ||
359 | ny /= l; | ||
360 | nz /= l; | ||
361 | |||
362 | mesh.normals[i] = nx; | ||
363 | mesh.normals[i + 1] = ny; | ||
364 | mesh.normals[i + 2] = nz; | ||
365 | |||
366 | i += 3; | ||
367 | } | ||
368 | } | ||
369 | |||
370 | public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, PhysicsVector size) | ||
371 | { | ||
372 | Mesh mesh = null; | ||
373 | |||
374 | switch (primShape.ProfileShape) | ||
375 | { | ||
376 | case ProfileShape.Square: | ||
377 | mesh=CreateBoxMesh(primName, primShape, size); | ||
378 | CalcNormals(mesh); | ||
379 | break; | ||
380 | default: | ||
381 | mesh = CreateBoxMesh(primName, primShape, size); | ||
382 | CalcNormals(mesh); | ||
383 | //Set default mesh to cube otherwise it'll return | ||
384 | // null and crash on the 'setMesh' method in the physics plugins. | ||
385 | //mesh = null; | ||
386 | break; | ||
387 | } | ||
388 | |||
389 | return mesh; | ||
390 | } | ||
391 | } | ||
392 | |||
393 | } | ||