diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs index 73b0a35b7c..cfcfc79c11 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs @@ -274,7 +274,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess float minX, minY, minZ; float maxX, maxY, maxZ; - Vector3[] offsets = m_Scene.GetCombinedBoundingBox(objlist, + Vector3[] offsets = Scene.GetCombinedBoundingBox(objlist, out minX, out maxX, out minY, out maxY, out minZ, out maxZ); @@ -289,7 +289,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess XmlDocument doc = new XmlDocument(); SceneObjectGroup g = objlist[i]; doc.LoadXml(xmlStrings[g.UUID]); - XmlElement e = (XmlElement)doc.SelectSingleNode("/SceneObjectGroup"); + XmlElement e = (XmlElement)doc.SelectSingleNode("/SceneObjectGroup"); e.SetAttribute("offsetx", offsets[i].X.ToString()); e.SetAttribute("offsety", offsets[i].Y.ToString()); e.SetAttribute("offsetz", offsets[i].Z.ToString()); @@ -659,9 +659,18 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess itemId, n.OuterXml); objlist.Add(g); XmlElement el = (XmlElement)n; - float x = Convert.ToSingle(el.GetAttribute("offsetx")); - float y = Convert.ToSingle(el.GetAttribute("offsety")); - float z = Convert.ToSingle(el.GetAttribute("offsetz")); + + string rawX = el.GetAttribute("offsetx"); + string rawY = el.GetAttribute("offsety"); + string rawZ = el.GetAttribute("offsetz"); +// +// m_log.DebugFormat( +// "[INVENTORY ACCESS MODULE]: Converting coalesced object {0} offset <{1}, {2}, {3}>", +// g.Name, rawX, rawY, rawZ); + + float x = Convert.ToSingle(rawX); + float y = Convert.ToSingle(rawY); + float z = Convert.ToSingle(rawZ); veclist.Add(new Vector3(x, y, z)); } } diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs index 5eca753d03..f8fa424738 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs @@ -80,6 +80,85 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests m_tc = new TestClient(acd, m_scene); } + [Test] + public void TestRezCoalescedObject() + { + TestHelper.InMethod(); + log4net.Config.XmlConfigurator.Configure(); + + // Create asset + SceneObjectGroup object1; + { + string partName = "Object1"; + UUID ownerId = UUID.Parse("00000000-0000-0000-0000-000000000040"); + PrimitiveBaseShape shape = PrimitiveBaseShape.CreateSphere(); + Vector3 groupPosition = new Vector3(10, 20, 30); + Quaternion rotationOffset = new Quaternion(20, 30, 40, 50); + Vector3 offsetPosition = new Vector3(5, 10, 15); + + SceneObjectPart part1 + = new SceneObjectPart( + ownerId, shape, groupPosition, rotationOffset, offsetPosition); + part1.Name = partName; + + object1 = new SceneObjectGroup(part1); + } + + SceneObjectGroup object2; + { + string partName = "Object2"; + UUID ownerId = UUID.Parse("00000000-0000-0000-0000-000000000040"); + PrimitiveBaseShape shape = PrimitiveBaseShape.CreateSphere(); + Vector3 groupPosition = new Vector3(10, 20, 30); + Quaternion rotationOffset = new Quaternion(20, 30, 40, 50); + Vector3 offsetPosition = new Vector3(5, 10, 15); + + SceneObjectPart part1 + = new SceneObjectPart( + ownerId, shape, groupPosition, rotationOffset, offsetPosition); + part1.Name = partName; + + object2 = new SceneObjectGroup(part1); + } + + CoalescedSceneObjects coa = new CoalescedSceneObjects(m_userId, object1, object2); + + UUID asset1Id = UUID.Parse("00000000-0000-0000-0000-000000000060"); + AssetBase asset1 = AssetHelpers.CreateAsset(asset1Id, coa); + m_scene.AssetService.Store(asset1); + + // Create item + UUID item1Id = UUID.Parse("00000000-0000-0000-0000-000000000080"); + string item1Name = "My Little Dog"; + InventoryItemBase item1 = new InventoryItemBase(); + item1.Name = item1Name; + item1.AssetID = asset1.FullID; + item1.ID = item1Id; + InventoryFolderBase objsFolder + = InventoryArchiveUtils.FindFolderByPath(m_scene.InventoryService, m_userId, "Objects")[0]; + item1.Folder = objsFolder.ID; + m_scene.AddInventoryItem(item1); + + SceneObjectGroup so + = m_iam.RezObject( + m_tc, item1Id, Vector3.Zero, Vector3.Zero, UUID.Zero, 1, false, false, false, UUID.Zero, false); + + Assert.That(so, Is.Not.Null); + + Assert.That(m_scene.SceneGraph.GetTotalObjectsCount(), Is.EqualTo(2)); + + SceneObjectPart retrievedObj1Part = m_scene.GetSceneObjectPart(object1.Name); + Assert.That(retrievedObj1Part, Is.Null); + + retrievedObj1Part = m_scene.GetSceneObjectPart(item1.Name); + Assert.That(retrievedObj1Part, Is.Not.Null); + Assert.That(retrievedObj1Part.Name, Is.EqualTo(item1.Name)); + + // FIXME: Can't test yet due to a bug where objects in coalescence all get the item name when rerezzed. +// SceneObjectPart retrievedObj2Part = m_scene.GetSceneObjectPart(object2.Name); +// Assert.That(retrievedObj2Part, Is.Not.Null); + } + [Test] public void TestRezObject() { diff --git a/OpenSim/Region/Framework/Scenes/CoalescedSceneObjects.cs b/OpenSim/Region/Framework/Scenes/CoalescedSceneObjects.cs new file mode 100644 index 0000000000..51eac5f3f6 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/CoalescedSceneObjects.cs @@ -0,0 +1,128 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using OpenMetaverse; + +namespace OpenSim.Region.Framework.Scenes +{ + /// + /// Represents a coalescene of scene objects. A coalescence occurs when objects that are not in the same linkset + /// are grouped together. + /// + public class CoalescedSceneObjects + { + /// + /// The creator of this coalesence, though not necessarily the objects within it. + /// + public UUID CreatorId { get; set; } + + /// + /// The number of objects in this coalesence + /// + public int Count + { + get + { + lock (m_memberObjects) + return m_memberObjects.Count; + } + } + + /// + /// Does this coalesence have any member objects? + /// + public bool HasObjects { get { return Count > 0; } } + + /// + /// Get the objects currently in this coalescence + /// + public List Objects + { + get + { + lock (m_memberObjects) + return new List(m_memberObjects); + } + } + + /// + /// Get the scene that contains the objects in this coalescence. If there are no objects then null is returned. + /// + public Scene Scene + { + get + { + if (!HasObjects) + return null; + else + return Objects[0].Scene; + } + } + + /// + /// At this point, we need to preserve the order of objects added to the coalescence, since the first + /// one will end up matching the item name when rerezzed. + /// + protected List m_memberObjects = new List(); + + public CoalescedSceneObjects(UUID creatorId) + { + CreatorId = creatorId; + } + + public CoalescedSceneObjects(UUID creatorId, params SceneObjectGroup[] objs) : this(creatorId) + { + foreach (SceneObjectGroup obj in objs) + Add(obj); + } + + /// + /// Add an object to the coalescence. + /// + /// + /// The offset of the object within the group + public void Add(SceneObjectGroup obj) + { + lock (m_memberObjects) + m_memberObjects.Add(obj); + } + + /// + /// Removes a scene object from the coalescene + /// + /// + /// true if the object was there to be removed, false if not. + public bool Remove(SceneObjectGroup obj) + { + lock (m_memberObjects) + return m_memberObjects.Remove(obj); + } + } +} \ No newline at end of file diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index f0acc38820..e6dd48940f 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -4839,7 +4839,20 @@ namespace OpenSim.Region.Framework.Scenes } } - public Vector3[] GetCombinedBoundingBox(List objects, out float minX, out float maxX, out float minY, out float maxY, out float minZ, out float maxZ) + /// + /// Get the volume of space that will encompass all the given objects. + /// + /// + /// + /// + /// + /// + /// + /// + /// + public static Vector3[] GetCombinedBoundingBox( + List objects, + out float minX, out float maxX, out float minY, out float maxY, out float minZ, out float maxZ) { minX = 256; maxX = -256; diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs index 97af0a0b47..72f040252f 100644 --- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs +++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs @@ -997,6 +997,8 @@ namespace OpenSim.Region.Framework.Scenes { foreach (SceneObjectPart p in ((SceneObjectGroup)entity).Parts) { +// m_log.DebugFormat("[SCENE GRAPH]: Part {0} has name {1}", p.UUID, p.Name); + if (p.Name == name) { sop = p; diff --git a/OpenSim/Region/Framework/Scenes/Serialization/CoalescedSceneObjectsSerializer.cs b/OpenSim/Region/Framework/Scenes/Serialization/CoalescedSceneObjectsSerializer.cs new file mode 100644 index 0000000000..3af2f76340 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/Serialization/CoalescedSceneObjectsSerializer.cs @@ -0,0 +1,117 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Reflection; +using System.Xml; +using log4net; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.Framework.Scenes.Serialization +{ + /// + /// Serialize and deserialize coalesced scene objects. + /// + public class CoalescedSceneObjectsSerializer + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Serialize coalesced objects to Xml + /// + /// + /// + public static string ToXml(CoalescedSceneObjects coa) + { + // TODO: Should probably return an empty xml serialization rather than a blank string + if (!coa.HasObjects) + return ""; + + using (StringWriter sw = new StringWriter()) + { + using (XmlTextWriter writer = new XmlTextWriter(sw)) + { + List coaObjects = coa.Objects; + +// m_log.DebugFormat( +// "[COALESCED SCENE OBJECTS SERIALIZER]: Writing {0} objects for coalesced object", +// coaObjects.Count); + + float minX, minY, minZ; + float maxX, maxY, maxZ; + + Vector3[] offsets = Scene.GetCombinedBoundingBox(coaObjects, + out minX, out maxX, out minY, out maxY, + out minZ, out maxZ); + + writer.WriteStartElement("CoalescedObject"); + + float sizeX = maxX - minX; + float sizeY = maxY - minY; + float sizeZ = maxZ - minZ; + + writer.WriteAttributeString("x", sizeX.ToString()); + writer.WriteAttributeString("y", sizeY.ToString()); + writer.WriteAttributeString("z", sizeZ.ToString()); + + // Embed the offsets into the group XML + for (int i = 0; i < coaObjects.Count; i++) + { + SceneObjectGroup obj = coaObjects[i]; + +// m_log.DebugFormat( +// "[COALESCED SCENE OBJECTS SERIALIZER]: Writing offset for object {0}, {1}", +// i, obj.Name); + + writer.WriteStartElement("SceneObjectGroup"); + writer.WriteAttributeString("offsetx", offsets[i].X.ToString()); + writer.WriteAttributeString("offsety", offsets[i].Y.ToString()); + writer.WriteAttributeString("offsetz", offsets[i].Z.ToString()); + + SceneObjectSerializer.ToOriginalXmlFormat(obj, writer, true); + + writer.WriteEndElement(); + } + + writer.WriteEndElement(); // CoalescedObject + } + + string output = sw.ToString(); + +// m_log.Debug(output); + + return output; + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs b/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs index b412e25579..bb8a83a1af 100644 --- a/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs +++ b/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs @@ -139,6 +139,7 @@ namespace OpenSim.Region.Framework.Scenes.Serialization return sw.ToString(); } } + /// /// Serialize a scene object to the original xml format @@ -146,11 +147,25 @@ namespace OpenSim.Region.Framework.Scenes.Serialization /// /// public static void ToOriginalXmlFormat(SceneObjectGroup sceneObject, XmlTextWriter writer) + { + ToOriginalXmlFormat(sceneObject, writer, false); + } + + /// + /// Serialize a scene object to the original xml format + /// + /// + /// + /// If false, don't write the enclosing SceneObjectGroup element + /// + public static void ToOriginalXmlFormat(SceneObjectGroup sceneObject, XmlTextWriter writer, bool noRootElement) { //m_log.DebugFormat("[SERIALIZER]: Starting serialization of {0}", Name); //int time = System.Environment.TickCount; - writer.WriteStartElement(String.Empty, "SceneObjectGroup", String.Empty); + if (!noRootElement) + writer.WriteStartElement(String.Empty, "SceneObjectGroup", String.Empty); + writer.WriteStartElement(String.Empty, "RootPart", String.Empty); ToXmlFormat(sceneObject.RootPart, writer); writer.WriteEndElement(); @@ -170,10 +185,12 @@ namespace OpenSim.Region.Framework.Scenes.Serialization writer.WriteEndElement(); // OtherParts sceneObject.SaveScriptedState(writer); - writer.WriteEndElement(); // SceneObjectGroup + + if (!noRootElement) + writer.WriteEndElement(); // SceneObjectGroup //m_log.DebugFormat("[SERIALIZER]: Finished serialization of SOG {0}, {1}ms", Name, System.Environment.TickCount - time); - } + } protected static void ToXmlFormat(SceneObjectPart part, XmlTextWriter writer) { diff --git a/OpenSim/Tests/Common/Setup/AssetHelpers.cs b/OpenSim/Tests/Common/Setup/AssetHelpers.cs index ff4423f7ab..af66d7f0a7 100644 --- a/OpenSim/Tests/Common/Setup/AssetHelpers.cs +++ b/OpenSim/Tests/Common/Setup/AssetHelpers.cs @@ -71,6 +71,21 @@ namespace OpenSim.Tests.Common Encoding.ASCII.GetBytes(SceneObjectSerializer.ToOriginalXmlFormat(sog)), sog.OwnerID); } + + /// + /// Create an asset from the given scene object. + /// + /// + /// + /// + public static AssetBase CreateAsset(UUID assetUuid, CoalescedSceneObjects coa) + { + return CreateAsset( + assetUuid, + AssetType.Object, + Encoding.ASCII.GetBytes(CoalescedSceneObjectsSerializer.ToXml(coa)), + coa.CreatorId); + } /// /// Create an asset from the given data.