clockwerk-opensim-stable – Blame information for rev 1

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