opensim-development – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 eva 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  
29 using System;
30 using System.Collections.Generic;
31 using OpenSim.Framework;
32 using OpenSim.Region.Physics.Manager;
33 using OpenMetaverse;
34 using OpenMetaverse.StructuredData;
35 using System.Drawing;
36 using System.Drawing.Imaging;
37 using System.IO.Compression;
38 using PrimMesher;
39 using log4net;
40 using Nini.Config;
41 using System.Reflection;
42 using System.IO;
43  
44 namespace OpenSim.Region.Physics.Meshing
45 {
46 public class MeshmerizerPlugin : IMeshingPlugin
47 {
48 public MeshmerizerPlugin()
49 {
50 }
51  
52 public string GetName()
53 {
54 return "Meshmerizer";
55 }
56  
57 public IMesher GetMesher(IConfigSource config)
58 {
59 return new Meshmerizer(config);
60 }
61 }
62  
63 public class Meshmerizer : IMesher
64 {
65 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
66 private static string LogHeader = "[MESH]";
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 // If 'true', lots of DEBUG logging of asset parsing details
76 private bool debugDetail = false;
77  
78 private bool cacheSculptMaps = true;
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 List<List<Vector3>> mConvexHulls = null;
85 private List<Vector3> mBoundingHull = null;
86  
87 private Dictionary<ulong, Mesh> m_uniqueMeshes = new Dictionary<ulong, Mesh>();
88  
89 public Meshmerizer(IConfigSource config)
90 {
91 IConfig start_config = config.Configs["Startup"];
92 IConfig mesh_config = config.Configs["Mesh"];
93  
94 decodedSculptMapPath = start_config.GetString("DecodedSculptMapPath","j2kDecodeCache");
95 cacheSculptMaps = start_config.GetBoolean("CacheSculptMaps", cacheSculptMaps);
96 if (mesh_config != null)
97 {
98 useMeshiesPhysicsMesh = mesh_config.GetBoolean("UseMeshiesPhysicsMesh", useMeshiesPhysicsMesh);
99 debugDetail = mesh_config.GetBoolean("LogMeshDetails", debugDetail);
100 }
101  
102 try
103 {
104 if (!Directory.Exists(decodedSculptMapPath))
105 Directory.CreateDirectory(decodedSculptMapPath);
106 }
107 catch (Exception e)
108 {
109 m_log.WarnFormat("[SCULPT]: Unable to create {0} directory: ", decodedSculptMapPath, e.Message);
110 }
111 }
112  
113 /// <summary>
114 /// creates a simple box mesh of the specified size. This mesh is of very low vertex count and may
115 /// be useful as a backup proxy when level of detail is not needed or when more complex meshes fail
116 /// for some reason
117 /// </summary>
118 /// <param name="minX"></param>
119 /// <param name="maxX"></param>
120 /// <param name="minY"></param>
121 /// <param name="maxY"></param>
122 /// <param name="minZ"></param>
123 /// <param name="maxZ"></param>
124 /// <returns></returns>
125 private static Mesh CreateSimpleBoxMesh(float minX, float maxX, float minY, float maxY, float minZ, float maxZ)
126 {
127 Mesh box = new Mesh();
128 List<Vertex> vertices = new List<Vertex>();
129 // bottom
130  
131 vertices.Add(new Vertex(minX, maxY, minZ));
132 vertices.Add(new Vertex(maxX, maxY, minZ));
133 vertices.Add(new Vertex(maxX, minY, minZ));
134 vertices.Add(new Vertex(minX, minY, minZ));
135  
136 box.Add(new Triangle(vertices[0], vertices[1], vertices[2]));
137 box.Add(new Triangle(vertices[0], vertices[2], vertices[3]));
138  
139 // top
140  
141 vertices.Add(new Vertex(maxX, maxY, maxZ));
142 vertices.Add(new Vertex(minX, maxY, maxZ));
143 vertices.Add(new Vertex(minX, minY, maxZ));
144 vertices.Add(new Vertex(maxX, minY, maxZ));
145  
146 box.Add(new Triangle(vertices[4], vertices[5], vertices[6]));
147 box.Add(new Triangle(vertices[4], vertices[6], vertices[7]));
148  
149 // sides
150  
151 box.Add(new Triangle(vertices[5], vertices[0], vertices[3]));
152 box.Add(new Triangle(vertices[5], vertices[3], vertices[6]));
153  
154 box.Add(new Triangle(vertices[1], vertices[0], vertices[5]));
155 box.Add(new Triangle(vertices[1], vertices[5], vertices[4]));
156  
157 box.Add(new Triangle(vertices[7], vertices[1], vertices[4]));
158 box.Add(new Triangle(vertices[7], vertices[2], vertices[1]));
159  
160 box.Add(new Triangle(vertices[3], vertices[2], vertices[7]));
161 box.Add(new Triangle(vertices[3], vertices[7], vertices[6]));
162  
163 return box;
164 }
165  
166 /// <summary>
167 /// Creates a simple bounding box mesh for a complex input mesh
168 /// </summary>
169 /// <param name="meshIn"></param>
170 /// <returns></returns>
171 private static Mesh CreateBoundingBoxMesh(Mesh meshIn)
172 {
173 float minX = float.MaxValue;
174 float maxX = float.MinValue;
175 float minY = float.MaxValue;
176 float maxY = float.MinValue;
177 float minZ = float.MaxValue;
178 float maxZ = float.MinValue;
179  
180 foreach (Vector3 v in meshIn.getVertexList())
181 {
182 if (v.X < minX) minX = v.X;
183 if (v.Y < minY) minY = v.Y;
184 if (v.Z < minZ) minZ = v.Z;
185  
186 if (v.X > maxX) maxX = v.X;
187 if (v.Y > maxY) maxY = v.Y;
188 if (v.Z > maxZ) maxZ = v.Z;
189 }
190  
191 return CreateSimpleBoxMesh(minX, maxX, minY, maxY, minZ, maxZ);
192 }
193  
194 private void ReportPrimError(string message, string primName, PrimMesh primMesh)
195 {
196 m_log.Error(message);
197 m_log.Error("\nPrim Name: " + primName);
198 m_log.Error("****** PrimMesh Parameters ******\n" + primMesh.ParamsToDisplayString());
199 }
200  
201 /// <summary>
202 /// Add a submesh to an existing list of coords and faces.
203 /// </summary>
204 /// <param name="subMeshData"></param>
205 /// <param name="size">Size of entire object</param>
206 /// <param name="coords"></param>
207 /// <param name="faces"></param>
208 private void AddSubMesh(OSDMap subMeshData, Vector3 size, List<Coord> coords, List<Face> faces)
209 {
210 // Console.WriteLine("subMeshMap for {0} - {1}", primName, Util.GetFormattedXml((OSD)subMeshMap));
211  
212 // As per http://wiki.secondlife.com/wiki/Mesh/Mesh_Asset_Format, some Mesh Level
213 // of Detail Blocks (maps) contain just a NoGeometry key to signal there is no
214 // geometry for this submesh.
215 if (subMeshData.ContainsKey("NoGeometry") && ((OSDBoolean)subMeshData["NoGeometry"]))
216 return;
217  
218 OpenMetaverse.Vector3 posMax = ((OSDMap)subMeshData["PositionDomain"])["Max"].AsVector3();
219 OpenMetaverse.Vector3 posMin = ((OSDMap)subMeshData["PositionDomain"])["Min"].AsVector3();
220 ushort faceIndexOffset = (ushort)coords.Count;
221  
222 byte[] posBytes = subMeshData["Position"].AsBinary();
223 for (int i = 0; i < posBytes.Length; i += 6)
224 {
225 ushort uX = Utils.BytesToUInt16(posBytes, i);
226 ushort uY = Utils.BytesToUInt16(posBytes, i + 2);
227 ushort uZ = Utils.BytesToUInt16(posBytes, i + 4);
228  
229 Coord c = new Coord(
230 Utils.UInt16ToFloat(uX, posMin.X, posMax.X) * size.X,
231 Utils.UInt16ToFloat(uY, posMin.Y, posMax.Y) * size.Y,
232 Utils.UInt16ToFloat(uZ, posMin.Z, posMax.Z) * size.Z);
233  
234 coords.Add(c);
235 }
236  
237 byte[] triangleBytes = subMeshData["TriangleList"].AsBinary();
238 for (int i = 0; i < triangleBytes.Length; i += 6)
239 {
240 ushort v1 = (ushort)(Utils.BytesToUInt16(triangleBytes, i) + faceIndexOffset);
241 ushort v2 = (ushort)(Utils.BytesToUInt16(triangleBytes, i + 2) + faceIndexOffset);
242 ushort v3 = (ushort)(Utils.BytesToUInt16(triangleBytes, i + 4) + faceIndexOffset);
243 Face f = new Face(v1, v2, v3);
244 faces.Add(f);
245 }
246 }
247  
248 /// <summary>
249 /// Create a physics mesh from data that comes with the prim. The actual data used depends on the prim type.
250 /// </summary>
251 /// <param name="primName"></param>
252 /// <param name="primShape"></param>
253 /// <param name="size"></param>
254 /// <param name="lod"></param>
255 /// <returns></returns>
256 private Mesh CreateMeshFromPrimMesher(string primName, PrimitiveBaseShape primShape, Vector3 size, float lod)
257 {
258 // m_log.DebugFormat(
259 // "[MESH]: Creating physics proxy for {0}, shape {1}",
260 // primName, (OpenMetaverse.SculptType)primShape.SculptType);
261  
262 List<Coord> coords;
263 List<Face> faces;
264  
265 if (primShape.SculptEntry)
266 {
267 if (((OpenMetaverse.SculptType)primShape.SculptType) == SculptType.Mesh)
268 {
269 if (!useMeshiesPhysicsMesh)
270 return null;
271  
272 if (!GenerateCoordsAndFacesFromPrimMeshData(primName, primShape, size, out coords, out faces))
273 return null;
274 }
275 else
276 {
277 if (!GenerateCoordsAndFacesFromPrimSculptData(primName, primShape, size, lod, out coords, out faces))
278 return null;
279 }
280 }
281 else
282 {
283 if (!GenerateCoordsAndFacesFromPrimShapeData(primName, primShape, size, lod, out coords, out faces))
284 return null;
285 }
286  
287 // Remove the reference to any JPEG2000 sculpt data so it can be GCed
288 primShape.SculptData = Utils.EmptyBytes;
289  
290 int numCoords = coords.Count;
291 int numFaces = faces.Count;
292  
293 // Create the list of vertices
294 List<Vertex> vertices = new List<Vertex>();
295 for (int i = 0; i < numCoords; i++)
296 {
297 Coord c = coords[i];
298 vertices.Add(new Vertex(c.X, c.Y, c.Z));
299 }
300  
301 Mesh mesh = new Mesh();
302 // Add the corresponding triangles to the mesh
303 for (int i = 0; i < numFaces; i++)
304 {
305 Face f = faces[i];
306 mesh.Add(new Triangle(vertices[f.v1], vertices[f.v2], vertices[f.v3]));
307 }
308  
309 return mesh;
310 }
311  
312 /// <summary>
313 /// Generate the co-ords and faces necessary to construct a mesh from the mesh data the accompanies a prim.
314 /// </summary>
315 /// <param name="primName"></param>
316 /// <param name="primShape"></param>
317 /// <param name="size"></param>
318 /// <param name="coords">Coords are added to this list by the method.</param>
319 /// <param name="faces">Faces are added to this list by the method.</param>
320 /// <returns>true if coords and faces were successfully generated, false if not</returns>
321 private bool GenerateCoordsAndFacesFromPrimMeshData(
322 string primName, PrimitiveBaseShape primShape, Vector3 size, out List<Coord> coords, out List<Face> faces)
323 {
324 // m_log.DebugFormat("[MESH]: experimental mesh proxy generation for {0}", primName);
325  
326 coords = new List<Coord>();
327 faces = new List<Face>();
328 OSD meshOsd = null;
329  
330 mConvexHulls = null;
331 mBoundingHull = null;
332  
333 if (primShape.SculptData.Length <= 0)
334 {
335 // XXX: At the moment we can not log here since ODEPrim, for instance, ends up triggering this
336 // method twice - once before it has loaded sculpt data from the asset service and once afterwards.
337 // The first time will always call with unloaded SculptData if this needs to be uploaded.
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 {
370 physicsParms = (OSDMap)map["physics_shape"]; // old asset format
371 if (debugDetail) m_log.DebugFormat("{0} prim='{1}': using 'physics_shape' mesh data", LogHeader, primName);
372 }
373 else if (map.ContainsKey("physics_mesh"))
374 {
375 physicsParms = (OSDMap)map["physics_mesh"]; // new asset format
376 if (debugDetail) m_log.DebugFormat("{0} prim='{1}':using 'physics_mesh' mesh data", LogHeader, primName);
377 }
378 else if (map.ContainsKey("medium_lod"))
379 {
380 physicsParms = (OSDMap)map["medium_lod"]; // if no physics mesh, try to fall back to medium LOD display mesh
381 if (debugDetail) m_log.DebugFormat("{0} prim='{1}':using 'medium_lod' mesh data", LogHeader, primName);
382 }
383 else if (map.ContainsKey("high_lod"))
384 {
385 physicsParms = (OSDMap)map["high_lod"]; // if all else fails, use highest LOD display mesh and hope it works :)
386 if (debugDetail) m_log.DebugFormat("{0} prim='{1}':using 'high_lod' mesh data", LogHeader, primName);
387 }
388  
389 if (map.ContainsKey("physics_convex"))
390 { // pull this out also in case physics engine can use it
391 OSD convexBlockOsd = null;
392 try
393 {
394 OSDMap convexBlock = (OSDMap)map["physics_convex"];
395 {
396 int convexOffset = convexBlock["offset"].AsInteger() + (int)start;
397 int convexSize = convexBlock["size"].AsInteger();
398  
399 byte[] convexBytes = new byte[convexSize];
400  
401 System.Buffer.BlockCopy(primShape.SculptData, convexOffset, convexBytes, 0, convexSize);
402  
403 try
404 {
405 convexBlockOsd = DecompressOsd(convexBytes);
406 }
407 catch (Exception e)
408 {
409 m_log.ErrorFormat("{0} prim='{1}': exception decoding convex block: {2}", LogHeader, primName, e);
410 //return false;
411 }
412 }
413  
414 if (convexBlockOsd != null && convexBlockOsd is OSDMap)
415 {
416 convexBlock = convexBlockOsd as OSDMap;
417  
418 if (debugDetail)
419 {
420 string keys = LogHeader + " keys found in convexBlock: ";
421 foreach (KeyValuePair<string, OSD> kvp in convexBlock)
422 keys += "'" + kvp.Key + "' ";
423 m_log.Debug(keys);
424 }
425  
426 Vector3 min = new Vector3(-0.5f, -0.5f, -0.5f);
427 if (convexBlock.ContainsKey("Min")) min = convexBlock["Min"].AsVector3();
428 Vector3 max = new Vector3(0.5f, 0.5f, 0.5f);
429 if (convexBlock.ContainsKey("Max")) max = convexBlock["Max"].AsVector3();
430  
431 List<Vector3> boundingHull = null;
432  
433 if (convexBlock.ContainsKey("BoundingVerts"))
434 {
435 byte[] boundingVertsBytes = convexBlock["BoundingVerts"].AsBinary();
436 boundingHull = new List<Vector3>();
437 for (int i = 0; i < boundingVertsBytes.Length; )
438 {
439 ushort uX = Utils.BytesToUInt16(boundingVertsBytes, i); i += 2;
440 ushort uY = Utils.BytesToUInt16(boundingVertsBytes, i); i += 2;
441 ushort uZ = Utils.BytesToUInt16(boundingVertsBytes, i); i += 2;
442  
443 Vector3 pos = new Vector3(
444 Utils.UInt16ToFloat(uX, min.X, max.X),
445 Utils.UInt16ToFloat(uY, min.Y, max.Y),
446 Utils.UInt16ToFloat(uZ, min.Z, max.Z)
447 );
448  
449 boundingHull.Add(pos);
450 }
451  
452 mBoundingHull = boundingHull;
453 if (debugDetail) m_log.DebugFormat("{0} prim='{1}': parsed bounding hull. nVerts={2}", LogHeader, primName, mBoundingHull.Count);
454 }
455  
456 if (convexBlock.ContainsKey("HullList"))
457 {
458 byte[] hullList = convexBlock["HullList"].AsBinary();
459  
460 byte[] posBytes = convexBlock["Positions"].AsBinary();
461  
462 List<List<Vector3>> hulls = new List<List<Vector3>>();
463 int posNdx = 0;
464  
465 foreach (byte cnt in hullList)
466 {
467 int count = cnt == 0 ? 256 : cnt;
468 List<Vector3> hull = new List<Vector3>();
469  
470 for (int i = 0; i < count; i++)
471 {
472 ushort uX = Utils.BytesToUInt16(posBytes, posNdx); posNdx += 2;
473 ushort uY = Utils.BytesToUInt16(posBytes, posNdx); posNdx += 2;
474 ushort uZ = Utils.BytesToUInt16(posBytes, posNdx); posNdx += 2;
475  
476 Vector3 pos = new Vector3(
477 Utils.UInt16ToFloat(uX, min.X, max.X),
478 Utils.UInt16ToFloat(uY, min.Y, max.Y),
479 Utils.UInt16ToFloat(uZ, min.Z, max.Z)
480 );
481  
482 hull.Add(pos);
483 }
484  
485 hulls.Add(hull);
486 }
487  
488 mConvexHulls = hulls;
489 if (debugDetail) m_log.DebugFormat("{0} prim='{1}': parsed hulls. nHulls={2}", LogHeader, primName, mConvexHulls.Count);
490 }
491 else
492 {
493 if (debugDetail) m_log.DebugFormat("{0} prim='{1}' has physics_convex but no HullList", LogHeader, primName);
494 }
495 }
496 }
497 catch (Exception e)
498 {
499 m_log.WarnFormat("{0} exception decoding convex block: {1}", LogHeader, e);
500 }
501 }
502  
503 if (physicsParms == null)
504 {
505 m_log.WarnFormat("[MESH]: No recognized physics mesh found in mesh asset for {0}", primName);
506 return false;
507 }
508  
509 int physOffset = physicsParms["offset"].AsInteger() + (int)start;
510 int physSize = physicsParms["size"].AsInteger();
511  
512 if (physOffset < 0 || physSize == 0)
513 return false; // no mesh data in asset
514  
515 OSD decodedMeshOsd = new OSD();
516 byte[] meshBytes = new byte[physSize];
517 System.Buffer.BlockCopy(primShape.SculptData, physOffset, meshBytes, 0, physSize);
518 // byte[] decompressed = new byte[physSize * 5];
519 try
520 {
521 decodedMeshOsd = DecompressOsd(meshBytes);
522 }
523 catch (Exception e)
524 {
525 m_log.ErrorFormat("{0} prim='{1}': exception decoding physical mesh: {2}", LogHeader, primName, e);
526 return false;
527 }
528  
529 OSDArray decodedMeshOsdArray = null;
530  
531 // physics_shape is an array of OSDMaps, one for each submesh
532 if (decodedMeshOsd is OSDArray)
533 {
534 // Console.WriteLine("decodedMeshOsd for {0} - {1}", primName, Util.GetFormattedXml(decodedMeshOsd));
535  
536 decodedMeshOsdArray = (OSDArray)decodedMeshOsd;
537 foreach (OSD subMeshOsd in decodedMeshOsdArray)
538 {
539 if (subMeshOsd is OSDMap)
540 AddSubMesh(subMeshOsd as OSDMap, size, coords, faces);
541 }
542 if (debugDetail)
543 m_log.DebugFormat("{0} {1}: mesh decoded. offset={2}, size={3}, nCoords={4}, nFaces={5}",
544 LogHeader, primName, physOffset, physSize, coords.Count, faces.Count);
545 }
546 }
547  
548 return true;
549 }
550  
551 /// <summary>
552 /// decompresses a gzipped OSD object
553 /// </summary>
554 /// <param name="decodedOsd"></param> the OSD object
555 /// <param name="meshBytes"></param>
556 /// <returns></returns>
557 private static OSD DecompressOsd(byte[] meshBytes)
558 {
559 OSD decodedOsd = null;
560  
561 using (MemoryStream inMs = new MemoryStream(meshBytes))
562 {
563 using (MemoryStream outMs = new MemoryStream())
564 {
565 using (DeflateStream decompressionStream = new DeflateStream(inMs, CompressionMode.Decompress))
566 {
567 byte[] readBuffer = new byte[2048];
568 inMs.Read(readBuffer, 0, 2); // skip first 2 bytes in header
569 int readLen = 0;
570  
571 while ((readLen = decompressionStream.Read(readBuffer, 0, readBuffer.Length)) > 0)
572 outMs.Write(readBuffer, 0, readLen);
573  
574 outMs.Flush();
575  
576 outMs.Seek(0, SeekOrigin.Begin);
577  
578 byte[] decompressedBuf = outMs.GetBuffer();
579  
580 decodedOsd = OSDParser.DeserializeLLSDBinary(decompressedBuf);
581 }
582 }
583 }
584 return decodedOsd;
585 }
586  
587 /// <summary>
588 /// Generate the co-ords and faces necessary to construct a mesh from the sculpt data the accompanies a prim.
589 /// </summary>
590 /// <param name="primName"></param>
591 /// <param name="primShape"></param>
592 /// <param name="size"></param>
593 /// <param name="lod"></param>
594 /// <param name="coords">Coords are added to this list by the method.</param>
595 /// <param name="faces">Faces are added to this list by the method.</param>
596 /// <returns>true if coords and faces were successfully generated, false if not</returns>
597 private bool GenerateCoordsAndFacesFromPrimSculptData(
598 string primName, PrimitiveBaseShape primShape, Vector3 size, float lod, out List<Coord> coords, out List<Face> faces)
599 {
600 coords = new List<Coord>();
601 faces = new List<Face>();
602 PrimMesher.SculptMesh sculptMesh;
603 Image idata = null;
604 string decodedSculptFileName = "";
605  
606 if (cacheSculptMaps && primShape.SculptTexture != UUID.Zero)
607 {
608 decodedSculptFileName = System.IO.Path.Combine(decodedSculptMapPath, "smap_" + primShape.SculptTexture.ToString());
609 try
610 {
611 if (File.Exists(decodedSculptFileName))
612 {
613 idata = Image.FromFile(decodedSculptFileName);
614 }
615 }
616 catch (Exception e)
617 {
618 m_log.Error("[SCULPT]: unable to load cached sculpt map " + decodedSculptFileName + " " + e.Message);
619  
620 }
621 //if (idata != null)
622 // m_log.Debug("[SCULPT]: loaded cached map asset for map ID: " + primShape.SculptTexture.ToString());
623 }
624  
625 if (idata == null)
626 {
627 if (primShape.SculptData == null || primShape.SculptData.Length == 0)
628 return false;
629  
630 try
631 {
632 OpenMetaverse.Imaging.ManagedImage managedImage;
633  
634 OpenMetaverse.Imaging.OpenJPEG.DecodeToImage(primShape.SculptData, out managedImage);
635  
636 if (managedImage == null)
637 {
638 // In some cases it seems that the decode can return a null bitmap without throwing
639 // an exception
640 m_log.WarnFormat("[PHYSICS]: OpenJPEG decoded sculpt data for {0} to a null bitmap. Ignoring.", primName);
641  
642 return false;
643 }
644  
645 if ((managedImage.Channels & OpenMetaverse.Imaging.ManagedImage.ImageChannels.Alpha) != 0)
646 managedImage.ConvertChannels(managedImage.Channels & ~OpenMetaverse.Imaging.ManagedImage.ImageChannels.Alpha);
647  
648 Bitmap imgData = OpenMetaverse.Imaging.LoadTGAClass.LoadTGA(new MemoryStream(managedImage.ExportTGA()));
649 idata = (Image)imgData;
650 managedImage = null;
651  
652 if (cacheSculptMaps)
653 {
654 try { idata.Save(decodedSculptFileName, ImageFormat.MemoryBmp); }
655 catch (Exception e) { m_log.Error("[SCULPT]: unable to cache sculpt map " + decodedSculptFileName + " " + e.Message); }
656 }
657 }
658 catch (DllNotFoundException)
659 {
660 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!");
661 return false;
662 }
663 catch (IndexOutOfRangeException)
664 {
665 m_log.Error("[PHYSICS]: OpenJpeg was unable to decode this. Physics Proxy generation failed");
666 return false;
667 }
668 catch (Exception ex)
669 {
670 m_log.Error("[PHYSICS]: Unable to generate a Sculpty physics proxy. Sculpty texture decode failed: " + ex.Message);
671 return false;
672 }
673 }
674  
675 PrimMesher.SculptMesh.SculptType sculptType;
676 switch ((OpenMetaverse.SculptType)primShape.SculptType)
677 {
678 case OpenMetaverse.SculptType.Cylinder:
679 sculptType = PrimMesher.SculptMesh.SculptType.cylinder;
680 break;
681 case OpenMetaverse.SculptType.Plane:
682 sculptType = PrimMesher.SculptMesh.SculptType.plane;
683 break;
684 case OpenMetaverse.SculptType.Torus:
685 sculptType = PrimMesher.SculptMesh.SculptType.torus;
686 break;
687 case OpenMetaverse.SculptType.Sphere:
688 sculptType = PrimMesher.SculptMesh.SculptType.sphere;
689 break;
690 default:
691 sculptType = PrimMesher.SculptMesh.SculptType.plane;
692 break;
693 }
694  
695 bool mirror = ((primShape.SculptType & 128) != 0);
696 bool invert = ((primShape.SculptType & 64) != 0);
697  
698 sculptMesh = new PrimMesher.SculptMesh((Bitmap)idata, sculptType, (int)lod, false, mirror, invert);
699  
700 idata.Dispose();
701  
702 sculptMesh.DumpRaw(baseDir, primName, "primMesh");
703  
704 sculptMesh.Scale(size.X, size.Y, size.Z);
705  
706 coords = sculptMesh.coords;
707 faces = sculptMesh.faces;
708  
709 return true;
710 }
711  
712 /// <summary>
713 /// Generate the co-ords and faces necessary to construct a mesh from the shape data the accompanies a prim.
714 /// </summary>
715 /// <param name="primName"></param>
716 /// <param name="primShape"></param>
717 /// <param name="size"></param>
718 /// <param name="coords">Coords are added to this list by the method.</param>
719 /// <param name="faces">Faces are added to this list by the method.</param>
720 /// <returns>true if coords and faces were successfully generated, false if not</returns>
721 private bool GenerateCoordsAndFacesFromPrimShapeData(
722 string primName, PrimitiveBaseShape primShape, Vector3 size, float lod, out List<Coord> coords, out List<Face> faces)
723 {
724 PrimMesh primMesh;
725 coords = new List<Coord>();
726 faces = new List<Face>();
727  
728 float pathShearX = primShape.PathShearX < 128 ? (float)primShape.PathShearX * 0.01f : (float)(primShape.PathShearX - 256) * 0.01f;
729 float pathShearY = primShape.PathShearY < 128 ? (float)primShape.PathShearY * 0.01f : (float)(primShape.PathShearY - 256) * 0.01f;
730 float pathBegin = (float)primShape.PathBegin * 2.0e-5f;
731 float pathEnd = 1.0f - (float)primShape.PathEnd * 2.0e-5f;
732 float pathScaleX = (float)(primShape.PathScaleX - 100) * 0.01f;
733 float pathScaleY = (float)(primShape.PathScaleY - 100) * 0.01f;
734  
735 float profileBegin = (float)primShape.ProfileBegin * 2.0e-5f;
736 float profileEnd = 1.0f - (float)primShape.ProfileEnd * 2.0e-5f;
737 float profileHollow = (float)primShape.ProfileHollow * 2.0e-5f;
738 if (profileHollow > 0.95f)
739 profileHollow = 0.95f;
740  
741 int sides = 4;
742 LevelOfDetail iLOD = (LevelOfDetail)lod;
743 if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle)
744 sides = 3;
745 else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.Circle)
746 {
747 switch (iLOD)
748 {
749 case LevelOfDetail.High: sides = 24; break;
750 case LevelOfDetail.Medium: sides = 12; break;
751 case LevelOfDetail.Low: sides = 6; break;
752 case LevelOfDetail.VeryLow: sides = 3; break;
753 default: sides = 24; break;
754 }
755 }
756 else if ((primShape.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle)
757 { // half circle, prim is a sphere
758 switch (iLOD)
759 {
760 case LevelOfDetail.High: sides = 24; break;
761 case LevelOfDetail.Medium: sides = 12; break;
762 case LevelOfDetail.Low: sides = 6; break;
763 case LevelOfDetail.VeryLow: sides = 3; break;
764 default: sides = 24; break;
765 }
766  
767 profileBegin = 0.5f * profileBegin + 0.5f;
768 profileEnd = 0.5f * profileEnd + 0.5f;
769 }
770  
771 int hollowSides = sides;
772 if (primShape.HollowShape == HollowShape.Circle)
773 {
774 switch (iLOD)
775 {
776 case LevelOfDetail.High: hollowSides = 24; break;
777 case LevelOfDetail.Medium: hollowSides = 12; break;
778 case LevelOfDetail.Low: hollowSides = 6; break;
779 case LevelOfDetail.VeryLow: hollowSides = 3; break;
780 default: hollowSides = 24; break;
781 }
782 }
783 else if (primShape.HollowShape == HollowShape.Square)
784 hollowSides = 4;
785 else if (primShape.HollowShape == HollowShape.Triangle)
786 hollowSides = 3;
787  
788 primMesh = new PrimMesh(sides, profileBegin, profileEnd, profileHollow, hollowSides);
789  
790 if (primMesh.errorMessage != null)
791 if (primMesh.errorMessage.Length > 0)
792 m_log.Error("[ERROR] " + primMesh.errorMessage);
793  
794 primMesh.topShearX = pathShearX;
795 primMesh.topShearY = pathShearY;
796 primMesh.pathCutBegin = pathBegin;
797 primMesh.pathCutEnd = pathEnd;
798  
799 if (primShape.PathCurve == (byte)Extrusion.Straight || primShape.PathCurve == (byte) Extrusion.Flexible)
800 {
801 primMesh.twistBegin = primShape.PathTwistBegin * 18 / 10;
802 primMesh.twistEnd = primShape.PathTwist * 18 / 10;
803 primMesh.taperX = pathScaleX;
804 primMesh.taperY = pathScaleY;
805  
806 if (profileBegin < 0.0f || profileBegin >= profileEnd || profileEnd > 1.0f)
807 {
808 ReportPrimError("*** CORRUPT PRIM!! ***", primName, primMesh);
809 if (profileBegin < 0.0f) profileBegin = 0.0f;
810 if (profileEnd > 1.0f) profileEnd = 1.0f;
811 }
812 #if SPAM
813 m_log.Debug("****** PrimMesh Parameters (Linear) ******\n" + primMesh.ParamsToDisplayString());
814 #endif
815 try
816 {
817 primMesh.ExtrudeLinear();
818 }
819 catch (Exception ex)
820 {
821 ReportPrimError("Extrusion failure: exception: " + ex.ToString(), primName, primMesh);
822 return false;
823 }
824 }
825 else
826 {
827 primMesh.holeSizeX = (200 - primShape.PathScaleX) * 0.01f;
828 primMesh.holeSizeY = (200 - primShape.PathScaleY) * 0.01f;
829 primMesh.radius = 0.01f * primShape.PathRadiusOffset;
830 primMesh.revolutions = 1.0f + 0.015f * primShape.PathRevolutions;
831 primMesh.skew = 0.01f * primShape.PathSkew;
832 primMesh.twistBegin = primShape.PathTwistBegin * 36 / 10;
833 primMesh.twistEnd = primShape.PathTwist * 36 / 10;
834 primMesh.taperX = primShape.PathTaperX * 0.01f;
835 primMesh.taperY = primShape.PathTaperY * 0.01f;
836  
837 if (profileBegin < 0.0f || profileBegin >= profileEnd || profileEnd > 1.0f)
838 {
839 ReportPrimError("*** CORRUPT PRIM!! ***", primName, primMesh);
840 if (profileBegin < 0.0f) profileBegin = 0.0f;
841 if (profileEnd > 1.0f) profileEnd = 1.0f;
842 }
843 #if SPAM
844 m_log.Debug("****** PrimMesh Parameters (Circular) ******\n" + primMesh.ParamsToDisplayString());
845 #endif
846 try
847 {
848 primMesh.ExtrudeCircular();
849 }
850 catch (Exception ex)
851 {
852 ReportPrimError("Extrusion failure: exception: " + ex.ToString(), primName, primMesh);
853 return false;
854 }
855 }
856  
857 primMesh.DumpRaw(baseDir, primName, "primMesh");
858  
859 primMesh.Scale(size.X, size.Y, size.Z);
860  
861 coords = primMesh.coords;
862 faces = primMesh.faces;
863  
864 return true;
865 }
866  
867 /// <summary>
868 /// temporary prototype code - please do not use until the interface has been finalized!
869 /// </summary>
870 /// <param name="size">value to scale the hull points by</param>
871 /// <returns>a list of vertices in the bounding hull if it exists and has been successfully decoded, otherwise null</returns>
872 public List<Vector3> GetBoundingHull(Vector3 size)
873 {
874 if (mBoundingHull == null)
875 return null;
876  
877 List<Vector3> verts = new List<Vector3>();
878 foreach (var vert in mBoundingHull)
879 verts.Add(vert * size);
880  
881 return verts;
882 }
883  
884 /// <summary>
885 /// temporary prototype code - please do not use until the interface has been finalized!
886 /// </summary>
887 /// <param name="size">value to scale the hull points by</param>
888 /// <returns>a list of hulls if they exist and have been successfully decoded, otherwise null</returns>
889 public List<List<Vector3>> GetConvexHulls(Vector3 size)
890 {
891 if (mConvexHulls == null)
892 return null;
893  
894 List<List<Vector3>> hulls = new List<List<Vector3>>();
895 foreach (var hull in mConvexHulls)
896 {
897 List<Vector3> verts = new List<Vector3>();
898 foreach (var vert in hull)
899 verts.Add(vert * size);
900 hulls.Add(verts);
901 }
902  
903 return hulls;
904 }
905  
906 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod)
907 {
908 return CreateMesh(primName, primShape, size, lod, false, true);
909 }
910  
911 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical)
912 {
913 return CreateMesh(primName, primShape, size, lod, isPhysical, true);
914 }
915  
916 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool shouldCache)
917 {
918 #if SPAM
919 m_log.DebugFormat("[MESH]: Creating mesh for {0}", primName);
920 #endif
921  
922 Mesh mesh = null;
923 ulong key = 0;
924  
925 // If this mesh has been created already, return it instead of creating another copy
926 // For large regions with 100k+ prims and hundreds of copies of each, this can save a GB or more of memory
927 if (shouldCache)
928 {
929 key = primShape.GetMeshKey(size, lod);
930 if (m_uniqueMeshes.TryGetValue(key, out mesh))
931 return mesh;
932 }
933  
934 if (size.X < 0.01f) size.X = 0.01f;
935 if (size.Y < 0.01f) size.Y = 0.01f;
936 if (size.Z < 0.01f) size.Z = 0.01f;
937  
938 mesh = CreateMeshFromPrimMesher(primName, primShape, size, lod);
939  
940 if (mesh != null)
941 {
942 if ((!isPhysical) && size.X < minSizeForComplexMesh && size.Y < minSizeForComplexMesh && size.Z < minSizeForComplexMesh)
943 {
944 #if SPAM
945 m_log.Debug("Meshmerizer: prim " + primName + " has a size of " + size.ToString() + " which is below threshold of " +
946 minSizeForComplexMesh.ToString() + " - creating simple bounding box");
947 #endif
948 mesh = CreateBoundingBoxMesh(mesh);
949 mesh.DumpRaw(baseDir, primName, "Z extruded");
950 }
951  
952 // trim the vertex and triangle lists to free up memory
953 mesh.TrimExcess();
954  
955 if (shouldCache)
956 {
957 m_uniqueMeshes.Add(key, mesh);
958 }
959 }
960  
961 return mesh;
962 }
963 }
964 }