aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/UbitMeshing/Meshmerizer.cs
diff options
context:
space:
mode:
authorUbitUmarov2012-03-17 09:27:56 +0000
committerUbitUmarov2012-03-17 09:27:56 +0000
commit41a0c850f887d72ff64e9e616ea4bda25fd85de7 (patch)
treec0c22d6c5e75c3dfa7147baa56f82bf5d7c496b3 /OpenSim/Region/Physics/UbitMeshing/Meshmerizer.cs
parent some more work on costs (diff)
downloadopensim-SC-41a0c850f887d72ff64e9e616ea4bda25fd85de7.zip
opensim-SC-41a0c850f887d72ff64e9e616ea4bda25fd85de7.tar.gz
opensim-SC-41a0c850f887d72ff64e9e616ea4bda25fd85de7.tar.bz2
opensim-SC-41a0c850f887d72ff64e9e616ea4bda25fd85de7.tar.xz
added a new UbitMeshing module so i can mess it...
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/Physics/UbitMeshing/Meshmerizer.cs762
1 files changed, 762 insertions, 0 deletions
diff --git a/OpenSim/Region/Physics/UbitMeshing/Meshmerizer.cs b/OpenSim/Region/Physics/UbitMeshing/Meshmerizer.cs
new file mode 100644
index 0000000..c9c52c0
--- /dev/null
+++ b/OpenSim/Region/Physics/UbitMeshing/Meshmerizer.cs
@@ -0,0 +1,762 @@
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 OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27//#define SPAM
28
29using System;
30using System.Collections.Generic;
31using OpenSim.Framework;
32using OpenSim.Region.Physics.Manager;
33using OpenMetaverse;
34using OpenMetaverse.StructuredData;
35using System.Drawing;
36using System.Drawing.Imaging;
37using System.IO.Compression;
38using PrimMesher;
39using log4net;
40using Nini.Config;
41using System.Reflection;
42using System.IO;
43using ComponentAce.Compression.Libs.zlib;
44
45namespace OpenSim.Region.Physics.Meshing
46{
47 public class MeshmerizerPlugin : IMeshingPlugin
48 {
49 public MeshmerizerPlugin()
50 {
51 }
52
53 public string GetName()
54 {
55 return "UbitMeshmerizer";
56 }
57
58 public IMesher GetMesher(IConfigSource config)
59 {
60 return new Meshmerizer(config);
61 }
62 }
63
64 public class Meshmerizer : IMesher
65 {
66 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
67
68 // Setting baseDir to a path will enable the dumping of raw files
69 // raw files can be imported by blender so a visual inspection of the results can be done
70#if SPAM
71 const string baseDir = "rawFiles";
72#else
73 private const string baseDir = null; //"rawFiles";
74#endif
75
76 private bool cacheSculptMaps = true;
77 private bool cacheSculptAlphaMaps = true;
78
79 private string decodedSculptMapPath = null;
80 private bool useMeshiesPhysicsMesh = false;
81
82 private float minSizeForComplexMesh = 0.2f; // prims with all dimensions smaller than this will have a bounding box mesh
83
84 private Dictionary<ulong, Mesh> m_uniqueMeshes = new Dictionary<ulong, Mesh>();
85
86 public Meshmerizer(IConfigSource config)
87 {
88 IConfig start_config = config.Configs["Startup"];
89 IConfig mesh_config = config.Configs["Mesh"];
90
91 decodedSculptMapPath = start_config.GetString("DecodedSculptMapPath","j2kDecodeCache");
92
93 cacheSculptMaps = start_config.GetBoolean("CacheSculptMaps", cacheSculptMaps);
94
95 if (Environment.OSVersion.Platform == PlatformID.Unix)
96 {
97 cacheSculptAlphaMaps = false;
98 }
99 else
100 cacheSculptAlphaMaps = cacheSculptMaps;
101
102 if(mesh_config != null)
103 useMeshiesPhysicsMesh = mesh_config.GetBoolean("UseMeshiesPhysicsMesh", useMeshiesPhysicsMesh);
104
105 try
106 {
107 if (!Directory.Exists(decodedSculptMapPath))
108 Directory.CreateDirectory(decodedSculptMapPath);
109 }
110 catch (Exception e)
111 {
112 m_log.WarnFormat("[SCULPT]: Unable to create {0} directory: ", decodedSculptMapPath, e.Message);
113 }
114 }
115
116 /// <summary>
117 /// creates a simple box mesh of the specified size. This mesh is of very low vertex count and may
118 /// be useful as a backup proxy when level of detail is not needed or when more complex meshes fail
119 /// for some reason
120 /// </summary>
121 /// <param name="minX"></param>
122 /// <param name="maxX"></param>
123 /// <param name="minY"></param>
124 /// <param name="maxY"></param>
125 /// <param name="minZ"></param>
126 /// <param name="maxZ"></param>
127 /// <returns></returns>
128 private static Mesh CreateSimpleBoxMesh(float minX, float maxX, float minY, float maxY, float minZ, float maxZ)
129 {
130 Mesh box = new Mesh();
131 List<Vertex> vertices = new List<Vertex>();
132 // bottom
133
134 vertices.Add(new Vertex(minX, maxY, minZ));
135 vertices.Add(new Vertex(maxX, maxY, minZ));
136 vertices.Add(new Vertex(maxX, minY, minZ));
137 vertices.Add(new Vertex(minX, minY, minZ));
138
139 box.Add(new Triangle(vertices[0], vertices[1], vertices[2]));
140 box.Add(new Triangle(vertices[0], vertices[2], vertices[3]));
141
142 // top
143
144 vertices.Add(new Vertex(maxX, maxY, maxZ));
145 vertices.Add(new Vertex(minX, maxY, maxZ));
146 vertices.Add(new Vertex(minX, minY, maxZ));
147 vertices.Add(new Vertex(maxX, minY, maxZ));
148
149 box.Add(new Triangle(vertices[4], vertices[5], vertices[6]));
150 box.Add(new Triangle(vertices[4], vertices[6], vertices[7]));
151
152 // sides
153
154 box.Add(new Triangle(vertices[5], vertices[0], vertices[3]));
155 box.Add(new Triangle(vertices[5], vertices[3], vertices[6]));
156
157 box.Add(new Triangle(vertices[1], vertices[0], vertices[5]));
158 box.Add(new Triangle(vertices[1], vertices[5], vertices[4]));
159
160 box.Add(new Triangle(vertices[7], vertices[1], vertices[4]));
161 box.Add(new Triangle(vertices[7], vertices[2], vertices[1]));
162
163 box.Add(new Triangle(vertices[3], vertices[2], vertices[7]));
164 box.Add(new Triangle(vertices[3], vertices[7], vertices[6]));
165
166 return box;
167 }
168
169 /// <summary>
170 /// Creates a simple bounding box mesh for a complex input mesh
171 /// </summary>
172 /// <param name="meshIn"></param>
173 /// <returns></returns>
174 private static Mesh CreateBoundingBoxMesh(Mesh meshIn)
175 {
176 float minX = float.MaxValue;
177 float maxX = float.MinValue;
178 float minY = float.MaxValue;
179 float maxY = float.MinValue;
180 float minZ = float.MaxValue;
181 float maxZ = float.MinValue;
182
183 foreach (Vector3 v in meshIn.getVertexList())
184 {
185 if (v.X < minX) minX = v.X;
186 if (v.Y < minY) minY = v.Y;
187 if (v.Z < minZ) minZ = v.Z;
188
189 if (v.X > maxX) maxX = v.X;
190 if (v.Y > maxY) maxY = v.Y;
191 if (v.Z > maxZ) maxZ = v.Z;
192 }
193
194 return CreateSimpleBoxMesh(minX, maxX, minY, maxY, minZ, maxZ);
195 }
196
197 private void ReportPrimError(string message, string primName, PrimMesh primMesh)
198 {
199 m_log.Error(message);
200 m_log.Error("\nPrim Name: " + primName);
201 m_log.Error("****** PrimMesh Parameters ******\n" + primMesh.ParamsToDisplayString());
202 }
203
204 /// <summary>
205 /// Add a submesh to an existing list of coords and faces.
206 /// </summary>
207 /// <param name="subMeshData"></param>
208 /// <param name="size">Size of entire object</param>
209 /// <param name="coords"></param>
210 /// <param name="faces"></param>
211 private void AddSubMesh(OSDMap subMeshData, Vector3 size, List<Coord> coords, List<Face> faces)
212 {
213 // Console.WriteLine("subMeshMap for {0} - {1}", primName, Util.GetFormattedXml((OSD)subMeshMap));
214
215 // As per http://wiki.secondlife.com/wiki/Mesh/Mesh_Asset_Format, some Mesh Level
216 // of Detail Blocks (maps) contain just a NoGeometry key to signal there is no
217 // geometry for this submesh.
218 if (subMeshData.ContainsKey("NoGeometry") && ((OSDBoolean)subMeshData["NoGeometry"]))
219 return;
220
221 OpenMetaverse.Vector3 posMax = ((OSDMap)subMeshData["PositionDomain"])["Max"].AsVector3();
222 OpenMetaverse.Vector3 posMin = ((OSDMap)subMeshData["PositionDomain"])["Min"].AsVector3();
223 ushort faceIndexOffset = (ushort)coords.Count;
224
225 byte[] posBytes = subMeshData["Position"].AsBinary();
226 for (int i = 0; i < posBytes.Length; i += 6)
227 {
228 ushort uX = Utils.BytesToUInt16(posBytes, i);
229 ushort uY = Utils.BytesToUInt16(posBytes, i + 2);
230 ushort uZ = Utils.BytesToUInt16(posBytes, i + 4);
231
232 Coord c = new Coord(
233 Utils.UInt16ToFloat(uX, posMin.X, posMax.X) * size.X,
234 Utils.UInt16ToFloat(uY, posMin.Y, posMax.Y) * size.Y,
235 Utils.UInt16ToFloat(uZ, posMin.Z, posMax.Z) * size.Z);
236
237 coords.Add(c);
238 }
239
240 byte[] triangleBytes = subMeshData["TriangleList"].AsBinary();
241 for (int i = 0; i < triangleBytes.Length; i += 6)
242 {
243 ushort v1 = (ushort)(Utils.BytesToUInt16(triangleBytes, i) + faceIndexOffset);
244 ushort v2 = (ushort)(Utils.BytesToUInt16(triangleBytes, i + 2) + faceIndexOffset);
245 ushort v3 = (ushort)(Utils.BytesToUInt16(triangleBytes, i + 4) + faceIndexOffset);
246 Face f = new Face(v1, v2, v3);
247 faces.Add(f);
248 }
249 }
250
251 /// <summary>
252 /// Create a physics mesh from data that comes with the prim. The actual data used depends on the prim type.
253 /// </summary>
254 /// <param name="primName"></param>
255 /// <param name="primShape"></param>
256 /// <param name="size"></param>
257 /// <param name="lod"></param>
258 /// <returns></returns>
259 private Mesh CreateMeshFromPrimMesher(string primName, PrimitiveBaseShape primShape, Vector3 size, float lod)
260 {
261// m_log.DebugFormat(
262// "[MESH]: Creating physics proxy for {0}, shape {1}",
263// primName, (OpenMetaverse.SculptType)primShape.SculptType);
264
265 List<Coord> coords;
266 List<Face> faces;
267
268 if (primShape.SculptEntry)
269 {
270 if (((OpenMetaverse.SculptType)primShape.SculptType) == SculptType.Mesh)
271 {
272 if (!useMeshiesPhysicsMesh)
273 return null;
274
275 if (!GenerateCoordsAndFacesFromPrimMeshData(primName, primShape, size, out coords, out faces))
276 return null;
277 }
278 else
279 {
280 if (!GenerateCoordsAndFacesFromPrimSculptData(primName, primShape, size, lod, out coords, out faces))
281 return null;
282 // Remove the reference to any JPEG2000 sculpt data so it can be GCed
283 // don't loose it
284 // primShape.SculptData = Utils.EmptyBytes;
285 }
286// primShape.SculptDataLoaded = true;
287 }
288 else
289 {
290 if (!GenerateCoordsAndFacesFromPrimShapeData(primName, primShape, size, lod, out coords, out faces))
291 return null;
292 }
293 // keep compatible
294 primShape.SculptData = Utils.EmptyBytes;
295
296 int numCoords = coords.Count;
297 int numFaces = faces.Count;
298
299 // Create the list of vertices
300 List<Vertex> vertices = new List<Vertex>();
301 for (int i = 0; i < numCoords; i++)
302 {
303 Coord c = coords[i];
304 vertices.Add(new Vertex(c.X, c.Y, c.Z));
305 }
306
307 Mesh mesh = new Mesh();
308 // Add the corresponding triangles to the mesh
309 for (int i = 0; i < numFaces; i++)
310 {
311 Face f = faces[i];
312 mesh.Add(new Triangle(vertices[f.v1], vertices[f.v2], vertices[f.v3]));
313 }
314
315 return mesh;
316 }
317
318 /// <summary>
319 /// Generate the co-ords and faces necessary to construct a mesh from the mesh data the accompanies a prim.
320 /// </summary>
321 /// <param name="primName"></param>
322 /// <param name="primShape"></param>
323 /// <param name="size"></param>
324 /// <param name="coords">Coords are added to this list by the method.</param>
325 /// <param name="faces">Faces are added to this list by the method.</param>
326 /// <returns>true if coords and faces were successfully generated, false if not</returns>
327 private bool GenerateCoordsAndFacesFromPrimMeshData(
328 string primName, PrimitiveBaseShape primShape, Vector3 size, out List<Coord> coords, out List<Face> faces)
329 {
330// m_log.DebugFormat("[MESH]: experimental mesh proxy generation for {0}", primName);
331
332 coords = new List<Coord>();
333 faces = new List<Face>();
334 OSD meshOsd = null;
335
336 if (primShape.SculptData.Length <= 0)
337 {
338 m_log.ErrorFormat("[MESH]: asset data for {0} is zero length", primName);
339 return false;
340 }
341
342 long start = 0;
343 using (MemoryStream data = new MemoryStream(primShape.SculptData))
344 {
345 try
346 {
347 OSD osd = OSDParser.DeserializeLLSDBinary(data);
348 if (osd is OSDMap)
349 meshOsd = (OSDMap)osd;
350 else
351 {
352 m_log.Warn("[Mesh}: unable to cast mesh asset to OSDMap");
353 return false;
354 }
355 }
356 catch (Exception e)
357 {
358 m_log.Error("[MESH]: Exception deserializing mesh asset header:" + e.ToString());
359 }
360
361 start = data.Position;
362 }
363
364 if (meshOsd is OSDMap)
365 {
366 OSDMap physicsParms = null;
367 OSDMap map = (OSDMap)meshOsd;
368 if (map.ContainsKey("physics_shape"))
369 physicsParms = (OSDMap)map["physics_shape"]; // old asset format
370 else if (map.ContainsKey("physics_mesh"))
371 physicsParms = (OSDMap)map["physics_mesh"]; // new asset format
372
373 if (physicsParms == null)
374 {
375 m_log.Warn("[MESH]: no recognized physics mesh found in mesh asset");
376 return false;
377 }
378
379 int physOffset = physicsParms["offset"].AsInteger() + (int)start;
380 int physSize = physicsParms["size"].AsInteger();
381
382 if (physOffset < 0 || physSize == 0)
383 return false; // no mesh data in asset
384
385 OSD decodedMeshOsd = new OSD();
386 byte[] meshBytes = new byte[physSize];
387 System.Buffer.BlockCopy(primShape.SculptData, physOffset, meshBytes, 0, physSize);
388// byte[] decompressed = new byte[physSize * 5];
389 try
390 {
391 using (MemoryStream inMs = new MemoryStream(meshBytes))
392 {
393 using (MemoryStream outMs = new MemoryStream())
394 {
395 using (ZOutputStream zOut = new ZOutputStream(outMs))
396 {
397 byte[] readBuffer = new byte[2048];
398 int readLen = 0;
399 while ((readLen = inMs.Read(readBuffer, 0, readBuffer.Length)) > 0)
400 {
401 zOut.Write(readBuffer, 0, readLen);
402 }
403 zOut.Flush();
404 outMs.Seek(0, SeekOrigin.Begin);
405
406 byte[] decompressedBuf = outMs.GetBuffer();
407
408 decodedMeshOsd = OSDParser.DeserializeLLSDBinary(decompressedBuf);
409 }
410 }
411 }
412 }
413 catch (Exception e)
414 {
415 m_log.Error("[MESH]: exception decoding physical mesh: " + e.ToString());
416 return false;
417 }
418
419 OSDArray decodedMeshOsdArray = null;
420
421 // physics_shape is an array of OSDMaps, one for each submesh
422 if (decodedMeshOsd is OSDArray)
423 {
424// Console.WriteLine("decodedMeshOsd for {0} - {1}", primName, Util.GetFormattedXml(decodedMeshOsd));
425
426 decodedMeshOsdArray = (OSDArray)decodedMeshOsd;
427 foreach (OSD subMeshOsd in decodedMeshOsdArray)
428 {
429 if (subMeshOsd is OSDMap)
430 AddSubMesh(subMeshOsd as OSDMap, size, coords, faces);
431 }
432 }
433 }
434
435 return true;
436 }
437
438 /// <summary>
439 /// Generate the co-ords and faces necessary to construct a mesh from the sculpt data the accompanies a prim.
440 /// </summary>
441 /// <param name="primName"></param>
442 /// <param name="primShape"></param>
443 /// <param name="size"></param>
444 /// <param name="lod"></param>
445 /// <param name="coords">Coords are added to this list by the method.</param>
446 /// <param name="faces">Faces are added to this list by the method.</param>
447 /// <returns>true if coords and faces were successfully generated, false if not</returns>
448 private bool GenerateCoordsAndFacesFromPrimSculptData(
449 string primName, PrimitiveBaseShape primShape, Vector3 size, float lod, out List<Coord> coords, out List<Face> faces)
450 {
451 coords = new List<Coord>();
452 faces = new List<Face>();
453 PrimMesher.SculptMesh sculptMesh;
454 Image idata = null;
455 string decodedSculptFileName = "";
456
457 if (cacheSculptMaps && primShape.SculptTexture != UUID.Zero)
458 {
459 decodedSculptFileName = System.IO.Path.Combine(decodedSculptMapPath, "smap_" + primShape.SculptTexture.ToString());
460 try
461 {
462 if (File.Exists(decodedSculptFileName))
463 {
464 idata = Image.FromFile(decodedSculptFileName);
465 }
466 }
467 catch (Exception e)
468 {
469 m_log.Error("[SCULPT]: unable to load cached sculpt map " + decodedSculptFileName + " " + e.Message);
470
471 }
472 //if (idata != null)
473 // m_log.Debug("[SCULPT]: loaded cached map asset for map ID: " + primShape.SculptTexture.ToString());
474 }
475
476 if (idata == null)
477 {
478 if (primShape.SculptData == null || primShape.SculptData.Length == 0)
479 return false;
480
481 try
482 {
483 OpenMetaverse.Imaging.ManagedImage unusedData;
484 OpenMetaverse.Imaging.OpenJPEG.DecodeToImage(primShape.SculptData, out unusedData, out idata);
485
486 if (idata == null)
487 {
488 // In some cases it seems that the decode can return a null bitmap without throwing
489 // an exception
490 m_log.WarnFormat("[PHYSICS]: OpenJPEG decoded sculpt data for {0} to a null bitmap. Ignoring.", primName);
491
492 return false;
493 }
494
495 unusedData = null;
496
497 //idata = CSJ2K.J2kImage.FromBytes(primShape.SculptData);
498
499 if (cacheSculptMaps && (cacheSculptAlphaMaps || (((ImageFlags)(idata.Flags) & ImageFlags.HasAlpha) ==0)))
500 // don't cache images with alpha channel in linux since mono can't load them correctly)
501 {
502 try { idata.Save(decodedSculptFileName, ImageFormat.MemoryBmp); }
503 catch (Exception e) { m_log.Error("[SCULPT]: unable to cache sculpt map " + decodedSculptFileName + " " + e.Message); }
504 }
505 }
506 catch (DllNotFoundException)
507 {
508 m_log.Error("[PHYSICS]: OpenJpeg is not installed correctly on this system. Physics Proxy generation failed. Often times this is because of an old version of GLIBC. You must have version 2.4 or above!");
509 return false;
510 }
511 catch (IndexOutOfRangeException)
512 {
513 m_log.Error("[PHYSICS]: OpenJpeg was unable to decode this. Physics Proxy generation failed");
514 return false;
515 }
516 catch (Exception ex)
517 {
518 m_log.Error("[PHYSICS]: Unable to generate a Sculpty physics proxy. Sculpty texture decode failed: " + ex.Message);
519 return false;
520 }
521 }
522
523 PrimMesher.SculptMesh.SculptType sculptType;
524 switch ((OpenMetaverse.SculptType)primShape.SculptType)
525 {
526 case OpenMetaverse.SculptType.Cylinder:
527 sculptType = PrimMesher.SculptMesh.SculptType.cylinder;
528 break;
529 case OpenMetaverse.SculptType.Plane:
530 sculptType = PrimMesher.SculptMesh.SculptType.plane;
531 break;
532 case OpenMetaverse.SculptType.Torus:
533 sculptType = PrimMesher.SculptMesh.SculptType.torus;
534 break;
535 case OpenMetaverse.SculptType.Sphere:
536 sculptType = PrimMesher.SculptMesh.SculptType.sphere;
537 break;
538 default:
539 sculptType = PrimMesher.SculptMesh.SculptType.plane;
540 break;
541 }
542
543 bool mirror = ((primShape.SculptType & 128) != 0);
544 bool invert = ((primShape.SculptType & 64) != 0);
545
546 sculptMesh = new PrimMesher.SculptMesh((Bitmap)idata, sculptType, (int)lod, false, mirror, invert);
547
548 idata.Dispose();
549
550 sculptMesh.DumpRaw(baseDir, primName, "primMesh");
551
552 sculptMesh.Scale(size.X, size.Y, size.Z);
553
554 coords = sculptMesh.coords;
555 faces = sculptMesh.faces;
556
557 return true;
558 }
559
560 /// <summary>
561 /// Generate the co-ords and faces necessary to construct a mesh from the shape data the accompanies a prim.
562 /// </summary>
563 /// <param name="primName"></param>
564 /// <param name="primShape"></param>
565 /// <param name="size"></param>
566 /// <param name="coords">Coords are added to this list by the method.</param>
567 /// <param name="faces">Faces are added to this list by the method.</param>
568 /// <returns>true if coords and faces were successfully generated, false if not</returns>
569 private bool GenerateCoordsAndFacesFromPrimShapeData(
570 string primName, PrimitiveBaseShape primShape, Vector3 size, float lod, out List<Coord> coords, out List<Face> faces)
571 {
572 PrimMesh primMesh;
573 coords = new List<Coord>();
574 faces = new List<Face>();
575
576 float pathShearX = primShape.PathShearX < 128 ? (float)primShape.PathShearX * 0.01f : (float)(primShape.PathShearX - 256) * 0.01f;
577 float pathShearY = primShape.PathShearY < 128 ? (float)primShape.PathShearY * 0.01f : (float)(primShape.PathShearY - 256) * 0.01f;
578 float pathBegin = (float)primShape.PathBegin * 2.0e-5f;
579 float pathEnd = 1.0f - (float)primShape.PathEnd * 2.0e-5f;
580 float pathScaleX = (float)(primShape.PathScaleX - 100) * 0.01f;
581 float pathScaleY = (float)(primShape.PathScaleY - 100) * 0.01f;
582
583 float profileBegin = (float)primShape.ProfileBegin * 2.0e-5f;
584 float profileEnd = 1.0f - (float)primShape.ProfileEnd * 2.0e-5f;
585 float profileHollow = (float)primShape.ProfileHollow * 2.0e-5f;
586 if (profileHollow > 0.95f)
587 profileHollow = 0.95f;
588
589 int sides = 4;
590 LevelOfDetail iLOD = (LevelOfDetail)lod;
591 if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle)
592 sides = 3;
593 else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.Circle)
594 {
595 switch (iLOD)
596 {
597 case LevelOfDetail.High: sides = 24; break;
598 case LevelOfDetail.Medium: sides = 12; break;
599 case LevelOfDetail.Low: sides = 6; break;
600 case LevelOfDetail.VeryLow: sides = 3; break;
601 default: sides = 24; break;
602 }
603 }
604 else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle)
605 { // half circle, prim is a sphere
606 switch (iLOD)
607 {
608 case LevelOfDetail.High: sides = 24; break;
609 case LevelOfDetail.Medium: sides = 12; break;
610 case LevelOfDetail.Low: sides = 6; break;
611 case LevelOfDetail.VeryLow: sides = 3; break;
612 default: sides = 24; break;
613 }
614
615 profileBegin = 0.5f * profileBegin + 0.5f;
616 profileEnd = 0.5f * profileEnd + 0.5f;
617 }
618
619 int hollowSides = sides;
620 if (primShape.HollowShape == HollowShape.Circle)
621 {
622 switch (iLOD)
623 {
624 case LevelOfDetail.High: hollowSides = 24; break;
625 case LevelOfDetail.Medium: hollowSides = 12; break;
626 case LevelOfDetail.Low: hollowSides = 6; break;
627 case LevelOfDetail.VeryLow: hollowSides = 3; break;
628 default: hollowSides = 24; break;
629 }
630 }
631 else if (primShape.HollowShape == HollowShape.Square)
632 hollowSides = 4;
633 else if (primShape.HollowShape == HollowShape.Triangle)
634 hollowSides = 3;
635
636 primMesh = new PrimMesh(sides, profileBegin, profileEnd, profileHollow, hollowSides);
637
638 if (primMesh.errorMessage != null)
639 if (primMesh.errorMessage.Length > 0)
640 m_log.Error("[ERROR] " + primMesh.errorMessage);
641
642 primMesh.topShearX = pathShearX;
643 primMesh.topShearY = pathShearY;
644 primMesh.pathCutBegin = pathBegin;
645 primMesh.pathCutEnd = pathEnd;
646
647 if (primShape.PathCurve == (byte)Extrusion.Straight || primShape.PathCurve == (byte) Extrusion.Flexible)
648 {
649 primMesh.twistBegin = primShape.PathTwistBegin * 18 / 10;
650 primMesh.twistEnd = primShape.PathTwist * 18 / 10;
651 primMesh.taperX = pathScaleX;
652 primMesh.taperY = pathScaleY;
653
654 if (profileBegin < 0.0f || profileBegin >= profileEnd || profileEnd > 1.0f)
655 {
656 ReportPrimError("*** CORRUPT PRIM!! ***", primName, primMesh);
657 if (profileBegin < 0.0f) profileBegin = 0.0f;
658 if (profileEnd > 1.0f) profileEnd = 1.0f;
659 }
660#if SPAM
661 m_log.Debug("****** PrimMesh Parameters (Linear) ******\n" + primMesh.ParamsToDisplayString());
662#endif
663 try
664 {
665 primMesh.ExtrudeLinear();
666 }
667 catch (Exception ex)
668 {
669 ReportPrimError("Extrusion failure: exception: " + ex.ToString(), primName, primMesh);
670 return false;
671 }
672 }
673 else
674 {
675 primMesh.holeSizeX = (200 - primShape.PathScaleX) * 0.01f;
676 primMesh.holeSizeY = (200 - primShape.PathScaleY) * 0.01f;
677 primMesh.radius = 0.01f * primShape.PathRadiusOffset;
678 primMesh.revolutions = 1.0f + 0.015f * primShape.PathRevolutions;
679 primMesh.skew = 0.01f * primShape.PathSkew;
680 primMesh.twistBegin = primShape.PathTwistBegin * 36 / 10;
681 primMesh.twistEnd = primShape.PathTwist * 36 / 10;
682 primMesh.taperX = primShape.PathTaperX * 0.01f;
683 primMesh.taperY = primShape.PathTaperY * 0.01f;
684
685 if (profileBegin < 0.0f || profileBegin >= profileEnd || profileEnd > 1.0f)
686 {
687 ReportPrimError("*** CORRUPT PRIM!! ***", primName, primMesh);
688 if (profileBegin < 0.0f) profileBegin = 0.0f;
689 if (profileEnd > 1.0f) profileEnd = 1.0f;
690 }
691#if SPAM
692 m_log.Debug("****** PrimMesh Parameters (Circular) ******\n" + primMesh.ParamsToDisplayString());
693#endif
694 try
695 {
696 primMesh.ExtrudeCircular();
697 }
698 catch (Exception ex)
699 {
700 ReportPrimError("Extrusion failure: exception: " + ex.ToString(), primName, primMesh);
701 return false;
702 }
703 }
704
705 primMesh.DumpRaw(baseDir, primName, "primMesh");
706
707 primMesh.Scale(size.X, size.Y, size.Z);
708
709 coords = primMesh.coords;
710 faces = primMesh.faces;
711
712 return true;
713 }
714
715 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod)
716 {
717 return CreateMesh(primName, primShape, size, lod, false);
718 }
719
720 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical)
721 {
722#if SPAM
723 m_log.DebugFormat("[MESH]: Creating mesh for {0}", primName);
724#endif
725
726 Mesh mesh = null;
727 ulong key = 0;
728
729 // If this mesh has been created already, return it instead of creating another copy
730 // For large regions with 100k+ prims and hundreds of copies of each, this can save a GB or more of memory
731 key = primShape.GetMeshKey(size, lod);
732 if (m_uniqueMeshes.TryGetValue(key, out mesh))
733 return mesh;
734
735 if (size.X < 0.01f) size.X = 0.01f;
736 if (size.Y < 0.01f) size.Y = 0.01f;
737 if (size.Z < 0.01f) size.Z = 0.01f;
738
739 mesh = CreateMeshFromPrimMesher(primName, primShape, size, lod);
740
741 if (mesh != null)
742 {
743 if ((!isPhysical) && size.X < minSizeForComplexMesh && size.Y < minSizeForComplexMesh && size.Z < minSizeForComplexMesh)
744 {
745#if SPAM
746 m_log.Debug("Meshmerizer: prim " + primName + " has a size of " + size.ToString() + " which is below threshold of " +
747 minSizeForComplexMesh.ToString() + " - creating simple bounding box");
748#endif
749 mesh = CreateBoundingBoxMesh(mesh);
750 mesh.DumpRaw(baseDir, primName, "Z extruded");
751 }
752
753 // trim the vertex and triangle lists to free up memory
754 mesh.TrimExcess();
755
756 m_uniqueMeshes.Add(key, mesh);
757 }
758
759 return mesh;
760 }
761 }
762}