void CreatePrim (WarpRenderer renderer, ISceneChildEntity prim) { try { if ((PCode)prim.Shape.PCode != PCode.Prim) return; if (prim.Scale.LengthSquared () < MIN_PRIM_SIZE * MIN_PRIM_SIZE) return; Primitive omvPrim = prim.Shape.ToOmvPrimitive (prim.OffsetPosition, prim.GetRotationOffset ()); FacetedMesh renderMesh = null; // Are we dealing with a sculptie or mesh? if (omvPrim.Sculpt != null && omvPrim.Sculpt.SculptTexture != UUID.Zero) { // Try fetching the asset byte [] sculptAsset = m_scene.AssetService.GetData (omvPrim.Sculpt.SculptTexture.ToString ()); if (sculptAsset != null) { // Is it a mesh? if (omvPrim.Sculpt.Type == SculptType.Mesh) { AssetMesh meshAsset = new AssetMesh (omvPrim.Sculpt.SculptTexture, sculptAsset); FacetedMesh.TryDecodeFromAsset (omvPrim, meshAsset, DetailLevel.Highest, out renderMesh); meshAsset = null; } else // It's sculptie { Image sculpt = m_imgDecoder.DecodeToImage (sculptAsset); if (sculpt != null) { renderMesh = m_primMesher.GenerateFacetedSculptMesh (omvPrim, (Bitmap)sculpt, DetailLevel.Medium); sculpt.Dispose (); } } sculptAsset = null; } else { // missing sculpt data... replace with something renderMesh = m_primMesher.GenerateFacetedMesh (omvPrim, DetailLevel.Medium); } } else // Prim { renderMesh = m_primMesher.GenerateFacetedMesh (omvPrim, DetailLevel.Medium); } if (renderMesh == null) return; warp_Vector primPos = ConvertVector (prim.GetWorldPosition ()); warp_Quaternion primRot = ConvertQuaternion (prim.GetRotationOffset ()); warp_Matrix m = warp_Matrix.quaternionMatrix (primRot); if (prim.ParentID != 0) { ISceneEntity group = m_scene.GetGroupByPrim (prim.LocalId); if (group != null) m.transform (warp_Matrix.quaternionMatrix (ConvertQuaternion (group.RootChild.GetRotationOffset ()))); } warp_Vector primScale = ConvertVector (prim.Scale); string primID = prim.UUID.ToString (); // Create the prim faces for (int i = 0; i < renderMesh.Faces.Count; i++) { Face renderFace = renderMesh.Faces [i]; string meshName = primID + "-Face-" + i; warp_Object faceObj = new warp_Object (renderFace.Vertices.Count, renderFace.Indices.Count / 3); foreach (Vertex v in renderFace.Vertices) { warp_Vector pos = ConvertVector (v.Position); warp_Vector norm = ConvertVector (v.Normal); if (prim.Shape.SculptTexture == UUID.Zero) norm = norm.reverse (); warp_Vertex vert = new warp_Vertex (pos, norm, v.TexCoord.X, v.TexCoord.Y); faceObj.addVertex (vert); } for (int j = 0; j < renderFace.Indices.Count;) { faceObj.addTriangle ( renderFace.Indices [j++], renderFace.Indices [j++], renderFace.Indices [j++]); } Primitive.TextureEntryFace teFace = prim.Shape.Textures.GetFace ((uint)i); string materialName; Color4 faceColor = GetFaceColor (teFace); if (m_texturePrims && (prim.Scale.LengthSquared () > m_texturePrimSize)) { materialName = GetOrCreateMaterial (renderer, faceColor, teFace.TextureID); } else { materialName = GetOrCreateMaterial (renderer, faceColor); } faceObj.transform (m); faceObj.setPos (primPos); faceObj.scaleSelf (primScale.x, primScale.y, primScale.z); renderer.Scene.addObject (meshName, faceObj); renderer.SetObjectMaterial (meshName, materialName); faceObj = null; } renderMesh.Faces.Clear (); renderMesh = null; } catch (Exception ex) { MainConsole.Instance.Warn ("[Warp3D]: Exception creating prim, " + ex); } }
private String PovMesh(Primitive prim) { string s; FacetedMesh renderMesh = null; if (prim.Sculpt != null && prim.Sculpt.SculptTexture != UUID.Zero) { if (prim.Sculpt.Type == SculptType.Mesh) { byte[] meshData = GetMesh(prim.Sculpt.SculptTexture); if (meshData == null) return string.Empty; AssetMesh meshAsset = new AssetMesh(prim.Sculpt.SculptTexture, meshData); FacetedMesh.TryDecodeFromAsset(prim, meshAsset, DetailLevel.Highest, out renderMesh); meshAsset = null; } else // not a mesh, must be a sculptie { Image sculpt = GetImage(Client, prim.Sculpt.SculptTexture); if (sculpt == null) return string.Empty; renderMesh = m_primMesher.GenerateFacetedSculptMesh(prim, (Bitmap)sculpt, DetailLevel.Medium); sculpt.Dispose(); } } else renderMesh = m_primMesher.GenerateFacetedMesh(prim, DetailLevel.Highest); if (renderMesh == null) return string.Empty; Matrix4 mv = GetModelviewMatrix(prim); using (StringWriter sw = new StringWriter()) { for (int i = 0; i < renderMesh.Faces.Count; i++) { var face = renderMesh.Faces[i]; Primitive.TextureEntryFace tef = null; try { tef = prim.Textures.GetFace((uint)i); } catch (Exception) { continue; } int numVerts = face.Vertices.Count; int numIndices = face.Indices.Count; if (numVerts == 0 || numIndices == 0 || tef == null) continue; sw.WriteLine("mesh2"); sw.WriteLine("{"); sw.WriteLine("vertex_vectors"); sw.WriteLine("{"); sw.WriteLine(numVerts.ToString()); for (int vi = 0; vi < numVerts; vi++) { Vector3 v = face.Vertices[vi].Position; sw.WriteLine(PovVector3((v * mv) * 0.1f)); } sw.WriteLine("}"); // vertex_vectors sw.WriteLine("face_indices"); sw.WriteLine("{"); sw.WriteLine((numIndices / 3).ToString()); for (int ti = 0; ti < numIndices; ti += 3) sw.WriteLine(string.Format("<{0},{1},{2}>", face.Indices[ti], face.Indices[ti + 1], face.Indices[ti + 2])); sw.WriteLine("}"); // face_indices // material Color4 clr = tef.RGBA; if (tef.TextureID != null && mKnownTextures.ContainsKey(tef.TextureID)) { var texInfo = mKnownTextures[tef.TextureID]; if (texInfo != null) clr *= texInfo.MeanColor; } sw.WriteLine("pigment {rgbf "); sw.WriteLine(string.Format("<{0},{1},{2},{3}>", clr.R, clr.G, clr.B, 1.0f - clr.A)); sw.WriteLine("}"); //sw.WriteLine("pigment {rgb <1, 0.6, 0.6>}"); sw.WriteLine("}"); // mesh2 } s = sw.ToString(); } return s; }
/// <summary> /// Full implementation of llGetBoundingBox according to SL 2015-04-15. /// http://wiki.secondlife.com/wiki/LlGetBoundingBox /// http://lslwiki.net/lslwiki/wakka.php?wakka=llGetBoundingBox /// Returns local bounding box of avatar without attachments /// if target is non-seated avatar or prim/mesh in avatar attachment. /// Returns local bounding box of object including seated avatars /// if target is seated avatar or prim/mesh in object. /// Uses meshing of prims for high accuracy /// or less accurate box models for speed. /// </summary> public LSL_List llGetBoundingBox(string obj) { m_host.AddScriptLPS(1); // Get target avatar if non-seated avatar or attachment, or prim and object UUID objID = UUID.Zero; UUID.TryParse(obj, out objID); ScenePresence agent = World.GetScenePresence(objID); if (agent != null) { if (agent.ParentPart != null) { objID = agent.ParentPart.UUID; agent = null; } } SceneObjectGroup group = null; SceneObjectPart target = World.GetSceneObjectPart(objID); if (target != null) { group = target.ParentGroup; if (group.IsAttachment) { objID = group.AttachedAvatar; agent = World.GetScenePresence(objID); group = null; target = null; } } // Initialize but break if no target LSL_List result = new LSL_List(); int groupCount = 0; int partCount = 0; int vertexCount = 0; if (target == null && agent == null) { result.Add(new LSL_Vector()); result.Add(new LSL_Vector()); if (m_addStatsInGetBoundingBox) result.Add(new LSL_Vector((float)groupCount, (float)partCount, (float)vertexCount)); return result; } Vector3 minPosition = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); Vector3 maxPosition = new Vector3(float.MinValue, float.MinValue, float.MinValue); // Try to get a mesher IRendering primMesher = null; List<string> renderers = RenderingLoader.ListRenderers(Util.ExecutingDirectory()); if (renderers.Count > 0) primMesher = RenderingLoader.LoadRenderer(renderers[0]); // Get bounding box of just avatar, seated or not if (agent != null) { bool hasParent = false; Vector3 lower; Vector3 upper; BoundingBoxOfScenePresence(agent, out lower, out upper); Vector3 offset = Vector3.Zero; // Since local bounding box unrotated and untilted, keep it simple AddBoundingBoxOfSimpleBox(lower, upper, offset, agent.Rotation, hasParent, ref minPosition, ref maxPosition, ref vertexCount); partCount++; groupCount++; // Return lower and upper bounding box corners result.Add(new LSL_Vector(minPosition)); result.Add(new LSL_Vector(maxPosition)); if (m_addStatsInGetBoundingBox) result.Add(new LSL_Vector((float)groupCount, (float)partCount, (float)vertexCount)); return result; } // Get bounding box of object including seated avatars else if (group != null) { // Merge bounding boxes of all parts (prims and mesh) foreach (SceneObjectPart part in group.Parts) { bool hasParent = (!part.IsRoot); // When requested or if no mesher, keep it simple if (m_useSimpleBoxesInGetBoundingBox || primMesher == null) { AddBoundingBoxOfSimpleBox(part.Scale * -0.5f, part.Scale * 0.5f, part.OffsetPosition, part.RotationOffset, hasParent, ref minPosition, ref maxPosition, ref vertexCount); } // Do the full mounty else { Primitive omvPrim = part.Shape.ToOmvPrimitive(part.OffsetPosition, part.RotationOffset); byte[] sculptAsset = null; if (omvPrim.Sculpt != null) sculptAsset = World.AssetService.GetData(omvPrim.Sculpt.SculptTexture.ToString()); // When part is mesh // Quirk: Only imports as incompletely populated faceted mesh object, so needs an own handler. if (omvPrim.Sculpt != null && omvPrim.Sculpt.Type == SculptType.Mesh && sculptAsset != null) { AssetMesh meshAsset = new AssetMesh(omvPrim.Sculpt.SculptTexture, sculptAsset); FacetedMesh mesh = null; FacetedMesh.TryDecodeFromAsset(omvPrim, meshAsset, DetailLevel.Highest, out mesh); meshAsset = null; if (mesh != null) { AddBoundingBoxOfFacetedMesh(mesh, omvPrim, hasParent, ref minPosition, ref maxPosition, ref vertexCount); mesh = null; } } // When part is sculpt // Quirk: Generated sculpt mesh is about 2.8% smaller in X and Y than visual sculpt. else if (omvPrim.Sculpt != null && omvPrim.Sculpt.Type != SculptType.Mesh && sculptAsset != null) { IJ2KDecoder imgDecoder = World.RequestModuleInterface<IJ2KDecoder>(); if (imgDecoder != null) { Image sculpt = imgDecoder.DecodeToImage(sculptAsset); if (sculpt != null) { SimpleMesh mesh = primMesher.GenerateSimpleSculptMesh(omvPrim, (Bitmap)sculpt, DetailLevel.Medium); sculpt.Dispose(); if (mesh != null) { AddBoundingBoxOfSimpleMesh(mesh, omvPrim, hasParent, ref minPosition, ref maxPosition, ref vertexCount); mesh = null; } } } } // When part is prim else if (omvPrim.Sculpt == null) { SimpleMesh mesh = primMesher.GenerateSimpleMesh(omvPrim, DetailLevel.Medium); if (mesh != null) { AddBoundingBoxOfSimpleMesh(mesh, omvPrim, hasParent, ref minPosition, ref maxPosition, ref vertexCount); mesh = null; } } // When all else fails, try fallback to simple box else { AddBoundingBoxOfSimpleBox(part.Scale * -0.5f, part.Scale * 0.5f, part.OffsetPosition, part.RotationOffset, hasParent, ref minPosition, ref maxPosition, ref vertexCount); } } partCount++; } } // Merge bounding boxes of seated avatars foreach (ScenePresence sp in group.GetSittingAvatars()) { Vector3 lower; Vector3 upper; BoundingBoxOfScenePresence(sp, out lower, out upper); Vector3 offset = sp.OffsetPosition; bool hasParent = true; // When requested or if no mesher, keep it simple if (m_useSimpleBoxesInGetBoundingBox || primMesher == null) { AddBoundingBoxOfSimpleBox(lower, upper, offset, sp.Rotation, hasParent, ref minPosition, ref maxPosition, ref vertexCount); } // Do the full mounty else { // Prim shapes don't do center offsets, so add it here. offset = offset + (lower + upper) * 0.5f * sp.Rotation; Primitive omvPrim = MakeOpenMetaversePrim(upper - lower, offset, sp.Rotation, ScriptBaseClass.PRIM_TYPE_SPHERE); SimpleMesh mesh = primMesher.GenerateSimpleMesh(omvPrim, DetailLevel.Medium); AddBoundingBoxOfSimpleMesh(mesh, omvPrim, hasParent, ref minPosition, ref maxPosition, ref vertexCount); mesh = null; } partCount++; } groupCount++; // Return lower and upper bounding box corners result.Add(new LSL_Vector(minPosition)); result.Add(new LSL_Vector(maxPosition)); if (m_addStatsInGetBoundingBox) result.Add(new LSL_Vector((float)groupCount, (float)partCount, (float)vertexCount)); primMesher = null; return result; }
/// <summary> /// Implementation of llCastRay similar to SL 2015-04-21. /// http://wiki.secondlife.com/wiki/LlCastRay /// Uses pure geometry, bounding shapes, meshing and no physics /// for prims, sculpts, meshes, avatars and terrain. /// Implements all flags, reject types and data flags. /// Can handle both objects/groups and prims/parts, by config. /// May sometimes be inaccurate owing to calculation precision, /// meshing detail level and a bug in libopenmetaverse PrimMesher. /// </summary> public LSL_List llCastRayV3(LSL_Vector start, LSL_Vector end, LSL_List options) { m_host.AddScriptLPS(1); LSL_List result = new LSL_List(); // Prepare throttle data int calledMs = Environment.TickCount; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); UUID regionId = World.RegionInfo.RegionID; UUID userId = UUID.Zero; int msAvailable = 0; // Throttle per owner when attachment or "vehicle" (sat upon) if (m_host.ParentGroup.IsAttachment || m_host.ParentGroup.GetSittingAvatars().Count > 0) { userId = m_host.OwnerID; msAvailable = m_msPerAvatarInCastRay; } // Throttle per parcel when not attachment or vehicle else { LandData land = World.GetLandData(m_host.GetWorldPosition()); if (land != null) msAvailable = m_msPerRegionInCastRay * land.Area / 65536; } // Clamp for "oversized" parcels on varregions if (msAvailable > m_msMaxInCastRay) msAvailable = m_msMaxInCastRay; // Check throttle data int fromCalledMs = calledMs - m_msThrottleInCastRay; lock (m_castRayCalls) { for (int i = m_castRayCalls.Count - 1; i >= 0; i--) { // Delete old calls from throttle data if (m_castRayCalls[i].CalledMs < fromCalledMs) m_castRayCalls.RemoveAt(i); // Use current region (in multi-region sims) else if (m_castRayCalls[i].RegionId == regionId) { // Reduce available time with recent calls if (m_castRayCalls[i].UserId == userId) msAvailable -= m_castRayCalls[i].UsedMs; } } } // Return failure if not enough available time if (msAvailable < m_msMinInCastRay) { result.Add(new LSL_Integer(ScriptBaseClass.RCERR_CAST_TIME_EXCEEDED)); return result; } // Initialize List<RayHit> rayHits = new List<RayHit>(); float tol = m_floatToleranceInCastRay; Vector3 pos1Ray = start; Vector3 pos2Ray = end; // Get input options int rejectTypes = 0; int dataFlags = 0; int maxHits = 1; bool detectPhantom = false; for (int i = 0; i < options.Length; i += 2) { if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_REJECT_TYPES) rejectTypes = options.GetLSLIntegerItem(i + 1); else if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_DATA_FLAGS) dataFlags = options.GetLSLIntegerItem(i + 1); else if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_MAX_HITS) maxHits = options.GetLSLIntegerItem(i + 1); else if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_DETECT_PHANTOM) detectPhantom = (options.GetLSLIntegerItem(i + 1) != 0); } if (maxHits > m_maxHitsInCastRay) maxHits = m_maxHitsInCastRay; bool rejectAgents = ((rejectTypes & ScriptBaseClass.RC_REJECT_AGENTS) != 0); bool rejectPhysical = ((rejectTypes & ScriptBaseClass.RC_REJECT_PHYSICAL) != 0); bool rejectNonphysical = ((rejectTypes & ScriptBaseClass.RC_REJECT_NONPHYSICAL) != 0); bool rejectLand = ((rejectTypes & ScriptBaseClass.RC_REJECT_LAND) != 0); bool getNormal = ((dataFlags & ScriptBaseClass.RC_GET_NORMAL) != 0); bool getRootKey = ((dataFlags & ScriptBaseClass.RC_GET_ROOT_KEY) != 0); bool getLinkNum = ((dataFlags & ScriptBaseClass.RC_GET_LINK_NUM) != 0); // Calculate some basic parameters Vector3 vecRay = pos2Ray - pos1Ray; float rayLength = vecRay.Length(); // Try to get a mesher and return failure if none, degenerate ray, or max 0 hits IRendering primMesher = null; List<string> renderers = RenderingLoader.ListRenderers(Util.ExecutingDirectory()); if (renderers.Count < 1 || rayLength < tol || m_maxHitsInCastRay < 1) { result.Add(new LSL_Integer(ScriptBaseClass.RCERR_UNKNOWN)); return result; } primMesher = RenderingLoader.LoadRenderer(renderers[0]); // Iterate over all objects/groups and prims/parts in region World.ForEachSOG( delegate(SceneObjectGroup group) { // Check group filters unless part filters are configured bool isPhysical = (group.RootPart != null && group.RootPart.PhysActor != null && group.RootPart.PhysActor.IsPhysical); bool isNonphysical = !isPhysical; bool isPhantom = group.IsPhantom || group.IsVolumeDetect; bool isAttachment = group.IsAttachment; bool doGroup = true; if (isPhysical && rejectPhysical) doGroup = false; if (isNonphysical && rejectNonphysical) doGroup = false; if (isPhantom && detectPhantom) doGroup = true; if (m_filterPartsInCastRay) doGroup = true; if (isAttachment && !m_doAttachmentsInCastRay) doGroup = false; // Parse object/group if passed filters if (doGroup) { // Iterate over all prims/parts in object/group foreach(SceneObjectPart part in group.Parts) { // Check part filters if configured if (m_filterPartsInCastRay) { isPhysical = (part.PhysActor != null && part.PhysActor.IsPhysical); isNonphysical = !isPhysical; isPhantom = ((part.Flags & PrimFlags.Phantom) != 0) || (part.VolumeDetectActive); bool doPart = true; if (isPhysical && rejectPhysical) doPart = false; if (isNonphysical && rejectNonphysical) doPart = false; if (isPhantom && detectPhantom) doPart = true; if (!doPart) continue; } // Parse prim/part and project ray if passed filters Vector3 scalePart = part.Scale; Vector3 posPart = part.GetWorldPosition(); Quaternion rotPart = part.GetWorldRotation(); Quaternion rotPartInv = Quaternion.Inverse(rotPart); Vector3 pos1RayProj = ((pos1Ray - posPart) * rotPartInv) / scalePart; Vector3 pos2RayProj = ((pos2Ray - posPart) * rotPartInv) / scalePart; // Filter parts by shape bounding boxes Vector3 shapeBoxMax = new Vector3(0.5f, 0.5f, 0.5f); if (!part.Shape.SculptEntry) shapeBoxMax = shapeBoxMax * (new Vector3(m_primSafetyCoeffX, m_primSafetyCoeffY, m_primSafetyCoeffZ)); shapeBoxMax = shapeBoxMax + (new Vector3(tol, tol, tol)); if (RayIntersectsShapeBox(pos1RayProj, pos2RayProj, shapeBoxMax)) { // Prepare data needed to check for ray hits RayTrans rayTrans = new RayTrans(); rayTrans.PartId = part.UUID; rayTrans.GroupId = part.ParentGroup.UUID; rayTrans.Link = group.PrimCount > 1 ? part.LinkNum : 0; rayTrans.ScalePart = scalePart; rayTrans.PositionPart = posPart; rayTrans.RotationPart = rotPart; rayTrans.ShapeNeedsEnds = true; rayTrans.Position1Ray = pos1Ray; rayTrans.Position1RayProj = pos1RayProj; rayTrans.VectorRayProj = pos2RayProj - pos1RayProj; // Get detail level depending on type int lod = 0; // Mesh detail level if (part.Shape.SculptEntry && part.Shape.SculptType == (byte)SculptType.Mesh) lod = (int)m_meshLodInCastRay; // Sculpt detail level else if (part.Shape.SculptEntry && part.Shape.SculptType == (byte)SculptType.Mesh) lod = (int)m_sculptLodInCastRay; // Shape detail level else if (!part.Shape.SculptEntry) lod = (int)m_primLodInCastRay; // Try to get cached mesh if configured ulong meshKey = 0; FacetedMesh mesh = null; if (m_useMeshCacheInCastRay) { meshKey = part.Shape.GetMeshKey(Vector3.One, (float)(4 << lod)); lock (m_cachedMeshes) { m_cachedMeshes.TryGetValue(meshKey, out mesh); } } // Create mesh if no cached mesh if (mesh == null) { // Make an OMV prim to be able to mesh part Primitive omvPrim = part.Shape.ToOmvPrimitive(posPart, rotPart); byte[] sculptAsset = null; if (omvPrim.Sculpt != null) sculptAsset = World.AssetService.GetData(omvPrim.Sculpt.SculptTexture.ToString()); // When part is mesh, get mesh if (omvPrim.Sculpt != null && omvPrim.Sculpt.Type == SculptType.Mesh && sculptAsset != null) { AssetMesh meshAsset = new AssetMesh(omvPrim.Sculpt.SculptTexture, sculptAsset); FacetedMesh.TryDecodeFromAsset(omvPrim, meshAsset, m_meshLodInCastRay, out mesh); meshAsset = null; } // When part is sculpt, create mesh // Quirk: Generated sculpt mesh is about 2.8% smaller in X and Y than visual sculpt. else if (omvPrim.Sculpt != null && omvPrim.Sculpt.Type != SculptType.Mesh && sculptAsset != null) { IJ2KDecoder imgDecoder = World.RequestModuleInterface<IJ2KDecoder>(); if (imgDecoder != null) { Image sculpt = imgDecoder.DecodeToImage(sculptAsset); if (sculpt != null) { mesh = primMesher.GenerateFacetedSculptMesh(omvPrim, (Bitmap)sculpt, m_sculptLodInCastRay); sculpt.Dispose(); } } } // When part is shape, create mesh else if (omvPrim.Sculpt == null) { if ( omvPrim.PrimData.PathBegin == 0.0 && omvPrim.PrimData.PathEnd == 1.0 && omvPrim.PrimData.PathTaperX == 0.0 && omvPrim.PrimData.PathTaperY == 0.0 && omvPrim.PrimData.PathSkew == 0.0 && omvPrim.PrimData.PathTwist - omvPrim.PrimData.PathTwistBegin == 0.0 ) rayTrans.ShapeNeedsEnds = false; mesh = primMesher.GenerateFacetedMesh(omvPrim, m_primLodInCastRay); } // Cache mesh if configured if (m_useMeshCacheInCastRay && mesh != null) { lock(m_cachedMeshes) { if (!m_cachedMeshes.ContainsKey(meshKey)) m_cachedMeshes.Add(meshKey, mesh); } } } // Check mesh for ray hits AddRayInFacetedMesh(mesh, rayTrans, ref rayHits); mesh = null; } } } } ); // Check avatar filter if (!rejectAgents) { // Iterate over all avatars in region World.ForEachRootScenePresence( delegate (ScenePresence sp) { // Get bounding box Vector3 lower; Vector3 upper; BoundingBoxOfScenePresence(sp, out lower, out upper); // Parse avatar Vector3 scalePart = upper - lower; Vector3 posPart = sp.AbsolutePosition; Quaternion rotPart = sp.GetWorldRotation(); Quaternion rotPartInv = Quaternion.Inverse(rotPart); posPart = posPart + (lower + upper) * 0.5f * rotPart; // Project ray Vector3 pos1RayProj = ((pos1Ray - posPart) * rotPartInv) / scalePart; Vector3 pos2RayProj = ((pos2Ray - posPart) * rotPartInv) / scalePart; // Filter avatars by shape bounding boxes Vector3 shapeBoxMax = new Vector3(0.5f + tol, 0.5f + tol, 0.5f + tol); if (RayIntersectsShapeBox(pos1RayProj, pos2RayProj, shapeBoxMax)) { // Prepare data needed to check for ray hits RayTrans rayTrans = new RayTrans(); rayTrans.PartId = sp.UUID; rayTrans.GroupId = sp.ParentPart != null ? sp.ParentPart.ParentGroup.UUID : sp.UUID; rayTrans.Link = sp.ParentPart != null ? UUID2LinkNumber(sp.ParentPart, sp.UUID) : 0; rayTrans.ScalePart = scalePart; rayTrans.PositionPart = posPart; rayTrans.RotationPart = rotPart; rayTrans.ShapeNeedsEnds = false; rayTrans.Position1Ray = pos1Ray; rayTrans.Position1RayProj = pos1RayProj; rayTrans.VectorRayProj = pos2RayProj - pos1RayProj; // Try to get cached mesh if configured PrimitiveBaseShape prim = PrimitiveBaseShape.CreateSphere(); int lod = (int)m_avatarLodInCastRay; ulong meshKey = prim.GetMeshKey(Vector3.One, (float)(4 << lod)); FacetedMesh mesh = null; if (m_useMeshCacheInCastRay) { lock (m_cachedMeshes) { m_cachedMeshes.TryGetValue(meshKey, out mesh); } } // Create mesh if no cached mesh if (mesh == null) { // Make OMV prim and create mesh prim.Scale = scalePart; Primitive omvPrim = prim.ToOmvPrimitive(posPart, rotPart); mesh = primMesher.GenerateFacetedMesh(omvPrim, m_avatarLodInCastRay); // Cache mesh if configured if (m_useMeshCacheInCastRay && mesh != null) { lock(m_cachedMeshes) { if (!m_cachedMeshes.ContainsKey(meshKey)) m_cachedMeshes.Add(meshKey, mesh); } } } // Check mesh for ray hits AddRayInFacetedMesh(mesh, rayTrans, ref rayHits); mesh = null; } } ); } // Check terrain filter if (!rejectLand) { // Parse terrain // Mesh terrain and check bounding box Vector3 lower; Vector3 upper; List<Tri> triangles = TrisFromHeightmapUnderRay(pos1Ray, pos2Ray, out lower, out upper); lower.Z -= tol; upper.Z += tol; if ((pos1Ray.Z >= lower.Z || pos2Ray.Z >= lower.Z) && (pos1Ray.Z <= upper.Z || pos2Ray.Z <= upper.Z)) { // Prepare data needed to check for ray hits RayTrans rayTrans = new RayTrans(); rayTrans.PartId = UUID.Zero; rayTrans.GroupId = UUID.Zero; rayTrans.Link = 0; rayTrans.ScalePart = new Vector3 (1.0f, 1.0f, 1.0f); rayTrans.PositionPart = Vector3.Zero; rayTrans.RotationPart = Quaternion.Identity; rayTrans.ShapeNeedsEnds = true; rayTrans.Position1Ray = pos1Ray; rayTrans.Position1RayProj = pos1Ray; rayTrans.VectorRayProj = vecRay; // Check mesh AddRayInTris(triangles, rayTrans, ref rayHits); triangles = null; } } // Sort hits by ascending distance rayHits.Sort((s1, s2) => s1.Distance.CompareTo(s2.Distance)); // Check excess hits per part and group for (int t = 0; t < 2; t++) { int maxHitsPerType = 0; UUID id = UUID.Zero; if (t == 0) maxHitsPerType = m_maxHitsPerPrimInCastRay; else maxHitsPerType = m_maxHitsPerObjectInCastRay; // Handle excess hits only when needed if (maxHitsPerType < m_maxHitsInCastRay) { // Find excess hits Hashtable hits = new Hashtable(); for (int i = rayHits.Count - 1; i >= 0; i--) { if (t == 0) id = rayHits[i].PartId; else id = rayHits[i].GroupId; if (hits.ContainsKey(id)) hits[id] = (int)hits[id] + 1; else hits[id] = 1; } // Remove excess hits for (int i = rayHits.Count - 1; i >= 0; i--) { if (t == 0) id = rayHits[i].PartId; else id = rayHits[i].GroupId; int hit = (int)hits[id]; if (hit > m_maxHitsPerPrimInCastRay) { rayHits.RemoveAt(i); hit--; hits[id] = hit; } } } } // Parse hits into result list according to data flags int hitCount = rayHits.Count; if (hitCount > maxHits) hitCount = maxHits; for (int i = 0; i < hitCount; i++) { RayHit rayHit = rayHits[i]; if (getRootKey) result.Add(new LSL_Key(rayHit.GroupId.ToString())); else result.Add(new LSL_Key(rayHit.PartId.ToString())); result.Add(new LSL_Vector(rayHit.Position)); if (getLinkNum) result.Add(new LSL_Integer(rayHit.Link)); if (getNormal) result.Add(new LSL_Vector(rayHit.Normal)); } result.Add(new LSL_Integer(hitCount)); // Add to throttle data stopWatch.Stop(); CastRayCall castRayCall = new CastRayCall(); castRayCall.RegionId = regionId; castRayCall.UserId = userId; castRayCall.CalledMs = calledMs; castRayCall.UsedMs = (int)stopWatch.ElapsedMilliseconds; lock (m_castRayCalls) { m_castRayCalls.Add(castRayCall); } // Return hits return result; }
private void Asset_MeshCallback(bool success, AssetMesh assetMesh) { lock (Manager.AssetsReceived) Manager.AssetsReceived[assetMesh.AssetID] = success; }
/// <summary> /// Decodes mesh asset into FacetedMesh /// </summary> /// <param name="prim">Mesh primitive</param> /// <param name="meshAsset">Asset retrieved from the asset server</param> /// <param name="LOD">Level of detail</param> /// <param name="mesh">Resulting decoded FacetedMesh</param> /// <returns>True if mesh asset decoding was successful</returns> public static bool TryDecodeFromAsset(Primitive prim, AssetMesh meshAsset, DetailLevel LOD, out FacetedMesh mesh) { mesh = null; try { if (!meshAsset.Decode()) { return false; } OSDMap MeshData = meshAsset.MeshData; mesh = new FacetedMesh(); mesh.Faces = new List<Face>(); mesh.Prim = prim; mesh.Profile.Faces = new List<ProfileFace>(); mesh.Profile.Positions = new List<Vector3>(); mesh.Path.Points = new List<PathPoint>(); OSD facesOSD = null; switch (LOD) { default: case DetailLevel.Highest: facesOSD = MeshData["high_lod"]; break; case DetailLevel.High: facesOSD = MeshData["medium_lod"]; break; case DetailLevel.Medium: facesOSD = MeshData["low_lod"]; break; case DetailLevel.Low: facesOSD = MeshData["lowest_lod"]; break; } if (facesOSD == null || !(facesOSD is OSDArray)) { return false; } OSDArray decodedMeshOsdArray = (OSDArray)facesOSD; for (int faceNr = 0; faceNr < decodedMeshOsdArray.Count; faceNr++) { OSD subMeshOsd = decodedMeshOsdArray[faceNr]; // Decode each individual face if (subMeshOsd is OSDMap) { Face oface = new Face(); oface.ID = faceNr; oface.Vertices = new List<Vertex>(); oface.Indices = new List<ushort>(); oface.TextureFace = prim.Textures.GetFace((uint)faceNr); OSDMap subMeshMap = (OSDMap)subMeshOsd; Vector3 posMax; Vector3 posMin; // If PositionDomain is not specified, the default is from -0.5 to 0.5 if (subMeshMap.ContainsKey("PositionDomain")) { posMax = ((OSDMap)subMeshMap["PositionDomain"])["Max"]; posMin = ((OSDMap)subMeshMap["PositionDomain"])["Min"]; } else { posMax = new Vector3(0.5f, 0.5f, 0.5f); posMin = new Vector3(-0.5f, -0.5f, -0.5f); } // Vertex positions byte[] posBytes = subMeshMap["Position"]; // Normals byte[] norBytes = null; if (subMeshMap.ContainsKey("Normal")) { norBytes = subMeshMap["Normal"]; } // UV texture map Vector2 texPosMax = Vector2.Zero; Vector2 texPosMin = Vector2.Zero; byte[] texBytes = null; if (subMeshMap.ContainsKey("TexCoord0")) { texBytes = subMeshMap["TexCoord0"]; texPosMax = ((OSDMap)subMeshMap["TexCoord0Domain"])["Max"]; texPosMin = ((OSDMap)subMeshMap["TexCoord0Domain"])["Min"]; } // Extract the vertex position data // If present normals and texture coordinates too for (int i = 0; i < posBytes.Length; i += 6) { ushort uX = Utils.BytesToUInt16(posBytes, i); ushort uY = Utils.BytesToUInt16(posBytes, i + 2); ushort uZ = Utils.BytesToUInt16(posBytes, i + 4); Vertex vx = new Vertex(); vx.Position = new Vector3( Utils.UInt16ToFloat(uX, posMin.X, posMax.X), Utils.UInt16ToFloat(uY, posMin.Y, posMax.Y), Utils.UInt16ToFloat(uZ, posMin.Z, posMax.Z)); if (norBytes != null && norBytes.Length >= i + 4) { ushort nX = Utils.BytesToUInt16(norBytes, i); ushort nY = Utils.BytesToUInt16(norBytes, i + 2); ushort nZ = Utils.BytesToUInt16(norBytes, i + 4); vx.Normal = new Vector3( Utils.UInt16ToFloat(nX, posMin.X, posMax.X), Utils.UInt16ToFloat(nY, posMin.Y, posMax.Y), Utils.UInt16ToFloat(nZ, posMin.Z, posMax.Z)); } var vertexIndexOffset = oface.Vertices.Count * 4; if (texBytes != null && texBytes.Length >= vertexIndexOffset + 4) { ushort tX = Utils.BytesToUInt16(texBytes, vertexIndexOffset); ushort tY = Utils.BytesToUInt16(texBytes, vertexIndexOffset + 2); vx.TexCoord = new Vector2( Utils.UInt16ToFloat(tX, texPosMin.X, texPosMax.X), Utils.UInt16ToFloat(tY, texPosMin.Y, texPosMax.Y)); } oface.Vertices.Add(vx); } byte[] triangleBytes = subMeshMap["TriangleList"]; for (int i = 0; i < triangleBytes.Length; i += 6) { ushort v1 = (ushort)(Utils.BytesToUInt16(triangleBytes, i)); oface.Indices.Add(v1); ushort v2 = (ushort)(Utils.BytesToUInt16(triangleBytes, i + 2)); oface.Indices.Add(v2); ushort v3 = (ushort)(Utils.BytesToUInt16(triangleBytes, i + 4)); oface.Indices.Add(v3); } mesh.Faces.Add(oface); } } } catch (Exception ex) { Logger.Log("Failed to decode mesh asset: " + ex.Message, Helpers.LogLevel.Warning); return false; } return true; }
private void CreatePrim(WarpRenderer renderer, SceneObjectPart prim, bool useTextures) { const float MIN_SIZE = 2f; if ((PCode)prim.Shape.PCode != PCode.Prim) return; if (prim.Scale.LengthSquared() < MIN_SIZE * MIN_SIZE) return; FacetedMesh renderMesh = null; Primitive omvPrim = prim.Shape.ToOmvPrimitive(prim.OffsetPosition, prim.RotationOffset); if (m_renderMeshes) { if (omvPrim.Sculpt != null && omvPrim.Sculpt.SculptTexture != UUID.Zero) { // Try fetchinng the asset byte[] sculptAsset = m_scene.AssetService.GetData(omvPrim.Sculpt.SculptTexture.ToString()); if (sculptAsset != null) { // Is it a mesh? if (omvPrim.Sculpt.Type == SculptType.Mesh) { AssetMesh meshAsset = new AssetMesh(omvPrim.Sculpt.SculptTexture, sculptAsset); FacetedMesh.TryDecodeFromAsset(omvPrim, meshAsset, DetailLevel.Highest, out renderMesh); meshAsset = null; } else // It's sculptie { IJ2KDecoder imgDecoder = m_scene.RequestModuleInterface<IJ2KDecoder>(); if (imgDecoder != null) { Image sculpt = imgDecoder.DecodeToImage(sculptAsset); if (sculpt != null) { renderMesh = m_primMesher.GenerateFacetedSculptMesh(omvPrim, (Bitmap)sculpt, DetailLevel.Medium); sculpt.Dispose(); } } } } } } // If not a mesh or sculptie, try the regular mesher if (renderMesh == null) { renderMesh = m_primMesher.GenerateFacetedMesh(omvPrim, DetailLevel.Medium); } if (renderMesh == null) return; warp_Vector primPos = ConvertVector(prim.GetWorldPosition()); warp_Quaternion primRot = ConvertQuaternion(prim.RotationOffset); warp_Matrix m = warp_Matrix.quaternionMatrix(primRot); if (prim.ParentID != 0) { SceneObjectGroup group = m_scene.SceneGraph.GetGroupByPrim(prim.LocalId); if (group != null) m.transform(warp_Matrix.quaternionMatrix(ConvertQuaternion(group.RootPart.RotationOffset))); } warp_Vector primScale = ConvertVector(prim.Scale); string primID = prim.UUID.ToString(); // Create the prim faces // TODO: Implement the useTextures flag behavior for (int i = 0; i < renderMesh.Faces.Count; i++) { Face face = renderMesh.Faces[i]; string meshName = primID + "-Face-" + i.ToString(); // Avoid adding duplicate meshes to the scene if (renderer.Scene.objectData.ContainsKey(meshName)) { continue; } warp_Object faceObj = new warp_Object(face.Vertices.Count, face.Indices.Count / 3); for (int j = 0; j < face.Vertices.Count; j++) { Vertex v = face.Vertices[j]; warp_Vector pos = ConvertVector(v.Position); warp_Vector norm = ConvertVector(v.Normal); if (prim.Shape.SculptTexture == UUID.Zero) norm = norm.reverse(); warp_Vertex vert = new warp_Vertex(pos, norm, v.TexCoord.X, v.TexCoord.Y); faceObj.addVertex(vert); } for (int j = 0; j < face.Indices.Count; j += 3) { faceObj.addTriangle( face.Indices[j + 0], face.Indices[j + 1], face.Indices[j + 2]); } Primitive.TextureEntryFace teFace = prim.Shape.Textures.GetFace((uint)i); Color4 faceColor = GetFaceColor(teFace); string materialName = String.Empty; if (m_texturePrims && prim.Scale.LengthSquared() > m_texturePrimSize*m_texturePrimSize) materialName = GetOrCreateMaterial(renderer, faceColor, teFace.TextureID); else materialName = GetOrCreateMaterial(renderer, faceColor); faceObj.transform(m); faceObj.setPos(primPos); faceObj.scaleSelf(primScale.x, primScale.y, primScale.z); renderer.Scene.addObject(meshName, faceObj); renderer.SetObjectMaterial(meshName, materialName); } }