diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 5b4959afbb..e5c8cb97e8 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -164,6 +164,7 @@ This software uses components from the following developers: * log4net (http://logging.apache.org/log4net/) * GlynnTucker.Cache (http://gtcache.sourceforge.net/) * NDesk.Options 0.2.1 (http://www.ndesk.org/Options) +* Json.NET 3.5 Release 6. The binary used is actually Newtonsoft.Json.Net20.dll for Mono 2.4 compatability (http://james.newtonking.com/projects/json-net.aspx) Some plugins are based on Cable Beach Cable Beach is Copyright (c) 2008 Intel Corporation diff --git a/OpenSim/Data/IGridUserData.cs b/OpenSim/Data/IGridUserData.cs index abd2cef054..bd7a435391 100644 --- a/OpenSim/Data/IGridUserData.cs +++ b/OpenSim/Data/IGridUserData.cs @@ -44,7 +44,7 @@ namespace OpenSim.Data /// public interface IGridUserData { - GridUserData GetGridUserData(string userID); + GridUserData GetGridUserData(string userID); bool StoreGridUserData(GridUserData data); } } \ No newline at end of file diff --git a/OpenSim/Data/MSSQL/MSSQLAvatarData.cs b/OpenSim/Data/MSSQL/MSSQLAvatarData.cs index 4992183a3a..49a6b09f76 100644 --- a/OpenSim/Data/MSSQL/MSSQLAvatarData.cs +++ b/OpenSim/Data/MSSQL/MSSQLAvatarData.cs @@ -47,7 +47,7 @@ namespace OpenSim.Data.MSSQL public MSSQLAvatarData(string connectionString, string realm) : base(connectionString, realm, "Avatar") - { + { } public bool Delete(UUID principalID, string name) diff --git a/OpenSim/Data/MSSQL/MSSQLGenericTableHandler.cs b/OpenSim/Data/MSSQL/MSSQLGenericTableHandler.cs index 506056dd62..904366e995 100644 --- a/OpenSim/Data/MSSQL/MSSQLGenericTableHandler.cs +++ b/OpenSim/Data/MSSQL/MSSQLGenericTableHandler.cs @@ -110,7 +110,7 @@ namespace OpenSim.Data.MSSQL { List constraints = new List(); string query = string.Format(@"SELECT - COL_NAME(ic.object_id,ic.column_id) AS column_name + COL_NAME(ic.object_id,ic.column_id) AS column_name FROM sys.indexes AS i INNER JOIN sys.index_columns AS ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id @@ -157,7 +157,7 @@ namespace OpenSim.Data.MSSQL string where = String.Join(" AND ", terms.ToArray()); string query = String.Format("SELECT * FROM {0} WHERE {1}", - m_Realm, where); + m_Realm, where); cmd.Connection = conn; cmd.CommandText = query; @@ -296,7 +296,7 @@ namespace OpenSim.Data.MSSQL query.AppendFormat("[{0}] = {1} ", names[i], values[i]); if (constraints.Count > 0) { - List terms = new List(); + List terms = new List(); for (int j = 0; j < constraints.Count; j++) { terms.Add(" [" + constraints[j].Key + "] = @" + constraints[j].Key); diff --git a/OpenSim/Data/MSSQL/MSSQLGridUserData.cs b/OpenSim/Data/MSSQL/MSSQLGridUserData.cs index b4a945c08e..9993720a04 100644 --- a/OpenSim/Data/MSSQL/MSSQLGridUserData.cs +++ b/OpenSim/Data/MSSQL/MSSQLGridUserData.cs @@ -47,7 +47,7 @@ namespace OpenSim.Data.MSSQL public MSSQLGridUserData(string connectionString, string realm) : base(connectionString, realm, "UserGrid") - { + { } public GridUserData GetGridUserData(string userID) @@ -58,7 +58,7 @@ namespace OpenSim.Data.MSSQL return null; return ret[0]; - } + } public bool StoreGridUserData(GridUserData data) { diff --git a/OpenSim/Data/MSSQL/MSSQLManager.cs b/OpenSim/Data/MSSQL/MSSQLManager.cs index 4309b42366..575fd210c1 100644 --- a/OpenSim/Data/MSSQL/MSSQLManager.cs +++ b/OpenSim/Data/MSSQL/MSSQLManager.cs @@ -46,7 +46,7 @@ namespace OpenSim.Data.MSSQL /// /// Connection string for ADO.net /// - private readonly string connectionString; + private readonly string connectionString; /// /// Initialize the manager and set the connectionstring @@ -196,7 +196,7 @@ namespace OpenSim.Data.MSSQL migration.Update(); } - } + } /// /// Returns the version of this DB provider diff --git a/OpenSim/Data/MSSQL/MSSQLUserAccountData.cs b/OpenSim/Data/MSSQL/MSSQLUserAccountData.cs index 9f18e5e839..e7c8dc5a7e 100644 --- a/OpenSim/Data/MSSQL/MSSQLUserAccountData.cs +++ b/OpenSim/Data/MSSQL/MSSQLUserAccountData.cs @@ -43,7 +43,7 @@ namespace OpenSim.Data.MSSQL { } //private string m_Realm; - //private List m_ColumnNames = null; + //private List m_ColumnNames = null; //private MSSQLManager m_database; //public MSSQLUserAccountData(string connectionString, string realm) diff --git a/OpenSim/Data/MySQL/MySQLGenericTableHandler.cs b/OpenSim/Data/MySQL/MySQLGenericTableHandler.cs index 756b42d7c1..1253e0b455 100644 --- a/OpenSim/Data/MySQL/MySQLGenericTableHandler.cs +++ b/OpenSim/Data/MySQL/MySQLGenericTableHandler.cs @@ -197,7 +197,7 @@ namespace OpenSim.Data.MySQL public virtual T[] Get(string where) { using (MySqlCommand cmd = new MySqlCommand()) - { + { string query = String.Format("select * from {0} where {1}", m_Realm, where); diff --git a/OpenSim/Data/MySQL/MySQLGridUserData.cs b/OpenSim/Data/MySQL/MySQLGridUserData.cs index 15834d2ab7..df29ecdc2d 100644 --- a/OpenSim/Data/MySQL/MySQLGridUserData.cs +++ b/OpenSim/Data/MySQL/MySQLGridUserData.cs @@ -54,7 +54,7 @@ namespace OpenSim.Data.MySQL return null; return ret[0]; - } + } public bool StoreGridUserData(GridUserData data) { diff --git a/OpenSim/Data/MySQL/MySQLPresenceData.cs b/OpenSim/Data/MySQL/MySQLPresenceData.cs index 68a68af3c8..143dbe38cb 100644 --- a/OpenSim/Data/MySQL/MySQLPresenceData.cs +++ b/OpenSim/Data/MySQL/MySQLPresenceData.cs @@ -134,7 +134,7 @@ namespace OpenSim.Data.MySQL List deleteSessions = new List(); int online = 0; - while(reader.Read()) + while (reader.Read()) { if (bool.Parse(reader["Online"].ToString())) online++; diff --git a/OpenSim/Data/Null/NullPresenceData.cs b/OpenSim/Data/Null/NullPresenceData.cs index 9fc45958eb..b98b5c92b5 100644 --- a/OpenSim/Data/Null/NullPresenceData.cs +++ b/OpenSim/Data/Null/NullPresenceData.cs @@ -55,7 +55,7 @@ namespace OpenSim.Data.Null } public bool Store(PresenceData data) - { + { if (Instance != this) return Instance.Store(data); @@ -113,7 +113,7 @@ namespace OpenSim.Data.Null } public bool SetHomeLocation(string userID, UUID regionID, Vector3 position, Vector3 lookAt) - { + { if (Instance != this) return Instance.SetHomeLocation(userID, regionID, position, lookAt); @@ -130,28 +130,28 @@ namespace OpenSim.Data.Null p.Data["HomePosition"] = position.ToString(); p.Data["HomeLookAt"] = lookAt.ToString(); foundone = true; - } + } } return foundone; } public PresenceData[] Get(string field, string data) - { + { if (Instance != this) return Instance.Get(field, data); // m_log.DebugFormat( -// "[NULL PRESENCE DATA]: Getting presence data for field {0} with parameter {1}", field, data); +// "[NULL PRESENCE DATA]: Getting presence data for field {0} with parameter {1}", field, data); List presences = new List(); if (field == "UserID") { foreach (PresenceData p in m_presenceData.Values) { - if (p.UserID == data) + if (p.UserID == data) { - presences.Add(p); + presences.Add(p); // Console.WriteLine("HOME for " + p.UserID + " is " + (p.Data.ContainsKey("HomeRegionID") ? p.Data["HomeRegionID"] : "Not found")); } } @@ -194,7 +194,7 @@ namespace OpenSim.Data.Null } public void Prune(string userID) - { + { if (Instance != this) { Instance.Prune(userID); diff --git a/OpenSim/Data/SQLite/SQLiteAuthenticationData.cs b/OpenSim/Data/SQLite/SQLiteAuthenticationData.cs index 84ce77546a..2c28375485 100644 --- a/OpenSim/Data/SQLite/SQLiteAuthenticationData.cs +++ b/OpenSim/Data/SQLite/SQLiteAuthenticationData.cs @@ -120,7 +120,7 @@ namespace OpenSim.Data.SQLite } public bool Store(AuthenticationData data) - { + { if (data.Data.ContainsKey("UUID")) data.Data.Remove("UUID"); diff --git a/OpenSim/Framework/MultipartForm.cs b/OpenSim/Framework/MultipartForm.cs index 8ba6d22683..90c4007ddc 100644 --- a/OpenSim/Framework/MultipartForm.cs +++ b/OpenSim/Framework/MultipartForm.cs @@ -1,4 +1,31 @@ -using System; +/* + * 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.Net; using System.IO; diff --git a/OpenSim/Framework/SLUtil.cs b/OpenSim/Framework/SLUtil.cs index 2fc5bdf163..f6d6a7cf60 100644 --- a/OpenSim/Framework/SLUtil.cs +++ b/OpenSim/Framework/SLUtil.cs @@ -1,4 +1,31 @@ -using System; +/* + * 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.Reflection; using log4net; @@ -219,7 +246,7 @@ namespace OpenSim.Framework /// Parse a notecard in Linden format to a string of ordinary text. /// /// - /// + /// public static string ParseNotecardToString(string rawInput) { string[] output = ParseNotecardToList(rawInput).ToArray(); @@ -237,7 +264,7 @@ namespace OpenSim.Framework /// public static List ParseNotecardToList(string rawInput) { - string[] input = rawInput.Replace("\r", "").Split('\n'); + string[] input = rawInput.Replace("\r", "").Split('\n'); int idx = 0; int level = 0; List output = new List(); diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs index 214f936e1e..9a6ef77437 100644 --- a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs +++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs @@ -202,16 +202,14 @@ namespace OpenSim.Framework.Servers.HttpServer if (!m_pollHandlers.ContainsKey(methodName)) { m_pollHandlers.Add(methodName,args); - pollHandlerResult = true; - + pollHandlerResult = true; } } if (pollHandlerResult) return AddHTTPHandler(methodName, handler); - return false; - + return false; } // Note that the agent string is provided simply to differentiate diff --git a/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs index d13408de86..65b1eb5699 100644 --- a/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs +++ b/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs @@ -47,7 +47,7 @@ namespace OpenSim.Framework.Servers.HttpServer bool AddAgentHandler(string agent, IHttpAgentHandler handler); /// - /// Add a handler for an HTTP request + /// Add a handler for an HTTP request. /// /// /// This handler can actually be invoked either as @@ -66,6 +66,10 @@ namespace OpenSim.Framework.Servers.HttpServer /// or /// /// http://localhost:9000/object/ + /// + /// In addition, the handler invoked by the HTTP server for any request is the one when best matches the request + /// URI. So if a handler for "/myapp/" is registered and a request for "/myapp/page" is received, then + /// the "/myapp/" handler is invoked if no "/myapp/page" handler exists. /// /// /// @@ -73,7 +77,6 @@ namespace OpenSim.Framework.Servers.HttpServer /// true if the handler was successfully registered, false if a handler with the same name already existed. /// bool AddHTTPHandler(string methodName, GenericHTTPMethod handler); - bool AddPollServiceHTTPHandler(string methodName, GenericHTTPMethod handler, PollServiceEventArgs args); diff --git a/OpenSim/Framework/UntrustedWebRequest.cs b/OpenSim/Framework/UntrustedWebRequest.cs index 1af7c41558..e6411cc1e2 100644 --- a/OpenSim/Framework/UntrustedWebRequest.cs +++ b/OpenSim/Framework/UntrustedWebRequest.cs @@ -1,4 +1,31 @@ -using System; +/* + * 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.IO; using System.Net; @@ -118,7 +145,7 @@ namespace OpenSim.Framework /// True to allow loopback addresses to be used /// The URI to test for whether it should be allowed. /// - /// true if [is URI allowable] [the specified URI]; otherwise, false. + /// true if [is URI allowable] [the specified URI]; otherwise, false. /// private static bool IsUriAllowable(Uri uri, bool allowLoopback) { @@ -180,7 +207,7 @@ namespace OpenSim.Framework /// /// The ip address to check. /// - /// true if this is a loopback IP address; false otherwise. + /// true if this is a loopback IP address; false otherwise. /// private static bool IsIPv6Loopback(IPAddress ip) { diff --git a/OpenSim/Framework/WebUtil.cs b/OpenSim/Framework/WebUtil.cs index d9782ff9ac..16e44aff44 100644 --- a/OpenSim/Framework/WebUtil.cs +++ b/OpenSim/Framework/WebUtil.cs @@ -1,4 +1,31 @@ -using System; +/* + * 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.Collections.Specialized; using System.IO; diff --git a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs index d458364052..f54e41abee 100644 --- a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs @@ -36,7 +36,7 @@ using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; namespace OpenSim.Region.CoreModules.Avatar.Attachments -{ +{ public class AttachmentsModule : IAttachmentsModule, IRegionModule { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); @@ -67,6 +67,36 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments get { return false; } } + public void AttachObject(IClientAPI remoteClient, uint objectLocalID, uint AttachmentPt, Quaternion rot, bool silent) + { + m_log.Debug("[ATTACHMENTS MODULE]: Invoking AttachObject"); + + // If we can't take it, we can't attach it! + SceneObjectPart part = m_scene.GetSceneObjectPart(objectLocalID); + if (part == null) + return; + + if (!m_scene.Permissions.CanTakeObject(part.UUID, remoteClient.AgentId)) + return; + + // Calls attach with a Zero position + if (AttachObject(remoteClient, objectLocalID, AttachmentPt, rot, Vector3.Zero, false)) + { + m_scene.EventManager.TriggerOnAttach(objectLocalID, part.ParentGroup.GetFromItemID(), remoteClient.AgentId); + + // Save avatar attachment information + ScenePresence presence; + if (m_scene.AvatarFactory != null && m_scene.TryGetAvatar(remoteClient.AgentId, out presence)) + { + m_log.Info( + "[ATTACHMENTS MODULE]: Saving avatar attachment. AgentID: " + remoteClient.AgentId + + ", AttachmentPoint: " + AttachmentPt); + + m_scene.AvatarFactory.UpdateDatabase(remoteClient.AgentId, presence.Appearance); + } + } + } + public bool AttachObject( IClientAPI remoteClient, uint objectLocalID, uint AttachmentPt, Quaternion rot, Vector3 attachPos, bool silent) { @@ -138,12 +168,87 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments return true; } + + public UUID RezSingleAttachmentFromInventory(IClientAPI remoteClient, UUID itemID, uint AttachmentPt) + { + m_log.DebugFormat("[ATTACHMENTS MODULE]: Rezzing single attachment from item {0} for {1}", itemID, remoteClient.Name); + + return RezSingleAttachmentFromInventory(remoteClient, itemID, AttachmentPt, true); + } + + public UUID RezSingleAttachmentFromInventory( + IClientAPI remoteClient, UUID itemID, uint AttachmentPt, bool updateInventoryStatus) + { + SceneObjectGroup att = RezSingleAttachmentFromInventoryInternal(remoteClient, itemID, AttachmentPt); + + if (updateInventoryStatus) + { + if (att == null) + { + ShowDetachInUserInventory(itemID, remoteClient); + } + + SetAttachmentInventoryStatus(att, remoteClient, itemID, AttachmentPt); + } + + if (null == att) + return UUID.Zero; + else + return att.UUID; + } + + protected SceneObjectGroup RezSingleAttachmentFromInventoryInternal( + IClientAPI remoteClient, UUID itemID, uint AttachmentPt) + { + IInventoryAccessModule invAccess = m_scene.RequestModuleInterface(); + if (invAccess != null) + { + SceneObjectGroup objatt = invAccess.RezObject(remoteClient, + itemID, Vector3.Zero, Vector3.Zero, UUID.Zero, (byte)1, true, + false, false, remoteClient.AgentId, true); + +// m_log.DebugFormat( +// "[ATTACHMENTS MODULE]: Retrieved single object {0} for attachment to {1} on point {2}", +// objatt.Name, remoteClient.Name, AttachmentPt); + + if (objatt != null) + { + bool tainted = false; + if (AttachmentPt != 0 && AttachmentPt != objatt.GetAttachmentPoint()) + tainted = true; + + AttachObject( + remoteClient, objatt.LocalId, AttachmentPt, Quaternion.Identity, objatt.AbsolutePosition, false); + //objatt.ScheduleGroupForFullUpdate(); + + if (tainted) + objatt.HasGroupChanged = true; + + // Fire after attach, so we don't get messy perms dialogs + // 3 == AttachedRez + objatt.CreateScriptInstances(0, true, m_scene.DefaultScriptEngine, 3); + + // Do this last so that event listeners have access to all the effects of the attachment + m_scene.EventManager.TriggerOnAttach(objatt.LocalId, itemID, remoteClient.AgentId); + } + else + { + m_log.WarnFormat( + "[ATTACHMENTS MODULE]: Could not retrieve item {0} for attaching to avatar {1} at point {2}", + itemID, remoteClient.Name, AttachmentPt); + } + + return objatt; + } + + return null; + } public UUID SetAttachmentInventoryStatus( SceneObjectGroup att, IClientAPI remoteClient, UUID itemID, uint AttachmentPt) { m_log.DebugFormat( - "[ATTACHMENTS MODULEY]: Updating inventory of {0} to show attachment of {1} (item ID {2})", + "[ATTACHMENTS MODULE]: Updating inventory of {0} to show attachment of {1} (item ID {2})", remoteClient.Name, att.Name, itemID); if (!att.IsDeleted) @@ -204,7 +309,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments if (m_scene.AvatarFactory != null) m_scene.AvatarFactory.UpdateDatabase(remoteClient.AgentId, presence.Appearance); } - } + } public void ShowDetachInUserInventory(UUID itemID, IClientAPI remoteClient) { @@ -222,7 +327,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments } DetachSingleAttachmentToInv(itemID, remoteClient); - } + } // What makes this method odd and unique is it tries to detach using an UUID.... Yay for standards. // To LocalId or UUID, *THAT* is the question. How now Brown UUID?? @@ -252,6 +357,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments } } } - } + } } } \ No newline at end of file diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs index de324c072b..312db38ed0 100644 --- a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs @@ -394,11 +394,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends public IClientAPI LocateClientObject(UUID agentID) { Scene scene = GetClientScene(agentID); - if(scene == null) + if (scene == null) return null; ScenePresence presence = scene.GetScenePresence(agentID); - if(presence == null) + if (presence == null) return null; return presence.ControllingClient; @@ -481,7 +481,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends m_log.DebugFormat("[FRIENDS]: {0} offered friendship to {1}", principalID, friendID); // This user wants to be friends with the other user. - // Let's add both relations to the DB, but one of them is inactive (-1) + // Let's add the relation backwards, in case the other is not online FriendsService.StoreFriend(friendID, principalID.ToString(), 0); // Now let's ask the other user to be friends with this user diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs index c0d3f31076..ad050a1410 100644 --- a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs @@ -108,7 +108,7 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage if (!m_Enabled) return; - lock(m_Scenes) + lock (m_Scenes) { m_Scenes.Remove(scene); } diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs index 0fc467bc43..16e05b7e1c 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs @@ -576,7 +576,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess foreach (SceneObjectPart part in partList) { if (part.OwnerID != item.Owner) - { + { part.LastOwnerID = part.OwnerID; part.OwnerID = item.Owner; part.Inventory.ChangeInventoryOwner(item.Owner); diff --git a/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml b/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml index 0195c03d4c..aaa318cabb 100644 --- a/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml +++ b/OpenSim/Region/CoreModules/Resources/CoreModulePlugin.addin.xml @@ -56,6 +56,9 @@ + + + diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/Tests/PresenceConnectorsTests.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/Tests/PresenceConnectorsTests.cs index 292ff8ea87..63a28fc255 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/Tests/PresenceConnectorsTests.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/Tests/PresenceConnectorsTests.cs @@ -61,7 +61,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence.Tests m_LocalConnector = new LocalPresenceServicesConnector(config); // Let's stick in a test presence - m_LocalConnector.m_PresenceService.LoginAgent(UUID.Zero.ToString(), UUID.Zero, UUID.Zero); + m_LocalConnector.m_PresenceService.LoginAgent(UUID.Zero.ToString(), UUID.Zero, UUID.Zero); } /// @@ -80,7 +80,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence.Tests p.Data = new Dictionary(); p.Data["Online"] = true.ToString(); m_presenceData.Add(UUID.Zero, p); - */ + */ string user1 = UUID.Zero.ToString(); UUID session1 = UUID.Zero; diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/UserAccounts/LocalUserAccountServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/UserAccounts/LocalUserAccountServiceConnector.cs index 07fee79f48..30ebb2147b 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/UserAccounts/LocalUserAccountServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/UserAccounts/LocalUserAccountServiceConnector.cs @@ -73,33 +73,31 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.UserAccounts IConfig userConfig = source.Configs["UserAccountService"]; if (userConfig == null) { - m_log.Error("[USER CONNECTOR]: UserAccountService missing from OpenSim.ini"); + m_log.Error("[LOCAL USER ACCOUNT SERVICE CONNECTOR]: UserAccountService missing from OpenSim.ini"); return; } - string serviceDll = userConfig.GetString("LocalServiceModule", - String.Empty); + string serviceDll = userConfig.GetString("LocalServiceModule", String.Empty); if (serviceDll == String.Empty) { - m_log.Error("[USER CONNECTOR]: No LocalServiceModule named in section UserService"); + m_log.Error("[LOCAL USER ACCOUNT SERVICE CONNECTOR]: No LocalServiceModule named in section UserService"); return; } Object[] args = new Object[] { source }; - m_UserService = - ServerUtils.LoadPlugin(serviceDll, - args); + m_UserService = ServerUtils.LoadPlugin(serviceDll, args); if (m_UserService == null) { - m_log.Error("[USER CONNECTOR]: Can't load user account service"); + m_log.ErrorFormat( + "[LOCAL USER ACCOUNT SERVICE CONNECTOR]: Cannot load user account service specified as {0}", serviceDll); return; } m_Enabled = true; m_Cache = new UserAccountCache(); - m_log.Info("[USER CONNECTOR]: Local user connector enabled"); + m_log.Info("[LOCAL USER ACCOUNT SERVICE CONNECTOR]: Local user connector enabled"); } } } @@ -134,6 +132,8 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.UserAccounts { if (!m_Enabled) return; + + m_log.InfoFormat("[LOCAL USER ACCOUNT SERVICE CONNECTOR]: Enabled local user accounts for region {0}", scene.RegionInfo.RegionName); } #endregion @@ -142,26 +142,27 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.UserAccounts public UserAccount GetUserAccount(UUID scopeID, UUID userID) { - UserAccount account = m_Cache.Get(userID); - if (account != null) + bool inCache = false; + UserAccount account = m_Cache.Get(userID, out inCache); + if (inCache) return account; account = m_UserService.GetUserAccount(scopeID, userID); - if (account != null) - m_Cache.Cache(account); + m_Cache.Cache(userID, account); return account; } public UserAccount GetUserAccount(UUID scopeID, string firstName, string lastName) { - UserAccount account = m_Cache.Get(firstName + " " + lastName); - if (account != null) + bool inCache = false; + UserAccount account = m_Cache.Get(firstName + " " + lastName, out inCache); + if (inCache) return account; account = m_UserService.GetUserAccount(scopeID, firstName, lastName); if (account != null) - m_Cache.Cache(account); + m_Cache.Cache(account.PrincipalID, account); return account; } diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/UserAccounts/RemoteUserAccountServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/UserAccounts/RemoteUserAccountServiceConnector.cs index 1140692830..488dbd5dbe 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/UserAccounts/RemoteUserAccountServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/UserAccounts/RemoteUserAccountServiceConnector.cs @@ -119,26 +119,27 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.UserAccounts public override UserAccount GetUserAccount(UUID scopeID, UUID userID) { - UserAccount account = m_Cache.Get(userID); - if (account != null) + bool inCache = false; + UserAccount account = m_Cache.Get(userID, out inCache); + if (inCache) return account; account = base.GetUserAccount(scopeID, userID); - if (account != null) - m_Cache.Cache(account); + m_Cache.Cache(userID, account); return account; } public override UserAccount GetUserAccount(UUID scopeID, string firstName, string lastName) { - UserAccount account = m_Cache.Get(firstName + " " + lastName); - if (account != null) + bool inCache = false; + UserAccount account = m_Cache.Get(firstName + " " + lastName, out inCache); + if (inCache) return account; account = base.GetUserAccount(scopeID, firstName, lastName); if (account != null) - m_Cache.Cache(account); + m_Cache.Cache(account.PrincipalID, account); return account; } diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/UserAccounts/UserAccountCache.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/UserAccounts/UserAccountCache.cs index e430fc785c..a355661f6c 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/UserAccounts/UserAccountCache.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/UserAccounts/UserAccountCache.cs @@ -36,50 +36,58 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.UserAccounts { public class UserAccountCache { - //private static readonly ILog m_log = - // LogManager.GetLogger( - // MethodBase.GetCurrentMethod().DeclaringType); - - private ICnmCache m_UUIDCache; - private Dictionary m_NameCache; + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + private ExpiringCache m_UUIDCache; + private ExpiringCache m_NameCache; public UserAccountCache() { // Warning: the size values are a bit fuzzy. What matters // most for this cache is the count value (128 entries). - m_UUIDCache = CnmSynchronizedCache.Synchronized(new CnmMemoryCache( - 128, 128*512, TimeSpan.FromMinutes(30.0))); - m_NameCache = new Dictionary(); // this one is unbound + m_UUIDCache = new ExpiringCache(); + m_NameCache = new ExpiringCache(); // this one is unbound } - public void Cache(UserAccount account) + public void Cache(UUID userID, UserAccount account) { - m_UUIDCache.Set(account.PrincipalID, account, 512); - m_NameCache[account.Name] = account.PrincipalID; + // Cache even null accounts + m_UUIDCache.AddOrUpdate(userID, account, DateTime.Now + TimeSpan.FromMinutes(2.0d)); + if (account != null) + m_NameCache.AddOrUpdate(account.Name, account.PrincipalID, DateTime.Now + TimeSpan.FromMinutes(2.0d)); - //m_log.DebugFormat("[USER CACHE]: cached user {0} {1}", account.FirstName, account.LastName); + m_log.DebugFormat("[USER CACHE]: cached user {0}", userID); } - public UserAccount Get(UUID userID) + public UserAccount Get(UUID userID, out bool inCache) { UserAccount account = null; + inCache = false; if (m_UUIDCache.TryGetValue(userID, out account)) { //m_log.DebugFormat("[USER CACHE]: Account {0} {1} found in cache", account.FirstName, account.LastName); + inCache = true; return account; } return null; } - public UserAccount Get(string name) + public UserAccount Get(string name, out bool inCache) { - if (!m_NameCache.ContainsKey(name)) + inCache = false; + if (!m_NameCache.Contains(name)) return null; UserAccount account = null; - if (m_UUIDCache.TryGetValue(m_NameCache[name], out account)) - return account; + UUID uuid = UUID.Zero; + if (m_NameCache.TryGetValue(name, out uuid)) + if (m_UUIDCache.TryGetValue(uuid, out account)) + { + inCache = true; + return account; + } return null; } diff --git a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs index e0cdb36efe..bf856c86bb 100644 --- a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs +++ b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs @@ -183,7 +183,7 @@ namespace OpenSim.Region.CoreModules.World.Land void ClientOnPreAgentUpdate(IClientAPI remoteClient, AgentUpdateArgs agentData) { //If we are forcing a position for them to go - if( forcedPosition != null ) + if (forcedPosition != null) { ScenePresence clientAvatar = m_scene.GetScenePresence(remoteClient.AgentId); @@ -199,7 +199,7 @@ namespace OpenSim.Region.CoreModules.World.Land forcedPosition = null; } //if we are far away, teleport - else if(Vector3.Distance(clientAvatar.AbsolutePosition,forcedPosition.Value) > 3 ) + else if (Vector3.Distance(clientAvatar.AbsolutePosition,forcedPosition.Value) > 3) { Debug.WriteLine(string.Format("Teleporting out because {0} is too far from avatar position {1}",forcedPosition.Value,clientAvatar.AbsolutePosition)); clientAvatar.Teleport(forcedPosition.Value); @@ -340,7 +340,7 @@ namespace OpenSim.Region.CoreModules.World.Land public void SendYouAreRestrictedNotice(ScenePresence avatar) { - avatar.ControllingClient.SendAlertMessage( + avatar.ControllingClient.SendAlertMessage( "You are not allowed on this parcel because the land owner has restricted access."); } @@ -475,7 +475,7 @@ namespace OpenSim.Region.CoreModules.World.Land ForceAvatarToPosition(clientAvatar, m_scene.GetNearestAllowedPosition(clientAvatar)); } } - else if ( parcel.IsRestrictedFromLand(clientAvatar.UUID)) + else if (parcel.IsRestrictedFromLand(clientAvatar.UUID)) { //once we've sent the message once, keep going toward the target until we are done if (forcedPosition == null) @@ -487,7 +487,7 @@ namespace OpenSim.Region.CoreModules.World.Land else { //when we are finally in a safe place, lets release the forced position lock - forcedPosition = null; + forcedPosition = null; } } } diff --git a/OpenSim/Region/CoreModules/World/Land/LandObject.cs b/OpenSim/Region/CoreModules/World/Land/LandObject.cs index 27d9fdb5aa..e85136ae17 100644 --- a/OpenSim/Region/CoreModules/World/Land/LandObject.cs +++ b/OpenSim/Region/CoreModules/World/Land/LandObject.cs @@ -287,7 +287,7 @@ namespace OpenSim.Region.CoreModules.World.Land entry.Flags = AccessList.Ban; entry.Time = new DateTime(); //See if they are on the list, but make sure the owner isn't banned - if (LandData.ParcelAccessList.Contains(entry) && LandData.OwnerID != avatar ) + if (LandData.ParcelAccessList.Contains(entry) && LandData.OwnerID != avatar) { //They are banned, so lets send them a notice about this parcel return true; diff --git a/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs b/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs index 845c4c2dba..5c7f3b7225 100644 --- a/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs +++ b/OpenSim/Region/CoreModules/World/Permissions/PermissionsModule.cs @@ -618,7 +618,7 @@ namespace OpenSim.Region.CoreModules.World.Permissions return objectOwnerMask; // Estate users should be able to edit anything in the sim if RegionOwnerIsGod is set - if (IsEstateManager(user) && m_RegionOwnerIsGod) + if (m_RegionOwnerIsGod && IsEstateManager(user) && !IsAdministrator(objectOwner)) return objectOwnerMask; // Admin should be able to edit anything in the sim (including admin objects) diff --git a/OpenSim/Region/Framework/Interfaces/IAttachmentsModule.cs b/OpenSim/Region/Framework/Interfaces/IAttachmentsModule.cs index 367ff3da5f..0222b020c7 100644 --- a/OpenSim/Region/Framework/Interfaces/IAttachmentsModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IAttachmentsModule.cs @@ -31,9 +31,20 @@ using OpenSim.Framework; using OpenSim.Region.Framework.Scenes; namespace OpenSim.Region.Framework.Interfaces -{ +{ public interface IAttachmentsModule { + /// + /// Attach an object to an avatar from the world. + /// + /// + /// + /// + /// + /// + void AttachObject( + IClientAPI remoteClient, uint objectLocalID, uint AttachmentPt, Quaternion rot, bool silent); + /// /// Attach an object to an avatar. /// @@ -41,11 +52,34 @@ namespace OpenSim.Region.Framework.Interfaces /// /// /// - /// + /// /// /// true if the object was successfully attached, false otherwise bool AttachObject( - IClientAPI controllingClient, uint localID, uint attachPoint, Quaternion rot, Vector3 pos, bool silent); + IClientAPI remoteClient, uint objectLocalID, uint AttachmentPt, Quaternion rot, Vector3 attachPos, bool silent); + + /// + /// Rez an attachment from user inventory and change inventory status to match. + /// + /// + /// + /// + /// The scene object that was attached. Null if the scene object could not be found + UUID RezSingleAttachmentFromInventory(IClientAPI remoteClient, UUID itemID, uint AttachmentPt); + + /// + /// Rez an attachment from user inventory + /// + /// + /// + /// + /// + /// If true, we also update the user's inventory to show that the attachment is set. If false, we do not. + /// False is required so that we don't attempt to update information when a user enters a scene with the + /// attachment already correctly set up in inventory. + /// The uuid of the scene object that was attached. Null if the scene object could not be found + UUID RezSingleAttachmentFromInventory( + IClientAPI remoteClient, UUID itemID, uint AttachmentPt, bool updateInventoryStatus); /// /// Update the user inventory to the attachment of an item @@ -54,7 +88,7 @@ namespace OpenSim.Region.Framework.Interfaces /// /// /// - /// + /// UUID SetAttachmentInventoryStatus( SceneObjectGroup att, IClientAPI remoteClient, UUID itemID, uint AttachmentPt); diff --git a/OpenSim/Region/Framework/Interfaces/IInventoryAccessModule.cs b/OpenSim/Region/Framework/Interfaces/IInventoryAccessModule.cs index 240140280b..81852583c6 100644 --- a/OpenSim/Region/Framework/Interfaces/IInventoryAccessModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IInventoryAccessModule.cs @@ -1,4 +1,31 @@ -using System; +/* + * 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 OpenSim.Framework; diff --git a/OpenSim/Region/Framework/Scenes/EventManager.cs b/OpenSim/Region/Framework/Scenes/EventManager.cs index 16509460bf..37a51d9bde 100644 --- a/OpenSim/Region/Framework/Scenes/EventManager.cs +++ b/OpenSim/Region/Framework/Scenes/EventManager.cs @@ -113,15 +113,15 @@ namespace OpenSim.Region.Framework.Scenes /// Fired when an object is touched/grabbed. /// /// The originalID is the local ID of the part that was actually touched. The localID itself is always that of - /// the root part. + /// the root part. public event ObjectGrabDelegate OnObjectGrab; - public delegate void ObjectGrabDelegate(uint localID, uint originalID, Vector3 offsetPos, IClientAPI remoteClient, SurfaceTouchEventArgs surfaceArgs); + public delegate void ObjectGrabDelegate(uint localID, uint originalID, Vector3 offsetPos, IClientAPI remoteClient, SurfaceTouchEventArgs surfaceArgs); public event ObjectGrabDelegate OnObjectGrabbing; public event ObjectDeGrabDelegate OnObjectDeGrab; public event ScriptResetDelegate OnScriptReset; - public event OnPermissionErrorDelegate OnPermissionError; + public event OnPermissionErrorDelegate OnPermissionError; /// /// Fired when a new script is created. @@ -169,7 +169,7 @@ namespace OpenSim.Region.Framework.Scenes public delegate void ClientClosed(UUID clientID, Scene scene); - public event ClientClosed OnClientClosed; + public event ClientClosed OnClientClosed; /// /// This is fired when a scene object property that a script might be interested in (such as color, scale or diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs index eb51019364..6ebd04811d 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs @@ -1106,18 +1106,18 @@ namespace OpenSim.Region.Framework.Scenes if (folder == null) return; - m_log.DebugFormat("[AGENT INVENTORY]: Send Inventory Folder {0} Update to {1} {2}", folder.Name, client.FirstName, client.LastName); + // Fetch the folder contents InventoryCollection contents = InventoryService.GetFolderContent(client.AgentId, folder.ID); - InventoryFolderBase containingFolder = new InventoryFolderBase(); - containingFolder.ID = folder.ID; - containingFolder.Owner = client.AgentId; - containingFolder = InventoryService.GetFolder(containingFolder); - if (containingFolder != null) - { - int version = containingFolder.Version; - client.SendInventoryFolderDetails(client.AgentId, folder.ID, contents.Items, contents.Folders, version, fetchFolders, fetchItems); - } + // Fetch the folder itself to get its current version + InventoryFolderBase containingFolder = new InventoryFolderBase(folder.ID, client.AgentId); + containingFolder = InventoryService.GetFolder(containingFolder); + + //m_log.DebugFormat("[AGENT INVENTORY]: Sending inventory folder contents ({0} nodes) for \"{1}\" to {2} {3}", + // contents.Folders.Count + contents.Items.Count, containingFolder.Name, client.FirstName, client.LastName); + + if (containingFolder != null) + client.SendInventoryFolderDetails(client.AgentId, folder.ID, contents.Items, contents.Folders, containingFolder.Version, fetchFolders, fetchItems); } /// @@ -1846,35 +1846,12 @@ namespace OpenSim.Region.Framework.Scenes EventManager.TriggerOnAttach(localID, itemID, avatarID); } - /// - /// Called when the client receives a request to rez a single attachment on to the avatar from inventory - /// (RezSingleAttachmentFromInv packet). - /// - /// - /// - /// - /// - public UUID RezSingleAttachment(IClientAPI remoteClient, UUID itemID, uint AttachmentPt) - { - m_log.DebugFormat("[USER INVENTORY]: Rezzing single attachment from item {0} for {1}", itemID, remoteClient.Name); - - SceneObjectGroup att = m_sceneGraph.RezSingleAttachment(remoteClient, itemID, AttachmentPt); - - if (att == null) - { - AttachmentsModule.ShowDetachInUserInventory(itemID, remoteClient); - return UUID.Zero; - } - - return AttachmentsModule.SetAttachmentInventoryStatus(att, remoteClient, itemID, AttachmentPt); - } - public void RezMultipleAttachments(IClientAPI remoteClient, RezMultipleAttachmentsFromInvPacket.HeaderDataBlock header, RezMultipleAttachmentsFromInvPacket.ObjectDataBlock[] objects) { foreach (RezMultipleAttachmentsFromInvPacket.ObjectDataBlock obj in objects) { - RezSingleAttachment(remoteClient, obj.ItemID, obj.AttachmentPt); + AttachmentsModule.RezSingleAttachmentFromInventory(remoteClient, obj.ItemID, obj.AttachmentPt); } } diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index d5d1825cbe..03f1ee2d87 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -2003,7 +2003,7 @@ namespace OpenSim.Region.Framework.Scenes public bool AddNewSceneObject(SceneObjectGroup sceneObject, bool attachToBackup, bool sendClientUpdates) { return m_sceneGraph.AddNewSceneObject(sceneObject, attachToBackup, sendClientUpdates); - } + } /// /// Delete every object from the scene @@ -2365,10 +2365,10 @@ namespace OpenSim.Region.Framework.Scenes //m_log.DebugFormat(" >>> IncomingCreateObject(userID, itemID) <<< {0} {1}", userID, itemID); ScenePresence sp = GetScenePresence(userID); - if (sp != null) + if (sp != null && AttachmentsModule != null) { - uint attPt = (uint)sp.Appearance.GetAttachpoint(itemID); - m_sceneGraph.RezSingleAttachment(sp.ControllingClient, itemID, attPt); + uint attPt = (uint)sp.Appearance.GetAttachpoint(itemID); + AttachmentsModule.RezSingleAttachmentFromInventory(sp.ControllingClient, itemID, attPt); } return false; @@ -2669,14 +2669,16 @@ namespace OpenSim.Region.Framework.Scenes } public virtual void SubscribeToClientAttachmentEvents(IClientAPI client) - { - client.OnRezSingleAttachmentFromInv += RezSingleAttachment; + { client.OnRezMultipleAttachmentsFromInv += RezMultipleAttachments; - client.OnObjectAttach += m_sceneGraph.AttachObject; client.OnObjectDetach += m_sceneGraph.DetachObject; if (AttachmentsModule != null) + { + client.OnRezSingleAttachmentFromInv += AttachmentsModule.RezSingleAttachmentFromInventory; + client.OnObjectAttach += AttachmentsModule.AttachObject; client.OnDetachAttachmentIntoInv += AttachmentsModule.ShowDetachInUserInventory; + } } public virtual void SubscribeToClientTeleportEvents(IClientAPI client) @@ -2723,7 +2725,7 @@ namespace OpenSim.Region.Framework.Scenes } protected virtual void UnsubscribeToClientEvents(IClientAPI client) - { + { } /// @@ -2801,7 +2803,6 @@ namespace OpenSim.Region.Framework.Scenes client.OnRezObject -= RezObject; } - public virtual void UnSubscribeToClientInventoryEvents(IClientAPI client) { client.OnCreateNewInventoryItem -= CreateNewInventoryItem; @@ -2824,14 +2825,16 @@ namespace OpenSim.Region.Framework.Scenes } public virtual void UnSubscribeToClientAttachmentEvents(IClientAPI client) - { - client.OnRezMultipleAttachmentsFromInv -= RezMultipleAttachments; - client.OnRezSingleAttachmentFromInv -= RezSingleAttachment; - client.OnObjectAttach -= m_sceneGraph.AttachObject; + { + client.OnRezMultipleAttachmentsFromInv -= RezMultipleAttachments; client.OnObjectDetach -= m_sceneGraph.DetachObject; - if (AttachmentsModule != null) + if (AttachmentsModule != null) + { + client.OnRezSingleAttachmentFromInv -= AttachmentsModule.RezSingleAttachmentFromInventory; + client.OnObjectAttach -= AttachmentsModule.AttachObject; client.OnDetachAttachmentIntoInv -= AttachmentsModule.ShowDetachInUserInventory; + } } public virtual void UnSubscribeToClientTeleportEvents(IClientAPI client) @@ -3559,7 +3562,7 @@ namespace OpenSim.Region.Framework.Scenes { foreach (var parcel in AllParcels()) { - if( parcel.ContainsPoint((int)x,(int)y)) + if (parcel.ContainsPoint((int)x,(int)y)) { return parcel; } @@ -4998,7 +5001,7 @@ namespace OpenSim.Region.Framework.Scenes private Vector3 GetPositionAtAvatarHeightOrGroundHeight(ScenePresence avatar, float x, float y) { Vector3 ground = GetPositionAtGround(x, y); - if( avatar.AbsolutePosition.Z > ground.Z) + if (avatar.AbsolutePosition.Z > ground.Z) { ground.Z = avatar.AbsolutePosition.Z; } diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs index 4e41b07533..db70d6ac05 100644 --- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs +++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs @@ -510,94 +510,6 @@ namespace OpenSim.Region.Framework.Scenes } } - /// - /// Event Handling routine for Attach Object - /// - /// - /// - /// - /// - protected internal void AttachObject(IClientAPI remoteClient, uint objectLocalID, uint AttachmentPt, Quaternion rot, bool silent) - { - // If we can't take it, we can't attach it! - SceneObjectPart part = m_parentScene.GetSceneObjectPart(objectLocalID); - if (part == null) - return; - - if (!m_parentScene.Permissions.CanTakeObject(part.UUID, remoteClient.AgentId)) - return; - - // Calls attach with a Zero position - if (m_parentScene.AttachmentsModule.AttachObject(remoteClient, objectLocalID, AttachmentPt, rot, Vector3.Zero, false)) - { - m_parentScene.SendAttachEvent(objectLocalID, part.ParentGroup.GetFromItemID(), remoteClient.AgentId); - - // Save avatar attachment information - ScenePresence presence; - if (m_parentScene.AvatarFactory != null && m_parentScene.TryGetAvatar(remoteClient.AgentId, out presence)) - { - m_log.Info( - "[SCENE]: Saving avatar attachment. AgentID: " + remoteClient.AgentId - + ", AttachmentPoint: " + AttachmentPt); - - m_parentScene.AvatarFactory.UpdateDatabase(remoteClient.AgentId, presence.Appearance); - } - } - } - - /// - /// Rez an attachment - /// - /// - /// - /// - /// The scene object that was attached. Null if the scene object could not be found - public SceneObjectGroup RezSingleAttachment(IClientAPI remoteClient, UUID itemID, uint AttachmentPt) - { - IInventoryAccessModule invAccess = m_parentScene.RequestModuleInterface(); - if (invAccess != null) - { - SceneObjectGroup objatt = invAccess.RezObject(remoteClient, - itemID, Vector3.Zero, Vector3.Zero, UUID.Zero, (byte)1, true, - false, false, remoteClient.AgentId, true); - -// m_log.DebugFormat( -// "[SCENE GRAPH]: Retrieved single object {0} for attachment to {1} on point {2}", -// objatt.Name, remoteClient.Name, AttachmentPt); - - if (objatt != null) - { - bool tainted = false; - if (AttachmentPt != 0 && AttachmentPt != objatt.GetAttachmentPoint()) - tainted = true; - - m_parentScene.AttachmentsModule.AttachObject( - remoteClient, objatt.LocalId, AttachmentPt, Quaternion.Identity, objatt.AbsolutePosition, false); - //objatt.ScheduleGroupForFullUpdate(); - - if (tainted) - objatt.HasGroupChanged = true; - - // Fire after attach, so we don't get messy perms dialogs - // 3 == AttachedRez - objatt.CreateScriptInstances(0, true, m_parentScene.DefaultScriptEngine, 3); - - // Do this last so that event listeners have access to all the effects of the attachment - m_parentScene.EventManager.TriggerOnAttach(objatt.LocalId, itemID, remoteClient.AgentId); - } - else - { - m_log.WarnFormat( - "[SCENE GRAPH]: Could not retrieve item {0} for attaching to avatar {1} at point {2}", - itemID, remoteClient.Name, AttachmentPt); - } - - return objatt; - } - - return null; - } - protected internal ScenePresence CreateAndAddChildScenePresence(IClientAPI client, AvatarAppearance appearance) { ScenePresence newAvatar = null; diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index 6c4b39d491..d083119d09 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -645,7 +645,7 @@ namespace OpenSim.Region.Framework.Scenes ApplyPhysics(m_scene.m_physicalPrim); // Don't trigger the update here - otherwise some client issues occur when multiple updates are scheduled - // for the same object with very different properties. The caller must schedule the update. + // for the same object with very different properties. The caller must schedule the update. //ScheduleGroupForFullUpdate(); } @@ -2185,11 +2185,11 @@ namespace OpenSim.Region.Framework.Scenes /// Immediately send a full update for this scene object. /// public void SendGroupFullUpdate() - { + { if (IsDeleted) return; -// m_log.DebugFormat("[SOG]: Sending immediate full group update for {0} {1}", Name, UUID); +// m_log.DebugFormat("[SOG]: Sending immediate full group update for {0} {1}", Name, UUID); RootPart.SendFullUpdateToAllClients(); diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index c8ac014d07..48e34ee4f3 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -2060,6 +2060,7 @@ namespace OpenSim.Region.Framework.Scenes { m_lastColliders.Remove(localID); } + if (m_parentGroup == null) return; if (m_parentGroup.IsDeleted) @@ -2855,7 +2856,7 @@ namespace OpenSim.Region.Framework.Scenes { SendFullUpdateToClient(remoteClient, clientFlags); } - } + } /// /// Send a full update for this part to all clients. diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index d8f93d7e41..24179a96be 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -686,7 +686,8 @@ namespace OpenSim.Region.Framework.Scenes UserAccount account = m_scene.UserAccountService.GetUserAccount(m_scene.RegionInfo.ScopeID, m_uuid); - m_userLevel = account.UserLevel; + if (account != null) + m_userLevel = account.UserLevel; IGroupsModule gm = m_scene.RequestModuleInterface(); if (gm != null) @@ -3957,7 +3958,7 @@ namespace OpenSim.Region.Framework.Scenes { if (null == m_appearance) { - m_log.WarnFormat("[ATTACHMENT] Appearance has not been initialized for agent {0}", UUID); + m_log.WarnFormat("[ATTACHMENT]: Appearance has not been initialized for agent {0}", UUID); return; } @@ -3981,12 +3982,12 @@ namespace OpenSim.Region.Framework.Scenes try { // Rez from inventory - UUID asset = m_scene.RezSingleAttachment(ControllingClient, - itemID, (uint)p); - - m_log.InfoFormat("[ATTACHMENT]: Rezzed attachment in point {0} from item {1} and asset {2} ({3})", - p, itemID, assetID, asset); + UUID asset + = m_scene.AttachmentsModule.RezSingleAttachmentFromInventory(ControllingClient, itemID, (uint)p); + m_log.InfoFormat( + "[ATTACHMENT]: Rezzed attachment in point {0} from item {1} and asset {2} ({3})", + p, itemID, assetID, asset); } catch (Exception e) { diff --git a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectBasicTests.cs b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectBasicTests.cs index b50d4ca5b2..78f2ae3aa1 100644 --- a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectBasicTests.cs +++ b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectBasicTests.cs @@ -86,6 +86,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests public void TestDeleteSceneObjectAsync() { TestHelper.InMethod(); + //log4net.Config.XmlConfigurator.Configure(); UUID agentId = UUID.Parse("00000000-0000-0000-0000-000000000001"); @@ -97,15 +98,9 @@ namespace OpenSim.Region.Framework.Scenes.Tests SceneObjectPart part = SceneSetupHelpers.AddSceneObject(scene); - try - { - IClientAPI client = SceneSetupHelpers.AddRootAgent(scene, agentId); - scene.DeRezObject(client, part.LocalId, UUID.Zero, DeRezAction.Delete, UUID.Zero); - } - catch (Exception e) - { - Console.WriteLine("Exception: " + e.StackTrace); - } + IClientAPI client = SceneSetupHelpers.AddRootAgent(scene, agentId); + scene.DeRezObject(client, part.LocalId, UUID.Zero, DeRezAction.Delete, UUID.Zero); + SceneObjectPart retrievedPart = scene.GetSceneObjectPart(part.LocalId); Assert.That(retrievedPart, Is.Not.Null); diff --git a/OpenSim/Region/Physics/BulletDotNETPlugin/BulletDotNETCharacter.cs b/OpenSim/Region/Physics/BulletDotNETPlugin/BulletDotNETCharacter.cs index 198962bf7e..f13c3230ba 100644 --- a/OpenSim/Region/Physics/BulletDotNETPlugin/BulletDotNETCharacter.cs +++ b/OpenSim/Region/Physics/BulletDotNETPlugin/BulletDotNETCharacter.cs @@ -108,12 +108,11 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin private bool[] m_colliderarr = new bool[11]; private bool[] m_colliderGroundarr = new bool[11]; - - private BulletDotNETScene m_parent_scene; public int m_eventsubscription = 0; - // private CollisionEventUpdate CollisionEventsThisFrame = new CollisionEventUpdate(); + private CollisionEventUpdate CollisionEventsThisFrame = null; + private int m_requestedUpdateFrequency = 0; public BulletDotNETCharacter(string avName, BulletDotNETScene parent_scene, Vector3 pos, Vector3 size, float pid_d, float pid_p, float capsule_radius, float tensor, float density, float height_fudge_factor, float walk_divisor, float rundivisor) { @@ -212,7 +211,8 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin m_mass = Mass; Body = new btRigidBody(m_mass, m_bodyMotionState, Shell); - Body.setUserPointer(new IntPtr((int)Body.Handle)); + // this is used for self identification. User localID instead of body handle + Body.setUserPointer(new IntPtr((int)m_localID)); if (ClosestCastResult != null) ClosestCastResult.Dispose(); @@ -716,6 +716,7 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin public override void SubscribeEvents(int ms) { m_eventsubscription = ms; + m_requestedUpdateFrequency = ms; m_parent_scene.addCollisionEventReporting(this); } @@ -723,6 +724,7 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin { m_parent_scene.remCollisionEventReporting(this); m_eventsubscription = 0; + m_requestedUpdateFrequency = 0; } public override bool SubscribedEvents() @@ -732,6 +734,29 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin return false; } + public void AddCollision(uint collideWith, ContactPoint contact) + { + if (CollisionEventsThisFrame == null) + { + CollisionEventsThisFrame = new CollisionEventUpdate(); + } + CollisionEventsThisFrame.addCollider(collideWith, contact); + } + + public void SendCollisions() + { + if (m_eventsubscription >= m_requestedUpdateFrequency) + { + if (CollisionEventsThisFrame != null) + { + base.SendCollisionUpdate(CollisionEventsThisFrame); + } + CollisionEventsThisFrame = new CollisionEventUpdate(); + m_eventsubscription = 0; + } + return; + } + internal void Dispose() { if (Body.isInWorld()) diff --git a/OpenSim/Region/Physics/BulletDotNETPlugin/BulletDotNETPrim.cs b/OpenSim/Region/Physics/BulletDotNETPlugin/BulletDotNETPrim.cs index 920ed96ee7..dc3229a752 100644 --- a/OpenSim/Region/Physics/BulletDotNETPlugin/BulletDotNETPrim.cs +++ b/OpenSim/Region/Physics/BulletDotNETPlugin/BulletDotNETPrim.cs @@ -154,7 +154,8 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin private Vector3 _target_velocity; public int m_eventsubscription; - // private CollisionEventUpdate CollisionEventsThisFrame = null; + private int m_requestedUpdateFrequency = 0; + private CollisionEventUpdate CollisionEventsThisFrame = null; public volatile bool childPrim; @@ -595,6 +596,7 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin public override void SubscribeEvents(int ms) { m_eventsubscription = ms; + m_requestedUpdateFrequency = ms; _parent_scene.addCollisionEventReporting(this); } @@ -602,6 +604,7 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin { _parent_scene.remCollisionEventReporting(this); m_eventsubscription = 0; + m_requestedUpdateFrequency = 0; } public override bool SubscribedEvents() @@ -611,7 +614,28 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin #endregion + public void AddCollision(uint collideWith, ContactPoint contact) + { + if (CollisionEventsThisFrame == null) + { + CollisionEventsThisFrame = new CollisionEventUpdate(); + } + CollisionEventsThisFrame.addCollider(collideWith, contact); + } + public void SendCollisions() + { + if (m_eventsubscription >= m_requestedUpdateFrequency) + { + if (CollisionEventsThisFrame != null) + { + base.SendCollisionUpdate(CollisionEventsThisFrame); + } + CollisionEventsThisFrame = null; + // m_eventsubscription = 0; + } + return; + } internal void Dispose() { @@ -759,7 +783,7 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin { if (m_taintadd) { - m_log.Debug("[PHYSICS]: TaintAdd"); + // m_log.Debug("[PHYSICS]: TaintAdd"); changeadd(timestep); } @@ -771,7 +795,7 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin SetBody(Mass); else SetBody(0); - m_log.Debug("[PHYSICS]: GEOM_DOESNT_EXSIT"); + // m_log.Debug("[PHYSICS]: GEOM_DOESNT_EXSIT"); } if (prim_geom.Handle == IntPtr.Zero) @@ -782,31 +806,31 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin SetBody(Mass); else SetBody(0); - m_log.Debug("[PHYSICS]: GEOM_DOESNT_EXSIT"); + // m_log.Debug("[PHYSICS]: GEOM_DOESNT_EXSIT"); } if (!_position.ApproxEquals(m_taintposition, 0f)) { - m_log.Debug("[PHYSICS]: TaintMove"); + // m_log.Debug("[PHYSICS]: TaintMove"); changemove(timestep); } if (m_taintrot != _orientation) { - m_log.Debug("[PHYSICS]: TaintRotate"); + // m_log.Debug("[PHYSICS]: TaintRotate"); rotate(timestep); } // if (m_taintPhysics != m_isphysical && !(m_taintparent != _parent)) { - m_log.Debug("[PHYSICS]: TaintPhysics"); + // m_log.Debug("[PHYSICS]: TaintPhysics"); changePhysicsStatus(timestep); } // if (!_size.ApproxEquals(m_taintsize, 0f)) { - m_log.Debug("[PHYSICS]: TaintSize"); + // m_log.Debug("[PHYSICS]: TaintSize"); changesize(timestep); } @@ -814,43 +838,43 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin if (m_taintshape) { - m_log.Debug("[PHYSICS]: TaintShape"); + // m_log.Debug("[PHYSICS]: TaintShape"); changeshape(timestep); } // if (m_taintforce) { - m_log.Debug("[PHYSICS]: TaintForce"); + // m_log.Debug("[PHYSICS]: TaintForce"); changeAddForce(timestep); } if (m_taintaddangularforce) { - m_log.Debug("[PHYSICS]: TaintAngularForce"); + // m_log.Debug("[PHYSICS]: TaintAngularForce"); changeAddAngularForce(timestep); } if (!m_taintTorque.ApproxEquals(Vector3.Zero, 0.001f)) { - m_log.Debug("[PHYSICS]: TaintTorque"); + // m_log.Debug("[PHYSICS]: TaintTorque"); changeSetTorque(timestep); } if (m_taintdisable) { - m_log.Debug("[PHYSICS]: TaintDisable"); + // m_log.Debug("[PHYSICS]: TaintDisable"); changedisable(timestep); } if (m_taintselected != m_isSelected) { - m_log.Debug("[PHYSICS]: TaintSelected"); + // m_log.Debug("[PHYSICS]: TaintSelected"); changeSelectedStatus(timestep); } if (!m_taintVelocity.ApproxEquals(Vector3.Zero, 0.001f)) { - m_log.Debug("[PHYSICS]: TaintVelocity"); + // m_log.Debug("[PHYSICS]: TaintVelocity"); changevelocity(timestep); } if (m_taintparent != _parent) { - m_log.Debug("[PHYSICS]: TaintLink"); + // m_log.Debug("[PHYSICS]: TaintLink"); changelink(timestep); } if (m_taintCollidesWater != m_collidesWater) @@ -859,7 +883,7 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin } if (!m_angularlock.ApproxEquals(m_taintAngularLock, 0)) { - m_log.Debug("[PHYSICS]: TaintAngularLock"); + // m_log.Debug("[PHYSICS]: TaintAngularLock"); changeAngularLock(timestep); } if (m_taintremove) @@ -917,7 +941,7 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin private void changemove(float timestep) { - m_log.Debug("[PHYSICS]: _________ChangeMove"); + // m_log.Debug("[PHYSICS]: _________ChangeMove"); if (!m_isphysical) { tempTransform2 = Body.getWorldTransform(); @@ -977,7 +1001,7 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin private void rotate(float timestep) { - m_log.Debug("[PHYSICS]: _________ChangeRotate"); + // m_log.Debug("[PHYSICS]: _________ChangeRotate"); tempTransform2 = Body.getWorldTransform(); tempOrientation2 = new btQuaternion(_orientation.X, _orientation.Y, _orientation.Z, _orientation.W); tempTransform2.setRotation(tempOrientation2); @@ -1000,7 +1024,7 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin //Body = null; // TODO: dispose parts that make up body } - m_log.Debug("[PHYSICS]: _________ChangePhysics"); + // m_log.Debug("[PHYSICS]: _________ChangePhysics"); ProcessGeomCreation(); @@ -1092,7 +1116,7 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin // TODO: dispose parts that make up body } - m_log.Debug("[PHYSICS]: _________ChangeSize"); + // m_log.Debug("[PHYSICS]: _________ChangeSize"); SetCollisionShape(null); // Construction of new prim ProcessGeomCreation(); @@ -1297,13 +1321,13 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin // TODO: throw new NotImplementedException(); if (m_taintselected) { - Body.setCollisionFlags((int)ContactFlags.CF_NO_CONTACT_RESPONSE); + // Body.setCollisionFlags((int)ContactFlags.CF_NO_CONTACT_RESPONSE); disableBodySoft(); } else { - Body.setCollisionFlags(0 | (int)ContactFlags.CF_CUSTOM_MATERIAL_CALLBACK); + // Body.setCollisionFlags(0 | (int)ContactFlags.CF_CUSTOM_MATERIAL_CALLBACK); enableBodySoft(); } m_isSelected = m_taintselected; @@ -1605,6 +1629,11 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin enableBodySoft(); } */ + if (!Body.isActive()) + { + Body.clearForces(); + enableBodySoft(); + } // 35x10 = 350n times the mass per second applied maximum. float nmax = 35f * m_mass; @@ -1632,6 +1661,12 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin Body.applyCentralImpulse(tempAddForce); } } + else + { + // if no forces on the prim, make sure everything is zero + Body.clearForces(); + enableBodySoft(); + } } else { @@ -1985,7 +2020,7 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin public void CreateGeom(IntPtr m_targetSpace, IMesh p_mesh) { - m_log.Debug("[PHYSICS]: _________CreateGeom"); + // m_log.Debug("[PHYSICS]: _________CreateGeom"); if (p_mesh != null) { //_mesh = _parent_scene.mesher.CreateMesh(m_primName, _pbs, _size, _parent_scene.meshSculptLOD, IsPhysical); @@ -2042,7 +2077,7 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin // TODO: Set Collision Body Mesh // This sleeper is there to moderate how long it takes between // setting up the mesh and pre-processing it when we get rapid fire mesh requests on a single object - m_log.Debug("_________SetMesh"); + // m_log.Debug("_________SetMesh"); Thread.Sleep(10); //Kill Body so that mesh can re-make the geom @@ -2159,7 +2194,14 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin // Body = new btRigidBody(mass, tempMotionState1, prim_geom); //else - Body = new btRigidBody(mass, tempMotionState1, prim_geom, tempInertia1); + // Body = new btRigidBody(mass, tempMotionState1, prim_geom, tempInertia1); + if (Body == null) + { + Body = new btRigidBody(mass, tempMotionState1, prim_geom, tempInertia1); + // add localID so we can later map bullet object back to OpenSim object + Body.setUserPointer(new IntPtr((int)m_localID)); + } + if (prim_geom is btGImpactMeshShape) { @@ -2250,7 +2292,13 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin // Body = new btRigidBody(mass, tempMotionState1, prim_geom); //else - Body = new btRigidBody(mass, tempMotionState1, prim_geom, tempInertia1); + // Body = new btRigidBody(mass, tempMotionState1, prim_geom, tempInertia1); + if (Body == null) + { + Body = new btRigidBody(mass, tempMotionState1, prim_geom, tempInertia1); + // each body has the localID stored into it so we can identify collision objects + Body.setUserPointer(new IntPtr((int)m_localID)); + } if (prim_geom is btGImpactMeshShape) { diff --git a/OpenSim/Region/Physics/BulletDotNETPlugin/BulletDotNETScene.cs b/OpenSim/Region/Physics/BulletDotNETPlugin/BulletDotNETScene.cs index 9e048ab69c..85e34c1e3d 100644 --- a/OpenSim/Region/Physics/BulletDotNETPlugin/BulletDotNETScene.cs +++ b/OpenSim/Region/Physics/BulletDotNETPlugin/BulletDotNETScene.cs @@ -47,7 +47,9 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin // private string m_sceneIdentifier = string.Empty; private List m_characters = new List(); + private Dictionary m_charactersLocalID = new Dictionary(); private List m_prims = new List(); + private Dictionary m_primsLocalID = new Dictionary(); private List m_activePrims = new List(); private List m_taintedActors = new List(); private btDiscreteDynamicsWorld m_world; @@ -134,7 +136,7 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin m_dispatcher = new btCollisionDispatcher(m_collisionConfiguration); m_world = new btDiscreteDynamicsWorld(m_dispatcher, m_broadphase, m_solver, m_collisionConfiguration); m_world.setGravity(m_gravity); - //EnableCollisionInterface(); + EnableCollisionInterface(); } @@ -145,7 +147,16 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin avCapRadius, avStandupTensor, avDensity, avHeightFudgeFactor, avMovementDivisorWalk, avMovementDivisorRun); - m_characters.Add(chr); + try + { + m_characters.Add(chr); + m_charactersLocalID.Add(chr.m_localID, chr); + } + catch + { + // noop if it's already there + m_log.Debug("[PHYSICS] BulletDotNet: adding duplicate avatar localID"); + } AddPhysicsActorTaint(chr); return chr; } @@ -154,6 +165,7 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin { BulletDotNETCharacter chr = (BulletDotNETCharacter) actor; + m_charactersLocalID.Remove(chr.m_localID); m_characters.Remove(chr); m_world.removeRigidBody(chr.Body); m_world.removeCollisionObject(chr.Body); @@ -279,7 +291,7 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin prim.Move(timeStep); } } - float steps = m_world.stepSimulation(timeStep * 1000, 10, WorldTimeComp); + float steps = m_world.stepSimulation(timeStep, 10, WorldTimeComp); foreach (BulletDotNETCharacter chr in m_characters) { @@ -296,20 +308,67 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin } if (m_CollisionInterface != null) { - List collisions = m_CollisionInterface.GetContactList(); - lock (collisions) + List primsWithCollisions = new List(); + List charactersWithCollisions = new List(); + + // get the collisions that happened this tick + List collisions = m_CollisionInterface.GetContactList(); + // passed back the localID of the prim so we can associate the prim + foreach (BulletDotNET.ContactAddedCallbackHandler.ContactInfo ci in collisions) { - foreach (int pvalue in collisions) - { - System.Console.Write(string.Format("{0} ", pvalue)); - } + // ContactPoint = { contactPoint, contactNormal, penetrationDepth } + ContactPoint contact = new ContactPoint(new Vector3(ci.pX, ci.pY, ci.pZ), + new Vector3(ci.nX, ci.nY, ci.nZ), ci.depth); + + ProcessContact(ci.contact, ci.contactWith, contact, ref primsWithCollisions, ref charactersWithCollisions); + ProcessContact(ci.contactWith, ci.contact, contact, ref primsWithCollisions, ref charactersWithCollisions); + } m_CollisionInterface.Clear(); - + // for those prims and characters that had collisions cause collision events + foreach (BulletDotNETPrim bdnp in primsWithCollisions) + { + bdnp.SendCollisions(); + } + foreach (BulletDotNETCharacter bdnc in charactersWithCollisions) + { + bdnc.SendCollisions(); + } } return steps; } + private void ProcessContact(uint cont, uint contWith, ContactPoint contact, + ref List primsWithCollisions, + ref List charactersWithCollisions) + { + BulletDotNETPrim bdnp; + // collisions with a normal prim? + if (m_primsLocalID.TryGetValue(cont, out bdnp)) + { + // Added collision event to the prim. This creates a pile of events + // that will be sent to any subscribed listeners. + bdnp.AddCollision(contWith, contact); + if (!primsWithCollisions.Contains(bdnp)) + { + primsWithCollisions.Add(bdnp); + } + } + else + { + BulletDotNETCharacter bdnc; + // if not a prim, maybe it's one of the characters + if (m_charactersLocalID.TryGetValue(cont, out bdnc)) + { + bdnc.AddCollision(contWith, contact); + if (!charactersWithCollisions.Contains(bdnc)) + { + charactersWithCollisions.Add(bdnc); + } + } + } + } + public override void GetResults() { @@ -387,6 +446,7 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin m_terrainTransform = new btTransform(QuatIdentity, m_terrainPosition); m_terrainMotionState = new btDefaultMotionState(m_terrainTransform); TerrainBody = new btRigidBody(0, m_terrainMotionState, m_terrainShape); + TerrainBody.setUserPointer((IntPtr)0); m_world.addRigidBody(TerrainBody); @@ -459,6 +519,7 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin { lock (m_prims) { + m_primsLocalID.Clear(); foreach (BulletDotNETPrim prim in m_prims) { if (prim.Body != null) @@ -513,6 +574,7 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin m_world.removeRigidBody(body); } remActivePrim(prm); + m_primsLocalID.Remove(prm.m_localID); m_prims.Remove(prm); } @@ -686,9 +748,18 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin { if (!m_prims.Contains(pPrim)) { - m_prims.Add(pPrim); + try + { + m_prims.Add(pPrim); + m_primsLocalID.Add(pPrim.m_localID, pPrim); + } + catch + { + // noop if it's already there + m_log.Debug("[PHYSICS] BulletDotNet: adding duplicate prim localID"); + } m_world.addRigidBody(pPrim.Body); - m_log.Debug("ADDED"); + // m_log.Debug("[PHYSICS] added prim to scene"); } } } @@ -696,8 +767,8 @@ namespace OpenSim.Region.Physics.BulletDotNETPlugin { if (m_CollisionInterface == null) { - m_CollisionInterface = new ContactAddedCallbackHandler(); - m_world.SetCollisionAddedCallback(m_CollisionInterface); + m_CollisionInterface = new ContactAddedCallbackHandler(m_world); + // m_world.SetCollisionAddedCallback(m_CollisionInterface); } } diff --git a/OpenSim/Region/Physics/Meshing/PrimMesher.cs b/OpenSim/Region/Physics/Meshing/PrimMesher.cs index 2a213c3d88..6e9654bb38 100644 --- a/OpenSim/Region/Physics/Meshing/PrimMesher.cs +++ b/OpenSim/Region/Physics/Meshing/PrimMesher.cs @@ -1,2202 +1,2201 @@ -/* - * Copyright (c) Contributors - * 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.Text; -using System.IO; - -namespace PrimMesher -{ - public struct Quat - { - /// X value - public float X; - /// Y value - public float Y; - /// Z value - public float Z; - /// W value - public float W; - - public Quat(float x, float y, float z, float w) - { - X = x; - Y = y; - Z = z; - W = w; - } - - public Quat(Coord axis, float angle) - { - axis = axis.Normalize(); - - angle *= 0.5f; - float c = (float)Math.Cos(angle); - float s = (float)Math.Sin(angle); - - X = axis.X * s; - Y = axis.Y * s; - Z = axis.Z * s; - W = c; - - Normalize(); - } - - public float Length() - { - return (float)Math.Sqrt(X * X + Y * Y + Z * Z + W * W); - } - - public Quat Normalize() - { - const float MAG_THRESHOLD = 0.0000001f; - float mag = Length(); - - // Catch very small rounding errors when normalizing - if (mag > MAG_THRESHOLD) - { - float oomag = 1f / mag; - X *= oomag; - Y *= oomag; - Z *= oomag; - W *= oomag; - } - else - { - X = 0f; - Y = 0f; - Z = 0f; - W = 1f; - } - - return this; - } - - public static Quat operator *(Quat q1, Quat q2) - { - float x = q1.W * q2.X + q1.X * q2.W + q1.Y * q2.Z - q1.Z * q2.Y; - float y = q1.W * q2.Y - q1.X * q2.Z + q1.Y * q2.W + q1.Z * q2.X; - float z = q1.W * q2.Z + q1.X * q2.Y - q1.Y * q2.X + q1.Z * q2.W; - float w = q1.W * q2.W - q1.X * q2.X - q1.Y * q2.Y - q1.Z * q2.Z; - return new Quat(x, y, z, w); - } - - public override string ToString() - { - return "< X: " + this.X.ToString() + ", Y: " + this.Y.ToString() + ", Z: " + this.Z.ToString() + ", W: " + this.W.ToString() + ">"; - } - } - - public struct Coord - { - public float X; - public float Y; - public float Z; - - public Coord(float x, float y, float z) - { - this.X = x; - this.Y = y; - this.Z = z; - } - - public float Length() - { - return (float)Math.Sqrt(this.X * this.X + this.Y * this.Y + this.Z * this.Z); - } - - public Coord Invert() - { - this.X = -this.X; - this.Y = -this.Y; - this.Z = -this.Z; - - return this; - } - - public Coord Normalize() - { - const float MAG_THRESHOLD = 0.0000001f; - float mag = Length(); - - // Catch very small rounding errors when normalizing - if (mag > MAG_THRESHOLD) - { - float oomag = 1.0f / mag; - this.X *= oomag; - this.Y *= oomag; - this.Z *= oomag; - } - else - { - this.X = 0.0f; - this.Y = 0.0f; - this.Z = 0.0f; - } - - return this; - } - - public override string ToString() - { - return this.X.ToString() + " " + this.Y.ToString() + " " + this.Z.ToString(); - } - - public static Coord Cross(Coord c1, Coord c2) - { - return new Coord( - c1.Y * c2.Z - c2.Y * c1.Z, - c1.Z * c2.X - c2.Z * c1.X, - c1.X * c2.Y - c2.X * c1.Y - ); - } - - public static Coord operator +(Coord v, Coord a) - { - return new Coord(v.X + a.X, v.Y + a.Y, v.Z + a.Z); - } - - public static Coord operator *(Coord v, Coord m) - { - return new Coord(v.X * m.X, v.Y * m.Y, v.Z * m.Z); - } - - public static Coord operator *(Coord v, Quat q) - { - // From http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/transforms/ - - Coord c2 = new Coord(0.0f, 0.0f, 0.0f); - - c2.X = q.W * q.W * v.X + - 2f * q.Y * q.W * v.Z - - 2f * q.Z * q.W * v.Y + - q.X * q.X * v.X + - 2f * q.Y * q.X * v.Y + - 2f * q.Z * q.X * v.Z - - q.Z * q.Z * v.X - - q.Y * q.Y * v.X; - - c2.Y = - 2f * q.X * q.Y * v.X + - q.Y * q.Y * v.Y + - 2f * q.Z * q.Y * v.Z + - 2f * q.W * q.Z * v.X - - q.Z * q.Z * v.Y + - q.W * q.W * v.Y - - 2f * q.X * q.W * v.Z - - q.X * q.X * v.Y; - - c2.Z = - 2f * q.X * q.Z * v.X + - 2f * q.Y * q.Z * v.Y + - q.Z * q.Z * v.Z - - 2f * q.W * q.Y * v.X - - q.Y * q.Y * v.Z + - 2f * q.W * q.X * v.Y - - q.X * q.X * v.Z + - q.W * q.W * v.Z; - - return c2; - } - } - - public struct UVCoord - { - public float U; - public float V; - - - public UVCoord(float u, float v) - { - this.U = u; - this.V = v; - } - } - - public struct Face - { - public int primFace; - - // vertices - public int v1; - public int v2; - public int v3; - - //normals - public int n1; - public int n2; - public int n3; - - // uvs - public int uv1; - public int uv2; - public int uv3; - - - public Face(int v1, int v2, int v3) - { - primFace = 0; - - this.v1 = v1; - this.v2 = v2; - this.v3 = v3; - - this.n1 = 0; - this.n2 = 0; - this.n3 = 0; - - this.uv1 = 0; - this.uv2 = 0; - this.uv3 = 0; - - } - - public Face(int v1, int v2, int v3, int n1, int n2, int n3) - { - primFace = 0; - - this.v1 = v1; - this.v2 = v2; - this.v3 = v3; - - this.n1 = n1; - this.n2 = n2; - this.n3 = n3; - - this.uv1 = 0; - this.uv2 = 0; - this.uv3 = 0; - } - - public Coord SurfaceNormal(List coordList) - { - Coord c1 = coordList[this.v1]; - Coord c2 = coordList[this.v2]; - Coord c3 = coordList[this.v3]; - - Coord edge1 = new Coord(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z); - Coord edge2 = new Coord(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z); - - return Coord.Cross(edge1, edge2).Normalize(); - } - } - - public struct ViewerFace - { - public int primFaceNumber; - - public Coord v1; - public Coord v2; - public Coord v3; - - public int coordIndex1; - public int coordIndex2; - public int coordIndex3; - - public Coord n1; - public Coord n2; - public Coord n3; - - public UVCoord uv1; - public UVCoord uv2; - public UVCoord uv3; - - public ViewerFace(int primFaceNumber) - { - this.primFaceNumber = primFaceNumber; - - this.v1 = new Coord(); - this.v2 = new Coord(); - this.v3 = new Coord(); - - this.coordIndex1 = this.coordIndex2 = this.coordIndex3 = -1; // -1 means not assigned yet - - this.n1 = new Coord(); - this.n2 = new Coord(); - this.n3 = new Coord(); - - this.uv1 = new UVCoord(); - this.uv2 = new UVCoord(); - this.uv3 = new UVCoord(); - } - - public void Scale(float x, float y, float z) - { - this.v1.X *= x; - this.v1.Y *= y; - this.v1.Z *= z; - - this.v2.X *= x; - this.v2.Y *= y; - this.v2.Z *= z; - - this.v3.X *= x; - this.v3.Y *= y; - this.v3.Z *= z; - } - - public void AddPos(float x, float y, float z) - { - this.v1.X += x; - this.v2.X += x; - this.v3.X += x; - - this.v1.Y += y; - this.v2.Y += y; - this.v3.Y += y; - - this.v1.Z += z; - this.v2.Z += z; - this.v3.Z += z; - } - - public void AddRot(Quat q) - { - this.v1 *= q; - this.v2 *= q; - this.v3 *= q; - - this.n1 *= q; - this.n2 *= q; - this.n3 *= q; - } - - public void CalcSurfaceNormal() - { - - Coord edge1 = new Coord(this.v2.X - this.v1.X, this.v2.Y - this.v1.Y, this.v2.Z - this.v1.Z); - Coord edge2 = new Coord(this.v3.X - this.v1.X, this.v3.Y - this.v1.Y, this.v3.Z - this.v1.Z); - - this.n1 = this.n2 = this.n3 = Coord.Cross(edge1, edge2).Normalize(); - } - } - - internal struct Angle - { - internal float angle; - internal float X; - internal float Y; - - internal Angle(float angle, float x, float y) - { - this.angle = angle; - this.X = x; - this.Y = y; - } - } - - internal class AngleList - { - private float iX, iY; // intersection point - - private static Angle[] angles3 = - { - new Angle(0.0f, 1.0f, 0.0f), - new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f), - new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f), - new Angle(1.0f, 1.0f, 0.0f) - }; - - private static Coord[] normals3 = - { - new Coord(0.25f, 0.4330127019f, 0.0f).Normalize(), - new Coord(-0.5f, 0.0f, 0.0f).Normalize(), - new Coord(0.25f, -0.4330127019f, 0.0f).Normalize(), - new Coord(0.25f, 0.4330127019f, 0.0f).Normalize() - }; - - private static Angle[] angles4 = - { - new Angle(0.0f, 1.0f, 0.0f), - new Angle(0.25f, 0.0f, 1.0f), - new Angle(0.5f, -1.0f, 0.0f), - new Angle(0.75f, 0.0f, -1.0f), - new Angle(1.0f, 1.0f, 0.0f) - }; - - private static Coord[] normals4 = - { - new Coord(0.5f, 0.5f, 0.0f).Normalize(), - new Coord(-0.5f, 0.5f, 0.0f).Normalize(), - new Coord(-0.5f, -0.5f, 0.0f).Normalize(), - new Coord(0.5f, -0.5f, 0.0f).Normalize(), - new Coord(0.5f, 0.5f, 0.0f).Normalize() - }; - - private static Angle[] angles24 = - { - new Angle(0.0f, 1.0f, 0.0f), - new Angle(0.041666666666666664f, 0.96592582628906831f, 0.25881904510252074f), - new Angle(0.083333333333333329f, 0.86602540378443871f, 0.5f), - new Angle(0.125f, 0.70710678118654757f, 0.70710678118654746f), - new Angle(0.16666666666666667f, 0.5f, 0.8660254037844386f), - new Angle(0.20833333333333331f, 0.25881904510252096f, 0.9659258262890682f), - new Angle(0.25f, 0.0f, 1.0f), - new Angle(0.29166666666666663f, -0.25881904510252063f, 0.96592582628906831f), - new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f), - new Angle(0.375f, -0.70710678118654746f, 0.70710678118654757f), - new Angle(0.41666666666666663f, -0.86602540378443849f, 0.5f), - new Angle(0.45833333333333331f, -0.9659258262890682f, 0.25881904510252102f), - new Angle(0.5f, -1.0f, 0.0f), - new Angle(0.54166666666666663f, -0.96592582628906842f, -0.25881904510252035f), - new Angle(0.58333333333333326f, -0.86602540378443882f, -0.5f), - new Angle(0.62499999999999989f, -0.70710678118654791f, -0.70710678118654713f), - new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f), - new Angle(0.70833333333333326f, -0.25881904510252152f, -0.96592582628906809f), - new Angle(0.75f, 0.0f, -1.0f), - new Angle(0.79166666666666663f, 0.2588190451025203f, -0.96592582628906842f), - new Angle(0.83333333333333326f, 0.5f, -0.86602540378443904f), - new Angle(0.875f, 0.70710678118654735f, -0.70710678118654768f), - new Angle(0.91666666666666663f, 0.86602540378443837f, -0.5f), - new Angle(0.95833333333333326f, 0.96592582628906809f, -0.25881904510252157f), - new Angle(1.0f, 1.0f, 0.0f) - }; - - private Angle interpolatePoints(float newPoint, Angle p1, Angle p2) - { - float m = (newPoint - p1.angle) / (p2.angle - p1.angle); - return new Angle(newPoint, p1.X + m * (p2.X - p1.X), p1.Y + m * (p2.Y - p1.Y)); - } - - private void intersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) - { // ref: http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/ - double denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1); - double uaNumerator = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3); - - if (denom != 0.0) - { - double ua = uaNumerator / denom; - iX = (float)(x1 + ua * (x2 - x1)); - iY = (float)(y1 + ua * (y2 - y1)); - } - } - - internal List angles; - internal List normals; - - internal void makeAngles(int sides, float startAngle, float stopAngle) - { - angles = new List(); - normals = new List(); - - double twoPi = System.Math.PI * 2.0; - float twoPiInv = 1.0f / (float)twoPi; - - if (sides < 1) - throw new Exception("number of sides not greater than zero"); - if (stopAngle <= startAngle) - throw new Exception("stopAngle not greater than startAngle"); - - if ((sides == 3 || sides == 4 || sides == 24)) - { - startAngle *= twoPiInv; - stopAngle *= twoPiInv; - - Angle[] sourceAngles; - if (sides == 3) - sourceAngles = angles3; - else if (sides == 4) - sourceAngles = angles4; - else sourceAngles = angles24; - - int startAngleIndex = (int)(startAngle * sides); - int endAngleIndex = sourceAngles.Length - 1; - if (stopAngle < 1.0f) - endAngleIndex = (int)(stopAngle * sides) + 1; - if (endAngleIndex == startAngleIndex) - endAngleIndex++; - - for (int angleIndex = startAngleIndex; angleIndex < endAngleIndex + 1; angleIndex++) - { - angles.Add(sourceAngles[angleIndex]); - if (sides == 3) - normals.Add(normals3[angleIndex]); - else if (sides == 4) - normals.Add(normals4[angleIndex]); - } - - if (startAngle > 0.0f) - angles[0] = interpolatePoints(startAngle, angles[0], angles[1]); - - if (stopAngle < 1.0f) - { - int lastAngleIndex = angles.Count - 1; - angles[lastAngleIndex] = interpolatePoints(stopAngle, angles[lastAngleIndex - 1], angles[lastAngleIndex]); - } - } - else - { - double stepSize = twoPi / sides; - - int startStep = (int)(startAngle / stepSize); - double angle = stepSize * startStep; - int step = startStep; - double stopAngleTest = stopAngle; - if (stopAngle < twoPi) - { - stopAngleTest = stepSize * ((int)(stopAngle / stepSize) + 1); - if (stopAngleTest < stopAngle) - stopAngleTest += stepSize; - if (stopAngleTest > twoPi) - stopAngleTest = twoPi; - } - - while (angle <= stopAngleTest) - { - Angle newAngle; - newAngle.angle = (float)angle; - newAngle.X = (float)System.Math.Cos(angle); - newAngle.Y = (float)System.Math.Sin(angle); - angles.Add(newAngle); - step += 1; - angle = stepSize * step; - } - - if (startAngle > angles[0].angle) - { - Angle newAngle; - intersection(angles[0].X, angles[0].Y, angles[1].X, angles[1].Y, 0.0f, 0.0f, (float)Math.Cos(startAngle), (float)Math.Sin(startAngle)); - newAngle.angle = startAngle; - newAngle.X = iX; - newAngle.Y = iY; - angles[0] = newAngle; - } - - int index = angles.Count - 1; - if (stopAngle < angles[index].angle) - { - Angle newAngle; - intersection(angles[index - 1].X, angles[index - 1].Y, angles[index].X, angles[index].Y, 0.0f, 0.0f, (float)Math.Cos(stopAngle), (float)Math.Sin(stopAngle)); - newAngle.angle = stopAngle; - newAngle.X = iX; - newAngle.Y = iY; - angles[index] = newAngle; - } - } - } - } - - /// - /// generates a profile for extrusion - /// - internal class Profile - { - private const float twoPi = 2.0f * (float)Math.PI; - - internal string errorMessage = null; - - internal List coords; - internal List faces; - internal List vertexNormals; - internal List us; - internal List faceUVs; - internal List faceNumbers; - - // use these for making individual meshes for each prim face - internal List outerCoordIndices = null; - internal List hollowCoordIndices = null; - internal List cut1CoordIndices = null; - internal List cut2CoordIndices = null; - - internal Coord faceNormal = new Coord(0.0f, 0.0f, 1.0f); - internal Coord cutNormal1 = new Coord(); - internal Coord cutNormal2 = new Coord(); - - internal int numOuterVerts = 0; - internal int numHollowVerts = 0; - - internal bool calcVertexNormals = false; - internal int bottomFaceNumber = 0; - internal int numPrimFaces = 0; - - internal Profile() - { - this.coords = new List(); - this.faces = new List(); - this.vertexNormals = new List(); - this.us = new List(); - this.faceUVs = new List(); - this.faceNumbers = new List(); - } - - internal Profile(int sides, float profileStart, float profileEnd, float hollow, int hollowSides, bool createFaces, bool calcVertexNormals) - { - this.calcVertexNormals = calcVertexNormals; - this.coords = new List(); - this.faces = new List(); - this.vertexNormals = new List(); - this.us = new List(); - this.faceUVs = new List(); - this.faceNumbers = new List(); - - Coord center = new Coord(0.0f, 0.0f, 0.0f); - //bool hasCenter = false; - - List hollowCoords = new List(); - List hollowNormals = new List(); - List hollowUs = new List(); - - if (calcVertexNormals) - { - this.outerCoordIndices = new List(); - this.hollowCoordIndices = new List(); - this.cut1CoordIndices = new List(); - this.cut2CoordIndices = new List(); - } - - bool hasHollow = (hollow > 0.0f); - - bool hasProfileCut = (profileStart > 0.0f || profileEnd < 1.0f); - - AngleList angles = new AngleList(); - AngleList hollowAngles = new AngleList(); - - float xScale = 0.5f; - float yScale = 0.5f; - if (sides == 4) // corners of a square are sqrt(2) from center - { - xScale = 0.707f; - yScale = 0.707f; - } - - float startAngle = profileStart * twoPi; - float stopAngle = profileEnd * twoPi; - - try { angles.makeAngles(sides, startAngle, stopAngle); } - catch (Exception ex) - { - - errorMessage = "makeAngles failed: Exception: " + ex.ToString() - + "\nsides: " + sides.ToString() + " startAngle: " + startAngle.ToString() + " stopAngle: " + stopAngle.ToString(); - - return; - } - - this.numOuterVerts = angles.angles.Count; - - // flag to create as few triangles as possible for 3 or 4 side profile - bool simpleFace = (sides < 5 && !hasHollow && !hasProfileCut); - - if (hasHollow) - { - if (sides == hollowSides) - hollowAngles = angles; - else - { - try { hollowAngles.makeAngles(hollowSides, startAngle, stopAngle); } - catch (Exception ex) - { - errorMessage = "makeAngles failed: Exception: " + ex.ToString() - + "\nsides: " + sides.ToString() + " startAngle: " + startAngle.ToString() + " stopAngle: " + stopAngle.ToString(); - - return; - } - } - this.numHollowVerts = hollowAngles.angles.Count; - } - else if (!simpleFace) - { - this.coords.Add(center); - //hasCenter = true; - if (this.calcVertexNormals) - this.vertexNormals.Add(new Coord(0.0f, 0.0f, 1.0f)); - this.us.Add(0.0f); - } - - float z = 0.0f; - - Angle angle; - Coord newVert = new Coord(); - if (hasHollow && hollowSides != sides) - { - int numHollowAngles = hollowAngles.angles.Count; - for (int i = 0; i < numHollowAngles; i++) - { - angle = hollowAngles.angles[i]; - newVert.X = hollow * xScale * angle.X; - newVert.Y = hollow * yScale * angle.Y; - newVert.Z = z; - - hollowCoords.Add(newVert); - if (this.calcVertexNormals) - { - if (hollowSides < 5) - hollowNormals.Add(hollowAngles.normals[i].Invert()); - else - hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f)); - - hollowUs.Add(angle.angle * hollow); - } - } - } - - int index = 0; - int numAngles = angles.angles.Count; - - for (int i = 0; i < numAngles; i++) - { - angle = angles.angles[i]; - newVert.X = angle.X * xScale; - newVert.Y = angle.Y * yScale; - newVert.Z = z; - this.coords.Add(newVert); - if (this.calcVertexNormals) - { - this.outerCoordIndices.Add(this.coords.Count - 1); - - if (sides < 5) - { - this.vertexNormals.Add(angles.normals[i]); - float u = angle.angle; - this.us.Add(u); - } - else - { - this.vertexNormals.Add(new Coord(angle.X, angle.Y, 0.0f)); - this.us.Add(angle.angle); - } - } - - if (hasHollow) - { - if (hollowSides == sides) - { - newVert.X *= hollow; - newVert.Y *= hollow; - newVert.Z = z; - hollowCoords.Add(newVert); - if (this.calcVertexNormals) - { - if (sides < 5) - { - hollowNormals.Add(angles.normals[i].Invert()); - } - - else - hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f)); - - hollowUs.Add(angle.angle * hollow); - } - } - } - else if (!simpleFace && createFaces && angle.angle > 0.0001f) - { - Face newFace = new Face(); - newFace.v1 = 0; - newFace.v2 = index; - newFace.v3 = index + 1; - - this.faces.Add(newFace); - } - index += 1; - } - - if (hasHollow) - { - hollowCoords.Reverse(); - if (this.calcVertexNormals) - { - hollowNormals.Reverse(); - hollowUs.Reverse(); - } - - if (createFaces) - { - int numOuterVerts = this.coords.Count; - int numHollowVerts = hollowCoords.Count; - int numTotalVerts = numOuterVerts + numHollowVerts; - - if (numOuterVerts == numHollowVerts) - { - Face newFace = new Face(); - - for (int coordIndex = 0; coordIndex < numOuterVerts - 1; coordIndex++) - { - newFace.v1 = coordIndex; - newFace.v2 = coordIndex + 1; - newFace.v3 = numTotalVerts - coordIndex - 1; - this.faces.Add(newFace); - - newFace.v1 = coordIndex + 1; - newFace.v2 = numTotalVerts - coordIndex - 2; - newFace.v3 = numTotalVerts - coordIndex - 1; - this.faces.Add(newFace); - } - } - else - { - if (numOuterVerts < numHollowVerts) - { - Face newFace = new Face(); - int j = 0; // j is the index for outer vertices - int maxJ = numOuterVerts - 1; - for (int i = 0; i < numHollowVerts; i++) // i is the index for inner vertices - { - if (j < maxJ) - if (angles.angles[j + 1].angle - hollowAngles.angles[i].angle < hollowAngles.angles[i].angle - angles.angles[j].angle + 0.000001f) - { - newFace.v1 = numTotalVerts - i - 1; - newFace.v2 = j; - newFace.v3 = j + 1; - - this.faces.Add(newFace); - j += 1; - } - - newFace.v1 = j; - newFace.v2 = numTotalVerts - i - 2; - newFace.v3 = numTotalVerts - i - 1; - - this.faces.Add(newFace); - } - } - else // numHollowVerts < numOuterVerts - { - Face newFace = new Face(); - int j = 0; // j is the index for inner vertices - int maxJ = numHollowVerts - 1; - for (int i = 0; i < numOuterVerts; i++) - { - if (j < maxJ) - if (hollowAngles.angles[j + 1].angle - angles.angles[i].angle < angles.angles[i].angle - hollowAngles.angles[j].angle + 0.000001f) - { - newFace.v1 = i; - newFace.v2 = numTotalVerts - j - 2; - newFace.v3 = numTotalVerts - j - 1; - - this.faces.Add(newFace); - j += 1; - } - - newFace.v1 = numTotalVerts - j - 1; - newFace.v2 = i; - newFace.v3 = i + 1; - - this.faces.Add(newFace); - } - } - } - } - - if (calcVertexNormals) - { - foreach (Coord hc in hollowCoords) - { - this.coords.Add(hc); - hollowCoordIndices.Add(this.coords.Count - 1); - } - } - else - this.coords.AddRange(hollowCoords); - - if (this.calcVertexNormals) - { - this.vertexNormals.AddRange(hollowNormals); - this.us.AddRange(hollowUs); - - } - } - - if (simpleFace && createFaces) - { - if (sides == 3) - this.faces.Add(new Face(0, 1, 2)); - else if (sides == 4) - { - this.faces.Add(new Face(0, 1, 2)); - this.faces.Add(new Face(0, 2, 3)); - } - } - - if (calcVertexNormals && hasProfileCut) - { - if (hasHollow) - { - int lastOuterVertIndex = this.numOuterVerts - 1; - - this.cut1CoordIndices.Add(0); - this.cut1CoordIndices.Add(this.coords.Count - 1); - - this.cut2CoordIndices.Add(lastOuterVertIndex + 1); - this.cut2CoordIndices.Add(lastOuterVertIndex); - - this.cutNormal1.X = this.coords[0].Y - this.coords[this.coords.Count - 1].Y; - this.cutNormal1.Y = -(this.coords[0].X - this.coords[this.coords.Count - 1].X); - - this.cutNormal2.X = this.coords[lastOuterVertIndex + 1].Y - this.coords[lastOuterVertIndex].Y; - this.cutNormal2.Y = -(this.coords[lastOuterVertIndex + 1].X - this.coords[lastOuterVertIndex].X); - } - - else - { - this.cutNormal1.X = this.vertexNormals[1].Y; - this.cutNormal1.Y = -this.vertexNormals[1].X; - - this.cutNormal2.X = -this.vertexNormals[this.vertexNormals.Count - 2].Y; - this.cutNormal2.Y = this.vertexNormals[this.vertexNormals.Count - 2].X; - - } - this.cutNormal1.Normalize(); - this.cutNormal2.Normalize(); - } - - this.MakeFaceUVs(); - - hollowCoords = null; - hollowNormals = null; - hollowUs = null; - - if (calcVertexNormals) - { // calculate prim face numbers - - // face number order is top, outer, hollow, bottom, start cut, end cut - // I know it's ugly but so is the whole concept of prim face numbers - - int faceNum = 1; // start with outer faces - int startVert = hasProfileCut && !hasHollow ? 1 : 0; - if (startVert > 0) - this.faceNumbers.Add(-1); - for (int i = 0; i < numOuterVerts - 1; i++) - this.faceNumbers.Add(sides < 5 ? faceNum++ : faceNum); - - //if (!hasHollow && !hasProfileCut) - // this.bottomFaceNumber = faceNum++; - - this.faceNumbers.Add(hasProfileCut ? -1 : faceNum++); - - if (sides > 4 && (hasHollow || hasProfileCut)) - faceNum++; - - if (hasHollow) - { - for (int i = 0; i < numHollowVerts; i++) - this.faceNumbers.Add(faceNum); - - faceNum++; - } - //if (hasProfileCut || hasHollow) - // this.bottomFaceNumber = faceNum++; - this.bottomFaceNumber = faceNum++; - - if (hasHollow && hasProfileCut) - this.faceNumbers.Add(faceNum++); - for (int i = 0; i < this.faceNumbers.Count; i++) - if (this.faceNumbers[i] == -1) - this.faceNumbers[i] = faceNum++; - - - this.numPrimFaces = faceNum; - } - - } - - internal void MakeFaceUVs() - { - this.faceUVs = new List(); - foreach (Coord c in this.coords) - this.faceUVs.Add(new UVCoord(1.0f - (0.5f + c.X), 1.0f - (0.5f - c.Y))); - } - - internal Profile Copy() - { - return this.Copy(true); - } - - internal Profile Copy(bool needFaces) - { - Profile copy = new Profile(); - - copy.coords.AddRange(this.coords); - copy.faceUVs.AddRange(this.faceUVs); - - if (needFaces) - copy.faces.AddRange(this.faces); - if ((copy.calcVertexNormals = this.calcVertexNormals) == true) - { - copy.vertexNormals.AddRange(this.vertexNormals); - copy.faceNormal = this.faceNormal; - copy.cutNormal1 = this.cutNormal1; - copy.cutNormal2 = this.cutNormal2; - copy.us.AddRange(this.us); - copy.faceNumbers.AddRange(this.faceNumbers); - - copy.cut1CoordIndices = new List(this.cut1CoordIndices); - copy.cut2CoordIndices = new List(this.cut2CoordIndices); - copy.hollowCoordIndices = new List(this.hollowCoordIndices); - copy.outerCoordIndices = new List(this.outerCoordIndices); - } - copy.numOuterVerts = this.numOuterVerts; - copy.numHollowVerts = this.numHollowVerts; - - return copy; - } - - internal void AddPos(Coord v) - { - this.AddPos(v.X, v.Y, v.Z); - } - - internal void AddPos(float x, float y, float z) - { - int i; - int numVerts = this.coords.Count; - Coord vert; - - for (i = 0; i < numVerts; i++) - { - vert = this.coords[i]; - vert.X += x; - vert.Y += y; - vert.Z += z; - this.coords[i] = vert; - } - } - - internal void AddRot(Quat q) - { - int i; - int numVerts = this.coords.Count; - - for (i = 0; i < numVerts; i++) - this.coords[i] *= q; - - if (this.calcVertexNormals) - { - int numNormals = this.vertexNormals.Count; - for (i = 0; i < numNormals; i++) - this.vertexNormals[i] *= q; - - this.faceNormal *= q; - this.cutNormal1 *= q; - this.cutNormal2 *= q; - - } - } - - internal void Scale(float x, float y) - { - int i; - int numVerts = this.coords.Count; - Coord vert; - - for (i = 0; i < numVerts; i++) - { - vert = this.coords[i]; - vert.X *= x; - vert.Y *= y; - this.coords[i] = vert; - } - } - - /// - /// Changes order of the vertex indices and negates the center vertex normal. Does not alter vertex normals of radial vertices - /// - internal void FlipNormals() - { - int i; - int numFaces = this.faces.Count; - Face tmpFace; - int tmp; - - for (i = 0; i < numFaces; i++) - { - tmpFace = this.faces[i]; - tmp = tmpFace.v3; - tmpFace.v3 = tmpFace.v1; - tmpFace.v1 = tmp; - this.faces[i] = tmpFace; - } - - if (this.calcVertexNormals) - { - int normalCount = this.vertexNormals.Count; - if (normalCount > 0) - { - Coord n = this.vertexNormals[normalCount - 1]; - n.Z = -n.Z; - this.vertexNormals[normalCount - 1] = n; - } - } - - this.faceNormal.X = -this.faceNormal.X; - this.faceNormal.Y = -this.faceNormal.Y; - this.faceNormal.Z = -this.faceNormal.Z; - - int numfaceUVs = this.faceUVs.Count; - for (i = 0; i < numfaceUVs; i++) - { - UVCoord uv = this.faceUVs[i]; - uv.V = 1.0f - uv.V; - this.faceUVs[i] = uv; - } - } - - internal void AddValue2FaceVertexIndices(int num) - { - int numFaces = this.faces.Count; - Face tmpFace; - for (int i = 0; i < numFaces; i++) - { - tmpFace = this.faces[i]; - tmpFace.v1 += num; - tmpFace.v2 += num; - tmpFace.v3 += num; - - this.faces[i] = tmpFace; - } - } - - internal void AddValue2FaceNormalIndices(int num) - { - if (this.calcVertexNormals) - { - int numFaces = this.faces.Count; - Face tmpFace; - for (int i = 0; i < numFaces; i++) - { - tmpFace = this.faces[i]; - tmpFace.n1 += num; - tmpFace.n2 += num; - tmpFace.n3 += num; - - this.faces[i] = tmpFace; - } - } - } - - internal void DumpRaw(String path, String name, String title) - { - if (path == null) - return; - String fileName = name + "_" + title + ".raw"; - String completePath = System.IO.Path.Combine(path, fileName); - StreamWriter sw = new StreamWriter(completePath); - - for (int i = 0; i < this.faces.Count; i++) - { - string s = this.coords[this.faces[i].v1].ToString(); - s += " " + this.coords[this.faces[i].v2].ToString(); - s += " " + this.coords[this.faces[i].v3].ToString(); - - sw.WriteLine(s); - } - - sw.Close(); - } - } - - public struct PathNode - { - public Coord position; - public Quat rotation; - public float xScale; - public float yScale; - public float percentOfPath; - } - - public enum PathType { Linear = 0, Circular = 1, Flexible = 2 } - - public class Path - { - public List pathNodes = new List(); - - public float twistBegin = 0.0f; - public float twistEnd = 0.0f; - public float topShearX = 0.0f; - public float topShearY = 0.0f; - public float pathCutBegin = 0.0f; - public float pathCutEnd = 1.0f; - public float dimpleBegin = 0.0f; - public float dimpleEnd = 1.0f; - public float skew = 0.0f; - public float holeSizeX = 1.0f; // called pathScaleX in pbs - public float holeSizeY = 0.25f; - public float taperX = 0.0f; - public float taperY = 0.0f; - public float radius = 0.0f; - public float revolutions = 1.0f; - public int stepsPerRevolution = 24; - - private const float twoPi = 2.0f * (float)Math.PI; - - public void Create(PathType pathType, int steps) - { - if (pathType == PathType.Linear || pathType == PathType.Flexible) - { - int step = 0; - - float length = this.pathCutEnd - this.pathCutBegin; - float twistTotal = twistEnd - twistBegin; - float twistTotalAbs = Math.Abs(twistTotal); - if (twistTotalAbs > 0.01f) - steps += (int)(twistTotalAbs * 3.66); // dahlia's magic number - - float start = -0.5f; - float stepSize = length / (float)steps; - float percentOfPathMultiplier = stepSize; - float xOffset = 0.0f; - float yOffset = 0.0f; - float zOffset = start; - float xOffsetStepIncrement = this.topShearX / steps; - float yOffsetStepIncrement = this.topShearY / steps; - - float percentOfPath = this.pathCutBegin; - zOffset += percentOfPath; - - // sanity checks - - bool done = false; - - while (!done) - { - PathNode newNode = new PathNode(); - - newNode.xScale = 1.0f; - if (this.taperX == 0.0f) - newNode.xScale = 1.0f; - else if (this.taperX > 0.0f) - newNode.xScale = 1.0f - percentOfPath * this.taperX; - else newNode.xScale = 1.0f + (1.0f - percentOfPath) * this.taperX; - - newNode.yScale = 1.0f; - if (this.taperY == 0.0f) - newNode.yScale = 1.0f; - else if (this.taperY > 0.0f) - newNode.yScale = 1.0f - percentOfPath * this.taperY; - else newNode.yScale = 1.0f + (1.0f - percentOfPath) * this.taperY; - - float twist = twistBegin + twistTotal * percentOfPath; - - newNode.rotation = new Quat(new Coord(0.0f, 0.0f, 1.0f), twist); - newNode.position = new Coord(xOffset, yOffset, zOffset); - newNode.percentOfPath = percentOfPath; - - pathNodes.Add(newNode); - - if (step < steps) - { - step += 1; - percentOfPath += percentOfPathMultiplier; - xOffset += xOffsetStepIncrement; - yOffset += yOffsetStepIncrement; - zOffset += stepSize; - if (percentOfPath > this.pathCutEnd) - done = true; - } - else done = true; - } - } // end of linear path code - - else // pathType == Circular - { - float twistTotal = twistEnd - twistBegin; - - // if the profile has a lot of twist, add more layers otherwise the layers may overlap - // and the resulting mesh may be quite inaccurate. This method is arbitrary and doesn't - // accurately match the viewer - float twistTotalAbs = Math.Abs(twistTotal); - if (twistTotalAbs > 0.01f) - { - if (twistTotalAbs > Math.PI * 1.5f) - steps *= 2; - if (twistTotalAbs > Math.PI * 3.0f) - steps *= 2; - } - - float yPathScale = this.holeSizeY * 0.5f; - float pathLength = this.pathCutEnd - this.pathCutBegin; - float totalSkew = this.skew * 2.0f * pathLength; - float skewStart = this.pathCutBegin * 2.0f * this.skew - this.skew; - float xOffsetTopShearXFactor = this.topShearX * (0.25f + 0.5f * (0.5f - this.holeSizeY)); - float yShearCompensation = 1.0f + Math.Abs(this.topShearY) * 0.25f; - - // It's not quite clear what pushY (Y top shear) does, but subtracting it from the start and end - // angles appears to approximate it's effects on path cut. Likewise, adding it to the angle used - // to calculate the sine for generating the path radius appears to approximate it's effects there - // too, but there are some subtle differences in the radius which are noticeable as the prim size - // increases and it may affect megaprims quite a bit. The effect of the Y top shear parameter on - // the meshes generated with this technique appear nearly identical in shape to the same prims when - // displayed by the viewer. - - float startAngle = (twoPi * this.pathCutBegin * this.revolutions) - this.topShearY * 0.9f; - float endAngle = (twoPi * this.pathCutEnd * this.revolutions) - this.topShearY * 0.9f; - float stepSize = twoPi / this.stepsPerRevolution; - - int step = (int)(startAngle / stepSize); -// int firstStep = step; - float angle = startAngle; - - bool done = false; - while (!done) // loop through the length of the path and add the layers - { - PathNode newNode = new PathNode(); - - float xProfileScale = (1.0f - Math.Abs(this.skew)) * this.holeSizeX; - float yProfileScale = this.holeSizeY; - - float percentOfPath = angle / (twoPi * this.revolutions); - float percentOfAngles = (angle - startAngle) / (endAngle - startAngle); - - if (this.taperX > 0.01f) - xProfileScale *= 1.0f - percentOfPath * this.taperX; - else if (this.taperX < -0.01f) - xProfileScale *= 1.0f + (1.0f - percentOfPath) * this.taperX; - - if (this.taperY > 0.01f) - yProfileScale *= 1.0f - percentOfPath * this.taperY; - else if (this.taperY < -0.01f) - yProfileScale *= 1.0f + (1.0f - percentOfPath) * this.taperY; - - newNode.xScale = xProfileScale; - newNode.yScale = yProfileScale; - - float radiusScale = 1.0f; - if (this.radius > 0.001f) - radiusScale = 1.0f - this.radius * percentOfPath; - else if (this.radius < 0.001f) - radiusScale = 1.0f + this.radius * (1.0f - percentOfPath); - - float twist = twistBegin + twistTotal * percentOfPath; - - float xOffset = 0.5f * (skewStart + totalSkew * percentOfAngles); - xOffset += (float)Math.Sin(angle) * xOffsetTopShearXFactor; - - float yOffset = yShearCompensation * (float)Math.Cos(angle) * (0.5f - yPathScale) * radiusScale; - - float zOffset = (float)Math.Sin(angle + this.topShearY) * (0.5f - yPathScale) * radiusScale; - - newNode.position = new Coord(xOffset, yOffset, zOffset); - - // now orient the rotation of the profile layer relative to it's position on the path - // adding taperY to the angle used to generate the quat appears to approximate the viewer - - newNode.rotation = new Quat(new Coord(1.0f, 0.0f, 0.0f), angle + this.topShearY); - - // next apply twist rotation to the profile layer - if (twistTotal != 0.0f || twistBegin != 0.0f) - newNode.rotation *= new Quat(new Coord(0.0f, 0.0f, 1.0f), twist); - - newNode.percentOfPath = percentOfPath; - - pathNodes.Add(newNode); - - // calculate terms for next iteration - // calculate the angle for the next iteration of the loop - - if (angle >= endAngle - 0.01) - done = true; - else - { - step += 1; - angle = stepSize * step; - if (angle > endAngle) - angle = endAngle; - } - } - } - } - } - - public class PrimMesh - { - public string errorMessage = ""; - private const float twoPi = 2.0f * (float)Math.PI; - - public List coords; - public List normals; - public List faces; - - public List viewerFaces; - - private int sides = 4; - private int hollowSides = 4; - private float profileStart = 0.0f; - private float profileEnd = 1.0f; - private float hollow = 0.0f; - public int twistBegin = 0; - public int twistEnd = 0; - public float topShearX = 0.0f; - public float topShearY = 0.0f; - public float pathCutBegin = 0.0f; - public float pathCutEnd = 1.0f; - public float dimpleBegin = 0.0f; - public float dimpleEnd = 1.0f; - public float skew = 0.0f; - public float holeSizeX = 1.0f; // called pathScaleX in pbs - public float holeSizeY = 0.25f; - public float taperX = 0.0f; - public float taperY = 0.0f; - public float radius = 0.0f; - public float revolutions = 1.0f; - public int stepsPerRevolution = 24; - - private bool hasProfileCut = false; - private bool hasHollow = false; - public bool calcVertexNormals = false; - private bool normalsProcessed = false; - public bool viewerMode = false; - - public int numPrimFaces = 0; - - /// - /// Human readable string representation of the parameters used to create a mesh. - /// - /// - public string ParamsToDisplayString() - { - string s = ""; - s += "sides..................: " + this.sides.ToString(); - s += "\nhollowSides..........: " + this.hollowSides.ToString(); - s += "\nprofileStart.........: " + this.profileStart.ToString(); - s += "\nprofileEnd...........: " + this.profileEnd.ToString(); - s += "\nhollow...............: " + this.hollow.ToString(); - s += "\ntwistBegin...........: " + this.twistBegin.ToString(); - s += "\ntwistEnd.............: " + this.twistEnd.ToString(); - s += "\ntopShearX............: " + this.topShearX.ToString(); - s += "\ntopShearY............: " + this.topShearY.ToString(); - s += "\npathCutBegin.........: " + this.pathCutBegin.ToString(); - s += "\npathCutEnd...........: " + this.pathCutEnd.ToString(); - s += "\ndimpleBegin..........: " + this.dimpleBegin.ToString(); - s += "\ndimpleEnd............: " + this.dimpleEnd.ToString(); - s += "\nskew.................: " + this.skew.ToString(); - s += "\nholeSizeX............: " + this.holeSizeX.ToString(); - s += "\nholeSizeY............: " + this.holeSizeY.ToString(); - s += "\ntaperX...............: " + this.taperX.ToString(); - s += "\ntaperY...............: " + this.taperY.ToString(); - s += "\nradius...............: " + this.radius.ToString(); - s += "\nrevolutions..........: " + this.revolutions.ToString(); - s += "\nstepsPerRevolution...: " + this.stepsPerRevolution.ToString(); - - return s; - } - - /// - /// Constructs a PrimMesh object and creates the profile for extrusion. - /// - /// - /// - /// - /// - /// - public PrimMesh(int sides, float profileStart, float profileEnd, float hollow, int hollowSides) - { - this.coords = new List(); - this.faces = new List(); - - this.sides = sides; - this.profileStart = profileStart; - this.profileEnd = profileEnd; - this.hollow = hollow; - this.hollowSides = hollowSides; - - if (sides < 3) - this.sides = 3; - if (hollowSides < 3) - this.hollowSides = 3; - if (profileStart < 0.0f) - this.profileStart = 0.0f; - if (profileEnd > 1.0f) - this.profileEnd = 1.0f; - if (profileEnd < 0.02f) - this.profileEnd = 0.02f; - if (profileStart >= profileEnd) - this.profileStart = profileEnd - 0.02f; - if (hollow > 0.99f) - this.hollow = 0.99f; - if (hollow < 0.0f) - this.hollow = 0.0f; - - this.hasProfileCut = (this.profileStart > 0.0f || this.profileEnd < 1.0f); - this.hasHollow = (this.hollow > 0.001f); - } - - /// - /// Extrudes a profile along a path. - /// - public void Extrude(PathType pathType) - { - this.coords = new List(); - this.faces = new List(); - - if (this.viewerMode) - { - this.viewerFaces = new List(); - this.calcVertexNormals = true; - } - - if (this.calcVertexNormals) - this.normals = new List(); - - int steps = 1; - - float length = this.pathCutEnd - this.pathCutBegin; - normalsProcessed = false; - - if (this.viewerMode && this.sides == 3) - { - // prisms don't taper well so add some vertical resolution - // other prims may benefit from this but just do prisms for now - if (Math.Abs(this.taperX) > 0.01 || Math.Abs(this.taperY) > 0.01) - steps = (int)(steps * 4.5 * length); - } - - - float twistBegin = this.twistBegin / 360.0f * twoPi; - float twistEnd = this.twistEnd / 360.0f * twoPi; - float twistTotal = twistEnd - twistBegin; - float twistTotalAbs = Math.Abs(twistTotal); - if (twistTotalAbs > 0.01f) - steps += (int)(twistTotalAbs * 3.66); // dahlia's magic number - - float hollow = this.hollow; - - // sanity checks - float initialProfileRot = 0.0f; - if (pathType == PathType.Circular) - { - if (this.sides == 3) - { - initialProfileRot = (float)Math.PI; - if (this.hollowSides == 4) - { - if (hollow > 0.7f) - hollow = 0.7f; - hollow *= 0.707f; - } - else hollow *= 0.5f; - } - else if (this.sides == 4) - { - initialProfileRot = 0.25f * (float)Math.PI; - if (this.hollowSides != 4) - hollow *= 0.707f; - } - else if (this.sides > 4) - { - initialProfileRot = (float)Math.PI; - if (this.hollowSides == 4) - { - if (hollow > 0.7f) - hollow = 0.7f; - hollow /= 0.7f; - } - } - } - else - { - if (this.sides == 3) - { - if (this.hollowSides == 4) - { - if (hollow > 0.7f) - hollow = 0.7f; - hollow *= 0.707f; - } - else hollow *= 0.5f; - } - else if (this.sides == 4) - { - initialProfileRot = 1.25f * (float)Math.PI; - if (this.hollowSides != 4) - hollow *= 0.707f; - } - else if (this.sides == 24 && this.hollowSides == 4) - hollow *= 1.414f; - } - - Profile profile = new Profile(this.sides, this.profileStart, this.profileEnd, hollow, this.hollowSides, true, calcVertexNormals); - this.errorMessage = profile.errorMessage; - - this.numPrimFaces = profile.numPrimFaces; - - int cut1Vert = -1; - int cut2Vert = -1; - if (hasProfileCut) - { - cut1Vert = hasHollow ? profile.coords.Count - 1 : 0; - cut2Vert = hasHollow ? profile.numOuterVerts - 1 : profile.numOuterVerts; - } - - if (initialProfileRot != 0.0f) - { - profile.AddRot(new Quat(new Coord(0.0f, 0.0f, 1.0f), initialProfileRot)); - if (viewerMode) - profile.MakeFaceUVs(); - } - - Coord lastCutNormal1 = new Coord(); - Coord lastCutNormal2 = new Coord(); - float lastV = 1.0f; - - Path path = new Path(); - path.twistBegin = twistBegin; - path.twistEnd = twistEnd; - path.topShearX = topShearX; - path.topShearY = topShearY; - path.pathCutBegin = pathCutBegin; - path.pathCutEnd = pathCutEnd; - path.dimpleBegin = dimpleBegin; - path.dimpleEnd = dimpleEnd; - path.skew = skew; - path.holeSizeX = holeSizeX; - path.holeSizeY = holeSizeY; - path.taperX = taperX; - path.taperY = taperY; - path.radius = radius; - path.revolutions = revolutions; - path.stepsPerRevolution = stepsPerRevolution; - - path.Create(pathType, steps); - - bool needEndFaces = false; - if (pathType == PathType.Circular) - { - needEndFaces = false; - if (this.pathCutBegin != 0.0f || this.pathCutEnd != 1.0f) - needEndFaces = true; - else if (this.taperX != 0.0f || this.taperY != 0.0f) - needEndFaces = true; - else if (this.skew != 0.0f) - needEndFaces = true; - else if (twistTotal != 0.0f) - needEndFaces = true; - else if (this.radius != 0.0f) - needEndFaces = true; - } - else needEndFaces = true; - - for (int nodeIndex = 0; nodeIndex < path.pathNodes.Count; nodeIndex++) - { - PathNode node = path.pathNodes[nodeIndex]; - Profile newLayer = profile.Copy(); - newLayer.Scale(node.xScale, node.yScale); - - newLayer.AddRot(node.rotation); - newLayer.AddPos(node.position); - - if (needEndFaces && nodeIndex == 0) - { - newLayer.FlipNormals(); - - // add the top faces to the viewerFaces list here - if (this.viewerMode) - { - Coord faceNormal = newLayer.faceNormal; - ViewerFace newViewerFace = new ViewerFace(profile.bottomFaceNumber); - int numFaces = newLayer.faces.Count; - List faces = newLayer.faces; - - for (int i = 0; i < numFaces; i++) - { - Face face = faces[i]; - newViewerFace.v1 = newLayer.coords[face.v1]; - newViewerFace.v2 = newLayer.coords[face.v2]; - newViewerFace.v3 = newLayer.coords[face.v3]; - - newViewerFace.coordIndex1 = face.v1; - newViewerFace.coordIndex2 = face.v2; - newViewerFace.coordIndex3 = face.v3; - - newViewerFace.n1 = faceNormal; - newViewerFace.n2 = faceNormal; - newViewerFace.n3 = faceNormal; - - newViewerFace.uv1 = newLayer.faceUVs[face.v1]; - newViewerFace.uv2 = newLayer.faceUVs[face.v2]; - newViewerFace.uv3 = newLayer.faceUVs[face.v3]; - - this.viewerFaces.Add(newViewerFace); - } - } - } // if (nodeIndex == 0) - - // append this layer - - int coordsLen = this.coords.Count; -// int lastCoordsLen = coordsLen; - newLayer.AddValue2FaceVertexIndices(coordsLen); - - this.coords.AddRange(newLayer.coords); - - if (this.calcVertexNormals) - { - newLayer.AddValue2FaceNormalIndices(this.normals.Count); - this.normals.AddRange(newLayer.vertexNormals); - } - - if (node.percentOfPath < this.pathCutBegin + 0.01f || node.percentOfPath > this.pathCutEnd - 0.01f) - this.faces.AddRange(newLayer.faces); - - // fill faces between layers - - int numVerts = newLayer.coords.Count; - Face newFace = new Face(); - - if (nodeIndex > 0) - { - int startVert = coordsLen + 1; - int endVert = this.coords.Count; - - if (sides < 5 || this.hasProfileCut || hollow > 0.0f) - startVert--; - - for (int i = startVert; i < endVert; i++) - { - int iNext = i + 1; - if (i == endVert - 1) - iNext = startVert; - - int whichVert = i - startVert; - - newFace.v1 = i; - newFace.v2 = i - numVerts; - newFace.v3 = iNext - numVerts; - this.faces.Add(newFace); - - newFace.v2 = iNext - numVerts; - newFace.v3 = iNext; - this.faces.Add(newFace); - - if (this.viewerMode) - { - // add the side faces to the list of viewerFaces here - - int primFaceNum = profile.faceNumbers[whichVert]; - if (!needEndFaces) - primFaceNum -= 1; - - ViewerFace newViewerFace1 = new ViewerFace(primFaceNum); - ViewerFace newViewerFace2 = new ViewerFace(primFaceNum); - - float u1 = newLayer.us[whichVert]; - float u2 = 1.0f; - if (whichVert < newLayer.us.Count - 1) - u2 = newLayer.us[whichVert + 1]; - - if (whichVert == cut1Vert || whichVert == cut2Vert) - { - u1 = 0.0f; - u2 = 1.0f; - } - else if (sides < 5) - { - if (whichVert < profile.numOuterVerts) - { // boxes and prisms have one texture face per side of the prim, so the U values have to be scaled - // to reflect the entire texture width - u1 *= sides; - u2 *= sides; - u2 -= (int)u1; - u1 -= (int)u1; - if (u2 < 0.1f) - u2 = 1.0f; - } - else if (whichVert > profile.coords.Count - profile.numHollowVerts - 1) - { - u1 *= 2.0f; - u2 *= 2.0f; - } - } - - newViewerFace1.uv1.U = u1; - newViewerFace1.uv2.U = u1; - newViewerFace1.uv3.U = u2; - - newViewerFace1.uv1.V = 1.0f - node.percentOfPath; - newViewerFace1.uv2.V = lastV; - newViewerFace1.uv3.V = lastV; - - newViewerFace2.uv1.U = u1; - newViewerFace2.uv2.U = u2; - newViewerFace2.uv3.U = u2; - - newViewerFace2.uv1.V = 1.0f - node.percentOfPath; - newViewerFace2.uv2.V = lastV; - newViewerFace2.uv3.V = 1.0f - node.percentOfPath; - - newViewerFace1.v1 = this.coords[i]; - newViewerFace1.v2 = this.coords[i - numVerts]; - newViewerFace1.v3 = this.coords[iNext - numVerts]; - - newViewerFace2.v1 = this.coords[i]; - newViewerFace2.v2 = this.coords[iNext - numVerts]; - newViewerFace2.v3 = this.coords[iNext]; - - newViewerFace1.coordIndex1 = i; - newViewerFace1.coordIndex2 = i - numVerts; - newViewerFace1.coordIndex3 = iNext - numVerts; - - newViewerFace2.coordIndex1 = i; - newViewerFace2.coordIndex2 = iNext - numVerts; - newViewerFace2.coordIndex3 = iNext; - - // profile cut faces - if (whichVert == cut1Vert) - { - newViewerFace1.n1 = newLayer.cutNormal1; - newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal1; - - newViewerFace2.n1 = newViewerFace2.n3 = newLayer.cutNormal1; - newViewerFace2.n2 = lastCutNormal1; - } - else if (whichVert == cut2Vert) - { - newViewerFace1.n1 = newLayer.cutNormal2; - newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal2; - - newViewerFace2.n1 = newViewerFace2.n3 = newLayer.cutNormal2; - newViewerFace2.n2 = lastCutNormal2; - } - - else // outer and hollow faces - { - if ((sides < 5 && whichVert < newLayer.numOuterVerts) || (hollowSides < 5 && whichVert >= newLayer.numOuterVerts)) - { // looks terrible when path is twisted... need vertex normals here - newViewerFace1.CalcSurfaceNormal(); - newViewerFace2.CalcSurfaceNormal(); - } - else - { - newViewerFace1.n1 = this.normals[i]; - newViewerFace1.n2 = this.normals[i - numVerts]; - newViewerFace1.n3 = this.normals[iNext - numVerts]; - - newViewerFace2.n1 = this.normals[i]; - newViewerFace2.n2 = this.normals[iNext - numVerts]; - newViewerFace2.n3 = this.normals[iNext]; - } - } - - this.viewerFaces.Add(newViewerFace1); - this.viewerFaces.Add(newViewerFace2); - - } - } - } - - lastCutNormal1 = newLayer.cutNormal1; - lastCutNormal2 = newLayer.cutNormal2; - lastV = 1.0f - node.percentOfPath; - - if (needEndFaces && nodeIndex == path.pathNodes.Count - 1 && viewerMode) - { - // add the top faces to the viewerFaces list here - Coord faceNormal = newLayer.faceNormal; - ViewerFace newViewerFace = new ViewerFace(); - newViewerFace.primFaceNumber = 0; - int numFaces = newLayer.faces.Count; - List faces = newLayer.faces; - - for (int i = 0; i < numFaces; i++) - { - Face face = faces[i]; - newViewerFace.v1 = newLayer.coords[face.v1 - coordsLen]; - newViewerFace.v2 = newLayer.coords[face.v2 - coordsLen]; - newViewerFace.v3 = newLayer.coords[face.v3 - coordsLen]; - - newViewerFace.coordIndex1 = face.v1 - coordsLen; - newViewerFace.coordIndex2 = face.v2 - coordsLen; - newViewerFace.coordIndex3 = face.v3 - coordsLen; - - newViewerFace.n1 = faceNormal; - newViewerFace.n2 = faceNormal; - newViewerFace.n3 = faceNormal; - - newViewerFace.uv1 = newLayer.faceUVs[face.v1 - coordsLen]; - newViewerFace.uv2 = newLayer.faceUVs[face.v2 - coordsLen]; - newViewerFace.uv3 = newLayer.faceUVs[face.v3 - coordsLen]; - - this.viewerFaces.Add(newViewerFace); - } - } - - - } // for (int nodeIndex = 0; nodeIndex < path.pathNodes.Count; nodeIndex++) - - } - - - /// - /// DEPRICATED - use Extrude(PathType.Linear) instead - /// Extrudes a profile along a straight line path. Used for prim types box, cylinder, and prism. - /// - /// - public void ExtrudeLinear() - { - this.Extrude(PathType.Linear); - } - - - /// - /// DEPRICATED - use Extrude(PathType.Circular) instead - /// Extrude a profile into a circular path prim mesh. Used for prim types torus, tube, and ring. - /// - /// - public void ExtrudeCircular() - { - this.Extrude(PathType.Circular); - } - - - private Coord SurfaceNormal(Coord c1, Coord c2, Coord c3) - { - Coord edge1 = new Coord(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z); - Coord edge2 = new Coord(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z); - - Coord normal = Coord.Cross(edge1, edge2); - - normal.Normalize(); - - return normal; - } - - private Coord SurfaceNormal(Face face) - { - return SurfaceNormal(this.coords[face.v1], this.coords[face.v2], this.coords[face.v3]); - } - - /// - /// Calculate the surface normal for a face in the list of faces - /// - /// - /// - public Coord SurfaceNormal(int faceIndex) - { - int numFaces = this.faces.Count; - if (faceIndex < 0 || faceIndex >= numFaces) - throw new Exception("faceIndex out of range"); - - return SurfaceNormal(this.faces[faceIndex]); - } - - /// - /// Duplicates a PrimMesh object. All object properties are copied by value, including lists. - /// - /// - public PrimMesh Copy() - { - PrimMesh copy = new PrimMesh(this.sides, this.profileStart, this.profileEnd, this.hollow, this.hollowSides); - copy.twistBegin = this.twistBegin; - copy.twistEnd = this.twistEnd; - copy.topShearX = this.topShearX; - copy.topShearY = this.topShearY; - copy.pathCutBegin = this.pathCutBegin; - copy.pathCutEnd = this.pathCutEnd; - copy.dimpleBegin = this.dimpleBegin; - copy.dimpleEnd = this.dimpleEnd; - copy.skew = this.skew; - copy.holeSizeX = this.holeSizeX; - copy.holeSizeY = this.holeSizeY; - copy.taperX = this.taperX; - copy.taperY = this.taperY; - copy.radius = this.radius; - copy.revolutions = this.revolutions; - copy.stepsPerRevolution = this.stepsPerRevolution; - copy.calcVertexNormals = this.calcVertexNormals; - copy.normalsProcessed = this.normalsProcessed; - copy.viewerMode = this.viewerMode; - copy.numPrimFaces = this.numPrimFaces; - copy.errorMessage = this.errorMessage; - - copy.coords = new List(this.coords); - copy.faces = new List(this.faces); - copy.viewerFaces = new List(this.viewerFaces); - copy.normals = new List(this.normals); - - return copy; - } - - /// - /// Calculate surface normals for all of the faces in the list of faces in this mesh - /// - public void CalcNormals() - { - if (normalsProcessed) - return; - - normalsProcessed = true; - - int numFaces = faces.Count; - - if (!this.calcVertexNormals) - this.normals = new List(); - - for (int i = 0; i < numFaces; i++) - { - Face face = faces[i]; - - this.normals.Add(SurfaceNormal(i).Normalize()); - - int normIndex = normals.Count - 1; - face.n1 = normIndex; - face.n2 = normIndex; - face.n3 = normIndex; - - this.faces[i] = face; - } - } - - /// - /// Adds a value to each XYZ vertex coordinate in the mesh - /// - /// - /// - /// - public void AddPos(float x, float y, float z) - { - int i; - int numVerts = this.coords.Count; - Coord vert; - - for (i = 0; i < numVerts; i++) - { - vert = this.coords[i]; - vert.X += x; - vert.Y += y; - vert.Z += z; - this.coords[i] = vert; - } - - if (this.viewerFaces != null) - { - int numViewerFaces = this.viewerFaces.Count; - - for (i = 0; i < numViewerFaces; i++) - { - ViewerFace v = this.viewerFaces[i]; - v.AddPos(x, y, z); - this.viewerFaces[i] = v; - } - } - } - - /// - /// Rotates the mesh - /// - /// - public void AddRot(Quat q) - { - int i; - int numVerts = this.coords.Count; - - for (i = 0; i < numVerts; i++) - this.coords[i] *= q; - - if (this.normals != null) - { - int numNormals = this.normals.Count; - for (i = 0; i < numNormals; i++) - this.normals[i] *= q; - } - - if (this.viewerFaces != null) - { - int numViewerFaces = this.viewerFaces.Count; - - for (i = 0; i < numViewerFaces; i++) - { - ViewerFace v = this.viewerFaces[i]; - v.v1 *= q; - v.v2 *= q; - v.v3 *= q; - - v.n1 *= q; - v.n2 *= q; - v.n3 *= q; - this.viewerFaces[i] = v; - } - } - } - -#if VERTEX_INDEXER - public VertexIndexer GetVertexIndexer() - { - if (this.viewerMode && this.viewerFaces.Count > 0) - return new VertexIndexer(this); - return null; - } -#endif - - /// - /// Scales the mesh - /// - /// - /// - /// - public void Scale(float x, float y, float z) - { - int i; - int numVerts = this.coords.Count; - //Coord vert; - - Coord m = new Coord(x, y, z); - for (i = 0; i < numVerts; i++) - this.coords[i] *= m; - - if (this.viewerFaces != null) - { - int numViewerFaces = this.viewerFaces.Count; - for (i = 0; i < numViewerFaces; i++) - { - ViewerFace v = this.viewerFaces[i]; - v.v1 *= m; - v.v2 *= m; - v.v3 *= m; - this.viewerFaces[i] = v; - } - - } - - } - - /// - /// Dumps the mesh to a Blender compatible "Raw" format file - /// - /// - /// - /// - public void DumpRaw(String path, String name, String title) - { - if (path == null) - return; - String fileName = name + "_" + title + ".raw"; - String completePath = System.IO.Path.Combine(path, fileName); - StreamWriter sw = new StreamWriter(completePath); - - for (int i = 0; i < this.faces.Count; i++) - { - string s = this.coords[this.faces[i].v1].ToString(); - s += " " + this.coords[this.faces[i].v2].ToString(); - s += " " + this.coords[this.faces[i].v3].ToString(); - - sw.WriteLine(s); - } - - sw.Close(); - } - } -} +/* + * Copyright (c) Contributors + * 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.Text; +using System.IO; + +namespace PrimMesher +{ + public struct Quat + { + /// X value + public float X; + /// Y value + public float Y; + /// Z value + public float Z; + /// W value + public float W; + + public Quat(float x, float y, float z, float w) + { + X = x; + Y = y; + Z = z; + W = w; + } + + public Quat(Coord axis, float angle) + { + axis = axis.Normalize(); + + angle *= 0.5f; + float c = (float)Math.Cos(angle); + float s = (float)Math.Sin(angle); + + X = axis.X * s; + Y = axis.Y * s; + Z = axis.Z * s; + W = c; + + Normalize(); + } + + public float Length() + { + return (float)Math.Sqrt(X * X + Y * Y + Z * Z + W * W); + } + + public Quat Normalize() + { + const float MAG_THRESHOLD = 0.0000001f; + float mag = Length(); + + // Catch very small rounding errors when normalizing + if (mag > MAG_THRESHOLD) + { + float oomag = 1f / mag; + X *= oomag; + Y *= oomag; + Z *= oomag; + W *= oomag; + } + else + { + X = 0f; + Y = 0f; + Z = 0f; + W = 1f; + } + + return this; + } + + public static Quat operator *(Quat q1, Quat q2) + { + float x = q1.W * q2.X + q1.X * q2.W + q1.Y * q2.Z - q1.Z * q2.Y; + float y = q1.W * q2.Y - q1.X * q2.Z + q1.Y * q2.W + q1.Z * q2.X; + float z = q1.W * q2.Z + q1.X * q2.Y - q1.Y * q2.X + q1.Z * q2.W; + float w = q1.W * q2.W - q1.X * q2.X - q1.Y * q2.Y - q1.Z * q2.Z; + return new Quat(x, y, z, w); + } + + public override string ToString() + { + return "< X: " + this.X.ToString() + ", Y: " + this.Y.ToString() + ", Z: " + this.Z.ToString() + ", W: " + this.W.ToString() + ">"; + } + } + + public struct Coord + { + public float X; + public float Y; + public float Z; + + public Coord(float x, float y, float z) + { + this.X = x; + this.Y = y; + this.Z = z; + } + + public float Length() + { + return (float)Math.Sqrt(this.X * this.X + this.Y * this.Y + this.Z * this.Z); + } + + public Coord Invert() + { + this.X = -this.X; + this.Y = -this.Y; + this.Z = -this.Z; + + return this; + } + + public Coord Normalize() + { + const float MAG_THRESHOLD = 0.0000001f; + float mag = Length(); + + // Catch very small rounding errors when normalizing + if (mag > MAG_THRESHOLD) + { + float oomag = 1.0f / mag; + this.X *= oomag; + this.Y *= oomag; + this.Z *= oomag; + } + else + { + this.X = 0.0f; + this.Y = 0.0f; + this.Z = 0.0f; + } + + return this; + } + + public override string ToString() + { + return this.X.ToString() + " " + this.Y.ToString() + " " + this.Z.ToString(); + } + + public static Coord Cross(Coord c1, Coord c2) + { + return new Coord( + c1.Y * c2.Z - c2.Y * c1.Z, + c1.Z * c2.X - c2.Z * c1.X, + c1.X * c2.Y - c2.X * c1.Y + ); + } + + public static Coord operator +(Coord v, Coord a) + { + return new Coord(v.X + a.X, v.Y + a.Y, v.Z + a.Z); + } + + public static Coord operator *(Coord v, Coord m) + { + return new Coord(v.X * m.X, v.Y * m.Y, v.Z * m.Z); + } + + public static Coord operator *(Coord v, Quat q) + { + // From http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/transforms/ + + Coord c2 = new Coord(0.0f, 0.0f, 0.0f); + + c2.X = q.W * q.W * v.X + + 2f * q.Y * q.W * v.Z - + 2f * q.Z * q.W * v.Y + + q.X * q.X * v.X + + 2f * q.Y * q.X * v.Y + + 2f * q.Z * q.X * v.Z - + q.Z * q.Z * v.X - + q.Y * q.Y * v.X; + + c2.Y = + 2f * q.X * q.Y * v.X + + q.Y * q.Y * v.Y + + 2f * q.Z * q.Y * v.Z + + 2f * q.W * q.Z * v.X - + q.Z * q.Z * v.Y + + q.W * q.W * v.Y - + 2f * q.X * q.W * v.Z - + q.X * q.X * v.Y; + + c2.Z = + 2f * q.X * q.Z * v.X + + 2f * q.Y * q.Z * v.Y + + q.Z * q.Z * v.Z - + 2f * q.W * q.Y * v.X - + q.Y * q.Y * v.Z + + 2f * q.W * q.X * v.Y - + q.X * q.X * v.Z + + q.W * q.W * v.Z; + + return c2; + } + } + + public struct UVCoord + { + public float U; + public float V; + + + public UVCoord(float u, float v) + { + this.U = u; + this.V = v; + } + } + + public struct Face + { + public int primFace; + + // vertices + public int v1; + public int v2; + public int v3; + + //normals + public int n1; + public int n2; + public int n3; + + // uvs + public int uv1; + public int uv2; + public int uv3; + + + public Face(int v1, int v2, int v3) + { + primFace = 0; + + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; + + this.n1 = 0; + this.n2 = 0; + this.n3 = 0; + + this.uv1 = 0; + this.uv2 = 0; + this.uv3 = 0; + + } + + public Face(int v1, int v2, int v3, int n1, int n2, int n3) + { + primFace = 0; + + this.v1 = v1; + this.v2 = v2; + this.v3 = v3; + + this.n1 = n1; + this.n2 = n2; + this.n3 = n3; + + this.uv1 = 0; + this.uv2 = 0; + this.uv3 = 0; + } + + public Coord SurfaceNormal(List coordList) + { + Coord c1 = coordList[this.v1]; + Coord c2 = coordList[this.v2]; + Coord c3 = coordList[this.v3]; + + Coord edge1 = new Coord(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z); + Coord edge2 = new Coord(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z); + + return Coord.Cross(edge1, edge2).Normalize(); + } + } + + public struct ViewerFace + { + public int primFaceNumber; + + public Coord v1; + public Coord v2; + public Coord v3; + + public int coordIndex1; + public int coordIndex2; + public int coordIndex3; + + public Coord n1; + public Coord n2; + public Coord n3; + + public UVCoord uv1; + public UVCoord uv2; + public UVCoord uv3; + + public ViewerFace(int primFaceNumber) + { + this.primFaceNumber = primFaceNumber; + + this.v1 = new Coord(); + this.v2 = new Coord(); + this.v3 = new Coord(); + + this.coordIndex1 = this.coordIndex2 = this.coordIndex3 = -1; // -1 means not assigned yet + + this.n1 = new Coord(); + this.n2 = new Coord(); + this.n3 = new Coord(); + + this.uv1 = new UVCoord(); + this.uv2 = new UVCoord(); + this.uv3 = new UVCoord(); + } + + public void Scale(float x, float y, float z) + { + this.v1.X *= x; + this.v1.Y *= y; + this.v1.Z *= z; + + this.v2.X *= x; + this.v2.Y *= y; + this.v2.Z *= z; + + this.v3.X *= x; + this.v3.Y *= y; + this.v3.Z *= z; + } + + public void AddPos(float x, float y, float z) + { + this.v1.X += x; + this.v2.X += x; + this.v3.X += x; + + this.v1.Y += y; + this.v2.Y += y; + this.v3.Y += y; + + this.v1.Z += z; + this.v2.Z += z; + this.v3.Z += z; + } + + public void AddRot(Quat q) + { + this.v1 *= q; + this.v2 *= q; + this.v3 *= q; + + this.n1 *= q; + this.n2 *= q; + this.n3 *= q; + } + + public void CalcSurfaceNormal() + { + + Coord edge1 = new Coord(this.v2.X - this.v1.X, this.v2.Y - this.v1.Y, this.v2.Z - this.v1.Z); + Coord edge2 = new Coord(this.v3.X - this.v1.X, this.v3.Y - this.v1.Y, this.v3.Z - this.v1.Z); + + this.n1 = this.n2 = this.n3 = Coord.Cross(edge1, edge2).Normalize(); + } + } + + internal struct Angle + { + internal float angle; + internal float X; + internal float Y; + + internal Angle(float angle, float x, float y) + { + this.angle = angle; + this.X = x; + this.Y = y; + } + } + + internal class AngleList + { + private float iX, iY; // intersection point + + private static Angle[] angles3 = + { + new Angle(0.0f, 1.0f, 0.0f), + new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f), + new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f), + new Angle(1.0f, 1.0f, 0.0f) + }; + + private static Coord[] normals3 = + { + new Coord(0.25f, 0.4330127019f, 0.0f).Normalize(), + new Coord(-0.5f, 0.0f, 0.0f).Normalize(), + new Coord(0.25f, -0.4330127019f, 0.0f).Normalize(), + new Coord(0.25f, 0.4330127019f, 0.0f).Normalize() + }; + + private static Angle[] angles4 = + { + new Angle(0.0f, 1.0f, 0.0f), + new Angle(0.25f, 0.0f, 1.0f), + new Angle(0.5f, -1.0f, 0.0f), + new Angle(0.75f, 0.0f, -1.0f), + new Angle(1.0f, 1.0f, 0.0f) + }; + + private static Coord[] normals4 = + { + new Coord(0.5f, 0.5f, 0.0f).Normalize(), + new Coord(-0.5f, 0.5f, 0.0f).Normalize(), + new Coord(-0.5f, -0.5f, 0.0f).Normalize(), + new Coord(0.5f, -0.5f, 0.0f).Normalize(), + new Coord(0.5f, 0.5f, 0.0f).Normalize() + }; + + private static Angle[] angles24 = + { + new Angle(0.0f, 1.0f, 0.0f), + new Angle(0.041666666666666664f, 0.96592582628906831f, 0.25881904510252074f), + new Angle(0.083333333333333329f, 0.86602540378443871f, 0.5f), + new Angle(0.125f, 0.70710678118654757f, 0.70710678118654746f), + new Angle(0.16666666666666667f, 0.5f, 0.8660254037844386f), + new Angle(0.20833333333333331f, 0.25881904510252096f, 0.9659258262890682f), + new Angle(0.25f, 0.0f, 1.0f), + new Angle(0.29166666666666663f, -0.25881904510252063f, 0.96592582628906831f), + new Angle(0.33333333333333333f, -0.5f, 0.86602540378443871f), + new Angle(0.375f, -0.70710678118654746f, 0.70710678118654757f), + new Angle(0.41666666666666663f, -0.86602540378443849f, 0.5f), + new Angle(0.45833333333333331f, -0.9659258262890682f, 0.25881904510252102f), + new Angle(0.5f, -1.0f, 0.0f), + new Angle(0.54166666666666663f, -0.96592582628906842f, -0.25881904510252035f), + new Angle(0.58333333333333326f, -0.86602540378443882f, -0.5f), + new Angle(0.62499999999999989f, -0.70710678118654791f, -0.70710678118654713f), + new Angle(0.66666666666666667f, -0.5f, -0.86602540378443837f), + new Angle(0.70833333333333326f, -0.25881904510252152f, -0.96592582628906809f), + new Angle(0.75f, 0.0f, -1.0f), + new Angle(0.79166666666666663f, 0.2588190451025203f, -0.96592582628906842f), + new Angle(0.83333333333333326f, 0.5f, -0.86602540378443904f), + new Angle(0.875f, 0.70710678118654735f, -0.70710678118654768f), + new Angle(0.91666666666666663f, 0.86602540378443837f, -0.5f), + new Angle(0.95833333333333326f, 0.96592582628906809f, -0.25881904510252157f), + new Angle(1.0f, 1.0f, 0.0f) + }; + + private Angle interpolatePoints(float newPoint, Angle p1, Angle p2) + { + float m = (newPoint - p1.angle) / (p2.angle - p1.angle); + return new Angle(newPoint, p1.X + m * (p2.X - p1.X), p1.Y + m * (p2.Y - p1.Y)); + } + + private void intersection(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) + { // ref: http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/ + double denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1); + double uaNumerator = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3); + + if (denom != 0.0) + { + double ua = uaNumerator / denom; + iX = (float)(x1 + ua * (x2 - x1)); + iY = (float)(y1 + ua * (y2 - y1)); + } + } + + internal List angles; + internal List normals; + + internal void makeAngles(int sides, float startAngle, float stopAngle) + { + angles = new List(); + normals = new List(); + + double twoPi = System.Math.PI * 2.0; + float twoPiInv = 1.0f / (float)twoPi; + + if (sides < 1) + throw new Exception("number of sides not greater than zero"); + if (stopAngle <= startAngle) + throw new Exception("stopAngle not greater than startAngle"); + + if ((sides == 3 || sides == 4 || sides == 24)) + { + startAngle *= twoPiInv; + stopAngle *= twoPiInv; + + Angle[] sourceAngles; + if (sides == 3) + sourceAngles = angles3; + else if (sides == 4) + sourceAngles = angles4; + else sourceAngles = angles24; + + int startAngleIndex = (int)(startAngle * sides); + int endAngleIndex = sourceAngles.Length - 1; + if (stopAngle < 1.0f) + endAngleIndex = (int)(stopAngle * sides) + 1; + if (endAngleIndex == startAngleIndex) + endAngleIndex++; + + for (int angleIndex = startAngleIndex; angleIndex < endAngleIndex + 1; angleIndex++) + { + angles.Add(sourceAngles[angleIndex]); + if (sides == 3) + normals.Add(normals3[angleIndex]); + else if (sides == 4) + normals.Add(normals4[angleIndex]); + } + + if (startAngle > 0.0f) + angles[0] = interpolatePoints(startAngle, angles[0], angles[1]); + + if (stopAngle < 1.0f) + { + int lastAngleIndex = angles.Count - 1; + angles[lastAngleIndex] = interpolatePoints(stopAngle, angles[lastAngleIndex - 1], angles[lastAngleIndex]); + } + } + else + { + double stepSize = twoPi / sides; + + int startStep = (int)(startAngle / stepSize); + double angle = stepSize * startStep; + int step = startStep; + double stopAngleTest = stopAngle; + if (stopAngle < twoPi) + { + stopAngleTest = stepSize * ((int)(stopAngle / stepSize) + 1); + if (stopAngleTest < stopAngle) + stopAngleTest += stepSize; + if (stopAngleTest > twoPi) + stopAngleTest = twoPi; + } + + while (angle <= stopAngleTest) + { + Angle newAngle; + newAngle.angle = (float)angle; + newAngle.X = (float)System.Math.Cos(angle); + newAngle.Y = (float)System.Math.Sin(angle); + angles.Add(newAngle); + step += 1; + angle = stepSize * step; + } + + if (startAngle > angles[0].angle) + { + Angle newAngle; + intersection(angles[0].X, angles[0].Y, angles[1].X, angles[1].Y, 0.0f, 0.0f, (float)Math.Cos(startAngle), (float)Math.Sin(startAngle)); + newAngle.angle = startAngle; + newAngle.X = iX; + newAngle.Y = iY; + angles[0] = newAngle; + } + + int index = angles.Count - 1; + if (stopAngle < angles[index].angle) + { + Angle newAngle; + intersection(angles[index - 1].X, angles[index - 1].Y, angles[index].X, angles[index].Y, 0.0f, 0.0f, (float)Math.Cos(stopAngle), (float)Math.Sin(stopAngle)); + newAngle.angle = stopAngle; + newAngle.X = iX; + newAngle.Y = iY; + angles[index] = newAngle; + } + } + } + } + + /// + /// generates a profile for extrusion + /// + internal class Profile + { + private const float twoPi = 2.0f * (float)Math.PI; + + internal string errorMessage = null; + + internal List coords; + internal List faces; + internal List vertexNormals; + internal List us; + internal List faceUVs; + internal List faceNumbers; + + // use these for making individual meshes for each prim face + internal List outerCoordIndices = null; + internal List hollowCoordIndices = null; + internal List cut1CoordIndices = null; + internal List cut2CoordIndices = null; + + internal Coord faceNormal = new Coord(0.0f, 0.0f, 1.0f); + internal Coord cutNormal1 = new Coord(); + internal Coord cutNormal2 = new Coord(); + + internal int numOuterVerts = 0; + internal int numHollowVerts = 0; + + internal bool calcVertexNormals = false; + internal int bottomFaceNumber = 0; + internal int numPrimFaces = 0; + + internal Profile() + { + this.coords = new List(); + this.faces = new List(); + this.vertexNormals = new List(); + this.us = new List(); + this.faceUVs = new List(); + this.faceNumbers = new List(); + } + + internal Profile(int sides, float profileStart, float profileEnd, float hollow, int hollowSides, bool createFaces, bool calcVertexNormals) + { + this.calcVertexNormals = calcVertexNormals; + this.coords = new List(); + this.faces = new List(); + this.vertexNormals = new List(); + this.us = new List(); + this.faceUVs = new List(); + this.faceNumbers = new List(); + + Coord center = new Coord(0.0f, 0.0f, 0.0f); + //bool hasCenter = false; + + List hollowCoords = new List(); + List hollowNormals = new List(); + List hollowUs = new List(); + + if (calcVertexNormals) + { + this.outerCoordIndices = new List(); + this.hollowCoordIndices = new List(); + this.cut1CoordIndices = new List(); + this.cut2CoordIndices = new List(); + } + + bool hasHollow = (hollow > 0.0f); + + bool hasProfileCut = (profileStart > 0.0f || profileEnd < 1.0f); + + AngleList angles = new AngleList(); + AngleList hollowAngles = new AngleList(); + + float xScale = 0.5f; + float yScale = 0.5f; + if (sides == 4) // corners of a square are sqrt(2) from center + { + xScale = 0.707f; + yScale = 0.707f; + } + + float startAngle = profileStart * twoPi; + float stopAngle = profileEnd * twoPi; + + try { angles.makeAngles(sides, startAngle, stopAngle); } + catch (Exception ex) + { + + errorMessage = "makeAngles failed: Exception: " + ex.ToString() + + "\nsides: " + sides.ToString() + " startAngle: " + startAngle.ToString() + " stopAngle: " + stopAngle.ToString(); + + return; + } + + this.numOuterVerts = angles.angles.Count; + + // flag to create as few triangles as possible for 3 or 4 side profile + bool simpleFace = (sides < 5 && !hasHollow && !hasProfileCut); + + if (hasHollow) + { + if (sides == hollowSides) + hollowAngles = angles; + else + { + try { hollowAngles.makeAngles(hollowSides, startAngle, stopAngle); } + catch (Exception ex) + { + errorMessage = "makeAngles failed: Exception: " + ex.ToString() + + "\nsides: " + sides.ToString() + " startAngle: " + startAngle.ToString() + " stopAngle: " + stopAngle.ToString(); + + return; + } + } + this.numHollowVerts = hollowAngles.angles.Count; + } + else if (!simpleFace) + { + this.coords.Add(center); + //hasCenter = true; + if (this.calcVertexNormals) + this.vertexNormals.Add(new Coord(0.0f, 0.0f, 1.0f)); + this.us.Add(0.0f); + } + + float z = 0.0f; + + Angle angle; + Coord newVert = new Coord(); + if (hasHollow && hollowSides != sides) + { + int numHollowAngles = hollowAngles.angles.Count; + for (int i = 0; i < numHollowAngles; i++) + { + angle = hollowAngles.angles[i]; + newVert.X = hollow * xScale * angle.X; + newVert.Y = hollow * yScale * angle.Y; + newVert.Z = z; + + hollowCoords.Add(newVert); + if (this.calcVertexNormals) + { + if (hollowSides < 5) + hollowNormals.Add(hollowAngles.normals[i].Invert()); + else + hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f)); + + hollowUs.Add(angle.angle * hollow); + } + } + } + + int index = 0; + int numAngles = angles.angles.Count; + + for (int i = 0; i < numAngles; i++) + { + angle = angles.angles[i]; + newVert.X = angle.X * xScale; + newVert.Y = angle.Y * yScale; + newVert.Z = z; + this.coords.Add(newVert); + if (this.calcVertexNormals) + { + this.outerCoordIndices.Add(this.coords.Count - 1); + + if (sides < 5) + { + this.vertexNormals.Add(angles.normals[i]); + float u = angle.angle; + this.us.Add(u); + } + else + { + this.vertexNormals.Add(new Coord(angle.X, angle.Y, 0.0f)); + this.us.Add(angle.angle); + } + } + + if (hasHollow) + { + if (hollowSides == sides) + { + newVert.X *= hollow; + newVert.Y *= hollow; + newVert.Z = z; + hollowCoords.Add(newVert); + if (this.calcVertexNormals) + { + if (sides < 5) + { + hollowNormals.Add(angles.normals[i].Invert()); + } + + else + hollowNormals.Add(new Coord(-angle.X, -angle.Y, 0.0f)); + + hollowUs.Add(angle.angle * hollow); + } + } + } + else if (!simpleFace && createFaces && angle.angle > 0.0001f) + { + Face newFace = new Face(); + newFace.v1 = 0; + newFace.v2 = index; + newFace.v3 = index + 1; + + this.faces.Add(newFace); + } + index += 1; + } + + if (hasHollow) + { + hollowCoords.Reverse(); + if (this.calcVertexNormals) + { + hollowNormals.Reverse(); + hollowUs.Reverse(); + } + + if (createFaces) + { + //int numOuterVerts = this.coords.Count; + //numOuterVerts = this.coords.Count; + //int numHollowVerts = hollowCoords.Count; + int numTotalVerts = this.numOuterVerts + this.numHollowVerts; + + if (this.numOuterVerts == this.numHollowVerts) + { + Face newFace = new Face(); + + for (int coordIndex = 0; coordIndex < this.numOuterVerts - 1; coordIndex++) + { + newFace.v1 = coordIndex; + newFace.v2 = coordIndex + 1; + newFace.v3 = numTotalVerts - coordIndex - 1; + this.faces.Add(newFace); + + newFace.v1 = coordIndex + 1; + newFace.v2 = numTotalVerts - coordIndex - 2; + newFace.v3 = numTotalVerts - coordIndex - 1; + this.faces.Add(newFace); + } + } + else + { + if (this.numOuterVerts < this.numHollowVerts) + { + Face newFace = new Face(); + int j = 0; // j is the index for outer vertices + int maxJ = this.numOuterVerts - 1; + for (int i = 0; i < this.numHollowVerts; i++) // i is the index for inner vertices + { + if (j < maxJ) + if (angles.angles[j + 1].angle - hollowAngles.angles[i].angle < hollowAngles.angles[i].angle - angles.angles[j].angle + 0.000001f) + { + newFace.v1 = numTotalVerts - i - 1; + newFace.v2 = j; + newFace.v3 = j + 1; + + this.faces.Add(newFace); + j += 1; + } + + newFace.v1 = j; + newFace.v2 = numTotalVerts - i - 2; + newFace.v3 = numTotalVerts - i - 1; + + this.faces.Add(newFace); + } + } + else // numHollowVerts < numOuterVerts + { + Face newFace = new Face(); + int j = 0; // j is the index for inner vertices + int maxJ = this.numHollowVerts - 1; + for (int i = 0; i < this.numOuterVerts; i++) + { + if (j < maxJ) + if (hollowAngles.angles[j + 1].angle - angles.angles[i].angle < angles.angles[i].angle - hollowAngles.angles[j].angle + 0.000001f) + { + newFace.v1 = i; + newFace.v2 = numTotalVerts - j - 2; + newFace.v3 = numTotalVerts - j - 1; + + this.faces.Add(newFace); + j += 1; + } + + newFace.v1 = numTotalVerts - j - 1; + newFace.v2 = i; + newFace.v3 = i + 1; + + this.faces.Add(newFace); + } + } + } + } + + if (calcVertexNormals) + { + foreach (Coord hc in hollowCoords) + { + this.coords.Add(hc); + hollowCoordIndices.Add(this.coords.Count - 1); + } + } + else + this.coords.AddRange(hollowCoords); + + if (this.calcVertexNormals) + { + this.vertexNormals.AddRange(hollowNormals); + this.us.AddRange(hollowUs); + + } + } + + if (simpleFace && createFaces) + { + if (sides == 3) + this.faces.Add(new Face(0, 1, 2)); + else if (sides == 4) + { + this.faces.Add(new Face(0, 1, 2)); + this.faces.Add(new Face(0, 2, 3)); + } + } + + if (calcVertexNormals && hasProfileCut) + { + if (hasHollow) + { + int lastOuterVertIndex = this.numOuterVerts - 1; + + this.cut1CoordIndices.Add(0); + this.cut1CoordIndices.Add(this.coords.Count - 1); + + this.cut2CoordIndices.Add(lastOuterVertIndex + 1); + this.cut2CoordIndices.Add(lastOuterVertIndex); + + this.cutNormal1.X = this.coords[0].Y - this.coords[this.coords.Count - 1].Y; + this.cutNormal1.Y = -(this.coords[0].X - this.coords[this.coords.Count - 1].X); + + this.cutNormal2.X = this.coords[lastOuterVertIndex + 1].Y - this.coords[lastOuterVertIndex].Y; + this.cutNormal2.Y = -(this.coords[lastOuterVertIndex + 1].X - this.coords[lastOuterVertIndex].X); + } + + else + { + this.cutNormal1.X = this.vertexNormals[1].Y; + this.cutNormal1.Y = -this.vertexNormals[1].X; + + this.cutNormal2.X = -this.vertexNormals[this.vertexNormals.Count - 2].Y; + this.cutNormal2.Y = this.vertexNormals[this.vertexNormals.Count - 2].X; + + } + this.cutNormal1.Normalize(); + this.cutNormal2.Normalize(); + } + + this.MakeFaceUVs(); + + hollowCoords = null; + hollowNormals = null; + hollowUs = null; + + if (calcVertexNormals) + { // calculate prim face numbers + + // face number order is top, outer, hollow, bottom, start cut, end cut + // I know it's ugly but so is the whole concept of prim face numbers + + int faceNum = 1; // start with outer faces + int startVert = hasProfileCut && !hasHollow ? 1 : 0; + if (startVert > 0) + this.faceNumbers.Add(-1); + for (int i = 0; i < this.numOuterVerts - 1; i++) + this.faceNumbers.Add(sides < 5 ? faceNum++ : faceNum); + + //if (!hasHollow && !hasProfileCut) + // this.bottomFaceNumber = faceNum++; + + this.faceNumbers.Add(hasProfileCut ? -1 : faceNum++); + + if (sides > 4 && (hasHollow || hasProfileCut)) + faceNum++; + + if (hasHollow) + { + for (int i = 0; i < this.numHollowVerts; i++) + this.faceNumbers.Add(faceNum); + + faceNum++; + } + //if (hasProfileCut || hasHollow) + // this.bottomFaceNumber = faceNum++; + this.bottomFaceNumber = faceNum++; + + if (hasHollow && hasProfileCut) + this.faceNumbers.Add(faceNum++); + for (int i = 0; i < this.faceNumbers.Count; i++) + if (this.faceNumbers[i] == -1) + this.faceNumbers[i] = faceNum++; + + + this.numPrimFaces = faceNum; + } + + } + + internal void MakeFaceUVs() + { + this.faceUVs = new List(); + foreach (Coord c in this.coords) + this.faceUVs.Add(new UVCoord(0.5f + c.X, 0.5f - c.Y)); + } + + internal Profile Copy() + { + return this.Copy(true); + } + + internal Profile Copy(bool needFaces) + { + Profile copy = new Profile(); + + copy.coords.AddRange(this.coords); + copy.faceUVs.AddRange(this.faceUVs); + + if (needFaces) + copy.faces.AddRange(this.faces); + if ((copy.calcVertexNormals = this.calcVertexNormals) == true) + { + copy.vertexNormals.AddRange(this.vertexNormals); + copy.faceNormal = this.faceNormal; + copy.cutNormal1 = this.cutNormal1; + copy.cutNormal2 = this.cutNormal2; + copy.us.AddRange(this.us); + copy.faceNumbers.AddRange(this.faceNumbers); + + copy.cut1CoordIndices = new List(this.cut1CoordIndices); + copy.cut2CoordIndices = new List(this.cut2CoordIndices); + copy.hollowCoordIndices = new List(this.hollowCoordIndices); + copy.outerCoordIndices = new List(this.outerCoordIndices); + } + copy.numOuterVerts = this.numOuterVerts; + copy.numHollowVerts = this.numHollowVerts; + + return copy; + } + + internal void AddPos(Coord v) + { + this.AddPos(v.X, v.Y, v.Z); + } + + internal void AddPos(float x, float y, float z) + { + int i; + int numVerts = this.coords.Count; + Coord vert; + + for (i = 0; i < numVerts; i++) + { + vert = this.coords[i]; + vert.X += x; + vert.Y += y; + vert.Z += z; + this.coords[i] = vert; + } + } + + internal void AddRot(Quat q) + { + int i; + int numVerts = this.coords.Count; + + for (i = 0; i < numVerts; i++) + this.coords[i] *= q; + + if (this.calcVertexNormals) + { + int numNormals = this.vertexNormals.Count; + for (i = 0; i < numNormals; i++) + this.vertexNormals[i] *= q; + + this.faceNormal *= q; + this.cutNormal1 *= q; + this.cutNormal2 *= q; + + } + } + + internal void Scale(float x, float y) + { + int i; + int numVerts = this.coords.Count; + Coord vert; + + for (i = 0; i < numVerts; i++) + { + vert = this.coords[i]; + vert.X *= x; + vert.Y *= y; + this.coords[i] = vert; + } + } + + /// + /// Changes order of the vertex indices and negates the center vertex normal. Does not alter vertex normals of radial vertices + /// + internal void FlipNormals() + { + int i; + int numFaces = this.faces.Count; + Face tmpFace; + int tmp; + + for (i = 0; i < numFaces; i++) + { + tmpFace = this.faces[i]; + tmp = tmpFace.v3; + tmpFace.v3 = tmpFace.v1; + tmpFace.v1 = tmp; + this.faces[i] = tmpFace; + } + + if (this.calcVertexNormals) + { + int normalCount = this.vertexNormals.Count; + if (normalCount > 0) + { + Coord n = this.vertexNormals[normalCount - 1]; + n.Z = -n.Z; + this.vertexNormals[normalCount - 1] = n; + } + } + + this.faceNormal.X = -this.faceNormal.X; + this.faceNormal.Y = -this.faceNormal.Y; + this.faceNormal.Z = -this.faceNormal.Z; + + int numfaceUVs = this.faceUVs.Count; + for (i = 0; i < numfaceUVs; i++) + { + UVCoord uv = this.faceUVs[i]; + uv.V = 1.0f - uv.V; + this.faceUVs[i] = uv; + } + } + + internal void AddValue2FaceVertexIndices(int num) + { + int numFaces = this.faces.Count; + Face tmpFace; + for (int i = 0; i < numFaces; i++) + { + tmpFace = this.faces[i]; + tmpFace.v1 += num; + tmpFace.v2 += num; + tmpFace.v3 += num; + + this.faces[i] = tmpFace; + } + } + + internal void AddValue2FaceNormalIndices(int num) + { + if (this.calcVertexNormals) + { + int numFaces = this.faces.Count; + Face tmpFace; + for (int i = 0; i < numFaces; i++) + { + tmpFace = this.faces[i]; + tmpFace.n1 += num; + tmpFace.n2 += num; + tmpFace.n3 += num; + + this.faces[i] = tmpFace; + } + } + } + + internal void DumpRaw(String path, String name, String title) + { + if (path == null) + return; + String fileName = name + "_" + title + ".raw"; + String completePath = System.IO.Path.Combine(path, fileName); + StreamWriter sw = new StreamWriter(completePath); + + for (int i = 0; i < this.faces.Count; i++) + { + string s = this.coords[this.faces[i].v1].ToString(); + s += " " + this.coords[this.faces[i].v2].ToString(); + s += " " + this.coords[this.faces[i].v3].ToString(); + + sw.WriteLine(s); + } + + sw.Close(); + } + } + + public struct PathNode + { + public Coord position; + public Quat rotation; + public float xScale; + public float yScale; + public float percentOfPath; + } + + public enum PathType { Linear = 0, Circular = 1, Flexible = 2 } + + public class Path + { + public List pathNodes = new List(); + + public float twistBegin = 0.0f; + public float twistEnd = 0.0f; + public float topShearX = 0.0f; + public float topShearY = 0.0f; + public float pathCutBegin = 0.0f; + public float pathCutEnd = 1.0f; + public float dimpleBegin = 0.0f; + public float dimpleEnd = 1.0f; + public float skew = 0.0f; + public float holeSizeX = 1.0f; // called pathScaleX in pbs + public float holeSizeY = 0.25f; + public float taperX = 0.0f; + public float taperY = 0.0f; + public float radius = 0.0f; + public float revolutions = 1.0f; + public int stepsPerRevolution = 24; + + private const float twoPi = 2.0f * (float)Math.PI; + + public void Create(PathType pathType, int steps) + { + if (pathType == PathType.Linear || pathType == PathType.Flexible) + { + int step = 0; + + float length = this.pathCutEnd - this.pathCutBegin; + float twistTotal = twistEnd - twistBegin; + float twistTotalAbs = Math.Abs(twistTotal); + if (twistTotalAbs > 0.01f) + steps += (int)(twistTotalAbs * 3.66); // dahlia's magic number + + float start = -0.5f; + float stepSize = length / (float)steps; + float percentOfPathMultiplier = stepSize; + float xOffset = 0.0f; + float yOffset = 0.0f; + float zOffset = start; + float xOffsetStepIncrement = this.topShearX / steps; + float yOffsetStepIncrement = this.topShearY / steps; + + float percentOfPath = this.pathCutBegin; + zOffset += percentOfPath; + + // sanity checks + + bool done = false; + + while (!done) + { + PathNode newNode = new PathNode(); + + newNode.xScale = 1.0f; + if (this.taperX == 0.0f) + newNode.xScale = 1.0f; + else if (this.taperX > 0.0f) + newNode.xScale = 1.0f - percentOfPath * this.taperX; + else newNode.xScale = 1.0f + (1.0f - percentOfPath) * this.taperX; + + newNode.yScale = 1.0f; + if (this.taperY == 0.0f) + newNode.yScale = 1.0f; + else if (this.taperY > 0.0f) + newNode.yScale = 1.0f - percentOfPath * this.taperY; + else newNode.yScale = 1.0f + (1.0f - percentOfPath) * this.taperY; + + float twist = twistBegin + twistTotal * percentOfPath; + + newNode.rotation = new Quat(new Coord(0.0f, 0.0f, 1.0f), twist); + newNode.position = new Coord(xOffset, yOffset, zOffset); + newNode.percentOfPath = percentOfPath; + + pathNodes.Add(newNode); + + if (step < steps) + { + step += 1; + percentOfPath += percentOfPathMultiplier; + xOffset += xOffsetStepIncrement; + yOffset += yOffsetStepIncrement; + zOffset += stepSize; + if (percentOfPath > this.pathCutEnd) + done = true; + } + else done = true; + } + } // end of linear path code + + else // pathType == Circular + { + float twistTotal = twistEnd - twistBegin; + + // if the profile has a lot of twist, add more layers otherwise the layers may overlap + // and the resulting mesh may be quite inaccurate. This method is arbitrary and doesn't + // accurately match the viewer + float twistTotalAbs = Math.Abs(twistTotal); + if (twistTotalAbs > 0.01f) + { + if (twistTotalAbs > Math.PI * 1.5f) + steps *= 2; + if (twistTotalAbs > Math.PI * 3.0f) + steps *= 2; + } + + float yPathScale = this.holeSizeY * 0.5f; + float pathLength = this.pathCutEnd - this.pathCutBegin; + float totalSkew = this.skew * 2.0f * pathLength; + float skewStart = this.pathCutBegin * 2.0f * this.skew - this.skew; + float xOffsetTopShearXFactor = this.topShearX * (0.25f + 0.5f * (0.5f - this.holeSizeY)); + float yShearCompensation = 1.0f + Math.Abs(this.topShearY) * 0.25f; + + // It's not quite clear what pushY (Y top shear) does, but subtracting it from the start and end + // angles appears to approximate it's effects on path cut. Likewise, adding it to the angle used + // to calculate the sine for generating the path radius appears to approximate it's effects there + // too, but there are some subtle differences in the radius which are noticeable as the prim size + // increases and it may affect megaprims quite a bit. The effect of the Y top shear parameter on + // the meshes generated with this technique appear nearly identical in shape to the same prims when + // displayed by the viewer. + + float startAngle = (twoPi * this.pathCutBegin * this.revolutions) - this.topShearY * 0.9f; + float endAngle = (twoPi * this.pathCutEnd * this.revolutions) - this.topShearY * 0.9f; + float stepSize = twoPi / this.stepsPerRevolution; + + int step = (int)(startAngle / stepSize); + float angle = startAngle; + + bool done = false; + while (!done) // loop through the length of the path and add the layers + { + PathNode newNode = new PathNode(); + + float xProfileScale = (1.0f - Math.Abs(this.skew)) * this.holeSizeX; + float yProfileScale = this.holeSizeY; + + float percentOfPath = angle / (twoPi * this.revolutions); + float percentOfAngles = (angle - startAngle) / (endAngle - startAngle); + + if (this.taperX > 0.01f) + xProfileScale *= 1.0f - percentOfPath * this.taperX; + else if (this.taperX < -0.01f) + xProfileScale *= 1.0f + (1.0f - percentOfPath) * this.taperX; + + if (this.taperY > 0.01f) + yProfileScale *= 1.0f - percentOfPath * this.taperY; + else if (this.taperY < -0.01f) + yProfileScale *= 1.0f + (1.0f - percentOfPath) * this.taperY; + + newNode.xScale = xProfileScale; + newNode.yScale = yProfileScale; + + float radiusScale = 1.0f; + if (this.radius > 0.001f) + radiusScale = 1.0f - this.radius * percentOfPath; + else if (this.radius < 0.001f) + radiusScale = 1.0f + this.radius * (1.0f - percentOfPath); + + float twist = twistBegin + twistTotal * percentOfPath; + + float xOffset = 0.5f * (skewStart + totalSkew * percentOfAngles); + xOffset += (float)Math.Sin(angle) * xOffsetTopShearXFactor; + + float yOffset = yShearCompensation * (float)Math.Cos(angle) * (0.5f - yPathScale) * radiusScale; + + float zOffset = (float)Math.Sin(angle + this.topShearY) * (0.5f - yPathScale) * radiusScale; + + newNode.position = new Coord(xOffset, yOffset, zOffset); + + // now orient the rotation of the profile layer relative to it's position on the path + // adding taperY to the angle used to generate the quat appears to approximate the viewer + + newNode.rotation = new Quat(new Coord(1.0f, 0.0f, 0.0f), angle + this.topShearY); + + // next apply twist rotation to the profile layer + if (twistTotal != 0.0f || twistBegin != 0.0f) + newNode.rotation *= new Quat(new Coord(0.0f, 0.0f, 1.0f), twist); + + newNode.percentOfPath = percentOfPath; + + pathNodes.Add(newNode); + + // calculate terms for next iteration + // calculate the angle for the next iteration of the loop + + if (angle >= endAngle - 0.01) + done = true; + else + { + step += 1; + angle = stepSize * step; + if (angle > endAngle) + angle = endAngle; + } + } + } + } + } + + public class PrimMesh + { + public string errorMessage = ""; + private const float twoPi = 2.0f * (float)Math.PI; + + public List coords; + public List normals; + public List faces; + + public List viewerFaces; + + private int sides = 4; + private int hollowSides = 4; + private float profileStart = 0.0f; + private float profileEnd = 1.0f; + private float hollow = 0.0f; + public int twistBegin = 0; + public int twistEnd = 0; + public float topShearX = 0.0f; + public float topShearY = 0.0f; + public float pathCutBegin = 0.0f; + public float pathCutEnd = 1.0f; + public float dimpleBegin = 0.0f; + public float dimpleEnd = 1.0f; + public float skew = 0.0f; + public float holeSizeX = 1.0f; // called pathScaleX in pbs + public float holeSizeY = 0.25f; + public float taperX = 0.0f; + public float taperY = 0.0f; + public float radius = 0.0f; + public float revolutions = 1.0f; + public int stepsPerRevolution = 24; + + private bool hasProfileCut = false; + private bool hasHollow = false; + public bool calcVertexNormals = false; + private bool normalsProcessed = false; + public bool viewerMode = false; + + public int numPrimFaces = 0; + + /// + /// Human readable string representation of the parameters used to create a mesh. + /// + /// + public string ParamsToDisplayString() + { + string s = ""; + s += "sides..................: " + this.sides.ToString(); + s += "\nhollowSides..........: " + this.hollowSides.ToString(); + s += "\nprofileStart.........: " + this.profileStart.ToString(); + s += "\nprofileEnd...........: " + this.profileEnd.ToString(); + s += "\nhollow...............: " + this.hollow.ToString(); + s += "\ntwistBegin...........: " + this.twistBegin.ToString(); + s += "\ntwistEnd.............: " + this.twistEnd.ToString(); + s += "\ntopShearX............: " + this.topShearX.ToString(); + s += "\ntopShearY............: " + this.topShearY.ToString(); + s += "\npathCutBegin.........: " + this.pathCutBegin.ToString(); + s += "\npathCutEnd...........: " + this.pathCutEnd.ToString(); + s += "\ndimpleBegin..........: " + this.dimpleBegin.ToString(); + s += "\ndimpleEnd............: " + this.dimpleEnd.ToString(); + s += "\nskew.................: " + this.skew.ToString(); + s += "\nholeSizeX............: " + this.holeSizeX.ToString(); + s += "\nholeSizeY............: " + this.holeSizeY.ToString(); + s += "\ntaperX...............: " + this.taperX.ToString(); + s += "\ntaperY...............: " + this.taperY.ToString(); + s += "\nradius...............: " + this.radius.ToString(); + s += "\nrevolutions..........: " + this.revolutions.ToString(); + s += "\nstepsPerRevolution...: " + this.stepsPerRevolution.ToString(); + + return s; + } + + /// + /// Constructs a PrimMesh object and creates the profile for extrusion. + /// + /// + /// + /// + /// + /// + public PrimMesh(int sides, float profileStart, float profileEnd, float hollow, int hollowSides) + { + this.coords = new List(); + this.faces = new List(); + + this.sides = sides; + this.profileStart = profileStart; + this.profileEnd = profileEnd; + this.hollow = hollow; + this.hollowSides = hollowSides; + + if (sides < 3) + this.sides = 3; + if (hollowSides < 3) + this.hollowSides = 3; + if (profileStart < 0.0f) + this.profileStart = 0.0f; + if (profileEnd > 1.0f) + this.profileEnd = 1.0f; + if (profileEnd < 0.02f) + this.profileEnd = 0.02f; + if (profileStart >= profileEnd) + this.profileStart = profileEnd - 0.02f; + if (hollow > 0.99f) + this.hollow = 0.99f; + if (hollow < 0.0f) + this.hollow = 0.0f; + + this.hasProfileCut = (this.profileStart > 0.0f || this.profileEnd < 1.0f); + this.hasHollow = (this.hollow > 0.001f); + } + + /// + /// Extrudes a profile along a path. + /// + public void Extrude(PathType pathType) + { + this.coords = new List(); + this.faces = new List(); + + if (this.viewerMode) + { + this.viewerFaces = new List(); + this.calcVertexNormals = true; + } + + if (this.calcVertexNormals) + this.normals = new List(); + + int steps = 1; + + float length = this.pathCutEnd - this.pathCutBegin; + normalsProcessed = false; + + if (this.viewerMode && this.sides == 3) + { + // prisms don't taper well so add some vertical resolution + // other prims may benefit from this but just do prisms for now + if (Math.Abs(this.taperX) > 0.01 || Math.Abs(this.taperY) > 0.01) + steps = (int)(steps * 4.5 * length); + } + + + float twistBegin = this.twistBegin / 360.0f * twoPi; + float twistEnd = this.twistEnd / 360.0f * twoPi; + float twistTotal = twistEnd - twistBegin; + float twistTotalAbs = Math.Abs(twistTotal); + if (twistTotalAbs > 0.01f) + steps += (int)(twistTotalAbs * 3.66); // dahlia's magic number + + float hollow = this.hollow; + + // sanity checks + float initialProfileRot = 0.0f; + if (pathType == PathType.Circular) + { + if (this.sides == 3) + { + initialProfileRot = (float)Math.PI; + if (this.hollowSides == 4) + { + if (hollow > 0.7f) + hollow = 0.7f; + hollow *= 0.707f; + } + else hollow *= 0.5f; + } + else if (this.sides == 4) + { + initialProfileRot = 0.25f * (float)Math.PI; + if (this.hollowSides != 4) + hollow *= 0.707f; + } + else if (this.sides > 4) + { + initialProfileRot = (float)Math.PI; + if (this.hollowSides == 4) + { + if (hollow > 0.7f) + hollow = 0.7f; + hollow /= 0.7f; + } + } + } + else + { + if (this.sides == 3) + { + if (this.hollowSides == 4) + { + if (hollow > 0.7f) + hollow = 0.7f; + hollow *= 0.707f; + } + else hollow *= 0.5f; + } + else if (this.sides == 4) + { + initialProfileRot = 1.25f * (float)Math.PI; + if (this.hollowSides != 4) + hollow *= 0.707f; + } + else if (this.sides == 24 && this.hollowSides == 4) + hollow *= 1.414f; + } + + Profile profile = new Profile(this.sides, this.profileStart, this.profileEnd, hollow, this.hollowSides, true, calcVertexNormals); + this.errorMessage = profile.errorMessage; + + this.numPrimFaces = profile.numPrimFaces; + + int cut1Vert = -1; + int cut2Vert = -1; + if (hasProfileCut) + { + cut1Vert = hasHollow ? profile.coords.Count - 1 : 0; + cut2Vert = hasHollow ? profile.numOuterVerts - 1 : profile.numOuterVerts; + } + + if (initialProfileRot != 0.0f) + { + profile.AddRot(new Quat(new Coord(0.0f, 0.0f, 1.0f), initialProfileRot)); + if (viewerMode) + profile.MakeFaceUVs(); + } + + Coord lastCutNormal1 = new Coord(); + Coord lastCutNormal2 = new Coord(); + float lastV = 1.0f; + + Path path = new Path(); + path.twistBegin = twistBegin; + path.twistEnd = twistEnd; + path.topShearX = topShearX; + path.topShearY = topShearY; + path.pathCutBegin = pathCutBegin; + path.pathCutEnd = pathCutEnd; + path.dimpleBegin = dimpleBegin; + path.dimpleEnd = dimpleEnd; + path.skew = skew; + path.holeSizeX = holeSizeX; + path.holeSizeY = holeSizeY; + path.taperX = taperX; + path.taperY = taperY; + path.radius = radius; + path.revolutions = revolutions; + path.stepsPerRevolution = stepsPerRevolution; + + path.Create(pathType, steps); + + bool needEndFaces = false; + if (pathType == PathType.Circular) + { + needEndFaces = false; + if (this.pathCutBegin != 0.0f || this.pathCutEnd != 1.0f) + needEndFaces = true; + else if (this.taperX != 0.0f || this.taperY != 0.0f) + needEndFaces = true; + else if (this.skew != 0.0f) + needEndFaces = true; + else if (twistTotal != 0.0f) + needEndFaces = true; + else if (this.radius != 0.0f) + needEndFaces = true; + } + else needEndFaces = true; + + for (int nodeIndex = 0; nodeIndex < path.pathNodes.Count; nodeIndex++) + { + PathNode node = path.pathNodes[nodeIndex]; + Profile newLayer = profile.Copy(); + newLayer.Scale(node.xScale, node.yScale); + + newLayer.AddRot(node.rotation); + newLayer.AddPos(node.position); + + if (needEndFaces && nodeIndex == 0) + { + newLayer.FlipNormals(); + + // add the top faces to the viewerFaces list here + if (this.viewerMode) + { + Coord faceNormal = newLayer.faceNormal; + ViewerFace newViewerFace = new ViewerFace(profile.bottomFaceNumber); + int numFaces = newLayer.faces.Count; + List faces = newLayer.faces; + + for (int i = 0; i < numFaces; i++) + { + Face face = faces[i]; + newViewerFace.v1 = newLayer.coords[face.v1]; + newViewerFace.v2 = newLayer.coords[face.v2]; + newViewerFace.v3 = newLayer.coords[face.v3]; + + newViewerFace.coordIndex1 = face.v1; + newViewerFace.coordIndex2 = face.v2; + newViewerFace.coordIndex3 = face.v3; + + newViewerFace.n1 = faceNormal; + newViewerFace.n2 = faceNormal; + newViewerFace.n3 = faceNormal; + + newViewerFace.uv1 = newLayer.faceUVs[face.v1]; + newViewerFace.uv2 = newLayer.faceUVs[face.v2]; + newViewerFace.uv3 = newLayer.faceUVs[face.v3]; + + this.viewerFaces.Add(newViewerFace); + } + } + } // if (nodeIndex == 0) + + // append this layer + + int coordsLen = this.coords.Count; + newLayer.AddValue2FaceVertexIndices(coordsLen); + + this.coords.AddRange(newLayer.coords); + + if (this.calcVertexNormals) + { + newLayer.AddValue2FaceNormalIndices(this.normals.Count); + this.normals.AddRange(newLayer.vertexNormals); + } + + if (node.percentOfPath < this.pathCutBegin + 0.01f || node.percentOfPath > this.pathCutEnd - 0.01f) + this.faces.AddRange(newLayer.faces); + + // fill faces between layers + + int numVerts = newLayer.coords.Count; + Face newFace = new Face(); + + if (nodeIndex > 0) + { + int startVert = coordsLen + 1; + int endVert = this.coords.Count; + + if (sides < 5 || this.hasProfileCut || hollow > 0.0f) + startVert--; + + for (int i = startVert; i < endVert; i++) + { + int iNext = i + 1; + if (i == endVert - 1) + iNext = startVert; + + int whichVert = i - startVert; + + newFace.v1 = i; + newFace.v2 = i - numVerts; + newFace.v3 = iNext - numVerts; + this.faces.Add(newFace); + + newFace.v2 = iNext - numVerts; + newFace.v3 = iNext; + this.faces.Add(newFace); + + if (this.viewerMode) + { + // add the side faces to the list of viewerFaces here + + int primFaceNum = profile.faceNumbers[whichVert]; + if (!needEndFaces) + primFaceNum -= 1; + + ViewerFace newViewerFace1 = new ViewerFace(primFaceNum); + ViewerFace newViewerFace2 = new ViewerFace(primFaceNum); + + float u1 = newLayer.us[whichVert]; + float u2 = 1.0f; + if (whichVert < newLayer.us.Count - 1) + u2 = newLayer.us[whichVert + 1]; + + if (whichVert == cut1Vert || whichVert == cut2Vert) + { + u1 = 0.0f; + u2 = 1.0f; + } + else if (sides < 5) + { + if (whichVert < profile.numOuterVerts) + { // boxes and prisms have one texture face per side of the prim, so the U values have to be scaled + // to reflect the entire texture width + u1 *= sides; + u2 *= sides; + u2 -= (int)u1; + u1 -= (int)u1; + if (u2 < 0.1f) + u2 = 1.0f; + } + else if (whichVert > profile.coords.Count - profile.numHollowVerts - 1) + { + u1 *= 2.0f; + u2 *= 2.0f; + } + } + + newViewerFace1.uv1.U = u1; + newViewerFace1.uv2.U = u1; + newViewerFace1.uv3.U = u2; + + newViewerFace1.uv1.V = 1.0f - node.percentOfPath; + newViewerFace1.uv2.V = lastV; + newViewerFace1.uv3.V = lastV; + + newViewerFace2.uv1.U = u1; + newViewerFace2.uv2.U = u2; + newViewerFace2.uv3.U = u2; + + newViewerFace2.uv1.V = 1.0f - node.percentOfPath; + newViewerFace2.uv2.V = lastV; + newViewerFace2.uv3.V = 1.0f - node.percentOfPath; + + newViewerFace1.v1 = this.coords[i]; + newViewerFace1.v2 = this.coords[i - numVerts]; + newViewerFace1.v3 = this.coords[iNext - numVerts]; + + newViewerFace2.v1 = this.coords[i]; + newViewerFace2.v2 = this.coords[iNext - numVerts]; + newViewerFace2.v3 = this.coords[iNext]; + + newViewerFace1.coordIndex1 = i; + newViewerFace1.coordIndex2 = i - numVerts; + newViewerFace1.coordIndex3 = iNext - numVerts; + + newViewerFace2.coordIndex1 = i; + newViewerFace2.coordIndex2 = iNext - numVerts; + newViewerFace2.coordIndex3 = iNext; + + // profile cut faces + if (whichVert == cut1Vert) + { + newViewerFace1.n1 = newLayer.cutNormal1; + newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal1; + + newViewerFace2.n1 = newViewerFace2.n3 = newLayer.cutNormal1; + newViewerFace2.n2 = lastCutNormal1; + } + else if (whichVert == cut2Vert) + { + newViewerFace1.n1 = newLayer.cutNormal2; + newViewerFace1.n2 = newViewerFace1.n3 = lastCutNormal2; + + newViewerFace2.n1 = newViewerFace2.n3 = newLayer.cutNormal2; + newViewerFace2.n2 = lastCutNormal2; + } + + else // outer and hollow faces + { + if ((sides < 5 && whichVert < newLayer.numOuterVerts) || (hollowSides < 5 && whichVert >= newLayer.numOuterVerts)) + { // looks terrible when path is twisted... need vertex normals here + newViewerFace1.CalcSurfaceNormal(); + newViewerFace2.CalcSurfaceNormal(); + } + else + { + newViewerFace1.n1 = this.normals[i]; + newViewerFace1.n2 = this.normals[i - numVerts]; + newViewerFace1.n3 = this.normals[iNext - numVerts]; + + newViewerFace2.n1 = this.normals[i]; + newViewerFace2.n2 = this.normals[iNext - numVerts]; + newViewerFace2.n3 = this.normals[iNext]; + } + } + + this.viewerFaces.Add(newViewerFace1); + this.viewerFaces.Add(newViewerFace2); + + } + } + } + + lastCutNormal1 = newLayer.cutNormal1; + lastCutNormal2 = newLayer.cutNormal2; + lastV = 1.0f - node.percentOfPath; + + if (needEndFaces && nodeIndex == path.pathNodes.Count - 1 && viewerMode) + { + // add the top faces to the viewerFaces list here + Coord faceNormal = newLayer.faceNormal; + ViewerFace newViewerFace = new ViewerFace(); + newViewerFace.primFaceNumber = 0; + int numFaces = newLayer.faces.Count; + List faces = newLayer.faces; + + for (int i = 0; i < numFaces; i++) + { + Face face = faces[i]; + newViewerFace.v1 = newLayer.coords[face.v1 - coordsLen]; + newViewerFace.v2 = newLayer.coords[face.v2 - coordsLen]; + newViewerFace.v3 = newLayer.coords[face.v3 - coordsLen]; + + newViewerFace.coordIndex1 = face.v1 - coordsLen; + newViewerFace.coordIndex2 = face.v2 - coordsLen; + newViewerFace.coordIndex3 = face.v3 - coordsLen; + + newViewerFace.n1 = faceNormal; + newViewerFace.n2 = faceNormal; + newViewerFace.n3 = faceNormal; + + newViewerFace.uv1 = newLayer.faceUVs[face.v1 - coordsLen]; + newViewerFace.uv2 = newLayer.faceUVs[face.v2 - coordsLen]; + newViewerFace.uv3 = newLayer.faceUVs[face.v3 - coordsLen]; + + this.viewerFaces.Add(newViewerFace); + } + } + + + } // for (int nodeIndex = 0; nodeIndex < path.pathNodes.Count; nodeIndex++) + + } + + + /// + /// DEPRICATED - use Extrude(PathType.Linear) instead + /// Extrudes a profile along a straight line path. Used for prim types box, cylinder, and prism. + /// + /// + public void ExtrudeLinear() + { + this.Extrude(PathType.Linear); + } + + + /// + /// DEPRICATED - use Extrude(PathType.Circular) instead + /// Extrude a profile into a circular path prim mesh. Used for prim types torus, tube, and ring. + /// + /// + public void ExtrudeCircular() + { + this.Extrude(PathType.Circular); + } + + + private Coord SurfaceNormal(Coord c1, Coord c2, Coord c3) + { + Coord edge1 = new Coord(c2.X - c1.X, c2.Y - c1.Y, c2.Z - c1.Z); + Coord edge2 = new Coord(c3.X - c1.X, c3.Y - c1.Y, c3.Z - c1.Z); + + Coord normal = Coord.Cross(edge1, edge2); + + normal.Normalize(); + + return normal; + } + + private Coord SurfaceNormal(Face face) + { + return SurfaceNormal(this.coords[face.v1], this.coords[face.v2], this.coords[face.v3]); + } + + /// + /// Calculate the surface normal for a face in the list of faces + /// + /// + /// + public Coord SurfaceNormal(int faceIndex) + { + int numFaces = this.faces.Count; + if (faceIndex < 0 || faceIndex >= numFaces) + throw new Exception("faceIndex out of range"); + + return SurfaceNormal(this.faces[faceIndex]); + } + + /// + /// Duplicates a PrimMesh object. All object properties are copied by value, including lists. + /// + /// + public PrimMesh Copy() + { + PrimMesh copy = new PrimMesh(this.sides, this.profileStart, this.profileEnd, this.hollow, this.hollowSides); + copy.twistBegin = this.twistBegin; + copy.twistEnd = this.twistEnd; + copy.topShearX = this.topShearX; + copy.topShearY = this.topShearY; + copy.pathCutBegin = this.pathCutBegin; + copy.pathCutEnd = this.pathCutEnd; + copy.dimpleBegin = this.dimpleBegin; + copy.dimpleEnd = this.dimpleEnd; + copy.skew = this.skew; + copy.holeSizeX = this.holeSizeX; + copy.holeSizeY = this.holeSizeY; + copy.taperX = this.taperX; + copy.taperY = this.taperY; + copy.radius = this.radius; + copy.revolutions = this.revolutions; + copy.stepsPerRevolution = this.stepsPerRevolution; + copy.calcVertexNormals = this.calcVertexNormals; + copy.normalsProcessed = this.normalsProcessed; + copy.viewerMode = this.viewerMode; + copy.numPrimFaces = this.numPrimFaces; + copy.errorMessage = this.errorMessage; + + copy.coords = new List(this.coords); + copy.faces = new List(this.faces); + copy.viewerFaces = new List(this.viewerFaces); + copy.normals = new List(this.normals); + + return copy; + } + + /// + /// Calculate surface normals for all of the faces in the list of faces in this mesh + /// + public void CalcNormals() + { + if (normalsProcessed) + return; + + normalsProcessed = true; + + int numFaces = faces.Count; + + if (!this.calcVertexNormals) + this.normals = new List(); + + for (int i = 0; i < numFaces; i++) + { + Face face = faces[i]; + + this.normals.Add(SurfaceNormal(i).Normalize()); + + int normIndex = normals.Count - 1; + face.n1 = normIndex; + face.n2 = normIndex; + face.n3 = normIndex; + + this.faces[i] = face; + } + } + + /// + /// Adds a value to each XYZ vertex coordinate in the mesh + /// + /// + /// + /// + public void AddPos(float x, float y, float z) + { + int i; + int numVerts = this.coords.Count; + Coord vert; + + for (i = 0; i < numVerts; i++) + { + vert = this.coords[i]; + vert.X += x; + vert.Y += y; + vert.Z += z; + this.coords[i] = vert; + } + + if (this.viewerFaces != null) + { + int numViewerFaces = this.viewerFaces.Count; + + for (i = 0; i < numViewerFaces; i++) + { + ViewerFace v = this.viewerFaces[i]; + v.AddPos(x, y, z); + this.viewerFaces[i] = v; + } + } + } + + /// + /// Rotates the mesh + /// + /// + public void AddRot(Quat q) + { + int i; + int numVerts = this.coords.Count; + + for (i = 0; i < numVerts; i++) + this.coords[i] *= q; + + if (this.normals != null) + { + int numNormals = this.normals.Count; + for (i = 0; i < numNormals; i++) + this.normals[i] *= q; + } + + if (this.viewerFaces != null) + { + int numViewerFaces = this.viewerFaces.Count; + + for (i = 0; i < numViewerFaces; i++) + { + ViewerFace v = this.viewerFaces[i]; + v.v1 *= q; + v.v2 *= q; + v.v3 *= q; + + v.n1 *= q; + v.n2 *= q; + v.n3 *= q; + this.viewerFaces[i] = v; + } + } + } + +#if VERTEX_INDEXER + public VertexIndexer GetVertexIndexer() + { + if (this.viewerMode && this.viewerFaces.Count > 0) + return new VertexIndexer(this); + return null; + } +#endif + + /// + /// Scales the mesh + /// + /// + /// + /// + public void Scale(float x, float y, float z) + { + int i; + int numVerts = this.coords.Count; + //Coord vert; + + Coord m = new Coord(x, y, z); + for (i = 0; i < numVerts; i++) + this.coords[i] *= m; + + if (this.viewerFaces != null) + { + int numViewerFaces = this.viewerFaces.Count; + for (i = 0; i < numViewerFaces; i++) + { + ViewerFace v = this.viewerFaces[i]; + v.v1 *= m; + v.v2 *= m; + v.v3 *= m; + this.viewerFaces[i] = v; + } + + } + + } + + /// + /// Dumps the mesh to a Blender compatible "Raw" format file + /// + /// + /// + /// + public void DumpRaw(String path, String name, String title) + { + if (path == null) + return; + String fileName = name + "_" + title + ".raw"; + String completePath = System.IO.Path.Combine(path, fileName); + StreamWriter sw = new StreamWriter(completePath); + + for (int i = 0; i < this.faces.Count; i++) + { + string s = this.coords[this.faces[i].v1].ToString(); + s += " " + this.coords[this.faces[i].v2].ToString(); + s += " " + this.coords[this.faces[i].v3].ToString(); + + sw.WriteLine(s); + } + + sw.Close(); + } + } +} diff --git a/OpenSim/Region/Physics/Meshing/SculptMesh.cs b/OpenSim/Region/Physics/Meshing/SculptMesh.cs index 4dc6e2e510..11b6cd42c5 100644 --- a/OpenSim/Region/Physics/Meshing/SculptMesh.cs +++ b/OpenSim/Region/Physics/Meshing/SculptMesh.cs @@ -1,627 +1,645 @@ -/* - * Copyright (c) Contributors - * 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. - */ - -// to build without references to System.Drawing, comment this out -#define SYSTEM_DRAWING - -using System; -using System.Collections.Generic; -using System.Text; -using System.IO; - -#if SYSTEM_DRAWING -using System.Drawing; -using System.Drawing.Imaging; -#endif - -namespace PrimMesher -{ - - public class SculptMesh - { - public List coords; - public List faces; - - public List viewerFaces; - public List normals; - public List uvs; - - public enum SculptType { sphere = 1, torus = 2, plane = 3, cylinder = 4 }; - -#if SYSTEM_DRAWING - // private Bitmap ScaleImage(Bitmap srcImage, float scale) - // { - // int sourceWidth = srcImage.Width; - // int sourceHeight = srcImage.Height; - // int sourceX = 0; - // int sourceY = 0; - - // int destX = 0; - // int destY = 0; - // int destWidth = (int)(srcImage.Width * scale); - // int destHeight = (int)(srcImage.Height * scale); - - // if (srcImage.PixelFormat == PixelFormat.Format32bppArgb) - // for (int y = 0; y < srcImage.Height; y++) - // for (int x = 0; x < srcImage.Width; x++) - // { - // Color c = srcImage.GetPixel(x, y); - // srcImage.SetPixel(x, y, Color.FromArgb(255, c.R, c.G, c.B)); - // } - - // Bitmap scaledImage = new Bitmap(destWidth, destHeight, - // PixelFormat.Format24bppRgb); - - // scaledImage.SetResolution(96.0f, 96.0f); - - // Graphics grPhoto = Graphics.FromImage(scaledImage); - // grPhoto.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Low; - - // grPhoto.DrawImage(srcImage, - // new Rectangle(destX, destY, destWidth, destHeight), - // new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight), - // GraphicsUnit.Pixel); - - // grPhoto.Dispose(); - // return scaledImage; - // } - - - public SculptMesh SculptMeshFromFile(string fileName, SculptType sculptType, int lod, bool viewerMode) - { - Bitmap bitmap = (Bitmap)Bitmap.FromFile(fileName); - SculptMesh sculptMesh = new SculptMesh(bitmap, sculptType, lod, viewerMode); - bitmap.Dispose(); - return sculptMesh; - } - - public SculptMesh(string fileName, int sculptType, int lod, int viewerMode, int mirror, int invert) - { - Bitmap bitmap = (Bitmap)Bitmap.FromFile(fileName); - _SculptMesh(bitmap, (SculptType)sculptType, lod, viewerMode != 0, mirror != 0, invert != 0); - bitmap.Dispose(); - } -#endif - - /// - /// ** Experimental ** May disappear from future versions ** not recommeneded for use in applications - /// Construct a sculpt mesh from a 2D array of floats - /// - /// - /// - /// - /// - /// - /// - public SculptMesh(float[,] zMap, float xBegin, float xEnd, float yBegin, float yEnd, bool viewerMode) - { - float xStep, yStep; - float uStep, vStep; - - int numYElements = zMap.GetLength(0); - int numXElements = zMap.GetLength(1); - - try - { - xStep = (xEnd - xBegin) / (float)(numXElements - 1); - yStep = (yEnd - yBegin) / (float)(numYElements - 1); - - uStep = 1.0f / (numXElements - 1); - vStep = 1.0f / (numYElements - 1); - } - catch (DivideByZeroException) - { - return; - } - - coords = new List(); - faces = new List(); - normals = new List(); - uvs = new List(); - - viewerFaces = new List(); - - int p1, p2, p3, p4; - - int x, y; - int xStart = 0, yStart = 0; - - for (y = yStart; y < numYElements; y++) - { - int rowOffset = y * numXElements; - - for (x = xStart; x < numXElements; x++) - { - /* - * p1-----p2 - * | \ f2 | - * | \ | - * | f1 \| - * p3-----p4 - */ - - p4 = rowOffset + x; - p3 = p4 - 1; - - p2 = p4 - numXElements; - p1 = p3 - numXElements; - - Coord c = new Coord(xBegin + x * xStep, yBegin + y * yStep, zMap[y, x]); - this.coords.Add(c); - if (viewerMode) - { - this.normals.Add(new Coord()); - this.uvs.Add(new UVCoord(uStep * x, 1.0f - vStep * y)); - } - - if (y > 0 && x > 0) - { - Face f1, f2; - - if (viewerMode) - { - f1 = new Face(p1, p4, p3, p1, p4, p3); - f1.uv1 = p1; - f1.uv2 = p4; - f1.uv3 = p3; - - f2 = new Face(p1, p2, p4, p1, p2, p4); - f2.uv1 = p1; - f2.uv2 = p2; - f2.uv3 = p4; - } - else - { - f1 = new Face(p1, p4, p3); - f2 = new Face(p1, p2, p4); - } - - this.faces.Add(f1); - this.faces.Add(f2); - } - } - } - - if (viewerMode) - calcVertexNormals(SculptType.plane, numXElements, numYElements); - } - -#if SYSTEM_DRAWING - public SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode) - { - _SculptMesh(sculptBitmap, sculptType, lod, viewerMode, false, false); - } - - public SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode, bool mirror, bool invert) - { - _SculptMesh(sculptBitmap, sculptType, lod, viewerMode, mirror, invert); - } -#endif - - public SculptMesh(List> rows, SculptType sculptType, bool viewerMode, bool mirror, bool invert) - { - _SculptMesh(rows, sculptType, viewerMode, mirror, invert); - } - -#if SYSTEM_DRAWING - /// - /// converts a bitmap to a list of lists of coords, while scaling the image. - /// the scaling is done in floating point so as to allow for reduced vertex position - /// quantization as the position will be averaged between pixel values. this routine will - /// likely fail if the bitmap width and height are not powers of 2. - /// - /// - /// - /// - /// - private List> bitmap2Coords(Bitmap bitmap, int scale, bool mirror) - { - int numRows = bitmap.Height / scale; - int numCols = bitmap.Width / scale; - List> rows = new List>(numRows); - - float pixScale = 1.0f / (scale * scale); - pixScale /= 255; - - int imageX, imageY = 0; - - int rowNdx, colNdx; - - for (rowNdx = 0; rowNdx < numRows; rowNdx++) - { - List row = new List(numCols); - for (colNdx = 0; colNdx < numCols; colNdx++) - { - imageX = colNdx * scale; - int imageYStart = rowNdx * scale; - int imageYEnd = imageYStart + scale; - int imageXEnd = imageX + scale; - float rSum = 0.0f; - float gSum = 0.0f; - float bSum = 0.0f; - for (; imageX < imageXEnd; imageX++) - { - for (imageY = imageYStart; imageY < imageYEnd; imageY++) - { - Color c = bitmap.GetPixel(imageX, imageY); - rSum += c.R; - gSum += c.G; - bSum += c.B; - } - } - if (mirror) - row.Add(new Coord(-(rSum * pixScale - 0.5f), gSum * pixScale - 0.5f, bSum * pixScale - 0.5f)); - else - row.Add(new Coord(rSum * pixScale - 0.5f, gSum * pixScale - 0.5f, bSum * pixScale - 0.5f)); - - } - rows.Add(row); - } - return rows; - } - - - void _SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode, bool mirror, bool invert) - { - coords = new List(); - faces = new List(); - normals = new List(); - uvs = new List(); - - sculptType = (SculptType)(((int)sculptType) & 0x07); - - if (mirror) - if (sculptType == SculptType.plane) - invert = !invert; - - float sourceScaleFactor = (float)(lod) / (float)Math.Sqrt(sculptBitmap.Width * sculptBitmap.Height); - - int scale = (int)(1.0f / sourceScaleFactor); - if (scale < 1) scale = 1; - - _SculptMesh(bitmap2Coords(sculptBitmap, scale, mirror), sculptType, viewerMode, mirror, invert); - } -#endif - - - void _SculptMesh(List> rows, SculptType sculptType, bool viewerMode, bool mirror, bool invert) - { - coords = new List(); - faces = new List(); - normals = new List(); - uvs = new List(); - - sculptType = (SculptType)(((int)sculptType) & 0x07); - - if (mirror) - if (sculptType == SculptType.plane) - invert = !invert; - - viewerFaces = new List(); - - int width = rows[0].Count; - - int p1, p2, p3, p4; - - int imageX, imageY; - - if (sculptType != SculptType.plane) - { - for (int rowNdx = 0; rowNdx < rows.Count; rowNdx++) - rows[rowNdx].Add(rows[rowNdx][0]); - } - - Coord topPole = rows[0][width / 2]; - Coord bottomPole = rows[rows.Count - 1][width / 2]; - - if (sculptType == SculptType.sphere) - { - int count = rows[0].Count; - List topPoleRow = new List(count); - List bottomPoleRow = new List(count); - - for (int i = 0; i < count; i++) - { - topPoleRow.Add(topPole); - bottomPoleRow.Add(bottomPole); - } - rows.Insert(0, topPoleRow); - rows.Add(bottomPoleRow); - } - else if (sculptType == SculptType.torus) - rows.Add(rows[0]); - - int coordsDown = rows.Count; - int coordsAcross = rows[0].Count; - - float widthUnit = 1.0f / (coordsAcross - 1); - float heightUnit = 1.0f / (coordsDown - 1); - - for (imageY = 0; imageY < coordsDown; imageY++) - { - int rowOffset = imageY * coordsAcross; - - for (imageX = 0; imageX < coordsAcross; imageX++) - { - /* - * p1-----p2 - * | \ f2 | - * | \ | - * | f1 \| - * p3-----p4 - */ - - p4 = rowOffset + imageX; - p3 = p4 - 1; - - p2 = p4 - coordsAcross; - p1 = p3 - coordsAcross; - - this.coords.Add(rows[imageY][imageX]); - if (viewerMode) - { - this.normals.Add(new Coord()); - this.uvs.Add(new UVCoord(widthUnit * imageX, heightUnit * imageY)); - } - - if (imageY > 0 && imageX > 0) - { - Face f1, f2; - - if (viewerMode) - { - if (invert) - { - f1 = new Face(p1, p4, p3, p1, p4, p3); - f1.uv1 = p1; - f1.uv2 = p4; - f1.uv3 = p3; - - f2 = new Face(p1, p2, p4, p1, p2, p4); - f2.uv1 = p1; - f2.uv2 = p2; - f2.uv3 = p4; - } - else - { - f1 = new Face(p1, p3, p4, p1, p3, p4); - f1.uv1 = p1; - f1.uv2 = p3; - f1.uv3 = p4; - - f2 = new Face(p1, p4, p2, p1, p4, p2); - f2.uv1 = p1; - f2.uv2 = p4; - f2.uv3 = p2; - } - } - else - { - if (invert) - { - f1 = new Face(p1, p4, p3); - f2 = new Face(p1, p2, p4); - } - else - { - f1 = new Face(p1, p3, p4); - f2 = new Face(p1, p4, p2); - } - } - - this.faces.Add(f1); - this.faces.Add(f2); - } - } - } - - if (viewerMode) - calcVertexNormals(sculptType, coordsAcross, coordsDown); - } - - /// - /// Duplicates a SculptMesh object. All object properties are copied by value, including lists. - /// - /// - public SculptMesh Copy() - { - return new SculptMesh(this); - } - - public SculptMesh(SculptMesh sm) - { - coords = new List(sm.coords); - faces = new List(sm.faces); - viewerFaces = new List(sm.viewerFaces); - normals = new List(sm.normals); - uvs = new List(sm.uvs); - } - - private void calcVertexNormals(SculptType sculptType, int xSize, int ySize) - { // compute vertex normals by summing all the surface normals of all the triangles sharing - // each vertex and then normalizing - int numFaces = this.faces.Count; - for (int i = 0; i < numFaces; i++) - { - Face face = this.faces[i]; - Coord surfaceNormal = face.SurfaceNormal(this.coords); - this.normals[face.n1] += surfaceNormal; - this.normals[face.n2] += surfaceNormal; - this.normals[face.n3] += surfaceNormal; - } - - int numNormals = this.normals.Count; - for (int i = 0; i < numNormals; i++) - this.normals[i] = this.normals[i].Normalize(); - - if (sculptType != SculptType.plane) - { // blend the vertex normals at the cylinder seam - for (int y = 0; y < ySize; y++) - { - int rowOffset = y * xSize; - - this.normals[rowOffset] = this.normals[rowOffset + xSize - 1] = (this.normals[rowOffset] + this.normals[rowOffset + xSize - 1]).Normalize(); - } - } - - foreach (Face face in this.faces) - { - ViewerFace vf = new ViewerFace(0); - vf.v1 = this.coords[face.v1]; - vf.v2 = this.coords[face.v2]; - vf.v3 = this.coords[face.v3]; - - vf.coordIndex1 = face.v1; - vf.coordIndex2 = face.v2; - vf.coordIndex3 = face.v3; - - vf.n1 = this.normals[face.n1]; - vf.n2 = this.normals[face.n2]; - vf.n3 = this.normals[face.n3]; - - vf.uv1 = this.uvs[face.uv1]; - vf.uv2 = this.uvs[face.uv2]; - vf.uv3 = this.uvs[face.uv3]; - - this.viewerFaces.Add(vf); - } - } - - /// - /// Adds a value to each XYZ vertex coordinate in the mesh - /// - /// - /// - /// - public void AddPos(float x, float y, float z) - { - int i; - int numVerts = this.coords.Count; - Coord vert; - - for (i = 0; i < numVerts; i++) - { - vert = this.coords[i]; - vert.X += x; - vert.Y += y; - vert.Z += z; - this.coords[i] = vert; - } - - if (this.viewerFaces != null) - { - int numViewerFaces = this.viewerFaces.Count; - - for (i = 0; i < numViewerFaces; i++) - { - ViewerFace v = this.viewerFaces[i]; - v.AddPos(x, y, z); - this.viewerFaces[i] = v; - } - } - } - - /// - /// Rotates the mesh - /// - /// - public void AddRot(Quat q) - { - int i; - int numVerts = this.coords.Count; - - for (i = 0; i < numVerts; i++) - this.coords[i] *= q; - - int numNormals = this.normals.Count; - for (i = 0; i < numNormals; i++) - this.normals[i] *= q; - - if (this.viewerFaces != null) - { - int numViewerFaces = this.viewerFaces.Count; - - for (i = 0; i < numViewerFaces; i++) - { - ViewerFace v = this.viewerFaces[i]; - v.v1 *= q; - v.v2 *= q; - v.v3 *= q; - - v.n1 *= q; - v.n2 *= q; - v.n3 *= q; - - this.viewerFaces[i] = v; - } - } - } - - public void Scale(float x, float y, float z) - { - int i; - int numVerts = this.coords.Count; - - Coord m = new Coord(x, y, z); - for (i = 0; i < numVerts; i++) - this.coords[i] *= m; - - if (this.viewerFaces != null) - { - int numViewerFaces = this.viewerFaces.Count; - for (i = 0; i < numViewerFaces; i++) - { - ViewerFace v = this.viewerFaces[i]; - v.v1 *= m; - v.v2 *= m; - v.v3 *= m; - this.viewerFaces[i] = v; - } - } - } - - public void DumpRaw(String path, String name, String title) - { - if (path == null) - return; - String fileName = name + "_" + title + ".raw"; - String completePath = System.IO.Path.Combine(path, fileName); - StreamWriter sw = new StreamWriter(completePath); - - for (int i = 0; i < this.faces.Count; i++) - { - string s = this.coords[this.faces[i].v1].ToString(); - s += " " + this.coords[this.faces[i].v2].ToString(); - s += " " + this.coords[this.faces[i].v3].ToString(); - - sw.WriteLine(s); - } - - sw.Close(); - } - } -} +/* + * Copyright (c) Contributors + * 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. + */ + +// to build without references to System.Drawing, comment this out +#define SYSTEM_DRAWING + +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; + +#if SYSTEM_DRAWING +using System.Drawing; +using System.Drawing.Imaging; +#endif + +namespace PrimMesher +{ + + public class SculptMesh + { + public List coords; + public List faces; + + public List viewerFaces; + public List normals; + public List uvs; + + public enum SculptType { sphere = 1, torus = 2, plane = 3, cylinder = 4 }; + +#if SYSTEM_DRAWING + private Bitmap ScaleImage(Bitmap srcImage, float scale, bool removeAlpha) + { + int sourceWidth = srcImage.Width; + int sourceHeight = srcImage.Height; + int sourceX = 0; + int sourceY = 0; + + int destX = 0; + int destY = 0; + int destWidth = (int)(srcImage.Width * scale); + int destHeight = (int)(srcImage.Height * scale); + + Bitmap scaledImage; + + if (removeAlpha) + { + if (srcImage.PixelFormat == PixelFormat.Format32bppArgb) + for (int y = 0; y < srcImage.Height; y++) + for (int x = 0; x < srcImage.Width; x++) + { + Color c = srcImage.GetPixel(x, y); + srcImage.SetPixel(x, y, Color.FromArgb(255, c.R, c.G, c.B)); + } + + scaledImage = new Bitmap(destWidth, destHeight, + PixelFormat.Format24bppRgb); + } + else + scaledImage = new Bitmap(srcImage, destWidth, destHeight); + + scaledImage.SetResolution(96.0f, 96.0f); + + Graphics grPhoto = Graphics.FromImage(scaledImage); + grPhoto.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Low; + + grPhoto.DrawImage(srcImage, + new Rectangle(destX, destY, destWidth, destHeight), + new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight), + GraphicsUnit.Pixel); + + grPhoto.Dispose(); + return scaledImage; + } + + + public SculptMesh SculptMeshFromFile(string fileName, SculptType sculptType, int lod, bool viewerMode) + { + Bitmap bitmap = (Bitmap)Bitmap.FromFile(fileName); + SculptMesh sculptMesh = new SculptMesh(bitmap, sculptType, lod, viewerMode); + bitmap.Dispose(); + return sculptMesh; + } + + public SculptMesh(string fileName, int sculptType, int lod, int viewerMode, int mirror, int invert) + { + Bitmap bitmap = (Bitmap)Bitmap.FromFile(fileName); + _SculptMesh(bitmap, (SculptType)sculptType, lod, viewerMode != 0, mirror != 0, invert != 0); + bitmap.Dispose(); + } +#endif + + /// + /// ** Experimental ** May disappear from future versions ** not recommeneded for use in applications + /// Construct a sculpt mesh from a 2D array of floats + /// + /// + /// + /// + /// + /// + /// + public SculptMesh(float[,] zMap, float xBegin, float xEnd, float yBegin, float yEnd, bool viewerMode) + { + float xStep, yStep; + float uStep, vStep; + + int numYElements = zMap.GetLength(0); + int numXElements = zMap.GetLength(1); + + try + { + xStep = (xEnd - xBegin) / (float)(numXElements - 1); + yStep = (yEnd - yBegin) / (float)(numYElements - 1); + + uStep = 1.0f / (numXElements - 1); + vStep = 1.0f / (numYElements - 1); + } + catch (DivideByZeroException) + { + return; + } + + coords = new List(); + faces = new List(); + normals = new List(); + uvs = new List(); + + viewerFaces = new List(); + + int p1, p2, p3, p4; + + int x, y; + int xStart = 0, yStart = 0; + + for (y = yStart; y < numYElements; y++) + { + int rowOffset = y * numXElements; + + for (x = xStart; x < numXElements; x++) + { + /* + * p1-----p2 + * | \ f2 | + * | \ | + * | f1 \| + * p3-----p4 + */ + + p4 = rowOffset + x; + p3 = p4 - 1; + + p2 = p4 - numXElements; + p1 = p3 - numXElements; + + Coord c = new Coord(xBegin + x * xStep, yBegin + y * yStep, zMap[y, x]); + this.coords.Add(c); + if (viewerMode) + { + this.normals.Add(new Coord()); + this.uvs.Add(new UVCoord(uStep * x, 1.0f - vStep * y)); + } + + if (y > 0 && x > 0) + { + Face f1, f2; + + if (viewerMode) + { + f1 = new Face(p1, p4, p3, p1, p4, p3); + f1.uv1 = p1; + f1.uv2 = p4; + f1.uv3 = p3; + + f2 = new Face(p1, p2, p4, p1, p2, p4); + f2.uv1 = p1; + f2.uv2 = p2; + f2.uv3 = p4; + } + else + { + f1 = new Face(p1, p4, p3); + f2 = new Face(p1, p2, p4); + } + + this.faces.Add(f1); + this.faces.Add(f2); + } + } + } + + if (viewerMode) + calcVertexNormals(SculptType.plane, numXElements, numYElements); + } + +#if SYSTEM_DRAWING + public SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode) + { + _SculptMesh(sculptBitmap, sculptType, lod, viewerMode, false, false); + } + + public SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode, bool mirror, bool invert) + { + _SculptMesh(sculptBitmap, sculptType, lod, viewerMode, mirror, invert); + } +#endif + + public SculptMesh(List> rows, SculptType sculptType, bool viewerMode, bool mirror, bool invert) + { + _SculptMesh(rows, sculptType, viewerMode, mirror, invert); + } + +#if SYSTEM_DRAWING + /// + /// converts a bitmap to a list of lists of coords, while scaling the image. + /// the scaling is done in floating point so as to allow for reduced vertex position + /// quantization as the position will be averaged between pixel values. this routine will + /// likely fail if the bitmap width and height are not powers of 2. + /// + /// + /// + /// + /// + private List> bitmap2Coords(Bitmap bitmap, int scale, bool mirror) + { + int numRows = bitmap.Height / scale; + int numCols = bitmap.Width / scale; + List> rows = new List>(numRows); + + float pixScale = 1.0f / (scale * scale); + pixScale /= 255; + + int imageX, imageY = 0; + + int rowNdx, colNdx; + + for (rowNdx = 0; rowNdx < numRows; rowNdx++) + { + List row = new List(numCols); + for (colNdx = 0; colNdx < numCols; colNdx++) + { + imageX = colNdx * scale; + int imageYStart = rowNdx * scale; + int imageYEnd = imageYStart + scale; + int imageXEnd = imageX + scale; + float rSum = 0.0f; + float gSum = 0.0f; + float bSum = 0.0f; + for (; imageX < imageXEnd; imageX++) + { + for (imageY = imageYStart; imageY < imageYEnd; imageY++) + { + Color c = bitmap.GetPixel(imageX, imageY); + if (c.A != 255) + { + bitmap.SetPixel(imageX, imageY, Color.FromArgb(255, c.R, c.G, c.B)); + c = bitmap.GetPixel(imageX, imageY); + } + rSum += c.R; + gSum += c.G; + bSum += c.B; + } + } + if (mirror) + row.Add(new Coord(-(rSum * pixScale - 0.5f), gSum * pixScale - 0.5f, bSum * pixScale - 0.5f)); + else + row.Add(new Coord(rSum * pixScale - 0.5f, gSum * pixScale - 0.5f, bSum * pixScale - 0.5f)); + + } + rows.Add(row); + } + return rows; + } + + + void _SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool viewerMode, bool mirror, bool invert) + { + coords = new List(); + faces = new List(); + normals = new List(); + uvs = new List(); + + sculptType = (SculptType)(((int)sculptType) & 0x07); + + if (mirror) + if (sculptType == SculptType.plane) + invert = !invert; + + float sculptBitmapLod = (float)Math.Sqrt(sculptBitmap.Width * sculptBitmap.Height); + + float sourceScaleFactor = (float)(lod) / sculptBitmapLod; + + float fScale = 1.0f / sourceScaleFactor; + + int iScale = (int)fScale; + if (iScale < 1) iScale = 1; + if (iScale > 2 && iScale % 2 == 0) + _SculptMesh(bitmap2Coords(ScaleImage(sculptBitmap, 64.0f / sculptBitmapLod, true), 64 / lod, mirror), sculptType, viewerMode, mirror, invert); + else + _SculptMesh(bitmap2Coords(sculptBitmap, iScale, mirror), sculptType, viewerMode, mirror, invert); + } +#endif + + + void _SculptMesh(List> rows, SculptType sculptType, bool viewerMode, bool mirror, bool invert) + { + coords = new List(); + faces = new List(); + normals = new List(); + uvs = new List(); + + sculptType = (SculptType)(((int)sculptType) & 0x07); + + if (mirror) + if (sculptType == SculptType.plane) + invert = !invert; + + viewerFaces = new List(); + + int width = rows[0].Count; + + int p1, p2, p3, p4; + + int imageX, imageY; + + if (sculptType != SculptType.plane) + { + for (int rowNdx = 0; rowNdx < rows.Count; rowNdx++) + rows[rowNdx].Add(rows[rowNdx][0]); + } + + Coord topPole = rows[0][width / 2]; + Coord bottomPole = rows[rows.Count - 1][width / 2]; + + if (sculptType == SculptType.sphere) + { + int count = rows[0].Count; + List topPoleRow = new List(count); + List bottomPoleRow = new List(count); + + for (int i = 0; i < count; i++) + { + topPoleRow.Add(topPole); + bottomPoleRow.Add(bottomPole); + } + rows.Insert(0, topPoleRow); + rows.Add(bottomPoleRow); + } + else if (sculptType == SculptType.torus) + rows.Add(rows[0]); + + int coordsDown = rows.Count; + int coordsAcross = rows[0].Count; + + float widthUnit = 1.0f / (coordsAcross - 1); + float heightUnit = 1.0f / (coordsDown - 1); + + for (imageY = 0; imageY < coordsDown; imageY++) + { + int rowOffset = imageY * coordsAcross; + + for (imageX = 0; imageX < coordsAcross; imageX++) + { + /* + * p1-----p2 + * | \ f2 | + * | \ | + * | f1 \| + * p3-----p4 + */ + + p4 = rowOffset + imageX; + p3 = p4 - 1; + + p2 = p4 - coordsAcross; + p1 = p3 - coordsAcross; + + this.coords.Add(rows[imageY][imageX]); + if (viewerMode) + { + this.normals.Add(new Coord()); + this.uvs.Add(new UVCoord(widthUnit * imageX, heightUnit * imageY)); + } + + if (imageY > 0 && imageX > 0) + { + Face f1, f2; + + if (viewerMode) + { + if (invert) + { + f1 = new Face(p1, p4, p3, p1, p4, p3); + f1.uv1 = p1; + f1.uv2 = p4; + f1.uv3 = p3; + + f2 = new Face(p1, p2, p4, p1, p2, p4); + f2.uv1 = p1; + f2.uv2 = p2; + f2.uv3 = p4; + } + else + { + f1 = new Face(p1, p3, p4, p1, p3, p4); + f1.uv1 = p1; + f1.uv2 = p3; + f1.uv3 = p4; + + f2 = new Face(p1, p4, p2, p1, p4, p2); + f2.uv1 = p1; + f2.uv2 = p4; + f2.uv3 = p2; + } + } + else + { + if (invert) + { + f1 = new Face(p1, p4, p3); + f2 = new Face(p1, p2, p4); + } + else + { + f1 = new Face(p1, p3, p4); + f2 = new Face(p1, p4, p2); + } + } + + this.faces.Add(f1); + this.faces.Add(f2); + } + } + } + + if (viewerMode) + calcVertexNormals(sculptType, coordsAcross, coordsDown); + } + + /// + /// Duplicates a SculptMesh object. All object properties are copied by value, including lists. + /// + /// + public SculptMesh Copy() + { + return new SculptMesh(this); + } + + public SculptMesh(SculptMesh sm) + { + coords = new List(sm.coords); + faces = new List(sm.faces); + viewerFaces = new List(sm.viewerFaces); + normals = new List(sm.normals); + uvs = new List(sm.uvs); + } + + private void calcVertexNormals(SculptType sculptType, int xSize, int ySize) + { // compute vertex normals by summing all the surface normals of all the triangles sharing + // each vertex and then normalizing + int numFaces = this.faces.Count; + for (int i = 0; i < numFaces; i++) + { + Face face = this.faces[i]; + Coord surfaceNormal = face.SurfaceNormal(this.coords); + this.normals[face.n1] += surfaceNormal; + this.normals[face.n2] += surfaceNormal; + this.normals[face.n3] += surfaceNormal; + } + + int numNormals = this.normals.Count; + for (int i = 0; i < numNormals; i++) + this.normals[i] = this.normals[i].Normalize(); + + if (sculptType != SculptType.plane) + { // blend the vertex normals at the cylinder seam + for (int y = 0; y < ySize; y++) + { + int rowOffset = y * xSize; + + this.normals[rowOffset] = this.normals[rowOffset + xSize - 1] = (this.normals[rowOffset] + this.normals[rowOffset + xSize - 1]).Normalize(); + } + } + + foreach (Face face in this.faces) + { + ViewerFace vf = new ViewerFace(0); + vf.v1 = this.coords[face.v1]; + vf.v2 = this.coords[face.v2]; + vf.v3 = this.coords[face.v3]; + + vf.coordIndex1 = face.v1; + vf.coordIndex2 = face.v2; + vf.coordIndex3 = face.v3; + + vf.n1 = this.normals[face.n1]; + vf.n2 = this.normals[face.n2]; + vf.n3 = this.normals[face.n3]; + + vf.uv1 = this.uvs[face.uv1]; + vf.uv2 = this.uvs[face.uv2]; + vf.uv3 = this.uvs[face.uv3]; + + this.viewerFaces.Add(vf); + } + } + + /// + /// Adds a value to each XYZ vertex coordinate in the mesh + /// + /// + /// + /// + public void AddPos(float x, float y, float z) + { + int i; + int numVerts = this.coords.Count; + Coord vert; + + for (i = 0; i < numVerts; i++) + { + vert = this.coords[i]; + vert.X += x; + vert.Y += y; + vert.Z += z; + this.coords[i] = vert; + } + + if (this.viewerFaces != null) + { + int numViewerFaces = this.viewerFaces.Count; + + for (i = 0; i < numViewerFaces; i++) + { + ViewerFace v = this.viewerFaces[i]; + v.AddPos(x, y, z); + this.viewerFaces[i] = v; + } + } + } + + /// + /// Rotates the mesh + /// + /// + public void AddRot(Quat q) + { + int i; + int numVerts = this.coords.Count; + + for (i = 0; i < numVerts; i++) + this.coords[i] *= q; + + int numNormals = this.normals.Count; + for (i = 0; i < numNormals; i++) + this.normals[i] *= q; + + if (this.viewerFaces != null) + { + int numViewerFaces = this.viewerFaces.Count; + + for (i = 0; i < numViewerFaces; i++) + { + ViewerFace v = this.viewerFaces[i]; + v.v1 *= q; + v.v2 *= q; + v.v3 *= q; + + v.n1 *= q; + v.n2 *= q; + v.n3 *= q; + + this.viewerFaces[i] = v; + } + } + } + + public void Scale(float x, float y, float z) + { + int i; + int numVerts = this.coords.Count; + + Coord m = new Coord(x, y, z); + for (i = 0; i < numVerts; i++) + this.coords[i] *= m; + + if (this.viewerFaces != null) + { + int numViewerFaces = this.viewerFaces.Count; + for (i = 0; i < numViewerFaces; i++) + { + ViewerFace v = this.viewerFaces[i]; + v.v1 *= m; + v.v2 *= m; + v.v3 *= m; + this.viewerFaces[i] = v; + } + } + } + + public void DumpRaw(String path, String name, String title) + { + if (path == null) + return; + String fileName = name + "_" + title + ".raw"; + String completePath = System.IO.Path.Combine(path, fileName); + StreamWriter sw = new StreamWriter(completePath); + + for (int i = 0; i < this.faces.Count; i++) + { + string s = this.coords[this.faces[i].v1].ToString(); + s += " " + this.coords[this.faces[i].v2].ToString(); + s += " " + this.coords[this.faces[i].v3].ToString(); + + sw.WriteLine(s); + } + + sw.Close(); + } + } +} diff --git a/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs b/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs index 2ab00a3b4c..be7c348b31 100644 --- a/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs +++ b/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs @@ -1575,11 +1575,11 @@ Console.WriteLine(" JointCreateFixed"); { //Console.WriteLine("Move " + m_primName); if (!d.BodyIsEnabled (Body)) d.BodyEnable (Body); // KF add 161009 - /* // NON-'VEHICLES' are dealt with here if (d.BodyIsEnabled(Body) && !m_angularlock.ApproxEquals(Vector3.Zero, 0.003f)) { d.Vector3 avel2 = d.BodyGetAngularVel(Body); + /* if (m_angularlock.X == 1) avel2.X = 0; if (m_angularlock.Y == 1) @@ -1587,8 +1587,8 @@ Console.WriteLine(" JointCreateFixed"); if (m_angularlock.Z == 1) avel2.Z = 0; d.BodySetAngularVel(Body, avel2.X, avel2.Y, avel2.Z); + */ } - */ //float PID_P = 900.0f; float m_mass = CalculateMass(); diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index 62eae76d08..8f82304ddb 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -4221,7 +4221,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return; } - if( message == string.Empty) + if (message == string.Empty) { ShoutError("Trying to use llTextBox with empty message."); } diff --git a/OpenSim/Server/Base/ServerUtils.cs b/OpenSim/Server/Base/ServerUtils.cs index 9c5441031d..a399672d4d 100644 --- a/OpenSim/Server/Base/ServerUtils.cs +++ b/OpenSim/Server/Base/ServerUtils.cs @@ -57,6 +57,12 @@ namespace OpenSim.Server.Base return ret; } + /// + /// Load a plugin from a dll with the given class or interface + /// + /// + /// The arguments which control which constructor is invoked on the plugin + /// public static T LoadPlugin(string dllName, Object[] args) where T:class { string[] parts = dllName.Split(new char[] {':'}); @@ -71,6 +77,13 @@ namespace OpenSim.Server.Base return LoadPlugin(dllName, className, args); } + /// + /// Load a plugin from a dll with the given class or interface + /// + /// + /// + /// The arguments which control which constructor is invoked on the plugin + /// public static T LoadPlugin(string dllName, string className, Object[] args) where T:class { string interfaceName = typeof(T).ToString(); @@ -83,28 +96,15 @@ namespace OpenSim.Server.Base { if (pluginType.IsPublic) { - if (className != String.Empty && - pluginType.ToString() != - pluginType.Namespace + "." + className) + if (className != String.Empty + && pluginType.ToString() != pluginType.Namespace + "." + className) continue; - Type typeInterface = - pluginType.GetInterface(interfaceName, true); + + Type typeInterface = pluginType.GetInterface(interfaceName, true); + if (typeInterface != null) { - T plug = null; - try - { - plug = (T)Activator.CreateInstance(pluginType, - args); - } - catch (Exception e) - { - if (!(e is System.MissingMethodException)) - m_log.ErrorFormat("Error loading plugin from {0}, exception {1}", dllName, e.InnerException); - return null; - } - - return plug; + return (T)Activator.CreateInstance(pluginType, args); } } } @@ -113,7 +113,7 @@ namespace OpenSim.Server.Base } catch (Exception e) { - m_log.ErrorFormat("Error loading plugin from {0}, exception {1}", dllName, e); + m_log.Error(string.Format("Error loading plugin from {0}", dllName), e); return null; } } diff --git a/OpenSim/Server/Handlers/Hypergrid/UserAgentServerConnector.cs b/OpenSim/Server/Handlers/Hypergrid/UserAgentServerConnector.cs index 79c6b2a866..6b1152b00e 100644 --- a/OpenSim/Server/Handlers/Hypergrid/UserAgentServerConnector.cs +++ b/OpenSim/Server/Handlers/Hypergrid/UserAgentServerConnector.cs @@ -1,4 +1,31 @@ -using System; +/* + * 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; using System.Collections.Generic; using System.Net; diff --git a/OpenSim/Server/Handlers/Inventory/XInventoryInConnector.cs b/OpenSim/Server/Handlers/Inventory/XInventoryInConnector.cs index f9db859aa7..34f7dccc4e 100644 --- a/OpenSim/Server/Handlers/Inventory/XInventoryInConnector.cs +++ b/OpenSim/Server/Handlers/Inventory/XInventoryInConnector.cs @@ -289,7 +289,7 @@ namespace OpenSim.Server.Handlers.Asset foreach (InventoryItemBase i in icoll.Items) items[i.ID.ToString()] = EncodeItem(i); result["ITEMS"] = items; - } + } string xmlString = ServerUtils.BuildXmlResponse(result); m_log.DebugFormat("[XXX]: resp string: {0}", xmlString); diff --git a/OpenSim/Services/Base/ServiceBase.cs b/OpenSim/Services/Base/ServiceBase.cs index 91d5c5650c..ef30cba6cf 100644 --- a/OpenSim/Services/Base/ServiceBase.cs +++ b/OpenSim/Services/Base/ServiceBase.cs @@ -72,7 +72,7 @@ namespace OpenSim.Services.Base // m_log.DebugFormat("[SERVICE BASE]: Found type {0}", pluginType); if (pluginType.IsPublic) - { + { if (className != String.Empty && pluginType.ToString() != pluginType.Namespace + "." + className) diff --git a/OpenSim/Services/Connectors/GridUser/GridUserServiceConnector.cs b/OpenSim/Services/Connectors/GridUser/GridUserServiceConnector.cs index ce88236ae3..0e8506706c 100644 --- a/OpenSim/Services/Connectors/GridUser/GridUserServiceConnector.cs +++ b/OpenSim/Services/Connectors/GridUser/GridUserServiceConnector.cs @@ -28,9 +28,9 @@ using System; namespace OpenSim.Services.Connectors -{ +{ public class GridUserServiceConnector - { + { public GridUserServiceConnector() { } diff --git a/OpenSim/Services/Connectors/Hypergrid/GatekeeperServiceConnector.cs b/OpenSim/Services/Connectors/Hypergrid/GatekeeperServiceConnector.cs index 9f73b388e8..c426bba807 100644 --- a/OpenSim/Services/Connectors/Hypergrid/GatekeeperServiceConnector.cs +++ b/OpenSim/Services/Connectors/Hypergrid/GatekeeperServiceConnector.cs @@ -1,4 +1,31 @@ -using System; +/* + * 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; using System.Collections.Generic; using System.Drawing; diff --git a/OpenSim/Services/Connectors/Hypergrid/UserAgentServiceConnector.cs b/OpenSim/Services/Connectors/Hypergrid/UserAgentServiceConnector.cs index 83d34496af..3e91e3a113 100644 --- a/OpenSim/Services/Connectors/Hypergrid/UserAgentServiceConnector.cs +++ b/OpenSim/Services/Connectors/Hypergrid/UserAgentServiceConnector.cs @@ -1,4 +1,31 @@ -using System; +/* + * 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; using System.Collections.Generic; using System.IO; @@ -334,7 +361,7 @@ namespace OpenSim.Services.Connectors.Hypergrid } catch (Exception e) { - m_log.Debug("[HGrid]: Exception " + e.Message); + m_log.Debug("[USER AGENT CONNECTOR]: Unable to contact remote server "); reason = "Exception: " + e.Message; return false; } diff --git a/OpenSim/Services/Connectors/Inventory/XInventoryConnector.cs b/OpenSim/Services/Connectors/Inventory/XInventoryConnector.cs index 9821dd40e2..0cc1978a0b 100644 --- a/OpenSim/Services/Connectors/Inventory/XInventoryConnector.cs +++ b/OpenSim/Services/Connectors/Inventory/XInventoryConnector.cs @@ -126,7 +126,7 @@ namespace OpenSim.Services.Connectors }); if (ret == null) - return null; + return null; if (ret.Count == 0) return null; diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianAssetServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianAssetServiceConnector.cs new file mode 100644 index 0000000000..1c22a72bc4 --- /dev/null +++ b/OpenSim/Services/Connectors/SimianGrid/SimianAssetServiceConnector.cs @@ -0,0 +1,410 @@ +/* + * 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.IO; +using System.Net; +using System.Reflection; +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +namespace OpenSim.Services.Connectors.SimianGrid +{ + /// + /// Connects to the SimianGrid asset service + /// + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")] + public class SimianAssetServiceConnector : IAssetService, ISharedRegionModule + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + private static string ZeroID = UUID.Zero.ToString(); + + private string m_serverUrl = String.Empty; + private IImprovedAssetCache m_cache; + + #region ISharedRegionModule + + public Type ReplaceableInterface { get { return null; } } + public void RegionLoaded(Scene scene) + { + if (m_cache == null) + { + IImprovedAssetCache cache = scene.RequestModuleInterface(); + if (cache is ISharedRegionModule) + m_cache = cache; + } + } + public void PostInitialise() { } + public void Close() { } + + public SimianAssetServiceConnector() { } + public string Name { get { return "SimianAssetServiceConnector"; } } + public void AddRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.RegisterModuleInterface(this); } } + public void RemoveRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.UnregisterModuleInterface(this); } } + + #endregion ISharedRegionModule + + public SimianAssetServiceConnector(IConfigSource source) + { + Initialise(source); + } + + public void Initialise(IConfigSource source) + { + if (Simian.IsSimianEnabled(source, "AssetServices")) + { + IConfig gridConfig = source.Configs["AssetService"]; + if (gridConfig == null) + { + m_log.Error("[ASSET CONNECTOR]: AssetService missing from OpenSim.ini"); + throw new Exception("Asset connector init error"); + } + + string serviceUrl = gridConfig.GetString("AssetServerURI"); + if (String.IsNullOrEmpty(serviceUrl)) + { + m_log.Error("[ASSET CONNECTOR]: No AssetServerURI in section AssetService"); + throw new Exception("Asset connector init error"); + } + + if (!serviceUrl.EndsWith("/") && !serviceUrl.EndsWith("=")) + serviceUrl = serviceUrl + '/'; + + m_serverUrl = serviceUrl; + } + } + + #region IAssetService + + public AssetBase Get(string id) + { + AssetBase asset = null; + + // Cache fetch + if (m_cache != null) + { + asset = m_cache.Get(id); + if (asset != null) + return asset; + } + + Uri url; + + // Determine if id is an absolute URL or a grid-relative UUID + if (!Uri.TryCreate(id, UriKind.Absolute, out url)) + url = new Uri(m_serverUrl + id); + + try + { + HttpWebRequest request = UntrustedHttpWebRequest.Create(url); + + using (WebResponse response = request.GetResponse()) + { + using (Stream responseStream = response.GetResponseStream()) + { + string creatorID = response.Headers.GetOne("X-Asset-Creator-Id") ?? String.Empty; + + // Create the asset object + asset = new AssetBase(id, String.Empty, SLUtil.ContentTypeToSLAssetType(response.ContentType), creatorID); + + UUID assetID; + if (UUID.TryParse(id, out assetID)) + asset.FullID = assetID; + + // Grab the asset data from the response stream + using (MemoryStream stream = new MemoryStream()) + { + responseStream.CopyTo(stream, Int32.MaxValue); + asset.Data = stream.ToArray(); + } + } + } + + // Cache store + if (m_cache != null && asset != null) + m_cache.Cache(asset); + + return asset; + } + catch (Exception ex) + { + m_log.Warn("[ASSET CONNECTOR]: Asset GET from " + url + " failed: " + ex.Message); + return null; + } + } + + /// + /// Get an asset's metadata + /// + /// + /// + public AssetMetadata GetMetadata(string id) + { + AssetMetadata metadata = null; + + // Cache fetch + if (m_cache != null) + { + AssetBase asset = m_cache.Get(id); + if (asset != null) + return asset.Metadata; + } + + Uri url; + + // Determine if id is an absolute URL or a grid-relative UUID + if (!Uri.TryCreate(id, UriKind.Absolute, out url)) + url = new Uri(m_serverUrl + id); + + try + { + HttpWebRequest request = UntrustedHttpWebRequest.Create(url); + request.Method = "HEAD"; + + using (WebResponse response = request.GetResponse()) + { + using (Stream responseStream = response.GetResponseStream()) + { + // Create the metadata object + metadata = new AssetMetadata(); + metadata.ContentType = response.ContentType; + metadata.ID = id; + + UUID uuid; + if (UUID.TryParse(id, out uuid)) + metadata.FullID = uuid; + + string lastModifiedStr = response.Headers.Get("Last-Modified"); + if (!String.IsNullOrEmpty(lastModifiedStr)) + { + DateTime lastModified; + if (DateTime.TryParse(lastModifiedStr, out lastModified)) + metadata.CreationDate = lastModified; + } + } + } + } + catch (Exception ex) + { + m_log.Warn("[ASSET CONNECTOR]: Asset GET from " + url + " failed: " + ex.Message); + } + + return metadata; + } + + public byte[] GetData(string id) + { + AssetBase asset = Get(id); + + if (asset != null) + return asset.Data; + + return null; + } + + /// + /// Get an asset asynchronously + /// + /// The asset id + /// Represents the requester. Passed back via the handler + /// The handler to call back once the asset has been retrieved + /// True if the id was parseable, false otherwise + public bool Get(string id, Object sender, AssetRetrieved handler) + { + Util.FireAndForget( + delegate(object o) + { + AssetBase asset = Get(id); + handler(id, sender, asset); + } + ); + + return true; + } + + /// + /// Creates a new asset + /// + /// Returns a random ID if none is passed into it + /// + /// + public string Store(AssetBase asset) + { + bool storedInCache = false; + string errorMessage = null; + + // AssetID handling + if (String.IsNullOrEmpty(asset.ID) || asset.ID == ZeroID) + { + asset.FullID = UUID.Random(); + asset.ID = asset.FullID.ToString(); + } + + // Cache handling + if (m_cache != null) + { + m_cache.Cache(asset); + storedInCache = true; + } + + // Local asset handling + if (asset.Local) + { + if (!storedInCache) + { + m_log.Error("Cannot store local " + asset.Metadata.ContentType + " asset without an asset cache"); + asset.ID = null; + asset.FullID = UUID.Zero; + } + + return asset.ID; + } + + // Distinguish public and private assets + bool isPublic = true; + switch ((AssetType)asset.Type) + { + case AssetType.CallingCard: + case AssetType.Gesture: + case AssetType.LSLBytecode: + case AssetType.LSLText: + isPublic = false; + break; + } + + // Make sure ContentType is set + if (String.IsNullOrEmpty(asset.Metadata.ContentType)) + asset.Metadata.ContentType = SLUtil.SLAssetTypeToContentType(asset.Type); + + // Build the remote storage request + List postParameters = new List() + { + new MultipartForm.Parameter("AssetID", asset.FullID.ToString()), + new MultipartForm.Parameter("CreatorID", asset.Metadata.CreatorID), + new MultipartForm.Parameter("Temporary", asset.Temporary ? "1" : "0"), + new MultipartForm.Parameter("Public", isPublic ? "1" : "0"), + new MultipartForm.File("Asset", asset.Name, asset.Metadata.ContentType, asset.Data) + }; + + // Make the remote storage request + try + { + HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(m_serverUrl); + + HttpWebResponse response = MultipartForm.Post(request, postParameters); + using (Stream responseStream = response.GetResponseStream()) + { + try + { + string responseStr = responseStream.GetStreamString(); + OSD responseOSD = OSDParser.Deserialize(responseStr); + if (responseOSD.Type == OSDType.Map) + { + OSDMap responseMap = (OSDMap)responseOSD; + if (responseMap["Success"].AsBoolean()) + return asset.ID; + else + errorMessage = "Upload failed: " + responseMap["Message"].AsString(); + } + else + { + errorMessage = "Response format was invalid."; + } + } + catch + { + errorMessage = "Failed to parse the response."; + } + } + } + catch (WebException ex) + { + errorMessage = ex.Message; + } + + m_log.WarnFormat("[ASSET CONNECTOR]: Failed to store asset \"{0}\" ({1}, {2}): {3}", + asset.Name, asset.ID, asset.Metadata.ContentType, errorMessage); + return null; + } + + /// + /// Update an asset's content + /// + /// Attachments and bare scripts need this!! + /// + /// + /// + public bool UpdateContent(string id, byte[] data) + { + AssetBase asset = Get(id); + + if (asset == null) + { + m_log.Warn("[ASSET CONNECTOR]: Failed to fetch asset " + id + " for updating"); + return false; + } + + asset.Data = data; + + string result = Store(asset); + return !String.IsNullOrEmpty(result); + } + + /// + /// Delete an asset + /// + /// + /// + public bool Delete(string id) + { + if (m_cache != null) + m_cache.Expire(id); + + string url = m_serverUrl + id; + + OSDMap response = WebUtil.ServiceRequest(url, "DELETE"); + if (response["Success"].AsBoolean()) + return true; + else + m_log.Warn("[ASSET CONNECTOR]: Failed to delete asset " + id + " from the asset service"); + + return false; + } + + #endregion IAssetService + } +} diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianAuthenticationServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianAuthenticationServiceConnector.cs new file mode 100644 index 0000000000..6317b8711e --- /dev/null +++ b/OpenSim/Services/Connectors/SimianGrid/SimianAuthenticationServiceConnector.cs @@ -0,0 +1,201 @@ +/* + * 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.Specialized; +using System.Reflection; +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; + +namespace OpenSim.Services.Connectors.SimianGrid +{ + /// + /// Connects authentication/authorization to the SimianGrid backend + /// + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")] + public class SimianAuthenticationServiceConnector : IAuthenticationService, ISharedRegionModule + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_serverUrl = String.Empty; + + #region ISharedRegionModule + + public Type ReplaceableInterface { get { return null; } } + public void RegionLoaded(Scene scene) { } + public void PostInitialise() { } + public void Close() { } + + public SimianAuthenticationServiceConnector() { } + public string Name { get { return "SimianAuthenticationServiceConnector"; } } + public void AddRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.RegisterModuleInterface(this); } } + public void RemoveRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.UnregisterModuleInterface(this); } } + + #endregion ISharedRegionModule + + public SimianAuthenticationServiceConnector(IConfigSource source) + { + Initialise(source); + } + + public void Initialise(IConfigSource source) + { + if (Simian.IsSimianEnabled(source, "AuthenticationServices")) + { + IConfig assetConfig = source.Configs["AuthenticationService"]; + if (assetConfig == null) + { + m_log.Error("[AUTH CONNECTOR]: AuthenticationService missing from OpenSim.ini"); + throw new Exception("Authentication connector init error"); + } + + string serviceURI = assetConfig.GetString("AuthenticationServerURI"); + if (String.IsNullOrEmpty(serviceURI)) + { + m_log.Error("[AUTH CONNECTOR]: No Server URI named in section AuthenticationService"); + throw new Exception("Authentication connector init error"); + } + + m_serverUrl = serviceURI; + } + } + + public string Authenticate(UUID principalID, string password, int lifetime) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetIdentities" }, + { "UserID", principalID.ToString() } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean() && response["Identities"] is OSDArray) + { + OSDArray identities = (OSDArray)response["Identities"]; + for (int i = 0; i < identities.Count; i++) + { + OSDMap identity = identities[i] as OSDMap; + if (identity != null) + { + if (identity["Type"].AsString() == "md5hash") + { + string credential = identity["Credential"].AsString(); + + if (password == credential || Utils.MD5String(password) == credential) + return Authorize(principalID); + } + } + } + + m_log.Warn("[AUTH CONNECTOR]: Authentication failed for " + principalID); + } + else + { + m_log.Warn("[AUTH CONNECTOR]: Failed to retrieve identities for " + principalID + ": " + + response["Message"].AsString()); + } + + return String.Empty; + } + + public bool Verify(UUID principalID, string token, int lifetime) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetSession" }, + { "SessionID", token } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean()) + { + return true; + } + else + { + m_log.Warn("[AUTH CONNECTOR]: Could not verify session for " + principalID + ": " + + response["Message"].AsString()); + } + + return false; + } + + public bool Release(UUID principalID, string token) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "RemoveSession" }, + { "UserID", principalID.ToString() } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean()) + { + return true; + } + else + { + m_log.Warn("[AUTH CONNECTOR]: Failed to remove session for " + principalID + ": " + + response["Message"].AsString()); + } + + return false; + } + + public bool SetPassword(UUID principalID, string passwd) + { + // TODO: Use GetIdentities to find the md5hash identity for principalID + // and then update it with AddIdentity + m_log.Error("[AUTH CONNECTOR]: Changing passwords is not implemented yet"); + return false; + } + + private string Authorize(UUID userID) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddSession" }, + { "UserID", userID.ToString() } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean()) + return response["SessionID"].AsUUID().ToString(); + else + return String.Empty; + } + } +} diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianAvatarServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianAvatarServiceConnector.cs new file mode 100644 index 0000000000..a18cb2286e --- /dev/null +++ b/OpenSim/Services/Connectors/SimianGrid/SimianAvatarServiceConnector.cs @@ -0,0 +1,262 @@ +/* + * 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.Collections.Specialized; +using System.IO; +using System.Net; +using System.Reflection; +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +namespace OpenSim.Services.Connectors.SimianGrid +{ + /// + /// Connects avatar appearance data to the SimianGrid backend + /// + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")] + public class SimianAvatarServiceConnector : IAvatarService, ISharedRegionModule + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + private static string ZeroID = UUID.Zero.ToString(); + + private string m_serverUrl = String.Empty; + + #region ISharedRegionModule + + public Type ReplaceableInterface { get { return null; } } + public void RegionLoaded(Scene scene) { } + public void PostInitialise() { } + public void Close() { } + + public SimianAvatarServiceConnector() { } + public string Name { get { return "SimianAvatarServiceConnector"; } } + public void AddRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.RegisterModuleInterface(this); } } + public void RemoveRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.UnregisterModuleInterface(this); } } + + #endregion ISharedRegionModule + + public SimianAvatarServiceConnector(IConfigSource source) + { + Initialise(source); + } + + public void Initialise(IConfigSource source) + { + if (Simian.IsSimianEnabled(source, "AvatarServices")) + { + IConfig gridConfig = source.Configs["AvatarService"]; + if (gridConfig == null) + { + m_log.Error("[AVATAR CONNECTOR]: AvatarService missing from OpenSim.ini"); + throw new Exception("Avatar connector init error"); + } + + string serviceUrl = gridConfig.GetString("AvatarServerURI"); + if (String.IsNullOrEmpty(serviceUrl)) + { + m_log.Error("[AVATAR CONNECTOR]: No AvatarServerURI in section AvatarService"); + throw new Exception("Avatar connector init error"); + } + + if (!serviceUrl.EndsWith("/")) + serviceUrl = serviceUrl + '/'; + + m_serverUrl = serviceUrl; + } + } + + #region IAvatarService + + public AvatarData GetAvatar(UUID userID) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetUser" }, + { "UserID", userID.ToString() } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean()) + { + OSDMap map = null; + try { map = OSDParser.DeserializeJson(response["LLAppearance"].AsString()) as OSDMap; } + catch { } + + if (map != null) + { + AvatarWearable[] wearables = new AvatarWearable[13]; + wearables[0] = new AvatarWearable(map["ShapeItem"].AsUUID(), map["ShapeAsset"].AsUUID()); + wearables[1] = new AvatarWearable(map["SkinItem"].AsUUID(), map["SkinAsset"].AsUUID()); + wearables[2] = new AvatarWearable(map["HairItem"].AsUUID(), map["HairAsset"].AsUUID()); + wearables[3] = new AvatarWearable(map["EyesItem"].AsUUID(), map["EyesAsset"].AsUUID()); + wearables[4] = new AvatarWearable(map["ShirtItem"].AsUUID(), map["ShirtAsset"].AsUUID()); + wearables[5] = new AvatarWearable(map["PantsItem"].AsUUID(), map["PantsAsset"].AsUUID()); + wearables[6] = new AvatarWearable(map["ShoesItem"].AsUUID(), map["ShoesAsset"].AsUUID()); + wearables[7] = new AvatarWearable(map["SocksItem"].AsUUID(), map["SocksAsset"].AsUUID()); + wearables[8] = new AvatarWearable(map["JacketItem"].AsUUID(), map["JacketAsset"].AsUUID()); + wearables[9] = new AvatarWearable(map["GlovesItem"].AsUUID(), map["GlovesAsset"].AsUUID()); + wearables[10] = new AvatarWearable(map["UndershirtItem"].AsUUID(), map["UndershirtAsset"].AsUUID()); + wearables[11] = new AvatarWearable(map["UnderpantsItem"].AsUUID(), map["UnderpantsAsset"].AsUUID()); + wearables[12] = new AvatarWearable(map["SkirtItem"].AsUUID(), map["SkirtAsset"].AsUUID()); + + AvatarAppearance appearance = new AvatarAppearance(userID); + appearance.Wearables = wearables; + appearance.AvatarHeight = (float)map["Height"].AsReal(); + + AvatarData avatar = new AvatarData(appearance); + + // Get attachments + map = null; + try { map = OSDParser.DeserializeJson(response["LLAttachments"].AsString()) as OSDMap; } + catch { } + + if (map != null) + { + foreach (KeyValuePair kvp in map) + avatar.Data[kvp.Key] = kvp.Value.AsString(); + } + + return avatar; + } + else + { + m_log.Warn("[AVATAR CONNECTOR]: Failed to get user appearance for " + userID + + ", LLAppearance is missing or invalid"); + return null; + } + } + else + { + m_log.Warn("[AVATAR CONNECTOR]: Failed to get user appearance for " + userID + ": " + + response["Message"].AsString()); + } + + return null; + } + + public bool SetAvatar(UUID userID, AvatarData avatar) + { + m_log.Debug("[AVATAR CONNECTOR]: SetAvatar called for " + userID); + + if (avatar.AvatarType == 1) // LLAvatar + { + AvatarAppearance appearance = avatar.ToAvatarAppearance(userID); + + OSDMap map = new OSDMap(); + + map["Height"] = OSD.FromReal(appearance.AvatarHeight); + + map["ShapeItem"] = OSD.FromUUID(appearance.BodyItem); + map["ShapeAsset"] = OSD.FromUUID(appearance.BodyAsset); + map["SkinItem"] = OSD.FromUUID(appearance.SkinItem); + map["SkinAsset"] = OSD.FromUUID(appearance.SkinAsset); + map["HairItem"] = OSD.FromUUID(appearance.HairItem); + map["HairAsset"] = OSD.FromUUID(appearance.HairAsset); + map["EyesItem"] = OSD.FromUUID(appearance.EyesItem); + map["EyesAsset"] = OSD.FromUUID(appearance.EyesAsset); + map["ShirtItem"] = OSD.FromUUID(appearance.ShirtItem); + map["ShirtAsset"] = OSD.FromUUID(appearance.ShirtAsset); + map["PantsItem"] = OSD.FromUUID(appearance.PantsItem); + map["PantsAsset"] = OSD.FromUUID(appearance.PantsAsset); + map["ShoesItem"] = OSD.FromUUID(appearance.ShoesItem); + map["ShoesAsset"] = OSD.FromUUID(appearance.ShoesAsset); + map["SocksItem"] = OSD.FromUUID(appearance.SocksItem); + map["SocksAsset"] = OSD.FromUUID(appearance.SocksAsset); + map["JacketItem"] = OSD.FromUUID(appearance.JacketItem); + map["JacketAsset"] = OSD.FromUUID(appearance.JacketAsset); + map["GlovesItem"] = OSD.FromUUID(appearance.GlovesItem); + map["GlovesAsset"] = OSD.FromUUID(appearance.GlovesAsset); + map["UndershirtItem"] = OSD.FromUUID(appearance.UnderShirtItem); + map["UndershirtAsset"] = OSD.FromUUID(appearance.UnderShirtAsset); + map["UnderpantsItem"] = OSD.FromUUID(appearance.UnderPantsItem); + map["UnderpantsAsset"] = OSD.FromUUID(appearance.UnderPantsAsset); + map["SkirtItem"] = OSD.FromUUID(appearance.SkirtItem); + map["SkirtAsset"] = OSD.FromUUID(appearance.SkirtAsset); + + OSDMap items = new OSDMap(); + foreach (KeyValuePair kvp in avatar.Data) + { + if (kvp.Key.StartsWith("_ap_")) + items.Add(kvp.Key, OSD.FromString(kvp.Value)); + } + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddUserData" }, + { "UserID", userID.ToString() }, + { "LLAppearance", OSDParser.SerializeJsonString(map) }, + { "LLAttachments", OSDParser.SerializeJsonString(items) } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + m_log.Warn("[AVATAR CONNECTOR]: Failed saving appearance for " + userID + ": " + response["Message"].AsString()); + + return success; + } + else + { + m_log.Error("[AVATAR CONNECTOR]: Can't save appearance for " + userID + ". Unhandled avatar type " + avatar.AvatarType); + return false; + } + } + + public bool ResetAvatar(UUID userID) + { + m_log.Error("[AVATAR CONNECTOR]: ResetAvatar called for " + userID + ", implement this"); + return false; + } + + public bool SetItems(UUID userID, string[] names, string[] values) + { + m_log.Error("[AVATAR CONNECTOR]: SetItems called for " + userID + " with " + names.Length + " names and " + values.Length + " values, implement this"); + return false; + } + + public bool RemoveItems(UUID userID, string[] names) + { + m_log.Error("[AVATAR CONNECTOR]: RemoveItems called for " + userID + " with " + names.Length + " names, implement this"); + return false; + } + + #endregion IAvatarService + } +} diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianFriendsServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianFriendsServiceConnector.cs new file mode 100644 index 0000000000..b3ecc7e907 --- /dev/null +++ b/OpenSim/Services/Connectors/SimianGrid/SimianFriendsServiceConnector.cs @@ -0,0 +1,232 @@ +/* + * 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.Collections.Specialized; +using System.Reflection; +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; + +using FriendInfo = OpenSim.Services.Interfaces.FriendInfo; + +namespace OpenSim.Services.Connectors.SimianGrid +{ + /// + /// Stores and retrieves friend lists from the SimianGrid backend + /// + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")] + public class SimianFriendsServiceConnector : IFriendsService, ISharedRegionModule + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_serverUrl = String.Empty; + + #region ISharedRegionModule + + public Type ReplaceableInterface { get { return null; } } + public void RegionLoaded(Scene scene) { } + public void PostInitialise() { } + public void Close() { } + + public SimianFriendsServiceConnector() { } + public string Name { get { return "SimianFriendsServiceConnector"; } } + public void AddRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.RegisterModuleInterface(this); } } + public void RemoveRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.UnregisterModuleInterface(this); } } + + #endregion ISharedRegionModule + + public SimianFriendsServiceConnector(IConfigSource source) + { + Initialise(source); + } + + public void Initialise(IConfigSource source) + { + if (Simian.IsSimianEnabled(source, "FriendsServices")) + { + IConfig assetConfig = source.Configs["FriendsService"]; + if (assetConfig == null) + { + m_log.Error("[FRIENDS CONNECTOR]: FriendsService missing from OpenSim.ini"); + throw new Exception("Friends connector init error"); + } + + string serviceURI = assetConfig.GetString("FriendsServerURI"); + if (String.IsNullOrEmpty(serviceURI)) + { + m_log.Error("[FRIENDS CONNECTOR]: No Server URI named in section FriendsService"); + throw new Exception("Friends connector init error"); + } + + m_serverUrl = serviceURI; + } + } + + #region IFriendsService + + public FriendInfo[] GetFriends(UUID principalID) + { + Dictionary friends = new Dictionary(); + + OSDArray friendsArray = GetFriended(principalID); + OSDArray friendedMeArray = GetFriendedBy(principalID); + + // Load the list of friends and their granted permissions + for (int i = 0; i < friendsArray.Count; i++) + { + OSDMap friendEntry = friendsArray[i] as OSDMap; + if (friendEntry != null) + { + UUID friendID = friendEntry["Key"].AsUUID(); + + FriendInfo friend = new FriendInfo(); + friend.PrincipalID = principalID; + friend.Friend = friendID.ToString(); + friend.MyFlags = friendEntry["Value"].AsInteger(); + friend.TheirFlags = -1; + + friends[friendID] = friend; + } + } + + // Load the permissions those friends have granted to this user + for (int i = 0; i < friendedMeArray.Count; i++) + { + OSDMap friendedMeEntry = friendedMeArray[i] as OSDMap; + if (friendedMeEntry != null) + { + UUID friendID = friendedMeEntry["OwnerID"].AsUUID(); + + FriendInfo friend; + if (friends.TryGetValue(friendID, out friend)) + friend.TheirFlags = friendedMeEntry["Value"].AsInteger(); + } + } + + // Convert the dictionary of friends to an array and return it + FriendInfo[] array = new FriendInfo[friends.Count]; + int j = 0; + foreach (FriendInfo friend in friends.Values) + array[j++] = friend; + + return array; + } + + public bool StoreFriend(UUID principalID, string friend, int flags) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddGeneric" }, + { "OwnerID", principalID.ToString() }, + { "Type", "Friend" }, + { "Key", friend }, + { "Value", flags.ToString() } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + m_log.Error("[FRIENDS CONNECTOR]: Failed to store friend " + friend + " for user " + principalID + ": " + response["Message"].AsString()); + + return success; + } + + public bool Delete(UUID principalID, string friend) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "RemoveGeneric" }, + { "OwnerID", principalID.ToString() }, + { "Type", "Friend" }, + { "Key", friend } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + m_log.Error("[FRIENDS CONNECTOR]: Failed to remove friend " + friend + " for user " + principalID + ": " + response["Message"].AsString()); + + return success; + } + + #endregion IFriendsService + + private OSDArray GetFriended(UUID ownerID) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetGenerics" }, + { "OwnerID", ownerID.ToString() }, + { "Type", "Friend" } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean() && response["Entries"] is OSDArray) + { + return (OSDArray)response["Entries"]; + } + else + { + m_log.Warn("[FRIENDS CONNECTOR]: Failed to retrieve friends for user " + ownerID + ": " + response["Message"].AsString()); + return new OSDArray(0); + } + } + + private OSDArray GetFriendedBy(UUID ownerID) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetGenerics" }, + { "Key", ownerID.ToString() }, + { "Type", "Friend" } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean() && response["Entries"] is OSDArray) + { + return (OSDArray)response["Entries"]; + } + else + { + m_log.Warn("[FRIENDS CONNECTOR]: Failed to retrieve reverse friends for user " + ownerID + ": " + response["Message"].AsString()); + return new OSDArray(0); + } + } + } +} diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianGrid.cs b/OpenSim/Services/Connectors/SimianGrid/SimianGrid.cs new file mode 100644 index 0000000000..c3de98ef58 --- /dev/null +++ b/OpenSim/Services/Connectors/SimianGrid/SimianGrid.cs @@ -0,0 +1,47 @@ +/* + * 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 Mono.Addins; +using Nini.Config; + +[assembly: Addin("SimianGrid", "1.0")] +[assembly: AddinDependency("OpenSim", "0.5")] + +public static class Simian +{ + public static bool IsSimianEnabled(IConfigSource config, string moduleName) + { + if (config.Configs["Modules"] != null) + { + string module = config.Configs["Modules"].GetString("AuthenticationServices"); + return !String.IsNullOrEmpty(module) && module.Contains("Simian"); + } + + return false; + } +} \ No newline at end of file diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianGridServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianGridServiceConnector.cs new file mode 100644 index 0000000000..eebdf14b92 --- /dev/null +++ b/OpenSim/Services/Connectors/SimianGrid/SimianGridServiceConnector.cs @@ -0,0 +1,421 @@ +/* + * 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.Collections.Specialized; +using System.Net; +using System.Reflection; +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using OpenSim.Server.Base; +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +using GridRegion = OpenSim.Services.Interfaces.GridRegion; + +namespace OpenSim.Services.Connectors.SimianGrid +{ + /// + /// Connects region registration and neighbor lookups to the SimianGrid + /// backend + /// + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")] + public class SimianGridServiceConnector : IGridService, ISharedRegionModule + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_serverUrl = String.Empty; + + #region ISharedRegionModule + + public Type ReplaceableInterface { get { return null; } } + public void RegionLoaded(Scene scene) { } + public void PostInitialise() { } + public void Close() { } + + public SimianGridServiceConnector() { } + public string Name { get { return "SimianGridServiceConnector"; } } + public void AddRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.RegisterModuleInterface(this); } } + public void RemoveRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.UnregisterModuleInterface(this); } } + + #endregion ISharedRegionModule + + public SimianGridServiceConnector(IConfigSource source) + { + Initialise(source); + } + + public void Initialise(IConfigSource source) + { + if (Simian.IsSimianEnabled(source, "GridServices")) + { + IConfig gridConfig = source.Configs["GridService"]; + if (gridConfig == null) + { + m_log.Error("[GRID CONNECTOR]: GridService missing from OpenSim.ini"); + throw new Exception("Grid connector init error"); + } + + string serviceUrl = gridConfig.GetString("GridServerURI"); + if (String.IsNullOrEmpty(serviceUrl)) + { + m_log.Error("[GRID CONNECTOR]: No Server URI named in section GridService"); + throw new Exception("Grid connector init error"); + } + + m_serverUrl = serviceUrl; + } + } + + #region IGridService + + public string RegisterRegion(UUID scopeID, GridRegion regionInfo) + { + Vector3d minPosition = new Vector3d(regionInfo.RegionLocX, regionInfo.RegionLocY, 0.0); + Vector3d maxPosition = minPosition + new Vector3d(Constants.RegionSize, Constants.RegionSize, 4096.0); + + string httpAddress = "http://" + regionInfo.ExternalHostName + ":" + regionInfo.HttpPort + "/"; + + OSDMap extraData = new OSDMap + { + { "ServerURI", OSD.FromString(regionInfo.ServerURI) }, + { "InternalAddress", OSD.FromString(regionInfo.InternalEndPoint.Address.ToString()) }, + { "InternalPort", OSD.FromInteger(regionInfo.InternalEndPoint.Port) }, + { "ExternalAddress", OSD.FromString(regionInfo.ExternalEndPoint.Address.ToString()) }, + { "ExternalPort", OSD.FromInteger(regionInfo.ExternalEndPoint.Port) }, + { "MapTexture", OSD.FromUUID(regionInfo.TerrainImage) }, + { "Access", OSD.FromInteger(regionInfo.Access) }, + { "RegionSecret", OSD.FromString(regionInfo.RegionSecret) }, + { "EstateOwner", OSD.FromUUID(regionInfo.EstateOwner) }, + { "Token", OSD.FromString(regionInfo.Token) } + }; + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddScene" }, + { "SceneID", regionInfo.RegionID.ToString() }, + { "Name", regionInfo.RegionName }, + { "MinPosition", minPosition.ToString() }, + { "MaxPosition", maxPosition.ToString() }, + { "Address", httpAddress }, + { "Enabled", "1" }, + { "ExtraData", OSDParser.SerializeJsonString(extraData) } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean()) + return String.Empty; + else + return "Region registration for " + regionInfo.RegionName + " failed: " + response["Message"].AsString(); + } + + public bool DeregisterRegion(UUID regionID) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddScene" }, + { "SceneID", regionID.ToString() }, + { "Enabled", "0" } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + m_log.Warn("[GRID CONNECTOR]: Region deregistration for " + regionID + " failed: " + response["Message"].AsString()); + + return success; + } + + public List GetNeighbours(UUID scopeID, UUID regionID) + { + const int NEIGHBOR_RADIUS = 128; + + GridRegion region = GetRegionByUUID(scopeID, regionID); + + if (region != null) + { + List regions = GetRegionRange(scopeID, + region.RegionLocX - NEIGHBOR_RADIUS, region.RegionLocX + (int)Constants.RegionSize + NEIGHBOR_RADIUS, + region.RegionLocY - NEIGHBOR_RADIUS, region.RegionLocY + (int)Constants.RegionSize + NEIGHBOR_RADIUS); + + for (int i = 0; i < regions.Count; i++) + { + if (regions[i].RegionID == regionID) + { + regions.RemoveAt(i); + break; + } + } + + m_log.Debug("[GRID CONNECTOR]: Found " + regions.Count + " neighbors for region " + regionID); + return regions; + } + + return new List(0); + } + + public GridRegion GetRegionByUUID(UUID scopeID, UUID regionID) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetScene" }, + { "SceneID", regionID.ToString() } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean()) + { + return ResponseToGridRegion(response); + } + else + { + m_log.Warn("[GRID CONNECTOR]: Grid service did not find a match for region " + regionID); + return null; + } + } + + public GridRegion GetRegionByPosition(UUID scopeID, int x, int y) + { + // Go one meter in from the requested x/y coords to avoid requesting a position + // that falls on the border of two sims + Vector3d position = new Vector3d(x + 1, y + 1, 0.0); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetScene" }, + { "Position", position.ToString() }, + { "Enabled", "1" } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean()) + { + return ResponseToGridRegion(response); + } + else + { + //m_log.InfoFormat("[GRID CONNECTOR]: Grid service did not find a match for region at {0},{1}", + // x / Constants.RegionSize, y / Constants.RegionSize); + return null; + } + } + + public GridRegion GetRegionByName(UUID scopeID, string regionName) + { + List regions = GetRegionsByName(scopeID, regionName, 1); + + m_log.Debug("[GRID CONNECTOR]: Got " + regions.Count + " matches for region name " + regionName); + + if (regions.Count > 0) + return regions[0]; + + return null; + } + + public List GetRegionsByName(UUID scopeID, string name, int maxNumber) + { + List foundRegions = new List(); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetScenes" }, + { "NameQuery", name }, + { "Enabled", "1" } + }; + if (maxNumber > 0) + requestArgs["MaxNumber"] = maxNumber.ToString(); + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean()) + { + OSDArray array = response["Scenes"] as OSDArray; + if (array != null) + { + for (int i = 0; i < array.Count; i++) + { + GridRegion region = ResponseToGridRegion(array[i] as OSDMap); + if (region != null) + foundRegions.Add(region); + } + } + } + + return foundRegions; + } + + public List GetRegionRange(UUID scopeID, int xmin, int xmax, int ymin, int ymax) + { + List foundRegions = new List(); + + Vector3d minPosition = new Vector3d(xmin, ymin, 0.0); + Vector3d maxPosition = new Vector3d(xmax, ymax, 4096.0); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetScenes" }, + { "MinPosition", minPosition.ToString() }, + { "MaxPosition", maxPosition.ToString() }, + { "Enabled", "1" } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean()) + { + OSDArray array = response["Scenes"] as OSDArray; + if (array != null) + { + for (int i = 0; i < array.Count; i++) + { + GridRegion region = ResponseToGridRegion(array[i] as OSDMap); + if (region != null) + foundRegions.Add(region); + } + } + } + + return foundRegions; + } + + public List GetDefaultRegions(UUID scopeID) + { + // TODO: Allow specifying the default grid location + const int DEFAULT_X = 1000 * 256; + const int DEFAULT_Y = 1000 * 256; + + GridRegion defRegion = GetNearestRegion(new Vector3d(DEFAULT_X, DEFAULT_Y, 0.0), true); + if (defRegion != null) + return new List(1) { defRegion }; + else + return new List(0); + } + + public List GetFallbackRegions(UUID scopeID, int x, int y) + { + GridRegion defRegion = GetNearestRegion(new Vector3d(x, y, 0.0), true); + if (defRegion != null) + return new List(1) { defRegion }; + else + return new List(0); + } + + public int GetRegionFlags(UUID scopeID, UUID regionID) + { + const int REGION_ONLINE = 4; + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetScene" }, + { "SceneID", regionID.ToString() } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean()) + { + return response["Enabled"].AsBoolean() ? REGION_ONLINE : 0; + } + else + { + m_log.Warn("[GRID CONNECTOR]: Grid service did not find a match for region " + regionID + " during region flags check"); + return -1; + } + } + + #endregion IGridService + + private GridRegion GetNearestRegion(Vector3d position, bool onlyEnabled) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetScene" }, + { "Position", position.ToString() }, + { "FindClosest", "1" } + }; + if (onlyEnabled) + requestArgs["Enabled"] = "1"; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean()) + { + return ResponseToGridRegion(response); + } + else + { + m_log.Warn("[GRID CONNECTOR]: Grid service did not find a match for region at " + position); + return null; + } + } + + private GridRegion ResponseToGridRegion(OSDMap response) + { + if (response == null) + return null; + + OSDMap extraData = response["ExtraData"] as OSDMap; + if (extraData == null) + return null; + + GridRegion region = new GridRegion(); + + region.RegionID = response["SceneID"].AsUUID(); + region.RegionName = response["Name"].AsString(); + + Vector3d minPosition = response["MinPosition"].AsVector3d(); + region.RegionLocX = (int)minPosition.X; + region.RegionLocY = (int)minPosition.Y; + + Uri httpAddress = response["Address"].AsUri(); + region.ExternalHostName = httpAddress.Host; + region.HttpPort = (uint)httpAddress.Port; + + region.ServerURI = extraData["ServerURI"].AsString(); + + IPAddress internalAddress; + IPAddress.TryParse(extraData["InternalAddress"].AsString(), out internalAddress); + if (internalAddress == null) + internalAddress = IPAddress.Any; + + region.InternalEndPoint = new IPEndPoint(internalAddress, extraData["InternalPort"].AsInteger()); + region.TerrainImage = extraData["MapTexture"].AsUUID(); + region.Access = (byte)extraData["Access"].AsInteger(); + region.RegionSecret = extraData["RegionSecret"].AsString(); + region.EstateOwner = extraData["EstateOwner"].AsUUID(); + region.Token = extraData["Token"].AsString(); + + return region; + } + } +} diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianInventoryServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianInventoryServiceConnector.cs new file mode 100644 index 0000000000..891782fdad --- /dev/null +++ b/OpenSim/Services/Connectors/SimianGrid/SimianInventoryServiceConnector.cs @@ -0,0 +1,895 @@ +/* + * 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.Collections.Specialized; +using System.Reflection; +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; + +namespace OpenSim.Services.Connectors.SimianGrid +{ + /// + /// Permissions bitflags + /// + [Flags] + public enum PermissionMask : uint + { + None = 0, + Transfer = 1 << 13, + Modify = 1 << 14, + Copy = 1 << 15, + Move = 1 << 19, + Damage = 1 << 20, + All = 0x7FFFFFFF + } + + /// + /// Connects avatar inventories to the SimianGrid backend + /// + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")] + public class SimianInventoryServiceConnector : IInventoryService, ISharedRegionModule + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_serverUrl = String.Empty; + private string m_userServerUrl = String.Empty; + private object m_gestureSyncRoot = new object(); + + #region ISharedRegionModule + + public Type ReplaceableInterface { get { return null; } } + public void RegionLoaded(Scene scene) { } + public void PostInitialise() { } + public void Close() { } + + public SimianInventoryServiceConnector() { } + public string Name { get { return "SimianInventoryServiceConnector"; } } + public void AddRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.RegisterModuleInterface(this); } } + public void RemoveRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.UnregisterModuleInterface(this); } } + + #endregion ISharedRegionModule + + public SimianInventoryServiceConnector(IConfigSource source) + { + Initialise(source); + } + + public void Initialise(IConfigSource source) + { + if (Simian.IsSimianEnabled(source, "InventoryServices")) + { + IConfig gridConfig = source.Configs["InventoryService"]; + if (gridConfig == null) + { + m_log.Error("[INVENTORY CONNECTOR]: InventoryService missing from OpenSim.ini"); + throw new Exception("Inventory connector init error"); + } + + string serviceUrl = gridConfig.GetString("InventoryServerURI"); + if (String.IsNullOrEmpty(serviceUrl)) + { + m_log.Error("[INVENTORY CONNECTOR]: No Server URI named in section InventoryService"); + throw new Exception("Inventory connector init error"); + } + + m_serverUrl = serviceUrl; + + gridConfig = source.Configs["UserAccountService"]; + if (gridConfig != null) + { + serviceUrl = gridConfig.GetString("UserAccountServerURI"); + if (!String.IsNullOrEmpty(serviceUrl)) + m_userServerUrl = serviceUrl; + else + m_log.Info("[INVENTORY CONNECTOR]: No Server URI named in section UserAccountService"); + } + else + { + m_log.Warn("[INVENTORY CONNECTOR]: UserAccountService missing from OpenSim.ini"); + } + } + } + + /// + /// Create the entire inventory for a given user + /// + /// + /// + public bool CreateUserInventory(UUID userID) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddInventory" }, + { "OwnerID", userID.ToString() } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + m_log.Warn("[INVENTORY CONNECTOR]: Inventory creation for " + userID + " failed: " + response["Message"].AsString()); + + return success; + } + + /// + /// Gets the skeleton of the inventory -- folders only + /// + /// + /// + public List GetInventorySkeleton(UUID userID) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetInventoryNode" }, + { "ItemID", userID.ToString() }, + { "OwnerID", userID.ToString() }, + { "IncludeFolders", "1" }, + { "IncludeItems", "0" }, + { "ChildrenOnly", "0" } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean() && response["Items"] is OSDArray) + { + OSDArray items = (OSDArray)response["Items"]; + return GetFoldersFromResponse(items, userID, true); + } + else + { + m_log.Warn("[INVENTORY CONNECTOR]: Failed to retrieve inventory skeleton for " + userID + ": " + + response["Message"].AsString()); + return new List(0); + } + } + + /// + /// Synchronous inventory fetch. + /// + /// + /// + [Obsolete] + public InventoryCollection GetUserInventory(UUID userID) + { + m_log.Error("[INVENTORY CONNECTOR]: Obsolete GetUserInventory called for " + userID); + + InventoryCollection inventory = new InventoryCollection(); + inventory.UserID = userID; + inventory.Folders = new List(); + inventory.Items = new List(); + + return inventory; + } + + /// + /// Request the inventory for a user. This is an asynchronous operation that will call the callback when the + /// inventory has been received + /// + /// + /// + [Obsolete] + public void GetUserInventory(UUID userID, InventoryReceiptCallback callback) + { + m_log.Error("[INVENTORY CONNECTOR]: Obsolete GetUserInventory called for " + userID); + callback(new List(0), new List(0)); + } + + /// + /// Retrieve the root inventory folder for the given user. + /// + /// + /// null if no root folder was found + public InventoryFolderBase GetRootFolder(UUID userID) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetInventoryNode" }, + { "ItemID", userID.ToString() }, + { "OwnerID", userID.ToString() }, + { "IncludeFolders", "1" }, + { "IncludeItems", "0" }, + { "ChildrenOnly", "1" } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean() && response["Items"] is OSDArray) + { + OSDArray items = (OSDArray)response["Items"]; + List folders = GetFoldersFromResponse(items, userID, true); + + if (folders.Count > 0) + return folders[0]; + } + + return null; + } + + /// + /// Gets the user folder for the given folder-type + /// + /// + /// + /// + public InventoryFolderBase GetFolderForType(UUID userID, AssetType type) + { + string contentType = SLUtil.SLAssetTypeToContentType((int)type); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetFolderForType" }, + { "ContentType", contentType }, + { "OwnerID", userID.ToString() } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean() && response["Folder"] is OSDMap) + { + OSDMap folder = (OSDMap)response["Folder"]; + + return new InventoryFolderBase( + folder["ID"].AsUUID(), + folder["Name"].AsString(), + folder["OwnerID"].AsUUID(), + (short)SLUtil.ContentTypeToSLAssetType(folder["ContentType"].AsString()), + folder["ParentID"].AsUUID(), + (ushort)folder["Version"].AsInteger() + ); + } + else + { + m_log.Warn("[INVENTORY CONNECTOR]: Default folder not found for content type " + contentType); + return GetRootFolder(userID); + } + } + + /// + /// Get an item, given by its UUID + /// + /// + /// + public InventoryItemBase GetItem(InventoryItemBase item) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetInventoryNode" }, + { "ItemID", item.ID.ToString() }, + { "OwnerID", item.Owner.ToString() }, + { "IncludeFolders", "1" }, + { "IncludeItems", "1" }, + { "ChildrenOnly", "1" } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean() && response["Items"] is OSDArray) + { + List items = GetItemsFromResponse((OSDArray)response["Items"]); + if (items.Count > 0) + { + // The requested item should be the first in this list, but loop through + // and sanity check just in case + for (int i = 0; i < items.Count; i++) + { + if (items[i].ID == item.ID) + return items[i]; + } + } + } + + m_log.Warn("[INVENTORY CONNECTOR]: Item " + item.ID + " owned by " + item.Owner + " not found"); + return null; + } + + /// + /// Get a folder, given by its UUID + /// + /// + /// + public InventoryFolderBase GetFolder(InventoryFolderBase folder) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetInventoryNode" }, + { "ItemID", folder.ID.ToString() }, + { "OwnerID", folder.Owner.ToString() }, + { "IncludeFolders", "1" }, + { "IncludeItems", "0" }, + { "ChildrenOnly", "1" } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean() && response["Items"] is OSDArray) + { + OSDArray items = (OSDArray)response["Items"]; + List folders = GetFoldersFromResponse(items, folder.ID, true); + + if (folders.Count > 0) + return folders[0]; + } + + return null; + } + + /// + /// Gets everything (folders and items) inside a folder + /// + /// + /// + /// + public InventoryCollection GetFolderContent(UUID userID, UUID folderID) + { + InventoryCollection inventory = new InventoryCollection(); + inventory.UserID = userID; + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetInventoryNode" }, + { "ItemID", folderID.ToString() }, + { "OwnerID", userID.ToString() }, + { "IncludeFolders", "1" }, + { "IncludeItems", "1" }, + { "ChildrenOnly", "1" } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean() && response["Items"] is OSDArray) + { + OSDArray items = (OSDArray)response["Items"]; + + inventory.Folders = GetFoldersFromResponse(items, folderID, false); + inventory.Items = GetItemsFromResponse(items); + } + else + { + m_log.Warn("[INVENTORY CONNECTOR]: Error fetching folder " + folderID + " content for " + userID + ": " + + response["Message"].AsString()); + inventory.Folders = new List(0); + inventory.Items = new List(0); + } + + return inventory; + } + + /// + /// Gets the items inside a folder + /// + /// + /// + /// + public List GetFolderItems(UUID userID, UUID folderID) + { + InventoryCollection inventory = new InventoryCollection(); + inventory.UserID = userID; + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetInventoryNode" }, + { "ItemID", folderID.ToString() }, + { "OwnerID", userID.ToString() }, + { "IncludeFolders", "0" }, + { "IncludeItems", "1" }, + { "ChildrenOnly", "1" } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean() && response["Items"] is OSDArray) + { + OSDArray items = (OSDArray)response["Items"]; + return GetItemsFromResponse(items); + } + else + { + m_log.Warn("[INVENTORY CONNECTOR]: Error fetching folder " + folderID + " for " + userID + ": " + + response["Message"].AsString()); + return new List(0); + } + } + + /// + /// Add a new folder to the user's inventory + /// + /// + /// true if the folder was successfully added + public bool AddFolder(InventoryFolderBase folder) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddInventoryFolder" }, + { "FolderID", folder.ID.ToString() }, + { "ParentID", folder.ParentID.ToString() }, + { "OwnerID", folder.Owner.ToString() }, + { "Name", folder.Name }, + { "ContentType", SLUtil.SLAssetTypeToContentType(folder.Type) } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + { + m_log.Warn("[INVENTORY CONNECTOR]: Error creating folder " + folder.Name + " for " + folder.Owner + ": " + + response["Message"].AsString()); + } + + return success; + } + + /// + /// Update a folder in the user's inventory + /// + /// + /// true if the folder was successfully updated + public bool UpdateFolder(InventoryFolderBase folder) + { + return AddFolder(folder); + } + + /// + /// Move an inventory folder to a new location + /// + /// A folder containing the details of the new location + /// true if the folder was successfully moved + public bool MoveFolder(InventoryFolderBase folder) + { + return AddFolder(folder); + } + + /// + /// Delete an item from the user's inventory + /// + /// + /// true if the item was successfully deleted + //bool DeleteItem(InventoryItemBase item); + public bool DeleteFolders(UUID userID, List folderIDs) + { + return DeleteItems(userID, folderIDs); + } + + /// + /// Delete an item from the user's inventory + /// + /// + /// true if the item was successfully deleted + public bool DeleteItems(UUID userID, List itemIDs) + { + // TODO: RemoveInventoryNode should be replaced with RemoveInventoryNodes + bool allSuccess = true; + + for (int i = 0; i < itemIDs.Count; i++) + { + UUID itemID = itemIDs[i]; + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "RemoveInventoryNode" }, + { "OwnerID", userID.ToString() }, + { "ItemID", itemID.ToString() } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + { + m_log.Warn("[INVENTORY CONNECTOR]: Error removing item " + itemID + " for " + userID + ": " + + response["Message"].AsString()); + allSuccess = false; + } + } + + return allSuccess; + } + + /// + /// Purge an inventory folder of all its items and subfolders. + /// + /// + /// true if the folder was successfully purged + public bool PurgeFolder(InventoryFolderBase folder) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "PurgeInventoryFolder" }, + { "OwnerID", folder.Owner.ToString() }, + { "FolderID", folder.ID.ToString() } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + { + m_log.Warn("[INVENTORY CONNECTOR]: Error purging folder " + folder.ID + " for " + folder.Owner + ": " + + response["Message"].AsString()); + } + + return success; + } + + /// + /// Add a new item to the user's inventory + /// + /// + /// true if the item was successfully added + public bool AddItem(InventoryItemBase item) + { + // A folder of UUID.Zero means we need to find the most appropriate home for this item + if (item.Folder == UUID.Zero) + { + InventoryFolderBase folder = GetFolderForType(item.Owner, (AssetType)item.AssetType); + if (folder != null && folder.ID != UUID.Zero) + item.Folder = folder.ID; + else + item.Folder = item.Owner; // Root folder + } + + if ((AssetType)item.AssetType == AssetType.Gesture) + UpdateGesture(item.Owner, item.ID, item.Flags == 1); + + if (item.BasePermissions == 0) + m_log.WarnFormat("[INVENTORY CONNECTOR]: Adding inventory item {0} ({1}) with no base permissions", item.Name, item.ID); + + OSDMap permissions = new OSDMap + { + { "BaseMask", OSD.FromInteger(item.BasePermissions) }, + { "EveryoneMask", OSD.FromInteger(item.EveryOnePermissions) }, + { "GroupMask", OSD.FromInteger(item.GroupPermissions) }, + { "NextOwnerMask", OSD.FromInteger(item.NextPermissions) }, + { "OwnerMask", OSD.FromInteger(item.CurrentPermissions) } + }; + + OSDMap extraData = new OSDMap() + { + { "Flags", OSD.FromInteger(item.Flags) }, + { "GroupID", OSD.FromUUID(item.GroupID) }, + { "GroupOwned", OSD.FromBoolean(item.GroupOwned) }, + { "SalePrice", OSD.FromInteger(item.SalePrice) }, + { "SaleType", OSD.FromInteger(item.SaleType) }, + { "Permissions", permissions } + }; + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddInventoryItem" }, + { "ItemID", item.ID.ToString() }, + { "AssetID", item.AssetID.ToString() }, + { "ParentID", item.Folder.ToString() }, + { "OwnerID", item.Owner.ToString() }, + { "Name", item.Name }, + { "Description", item.Description }, + { "CreatorID", item.CreatorId }, + { "ExtraData", OSDParser.SerializeJsonString(extraData) } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + { + m_log.Warn("[INVENTORY CONNECTOR]: Error creating item " + item.Name + " for " + item.Owner + ": " + + response["Message"].AsString()); + } + + return success; + } + + /// + /// Update an item in the user's inventory + /// + /// + /// true if the item was successfully updated + public bool UpdateItem(InventoryItemBase item) + { + if (item.AssetID != UUID.Zero) + { + return AddItem(item); + } + else + { + // This is actually a folder update + InventoryFolderBase folder = new InventoryFolderBase(item.ID, item.Name, item.Owner, (short)item.AssetType, item.Folder, 0); + return UpdateFolder(folder); + } + } + + public bool MoveItems(UUID ownerID, List items) + { + bool success = true; + + while (items.Count > 0) + { + List currentItems = new List(); + UUID destFolderID = items[0].Folder; + + // Find all of the items being moved to the current destination folder + for (int i = 0; i < items.Count; i++) + { + InventoryItemBase item = items[i]; + if (item.Folder == destFolderID) + currentItems.Add(item); + } + + // Do the inventory move for the current items + success &= MoveItems(ownerID, items, destFolderID); + + // Remove the processed items from the list + for (int i = 0; i < currentItems.Count; i++) + items.Remove(currentItems[i]); + } + + return success; + } + + /// + /// Does the given user have an inventory structure? + /// + /// + /// + public bool HasInventoryForUser(UUID userID) + { + return GetRootFolder(userID) != null; + } + + /// + /// Get the active gestures of the agent. + /// + /// + /// + public List GetActiveGestures(UUID userID) + { + OSDArray items = FetchGestures(userID); + + string[] itemIDs = new string[items.Count]; + for (int i = 0; i < items.Count; i++) + itemIDs[i] = items[i].AsUUID().ToString(); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetInventoryNodes" }, + { "OwnerID", userID.ToString() }, + { "Items", String.Join(",", itemIDs) } + }; + + // FIXME: Implement this in SimianGrid + return new List(0); + } + + /// + /// Get the union of permissions of all inventory items + /// that hold the given assetID. + /// + /// + /// + /// The permissions or 0 if no such asset is found in + /// the user's inventory + public int GetAssetPermissions(UUID userID, UUID assetID) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetInventoryNodes" }, + { "OwnerID", userID.ToString() }, + { "AssetID", assetID.ToString() } + }; + + // FIXME: Implement this in SimianGrid + return (int)PermissionMask.All; + } + + private List GetFoldersFromResponse(OSDArray items, UUID baseFolder, bool includeBaseFolder) + { + List invFolders = new List(items.Count); + + for (int i = 0; i < items.Count; i++) + { + OSDMap item = items[i] as OSDMap; + + if (item != null && item["Type"].AsString() == "Folder") + { + UUID folderID = item["ID"].AsUUID(); + + if (folderID == baseFolder && !includeBaseFolder) + continue; + + invFolders.Add(new InventoryFolderBase( + folderID, + item["Name"].AsString(), + item["OwnerID"].AsUUID(), + (short)SLUtil.ContentTypeToSLAssetType(item["ContentType"].AsString()), + item["ParentID"].AsUUID(), + (ushort)item["Version"].AsInteger() + )); + } + } + + return invFolders; + } + + private List GetItemsFromResponse(OSDArray items) + { + List invItems = new List(items.Count); + + for (int i = 0; i < items.Count; i++) + { + OSDMap item = items[i] as OSDMap; + + if (item != null && item["Type"].AsString() == "Item") + { + InventoryItemBase invItem = new InventoryItemBase(); + + invItem.AssetID = item["AssetID"].AsUUID(); + invItem.AssetType = SLUtil.ContentTypeToSLAssetType(item["ContentType"].AsString()); + invItem.CreationDate = item["CreationDate"].AsInteger(); + invItem.CreatorId = item["CreatorID"].AsString(); + invItem.CreatorIdAsUuid = item["CreatorID"].AsUUID(); + invItem.Description = item["Description"].AsString(); + invItem.Folder = item["ParentID"].AsUUID(); + invItem.ID = item["ID"].AsUUID(); + invItem.InvType = SLUtil.ContentTypeToSLInvType(item["ContentType"].AsString()); + invItem.Name = item["Name"].AsString(); + invItem.Owner = item["OwnerID"].AsUUID(); + + OSDMap extraData = item["ExtraData"] as OSDMap; + if (extraData != null && extraData.Count > 0) + { + invItem.Flags = extraData["Flags"].AsUInteger(); + invItem.GroupID = extraData["GroupID"].AsUUID(); + invItem.GroupOwned = extraData["GroupOwned"].AsBoolean(); + invItem.SalePrice = extraData["SalePrice"].AsInteger(); + invItem.SaleType = (byte)extraData["SaleType"].AsInteger(); + + OSDMap perms = extraData["Permissions"] as OSDMap; + if (perms != null) + { + invItem.BasePermissions = perms["BaseMask"].AsUInteger(); + invItem.CurrentPermissions = perms["OwnerMask"].AsUInteger(); + invItem.EveryOnePermissions = perms["EveryoneMask"].AsUInteger(); + invItem.GroupPermissions = perms["GroupMask"].AsUInteger(); + invItem.NextPermissions = perms["NextOwnerMask"].AsUInteger(); + } + } + + if (invItem.BasePermissions == 0) + { + m_log.InfoFormat("[INVENTORY CONNECTOR]: Forcing item permissions to full for item {0} ({1})", + invItem.Name, invItem.ID); + invItem.BasePermissions = (uint)PermissionMask.All; + invItem.CurrentPermissions = (uint)PermissionMask.All; + invItem.EveryOnePermissions = (uint)PermissionMask.All; + invItem.GroupPermissions = (uint)PermissionMask.All; + invItem.NextPermissions = (uint)PermissionMask.All; + } + + invItems.Add(invItem); + } + } + + return invItems; + } + + private bool MoveItems(UUID ownerID, List items, UUID destFolderID) + { + string[] itemIDs = new string[items.Count]; + for (int i = 0; i < items.Count; i++) + itemIDs[i] = items[i].ID.ToString(); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "MoveInventoryNodes" }, + { "OwnerID", ownerID.ToString() }, + { "FolderID", destFolderID.ToString() }, + { "Items", String.Join(",", itemIDs) } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + { + m_log.Warn("[INVENTORY CONNECTOR]: Failed to move " + items.Count + " items to " + + destFolderID + ": " + response["Message"].AsString()); + } + + return success; + } + + private void UpdateGesture(UUID userID, UUID itemID, bool enabled) + { + OSDArray gestures = FetchGestures(userID); + OSDArray newGestures = new OSDArray(); + + for (int i = 0; i < gestures.Count; i++) + { + UUID gesture = gestures[i].AsUUID(); + if (gesture != itemID) + newGestures.Add(OSD.FromUUID(gesture)); + } + + if (enabled) + newGestures.Add(OSD.FromUUID(itemID)); + + SaveGestures(userID, newGestures); + } + + private OSDArray FetchGestures(UUID userID) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetUser" }, + { "UserID", userID.ToString() } + }; + + OSDMap response = WebUtil.PostToService(m_userServerUrl, requestArgs); + if (response["Success"].AsBoolean()) + { + OSDMap user = response["User"] as OSDMap; + if (user != null && response.ContainsKey("Gestures")) + { + OSD gestures = OSDParser.DeserializeJson(response["Gestures"].AsString()); + if (gestures != null && gestures is OSDArray) + return (OSDArray)gestures; + else + m_log.Error("[INVENTORY CONNECTOR]: Unrecognized active gestures data for " + userID); + } + } + else + { + m_log.Warn("[INVENTORY CONNECTOR]: Failed to fetch active gestures for " + userID + ": " + + response["Message"].AsString()); + } + + return new OSDArray(); + } + + private void SaveGestures(UUID userID, OSDArray gestures) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddUserData" }, + { "UserID", userID.ToString() }, + { "Gestures", OSDParser.SerializeJsonString(gestures) } + }; + + OSDMap response = WebUtil.PostToService(m_userServerUrl, requestArgs); + if (!response["Success"].AsBoolean()) + { + m_log.Warn("[INVENTORY CONNECTOR]: Failed to save active gestures for " + userID + ": " + + response["Message"].AsString()); + } + } + } +} diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs new file mode 100644 index 0000000000..1b5edf4ba2 --- /dev/null +++ b/OpenSim/Services/Connectors/SimianGrid/SimianPresenceServiceConnector.cs @@ -0,0 +1,511 @@ +/* + * 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.Collections.Specialized; +using System.Net; +using System.Reflection; +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using OpenSim.Server.Base; +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo; + +namespace OpenSim.Services.Connectors.SimianGrid +{ + /// + /// Connects avatar presence information (for tracking current location and + /// message routing) to the SimianGrid backend + /// + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")] + public class SimianPresenceServiceConnector : IPresenceService, ISharedRegionModule + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_serverUrl = String.Empty; + + #region ISharedRegionModule + + public Type ReplaceableInterface { get { return null; } } + public void RegionLoaded(Scene scene) { } + public void PostInitialise() { } + public void Close() { } + + public SimianPresenceServiceConnector() { } + public string Name { get { return "SimianPresenceServiceConnector"; } } + public void AddRegion(Scene scene) + { + if (!String.IsNullOrEmpty(m_serverUrl)) + { + scene.RegisterModuleInterface(this); + + scene.EventManager.OnMakeRootAgent += MakeRootAgentHandler; + scene.EventManager.OnNewClient += NewClientHandler; + scene.EventManager.OnSignificantClientMovement += SignificantClientMovementHandler; + + LogoutRegionAgents(scene.RegionInfo.RegionID); + } + } + public void RemoveRegion(Scene scene) + { + if (!String.IsNullOrEmpty(m_serverUrl)) + { + scene.UnregisterModuleInterface(this); + + scene.EventManager.OnMakeRootAgent -= MakeRootAgentHandler; + scene.EventManager.OnNewClient -= NewClientHandler; + scene.EventManager.OnSignificantClientMovement -= SignificantClientMovementHandler; + + LogoutRegionAgents(scene.RegionInfo.RegionID); + } + } + + #endregion ISharedRegionModule + + public SimianPresenceServiceConnector(IConfigSource source) + { + Initialise(source); + } + + public void Initialise(IConfigSource source) + { + if (Simian.IsSimianEnabled(source, "PresenceServices")) + { + IConfig gridConfig = source.Configs["PresenceService"]; + if (gridConfig == null) + { + m_log.Error("[PRESENCE CONNECTOR]: PresenceService missing from OpenSim.ini"); + throw new Exception("Presence connector init error"); + } + + string serviceUrl = gridConfig.GetString("PresenceServerURI"); + if (String.IsNullOrEmpty(serviceUrl)) + { + m_log.Error("[PRESENCE CONNECTOR]: No PresenceServerURI in section PresenceService"); + throw new Exception("Presence connector init error"); + } + + m_serverUrl = serviceUrl; + } + } + + #region IPresenceService + + public bool LoginAgent(string userID, UUID sessionID, UUID secureSessionID) + { + m_log.ErrorFormat("[PRESENCE CONNECTOR]: Login requested, UserID={0}, SessionID={1}, SecureSessionID={2}", + userID, sessionID, secureSessionID); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddSession" }, + { "UserID", userID.ToString() } + }; + if (sessionID != UUID.Zero) + { + requestArgs["SessionID"] = sessionID.ToString(); + requestArgs["SecureSessionID"] = secureSessionID.ToString(); + } + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + m_log.Warn("[PRESENCE CONNECTOR]: Failed to login agent " + userID + ": " + response["Message"].AsString()); + + return success; + } + + public bool LogoutAgent(UUID sessionID, Vector3 position, Vector3 lookAt) + { + m_log.InfoFormat("[PRESENCE CONNECTOR]: Logout requested for agent with sessionID " + sessionID); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "RemoveSession" }, + { "SessionID", sessionID.ToString() } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + m_log.Warn("[PRESENCE CONNECTOR]: Failed to logout agent with sessionID " + sessionID + ": " + response["Message"].AsString()); + + return success; + } + + public bool LogoutRegionAgents(UUID regionID) + { + m_log.InfoFormat("[PRESENCE CONNECTOR]: Logout requested for all agents in region " + regionID); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "RemoveSessions" }, + { "SceneID", regionID.ToString() } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + m_log.Warn("[PRESENCE CONNECTOR]: Failed to logout agents from region " + regionID + ": " + response["Message"].AsString()); + + return success; + } + + public bool ReportAgent(UUID sessionID, UUID regionID, Vector3 position, Vector3 lookAt) + { + //m_log.DebugFormat("[PRESENCE CONNECTOR]: Updating session data for agent with sessionID " + sessionID); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "UpdateSession" }, + { "SessionID", sessionID.ToString() }, + { "SceneID", regionID.ToString() }, + { "ScenePosition", position.ToString() }, + { "SceneLookAt", lookAt.ToString() } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + m_log.Warn("[PRESENCE CONNECTOR]: Failed to update agent session " + sessionID + ": " + response["Message"].AsString()); + + return success; + } + + public PresenceInfo GetAgent(UUID sessionID) + { + m_log.DebugFormat("[PRESENCE CONNECTOR]: Requesting session data for agent with sessionID " + sessionID); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetSession" }, + { "SessionID", sessionID.ToString() } + }; + + OSDMap sessionResponse = WebUtil.PostToService(m_serverUrl, requestArgs); + if (sessionResponse["Success"].AsBoolean()) + { + UUID userID = sessionResponse["UserID"].AsUUID(); + m_log.DebugFormat("[PRESENCE CONNECTOR]: Requesting user data for " + userID); + + requestArgs = new NameValueCollection + { + { "RequestMethod", "GetUser" }, + { "UserID", userID.ToString() } + }; + + OSDMap userResponse = WebUtil.PostToService(m_serverUrl, requestArgs); + if (userResponse["Success"].AsBoolean()) + return ResponseToPresenceInfo(sessionResponse, userResponse); + else + m_log.Warn("[PRESENCE CONNECTOR]: Failed to retrieve user data for " + userID + ": " + userResponse["Message"].AsString()); + } + else + { + m_log.Warn("[PRESENCE CONNECTOR]: Failed to retrieve session " + sessionID + ": " + sessionResponse["Message"].AsString()); + } + + return null; + } + + public PresenceInfo[] GetAgents(string[] userIDs) + { + List presences = new List(userIDs.Length); + + for (int i = 0; i < userIDs.Length; i++) + { + UUID userID; + if (UUID.TryParse(userIDs[i], out userID) && userID != UUID.Zero) + presences.AddRange(GetSessions(userID)); + } + + return presences.ToArray(); + } + + public bool SetHomeLocation(string userID, UUID regionID, Vector3 position, Vector3 lookAt) + { + m_log.DebugFormat("[PRESENCE CONNECTOR]: Setting home location for user " + userID); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddUserData" }, + { "UserID", userID.ToString() }, + { "HomeLocation", SerializeLocation(regionID, position, lookAt) } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + m_log.Warn("[PRESENCE CONNECTOR]: Failed to set home location for " + userID + ": " + response["Message"].AsString()); + + return success; + } + + #endregion IPresenceService + + #region Presence Detection + + private void MakeRootAgentHandler(ScenePresence sp) + { + m_log.DebugFormat("[PRESENCE DETECTOR]: Detected root presence {0} in {1}", sp.UUID, sp.Scene.RegionInfo.RegionName); + + ReportAgent(sp.ControllingClient.SessionId, sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat); + SetLastLocation(sp.UUID, sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat); + } + + private void NewClientHandler(IClientAPI client) + { + client.OnConnectionClosed += LogoutHandler; + } + + private void SignificantClientMovementHandler(IClientAPI client) + { + ScenePresence sp; + if (client.Scene is Scene && ((Scene)client.Scene).TryGetAvatar(client.AgentId, out sp)) + ReportAgent(sp.ControllingClient.SessionId, sp.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat); + } + + private void LogoutHandler(IClientAPI client) + { + if (client.IsLoggingOut) + { + client.OnConnectionClosed -= LogoutHandler; + + object obj; + if (client.Scene.TryGetAvatar(client.AgentId, out obj) && obj is ScenePresence) + { + // The avatar is still in the scene, we can get the exact logout position + ScenePresence sp = (ScenePresence)obj; + SetLastLocation(client.AgentId, client.Scene.RegionInfo.RegionID, sp.AbsolutePosition, sp.Lookat); + } + else + { + // The avatar was already removed from the scene, store LastLocation using the most recent session data + m_log.Warn("[PRESENCE]: " + client.Name + " has already been removed from the scene, storing approximate LastLocation"); + SetLastLocation(client.SessionId); + } + + LogoutAgent(client.SessionId, Vector3.Zero, Vector3.UnitX); + } + } + + #endregion Presence Detection + + #region Helpers + + private OSDMap GetUserData(UUID userID) + { + m_log.DebugFormat("[PRESENCE CONNECTOR]: Requesting user data for " + userID); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetUser" }, + { "UserID", userID.ToString() } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean() && response["User"] is OSDMap) + return response; + else + m_log.Warn("[PRESENCE CONNECTOR]: Failed to retrieve user data for " + userID + ": " + response["Message"].AsString()); + + return null; + } + + private OSDMap GetSessionData(UUID sessionID) + { + m_log.DebugFormat("[PRESENCE CONNECTOR]: Requesting session data for session " + sessionID); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetSession" }, + { "SessionID", sessionID.ToString() } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean()) + return response; + else + m_log.Warn("[PRESENCE CONNECTOR]: Failed to retrieve session data for session " + sessionID); + + return null; + } + + private List GetSessions(UUID userID) + { + List presences = new List(1); + + OSDMap userResponse = GetUserData(userID); + if (userResponse != null) + { + m_log.DebugFormat("[PRESENCE CONNECTOR]: Requesting sessions for " + userID); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetSession" }, + { "UserID", userID.ToString() } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean()) + { + PresenceInfo presence = ResponseToPresenceInfo(response, userResponse); + if (presence != null) + presences.Add(presence); + } + else + { + m_log.Warn("[PRESENCE CONNECTOR]: Failed to retrieve sessions for " + userID + ": " + response["Message"].AsString()); + } + } + + return presences; + } + + /// + /// Fetch the last known avatar location with GetSession and persist it + /// as user data with AddUserData + /// + private bool SetLastLocation(UUID sessionID) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetSession" }, + { "SessionID", sessionID.ToString() } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (success) + { + UUID userID = response["UserID"].AsUUID(); + UUID sceneID = response["SceneID"].AsUUID(); + Vector3 position = response["ScenePosition"].AsVector3(); + Vector3 lookAt = response["SceneLookAt"].AsVector3(); + + return SetLastLocation(userID, sceneID, position, lookAt); + } + else + { + m_log.Warn("[PRESENCE CONNECTOR]: Failed to retrieve presence information for session " + sessionID + + " while saving last location: " + response["Message"].AsString()); + } + + return success; + } + + private bool SetLastLocation(UUID userID, UUID sceneID, Vector3 position, Vector3 lookAt) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddUserData" }, + { "UserID", userID.ToString() }, + { "LastLocation", SerializeLocation(sceneID, position, lookAt) } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + m_log.Warn("[PRESENCE CONNECTOR]: Failed to set last location for " + userID + ": " + response["Message"].AsString()); + + return success; + } + + private PresenceInfo ResponseToPresenceInfo(OSDMap sessionResponse, OSDMap userResponse) + { + if (sessionResponse == null) + return null; + + PresenceInfo info = new PresenceInfo(); + + info.Online = true; + info.UserID = sessionResponse["UserID"].AsUUID().ToString(); + info.RegionID = sessionResponse["SceneID"].AsUUID(); + info.Position = sessionResponse["ScenePosition"].AsVector3(); + info.LookAt = sessionResponse["SceneLookAt"].AsVector3(); + + if (userResponse != null && userResponse["User"] is OSDMap) + { + OSDMap user = (OSDMap)userResponse["User"]; + + info.Login = user["LastLoginDate"].AsDate(); + info.Logout = user["LastLogoutDate"].AsDate(); + DeserializeLocation(user["HomeLocation"].AsString(), out info.HomeRegionID, out info.HomePosition, out info.HomeLookAt); + } + + return info; + } + + private string SerializeLocation(UUID regionID, Vector3 position, Vector3 lookAt) + { + return "{" + String.Format("\"SceneID\":\"{0}\",\"Position\":\"{1}\",\"LookAt\":\"{2}\"", regionID, position, lookAt) + "}"; + } + + private bool DeserializeLocation(string location, out UUID regionID, out Vector3 position, out Vector3 lookAt) + { + OSDMap map = null; + + try { map = OSDParser.DeserializeJson(location) as OSDMap; } + catch { } + + if (map != null) + { + regionID = map["SceneID"].AsUUID(); + if (Vector3.TryParse(map["Position"].AsString(), out position) && + Vector3.TryParse(map["LookAt"].AsString(), out lookAt)) + { + return true; + } + } + + regionID = UUID.Zero; + position = Vector3.Zero; + lookAt = Vector3.Zero; + return false; + } + + #endregion Helpers + } +} diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianProfiles.cs b/OpenSim/Services/Connectors/SimianGrid/SimianProfiles.cs new file mode 100644 index 0000000000..9c226fb5b0 --- /dev/null +++ b/OpenSim/Services/Connectors/SimianGrid/SimianProfiles.cs @@ -0,0 +1,435 @@ +/* + * 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.Collections.Specialized; +using System.Reflection; +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenMetaverse; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; +using OpenSim.Framework.Client; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; + +namespace OpenSim.Services.Connectors.SimianGrid +{ + /// + /// Avatar profile flags + /// + [Flags] + public enum ProfileFlags : uint + { + AllowPublish = 1, + MaturePublish = 2, + Identified = 4, + Transacted = 8, + Online = 16 + } + + /// + /// Connects avatar profile and classified queries to the SimianGrid + /// backend + /// + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")] + public class SimianProfiles : INonSharedRegionModule + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_serverUrl = String.Empty; + + #region INonSharedRegionModule + + public Type ReplaceableInterface { get { return null; } } + public void RegionLoaded(Scene scene) { } + public void Close() { } + + public SimianProfiles() { } + public string Name { get { return "SimianProfiles"; } } + public void AddRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { CheckEstateManager(scene); scene.EventManager.OnClientConnect += ClientConnectHandler; } } + public void RemoveRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.EventManager.OnClientConnect -= ClientConnectHandler; } } + + #endregion INonSharedRegionModule + + public SimianProfiles(IConfigSource source) + { + Initialise(source); + } + + public void Initialise(IConfigSource source) + { + if (Simian.IsSimianEnabled(source, "UserAccountServices")) + { + IConfig gridConfig = source.Configs["UserAccountService"]; + if (gridConfig == null) + { + m_log.Error("[PROFILES]: UserAccountService missing from OpenSim.ini"); + throw new Exception("Profiles init error"); + } + + string serviceUrl = gridConfig.GetString("UserAccountServerURI"); + if (String.IsNullOrEmpty(serviceUrl)) + { + m_log.Error("[PROFILES]: No UserAccountServerURI in section UserAccountService"); + throw new Exception("Profiles init error"); + } + + if (!serviceUrl.EndsWith("/")) + serviceUrl = serviceUrl + '/'; + + m_serverUrl = serviceUrl; + } + } + + private void ClientConnectHandler(IClientCore clientCore) + { + if (clientCore is IClientAPI) + { + IClientAPI client = (IClientAPI)clientCore; + + // Classifieds + client.AddGenericPacketHandler("avatarclassifiedsrequest", AvatarClassifiedsRequestHandler); + client.OnClassifiedInfoRequest += ClassifiedInfoRequestHandler; + client.OnClassifiedInfoUpdate += ClassifiedInfoUpdateHandler; + client.OnClassifiedDelete += ClassifiedDeleteHandler; + + // Picks + client.AddGenericPacketHandler("avatarpicksrequest", HandleAvatarPicksRequest); + client.AddGenericPacketHandler("pickinforequest", HandlePickInfoRequest); + client.OnPickInfoUpdate += PickInfoUpdateHandler; + client.OnPickDelete += PickDeleteHandler; + + // Notes + client.AddGenericPacketHandler("avatarnotesrequest", HandleAvatarNotesRequest); + client.OnAvatarNotesUpdate += AvatarNotesUpdateHandler; + + // Profiles + client.OnRequestAvatarProperties += RequestAvatarPropertiesHandler; + client.OnUpdateAvatarProperties += UpdateAvatarPropertiesHandler; + client.OnAvatarInterestUpdate += AvatarInterestUpdateHandler; + client.OnUserInfoRequest += UserInfoRequestHandler; + client.OnUpdateUserInfo += UpdateUserInfoHandler; + } + } + + #region Classifieds + + private void AvatarClassifiedsRequestHandler(Object sender, string method, List args) + { + if (!(sender is IClientAPI)) + return; + IClientAPI client = (IClientAPI)sender; + + UUID targetAvatarID; + if (args.Count < 1 || !UUID.TryParse(args[0], out targetAvatarID)) + { + m_log.Error("[PROFILES]: Unrecognized arguments for " + method); + return; + } + + // FIXME: Query the generic key/value store for classifieds + client.SendAvatarClassifiedReply(targetAvatarID, new Dictionary(0)); + } + + private void ClassifiedInfoRequestHandler(UUID classifiedID, IClientAPI client) + { + // FIXME: Fetch this info + client.SendClassifiedInfoReply(classifiedID, UUID.Zero, 0, Utils.DateTimeToUnixTime(DateTime.UtcNow + TimeSpan.FromDays(1)), + 0, String.Empty, String.Empty, UUID.Zero, 0, UUID.Zero, String.Empty, Vector3.Zero, String.Empty, 0, 0); + } + + private void ClassifiedInfoUpdateHandler(UUID classifiedID, uint category, string name, string description, + UUID parcelID, uint parentEstate, UUID snapshotID, Vector3 globalPos, byte classifiedFlags, int price, + IClientAPI client) + { + // FIXME: Save this info + } + + private void ClassifiedDeleteHandler(UUID classifiedID, IClientAPI client) + { + // FIXME: Delete the specified classified ad + } + + #endregion Classifieds + + #region Picks + + private void HandleAvatarPicksRequest(Object sender, string method, List args) + { + if (!(sender is IClientAPI)) + return; + IClientAPI client = (IClientAPI)sender; + + UUID targetAvatarID; + if (args.Count < 1 || !UUID.TryParse(args[0], out targetAvatarID)) + { + m_log.Error("[PROFILES]: Unrecognized arguments for " + method); + return; + } + + // FIXME: Fetch these + client.SendAvatarPicksReply(targetAvatarID, new Dictionary(0)); + } + + private void HandlePickInfoRequest(Object sender, string method, List args) + { + if (!(sender is IClientAPI)) + return; + IClientAPI client = (IClientAPI)sender; + + UUID avatarID; + UUID pickID; + if (args.Count < 2 || !UUID.TryParse(args[0], out avatarID) || !UUID.TryParse(args[1], out pickID)) + { + m_log.Error("[PROFILES]: Unrecognized arguments for " + method); + return; + } + + // FIXME: Fetch this + client.SendPickInfoReply(pickID, avatarID, false, UUID.Zero, String.Empty, String.Empty, UUID.Zero, String.Empty, + String.Empty, String.Empty, Vector3.Zero, 0, false); + } + + private void PickInfoUpdateHandler(IClientAPI client, UUID pickID, UUID creatorID, bool topPick, string name, + string desc, UUID snapshotID, int sortOrder, bool enabled) + { + // FIXME: Save this + } + + private void PickDeleteHandler(IClientAPI client, UUID pickID) + { + // FIXME: Delete + } + + #endregion Picks + + #region Notes + + private void HandleAvatarNotesRequest(Object sender, string method, List args) + { + if (!(sender is IClientAPI)) + return; + IClientAPI client = (IClientAPI)sender; + + UUID targetAvatarID; + if (args.Count < 1 || !UUID.TryParse(args[0], out targetAvatarID)) + { + m_log.Error("[PROFILES]: Unrecognized arguments for " + method); + return; + } + + // FIXME: Fetch this + client.SendAvatarNotesReply(targetAvatarID, String.Empty); + } + + private void AvatarNotesUpdateHandler(IClientAPI client, UUID targetID, string notes) + { + // FIXME: Save this + } + + #endregion Notes + + #region Profiles + + private void RequestAvatarPropertiesHandler(IClientAPI client, UUID avatarID) + { + OSDMap user = FetchUserData(avatarID); + + ProfileFlags flags = ProfileFlags.AllowPublish | ProfileFlags.MaturePublish; + + if (user != null) + { + OSDMap about = null; + if (user.ContainsKey("LLAbout")) + { + try { about = OSDParser.DeserializeJson(user["LLAbout"].AsString()) as OSDMap; } + catch { } + } + + if (about == null) + about = new OSDMap(0); + + // Check if this user is a grid operator + byte[] charterMember; + if (user["AccessLevel"].AsInteger() >= 200) + charterMember = Utils.StringToBytes("Operator"); + else + charterMember = Utils.EmptyBytes; + + // Check if the user is online + if (client.Scene is Scene) + { + OpenSim.Services.Interfaces.PresenceInfo[] presences = ((Scene)client.Scene).PresenceService.GetAgents(new string[] { avatarID.ToString() }); + if (presences != null && presences.Length > 0) + flags |= ProfileFlags.Online; + } + + // Check if the user is identified + if (user["Identified"].AsBoolean()) + flags |= ProfileFlags.Identified; + + client.SendAvatarProperties(avatarID, about["About"].AsString(), user["CreationDate"].AsDate().ToString("M/d/yyyy", + System.Globalization.CultureInfo.InvariantCulture), charterMember, about["FLAbout"].AsString(), (uint)flags, + about["FLImage"].AsUUID(), about["Image"].AsUUID(), about["URL"].AsString(), user["Partner"].AsUUID()); + + } + else + { + m_log.Warn("[PROFILES]: Failed to fetch profile information for " + client.Name + ", returning default values"); + client.SendAvatarProperties(avatarID, String.Empty, "1/1/1970", Utils.EmptyBytes, + String.Empty, (uint)flags, UUID.Zero, UUID.Zero, String.Empty, UUID.Zero); + } + } + + private void UpdateAvatarPropertiesHandler(IClientAPI client, UserProfileData profileData) + { + OSDMap map = new OSDMap + { + { "About", OSD.FromString(profileData.AboutText) }, + { "Image", OSD.FromUUID(profileData.Image) }, + { "FLAbout", OSD.FromString(profileData.FirstLifeAboutText) }, + { "FLImage", OSD.FromUUID(profileData.FirstLifeImage) }, + { "URL", OSD.FromString(profileData.ProfileUrl) } + }; + + AddUserData(client.AgentId, "LLAbout", map); + } + + private void AvatarInterestUpdateHandler(IClientAPI client, uint wantmask, string wanttext, uint skillsmask, + string skillstext, string languages) + { + OSDMap map = new OSDMap + { + { "WantMask", OSD.FromInteger(wantmask) }, + { "WantText", OSD.FromString(wanttext) }, + { "SkillsMask", OSD.FromInteger(skillsmask) }, + { "SkillsText", OSD.FromString(skillstext) }, + { "Languages", OSD.FromString(languages) } + }; + + AddUserData(client.AgentId, "LLInterests", map); + } + + private void UserInfoRequestHandler(IClientAPI client) + { + m_log.Error("[PROFILES]: UserInfoRequestHandler"); + + // Fetch this user's e-mail address + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetUser" }, + { "UserID", client.AgentId.ToString() } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + string email = response["Email"].AsString(); + + if (!response["Success"].AsBoolean()) + m_log.Warn("[PROFILES]: GetUser failed during a user info request for " + client.Name); + + client.SendUserInfoReply(false, true, email); + } + + private void UpdateUserInfoHandler(bool imViaEmail, bool visible, IClientAPI client) + { + m_log.Info("[PROFILES]: Ignoring user info update from " + client.Name); + } + + #endregion Profiles + + /// + /// Sanity checks regions for a valid estate owner at startup + /// + private void CheckEstateManager(Scene scene) + { + EstateSettings estate = scene.RegionInfo.EstateSettings; + + if (estate.EstateOwner == UUID.Zero) + { + // Attempt to lookup the grid admin + UserAccount admin = scene.UserAccountService.GetUserAccount(scene.RegionInfo.ScopeID, UUID.Zero); + if (admin != null) + { + m_log.InfoFormat("[PROFILES]: Setting estate {0} (ID: {1}) owner to {2}", estate.EstateName, + estate.EstateID, admin.Name); + + estate.EstateOwner = admin.PrincipalID; + estate.Save(); + } + else + { + m_log.WarnFormat("[PROFILES]: Estate {0} (ID: {1}) does not have an owner", estate.EstateName, estate.EstateID); + } + } + } + + private bool AddUserData(UUID userID, string key, OSDMap value) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddUserData" }, + { "UserID", userID.ToString() }, + { key, OSDParser.SerializeJsonString(value) } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (!success) + m_log.WarnFormat("[PROFILES]: Failed to add user data with key {0} for {1}: {2}", key, userID, response["Message"].AsString()); + + return success; + } + + private OSDMap FetchUserData(UUID userID) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetUser" }, + { "UserID", userID.ToString() } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean() && response["User"] is OSDMap) + { + return (OSDMap)response["User"]; + } + else + { + m_log.Error("[PROFILES]: Failed to fetch user data for " + userID + ": " + response["Message"].AsString()); + } + + return null; + } + } +} diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianUserAccountServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianUserAccountServiceConnector.cs new file mode 100644 index 0000000000..bb0ac57b98 --- /dev/null +++ b/OpenSim/Services/Connectors/SimianGrid/SimianUserAccountServiceConnector.cs @@ -0,0 +1,311 @@ +/* + * 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.Collections.Specialized; +using System.IO; +using System.Reflection; +using OpenSim.Framework; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; +using log4net; +using Mono.Addins; +using Nini.Config; +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +namespace OpenSim.Services.Connectors.SimianGrid +{ + /// + /// Connects user account data (creating new users, looking up existing + /// users) to the SimianGrid backend + /// + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")] + public class SimianUserAccountServiceConnector : IUserAccountService, ISharedRegionModule + { + private static readonly ILog m_log = + LogManager.GetLogger( + MethodBase.GetCurrentMethod().DeclaringType); + + private string m_serverUrl = String.Empty; + private ExpiringCache m_accountCache; + + #region ISharedRegionModule + + public Type ReplaceableInterface { get { return null; } } + public void RegionLoaded(Scene scene) { } + public void PostInitialise() { } + public void Close() { } + + public SimianUserAccountServiceConnector() { } + public string Name { get { return "SimianUserAccountServiceConnector"; } } + public void AddRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.RegisterModuleInterface(this); } } + public void RemoveRegion(Scene scene) { if (!String.IsNullOrEmpty(m_serverUrl)) { scene.UnregisterModuleInterface(this); } } + + #endregion ISharedRegionModule + + public SimianUserAccountServiceConnector(IConfigSource source) + { + Initialise(source); + } + + public void Initialise(IConfigSource source) + { + if (Simian.IsSimianEnabled(source, "UserAccountServices")) + { + IConfig assetConfig = source.Configs["UserAccountService"]; + if (assetConfig == null) + { + m_log.Error("[ACCOUNT CONNECTOR]: UserAccountService missing from OpenSim.ini"); + throw new Exception("User account connector init error"); + } + + string serviceURI = assetConfig.GetString("UserAccountServerURI"); + if (String.IsNullOrEmpty(serviceURI)) + { + m_log.Error("[ACCOUNT CONNECTOR]: No UserAccountServerURI in section UserAccountService, skipping SimianUserAccountServiceConnector"); + throw new Exception("User account connector init error"); + } + + m_accountCache = new ExpiringCache(); + m_serverUrl = serviceURI; + } + } + + public UserAccount GetUserAccount(UUID scopeID, string firstName, string lastName) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetUser" }, + { "Name", firstName + ' ' + lastName } + }; + + return GetUser(requestArgs); + } + + public UserAccount GetUserAccount(UUID scopeID, string email) + { + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetUser" }, + { "Email", email } + }; + + return GetUser(requestArgs); + } + + public UserAccount GetUserAccount(UUID scopeID, UUID userID) + { + // Cache check + UserAccount account; + if (m_accountCache.TryGetValue(userID, out account)) + return account; + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetUser" }, + { "UserID", userID.ToString() } + }; + + return GetUser(requestArgs); + } + + public List GetUserAccounts(UUID scopeID, string query) + { + List accounts = new List(); + + m_log.DebugFormat("[ACCOUNT CONNECTOR]: Searching for user accounts with name query " + query); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "GetUsers" }, + { "NameQuery", query } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean()) + { + OSDArray array = response["Users"] as OSDArray; + if (array != null && array.Count > 0) + { + for (int i = 0; i < array.Count; i++) + { + UserAccount account = ResponseToUserAccount(array[i] as OSDMap); + if (account != null) + accounts.Add(account); + } + } + else + { + m_log.Warn("[ACCOUNT CONNECTOR]: Account search failed, response data was in an invalid format"); + } + } + else + { + m_log.Warn("[ACCOUNT CONNECTOR]: Failed to search for account data by name " + query); + } + + return accounts; + } + + public bool StoreUserAccount(UserAccount data) + { + m_log.InfoFormat("[ACCOUNT CONNECTOR]: Storing user account for " + data.Name); + + NameValueCollection requestArgs = new NameValueCollection + { + { "RequestMethod", "AddUser" }, + { "UserID", data.PrincipalID.ToString() }, + { "Name", data.Name }, + { "Email", data.Email }, + { "AccessLevel", data.UserLevel.ToString() } + }; + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + + if (response["Success"].AsBoolean()) + { + m_log.InfoFormat("[ACCOUNT CONNECTOR]: Storing user account data for " + data.Name); + + requestArgs = new NameValueCollection + { + { "RequestMethod", "AddUserData" }, + { "UserID", data.PrincipalID.ToString() }, + { "CreationDate", data.Created.ToString() }, + { "UserFlags", data.UserFlags.ToString() }, + { "UserTitle", data.UserTitle } + }; + + response = WebUtil.PostToService(m_serverUrl, requestArgs); + bool success = response["Success"].AsBoolean(); + + if (success) + { + // Cache the user account info + m_accountCache.AddOrUpdate(data.PrincipalID, data, DateTime.Now + TimeSpan.FromMinutes(2.0d)); + } + else + { + m_log.Warn("[ACCOUNT CONNECTOR]: Failed to store user account data for " + data.Name + ": " + response["Message"].AsString()); + } + + return success; + } + else + { + m_log.Warn("[ACCOUNT CONNECTOR]: Failed to store user account for " + data.Name + ": " + response["Message"].AsString()); + } + + return false; + } + + /// + /// Helper method for the various ways of retrieving a user account + /// + /// Service query parameters + /// A UserAccount object on success, null on failure + private UserAccount GetUser(NameValueCollection requestArgs) + { + string lookupValue = (requestArgs.Count > 1) ? requestArgs[1] : "(Unknown)"; + m_log.DebugFormat("[ACCOUNT CONNECTOR]: Looking up user account with query: " + lookupValue); + + OSDMap response = WebUtil.PostToService(m_serverUrl, requestArgs); + if (response["Success"].AsBoolean()) + { + OSDMap user = response["User"] as OSDMap; + if (user != null) + return ResponseToUserAccount(user); + else + m_log.Warn("[ACCOUNT CONNECTOR]: Account search failed, response data was in an invalid format"); + } + else + { + m_log.Warn("[ACCOUNT CONNECTOR]: Failed to lookup user account with query: " + lookupValue); + } + + return null; + } + + /// + /// Convert a User object in LLSD format to a UserAccount + /// + /// LLSD containing user account data + /// A UserAccount object on success, null on failure + private UserAccount ResponseToUserAccount(OSDMap response) + { + if (response == null) + return null; + + UserAccount account = new UserAccount(); + account.PrincipalID = response["UserID"].AsUUID(); + account.Created = response["CreationDate"].AsInteger(); + account.Email = response["Email"].AsString(); + account.ServiceURLs = new Dictionary(0); + account.UserFlags = response["UserFlags"].AsInteger(); + account.UserLevel = response["AccessLevel"].AsInteger(); + account.UserTitle = response["UserTitle"].AsString(); + GetFirstLastName(response["Name"].AsString(), out account.FirstName, out account.LastName); + + // Cache the user account info + m_accountCache.AddOrUpdate(account.PrincipalID, account, DateTime.Now + TimeSpan.FromMinutes(2.0d)); + + return account; + } + + /// + /// Convert a name with a single space in it to a first and last name + /// + /// A full name such as "John Doe" + /// First name + /// Last name (surname) + private static void GetFirstLastName(string name, out string firstName, out string lastName) + { + if (String.IsNullOrEmpty(name)) + { + firstName = String.Empty; + lastName = String.Empty; + } + else + { + string[] names = name.Split(' '); + + if (names.Length == 2) + { + firstName = names[0]; + lastName = names[1]; + } + else + { + firstName = String.Empty; + lastName = name; + } + } + } + } +} diff --git a/OpenSim/Services/Connectors/UserAccounts/UserAccountServiceConnector.cs b/OpenSim/Services/Connectors/UserAccounts/UserAccountServiceConnector.cs index 8e7c92b9ef..2f9b520de9 100644 --- a/OpenSim/Services/Connectors/UserAccounts/UserAccountServiceConnector.cs +++ b/OpenSim/Services/Connectors/UserAccounts/UserAccountServiceConnector.cs @@ -113,6 +113,7 @@ namespace OpenSim.Services.Connectors public virtual UserAccount GetUserAccount(UUID scopeID, UUID userID) { + m_log.DebugFormat("[ACCOUNTS CONNECTOR]: GetUSerAccount {0}", userID); Dictionary sendData = new Dictionary(); //sendData["SCOPEID"] = scopeID.ToString(); sendData["VERSIONMIN"] = ProtocolVersions.ClientProtocolVersionMin.ToString(); diff --git a/OpenSim/Services/GridService/GridService.cs b/OpenSim/Services/GridService/GridService.cs index 1368e46623..2faf018e72 100644 --- a/OpenSim/Services/GridService/GridService.cs +++ b/OpenSim/Services/GridService/GridService.cs @@ -123,8 +123,7 @@ namespace OpenSim.Services.GridService if ((rflags & OpenSim.Data.RegionFlags.Reservation) != 0) { // Regions reserved for the null key cannot be taken. - // - if (region.Data["PrincipalID"] == UUID.Zero.ToString()) + if ((string)region.Data["PrincipalID"] == UUID.Zero.ToString()) return "Region location us reserved"; // Treat it as an auth request @@ -132,7 +131,6 @@ namespace OpenSim.Services.GridService // NOTE: Fudging the flags value here, so these flags // should not be used elsewhere. Don't optimize // this with the later retrieval of the same flags! - // rflags |= OpenSim.Data.RegionFlags.Authenticate; } @@ -489,7 +487,7 @@ namespace OpenSim.Services.GridService f |= (OpenSim.Data.RegionFlags)val; } } - catch (Exception e) + catch (Exception) { MainConsole.Instance.Output("Error in flag specification: " + p); } diff --git a/OpenSim/Services/GridService/HypergridLinker.cs b/OpenSim/Services/GridService/HypergridLinker.cs index de5df9d645..58746d0a1b 100644 --- a/OpenSim/Services/GridService/HypergridLinker.cs +++ b/OpenSim/Services/GridService/HypergridLinker.cs @@ -168,10 +168,11 @@ namespace OpenSim.Services.GridService } // Sanity check. - IPAddress ipaddr = null; + //IPAddress ipaddr = null; try { - ipaddr = Util.GetHostFromDNS(host); + //ipaddr = Util.GetHostFromDNS(host); + Util.GetHostFromDNS(host); } catch { diff --git a/OpenSim/Services/HypergridService/UserAgentService.cs b/OpenSim/Services/HypergridService/UserAgentService.cs index 15379b5b3e..26f211b5d9 100644 --- a/OpenSim/Services/HypergridService/UserAgentService.cs +++ b/OpenSim/Services/HypergridService/UserAgentService.cs @@ -1,4 +1,31 @@ -using System; +/* + * 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.Net; using System.Reflection; diff --git a/OpenSim/Services/Interfaces/IUserAccountService.cs b/OpenSim/Services/Interfaces/IUserAccountService.cs index 3dacf53453..a45bf8c79b 100644 --- a/OpenSim/Services/Interfaces/IUserAccountService.cs +++ b/OpenSim/Services/Interfaces/IUserAccountService.cs @@ -140,14 +140,20 @@ namespace OpenSim.Services.Interfaces UserAccount GetUserAccount(UUID scopeID, UUID userID); UserAccount GetUserAccount(UUID scopeID, string FirstName, string LastName); UserAccount GetUserAccount(UUID scopeID, string Email); - // Returns the list of avatars that matches both the search - // criterion and the scope ID passed - // + + /// + /// Returns the list of avatars that matches both the search criterion and the scope ID passed + /// + /// + /// + /// List GetUserAccounts(UUID scopeID, string query); - // Store the data given, wich replaces the sotred data, therefore - // must be complete. - // + /// + /// Store the data given, wich replaces the sotred data, therefore must be complete. + /// + /// + /// bool StoreUserAccount(UserAccount data); } -} +} \ No newline at end of file diff --git a/OpenSim/Services/LLLoginService/LLLoginResponse.cs b/OpenSim/Services/LLLoginService/LLLoginResponse.cs index 05f5b4cba3..ee30fa38ef 100644 --- a/OpenSim/Services/LLLoginService/LLLoginResponse.cs +++ b/OpenSim/Services/LLLoginService/LLLoginResponse.cs @@ -47,9 +47,9 @@ namespace OpenSim.Services.LLLoginService { public class LLFailedLoginResponse : OpenSim.Services.Interfaces.FailedLoginResponse { - string m_key; - string m_value; - string m_login; + protected string m_key; + protected string m_value; + protected string m_login; public static LLFailedLoginResponse UserProblem; public static LLFailedLoginResponse AuthorizationProblem; @@ -623,7 +623,7 @@ namespace OpenSim.Services.LLLoginService } private InventoryData GetInventorySkeleton(List folders) - { + { UUID rootID = UUID.Zero; ArrayList AgentInventoryArray = new ArrayList(); Hashtable TempHash; diff --git a/OpenSim/Services/LLLoginService/LLLoginService.cs b/OpenSim/Services/LLLoginService/LLLoginService.cs index ae729f8ca4..1eaf4d4158 100644 --- a/OpenSim/Services/LLLoginService/LLLoginService.cs +++ b/OpenSim/Services/LLLoginService/LLLoginService.cs @@ -1,4 +1,31 @@ -using System; +/* + * 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.Net; using System.Reflection; @@ -24,24 +51,24 @@ namespace OpenSim.Services.LLLoginService private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static bool Initialized = false; - private IUserAccountService m_UserAccountService; - private IAuthenticationService m_AuthenticationService; - private IInventoryService m_InventoryService; - private IGridService m_GridService; - private IPresenceService m_PresenceService; + protected IUserAccountService m_UserAccountService; + protected IAuthenticationService m_AuthenticationService; + protected IInventoryService m_InventoryService; + protected IGridService m_GridService; + protected IPresenceService m_PresenceService; private ISimulationService m_LocalSimulationService; private ISimulationService m_RemoteSimulationService; - private ILibraryService m_LibraryService; - private IFriendsService m_FriendsService; - private IAvatarService m_AvatarService; + protected ILibraryService m_LibraryService; + protected IFriendsService m_FriendsService; + protected IAvatarService m_AvatarService; private IUserAgentService m_UserAgentService; private GatekeeperServiceConnector m_GatekeeperConnector; private string m_DefaultRegionName; - private string m_WelcomeMessage; + protected string m_WelcomeMessage; private bool m_RequireInventory; - private int m_MinLoginLevel; + protected int m_MinLoginLevel; private string m_GatekeeperURL; IConfig m_LoginServerConfig; @@ -259,7 +286,7 @@ namespace OpenSim.Services.LLLoginService } } - private GridRegion FindDestination(UserAccount account, PresenceInfo pinfo, UUID sessionID, string startLocation, out GridRegion gatekeeper, out string where, out Vector3 position, out Vector3 lookAt) + protected GridRegion FindDestination(UserAccount account, PresenceInfo pinfo, UUID sessionID, string startLocation, out GridRegion gatekeeper, out string where, out Vector3 position, out Vector3 lookAt) { m_log.DebugFormat("[LLOGIN SERVICE]: FindDestination for start location {0}", startLocation); @@ -470,7 +497,7 @@ namespace OpenSim.Services.LLLoginService } } - private AgentCircuitData LaunchAgentAtGrid(GridRegion gatekeeper, GridRegion destination, UserAccount account, AvatarData avatar, + protected AgentCircuitData LaunchAgentAtGrid(GridRegion gatekeeper, GridRegion destination, UserAccount account, AvatarData avatar, UUID session, UUID secureSession, Vector3 position, string currentWhere, out string where, out string reason) { where = currentWhere; diff --git a/OpenSim/Services/PresenceService/PresenceService.cs b/OpenSim/Services/PresenceService/PresenceService.cs index 304538ab65..ea8d673c1f 100644 --- a/OpenSim/Services/PresenceService/PresenceService.cs +++ b/OpenSim/Services/PresenceService/PresenceService.cs @@ -72,6 +72,10 @@ namespace OpenSim.Services.PresenceService data.Data["HomeRegionID"] = d[0].Data["HomeRegionID"]; data.Data["HomePosition"] = d[0].Data["HomePosition"]; data.Data["HomeLookAt"] = d[0].Data["HomeLookAt"]; + data.Data["Position"] = d[0].Data["Position"]; + data.Data["LookAt"] = d[0].Data["LookAt"]; + + data.RegionID = d[0].RegionID; } else { diff --git a/OpenSim/Services/UserAccountService/GridUserService.cs b/OpenSim/Services/UserAccountService/GridUserService.cs index 36cce758ce..c6e33bbbfe 100644 --- a/OpenSim/Services/UserAccountService/GridUserService.cs +++ b/OpenSim/Services/UserAccountService/GridUserService.cs @@ -58,7 +58,7 @@ namespace OpenSim.Services.UserAccountService info.HomePosition = Vector3.Parse(d.Data["HomePosition"]); info.HomeLookAt = Vector3.Parse(d.Data["HomeLookAt"]); - return info; + return info; } public bool StoreGridUserInfo(GridUserInfo info) diff --git a/OpenSim/Services/UserAccountService/UserAccountService.cs b/OpenSim/Services/UserAccountService/UserAccountService.cs index 82c34e7bfa..38caf745fe 100644 --- a/OpenSim/Services/UserAccountService/UserAccountService.cs +++ b/OpenSim/Services/UserAccountService/UserAccountService.cs @@ -200,7 +200,9 @@ namespace OpenSim.Services.UserAccountService } if (d.Length < 1) + { return null; + } return MakeUserAccount(d[0]); } diff --git a/OpenSim/Tests/Common/Mock/MockUserAccountService.cs b/OpenSim/Tests/Common/Mock/MockUserAccountService.cs new file mode 100644 index 0000000000..0769c7aa16 --- /dev/null +++ b/OpenSim/Tests/Common/Mock/MockUserAccountService.cs @@ -0,0 +1,46 @@ +/* + * 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.Collections.Generic; +using Nini.Config; +using OpenMetaverse; +using OpenSim.Services.Interfaces; + +namespace OpenSim.Tests.Common.Mock +{ + public class MockUserAccountService : IUserAccountService + { + + public MockUserAccountService(IConfigSource config) {} + + public UserAccount GetUserAccount(UUID scopeID, UUID userID) { return new UserAccount(); } + public UserAccount GetUserAccount(UUID scopeID, string FirstName, string LastName) { return new UserAccount(); } + public UserAccount GetUserAccount(UUID scopeID, string Email) { return new UserAccount(); } + public List GetUserAccounts(UUID scopeID, string query) { return new List(); } + public bool StoreUserAccount(UserAccount data) { return true; } + } +} \ No newline at end of file diff --git a/OpenSim/Tests/Common/Mock/TestScene.cs b/OpenSim/Tests/Common/Mock/TestScene.cs index 076cb7a36a..01f2c146e8 100644 --- a/OpenSim/Tests/Common/Mock/TestScene.cs +++ b/OpenSim/Tests/Common/Mock/TestScene.cs @@ -65,6 +65,6 @@ namespace OpenSim.Tests.Common.Mock public AsyncSceneObjectGroupDeleter SceneObjectGroupDeleter { get { return m_asyncSceneObjectDeleter; } - } + } } -} +} \ No newline at end of file diff --git a/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs b/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs index 9e718f6a1c..864e2aa7c3 100644 --- a/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs +++ b/OpenSim/Tests/Common/Setup/SceneSetupHelpers.cs @@ -128,7 +128,7 @@ namespace OpenSim.Tests.Common.Setup /// Starts real inventory and asset services, as opposed to mock ones, if true /// public static TestScene SetupScene( - string name, UUID id, uint x, uint y, String realServices) + string name, UUID id, uint x, uint y, String realServices) { bool newScene = false; @@ -179,15 +179,16 @@ namespace OpenSim.Tests.Common.Setup StartAssetService(testScene, true); else StartAssetService(testScene, false); + if (realServices.Contains("inventory")) StartInventoryService(testScene, true); else StartInventoryService(testScene, false); + if (realServices.Contains("grid")) StartGridService(testScene, true); - if (realServices.Contains("useraccounts")) - StartUserAccountService(testScene, true); - + + StartUserAccountService(testScene, realServices.Contains("useraccounts")); } // If not, make sure the shared module gets references to this new scene else @@ -196,9 +197,13 @@ namespace OpenSim.Tests.Common.Setup m_assetService.RegionLoaded(testScene); m_inventoryService.AddRegion(testScene); m_inventoryService.RegionLoaded(testScene); + m_userAccountService.AddRegion(testScene); + m_userAccountService.RegionLoaded(testScene); } + m_inventoryService.PostInitialise(); m_assetService.PostInitialise(); + m_userAccountService.PostInitialise(); testScene.SetModuleInterfaces(); @@ -209,6 +214,14 @@ namespace OpenSim.Tests.Common.Setup physicsPluginManager.LoadPluginsFromAssembly("Physics/OpenSim.Region.Physics.BasicPhysicsPlugin.dll"); testScene.PhysicsScene = physicsPluginManager.GetPhysicsScene("basicphysics", "ZeroMesher", new IniConfigSource(), "test"); + + // It's really not a good idea to use static variables as they carry over between tests, leading to + // problems that are extremely hard to debug. Really, these static fields need to be eliminated - + // tests using multiple regions that need to share modules need to find another solution. + m_assetService = null; + m_inventoryService = null; + m_gridService = null; + m_userAccountService = null; return testScene; } @@ -273,6 +286,11 @@ namespace OpenSim.Tests.Common.Setup //testScene.AddRegionModule(m_gridService.Name, m_gridService); } + /// + /// Start a user account service, whether real or mock + /// + /// + /// Starts a real service if true, a mock service if not private static void StartUserAccountService(Scene testScene, bool real) { IConfigSource config = new IniConfigSource(); @@ -280,8 +298,14 @@ namespace OpenSim.Tests.Common.Setup config.AddConfig("UserAccountService"); config.Configs["Modules"].Set("UserAccountServices", "LocalUserAccountServicesConnector"); config.Configs["UserAccountService"].Set("StorageProvider", "OpenSim.Data.Null.dll"); + if (real) - config.Configs["UserAccountService"].Set("LocalServiceModule", "OpenSim.Services.UserAccountService.dll:UserAccountService"); + config.Configs["UserAccountService"].Set( + "LocalServiceModule", "OpenSim.Services.UserAccountService.dll:UserAccountService"); + else + config.Configs["UserAccountService"].Set( + "LocalServiceModule", "OpenSim.Tests.Common.dll:MockUserAccountService"); + if (m_userAccountService == null) { ISharedRegionModule userAccountService = new LocalUserAccountServicesConnector(); @@ -292,10 +316,9 @@ namespace OpenSim.Tests.Common.Setup // config.Configs["GridService"].Set("LocalServiceModule", "OpenSim.Tests.Common.dll:TestGridService"); m_userAccountService.AddRegion(testScene); m_userAccountService.RegionLoaded(testScene); - //testScene.AddRegionModule(m_gridService.Name, m_gridService); + testScene.AddRegionModule(m_userAccountService.Name, m_userAccountService); } - /// /// Setup modules for a scene using their default settings. /// diff --git a/bin/BulletDotNET.dll b/bin/BulletDotNET.dll index 8abe03a849..40c4348472 100644 Binary files a/bin/BulletDotNET.dll and b/bin/BulletDotNET.dll differ diff --git a/bin/Newtonsoft.Json.XML b/bin/Newtonsoft.Json.XML index b84e336fd6..1a1e56cef3 100644 --- a/bin/Newtonsoft.Json.XML +++ b/bin/Newtonsoft.Json.XML @@ -1,747 +1,229 @@ - Newtonsoft.Json + Newtonsoft.Json.Net20 - + - Determines whether the string contains white space. - - The string to test for white space. - - true if the string contains white space; otherwise, false. - - - - - Determines whether the string is all white space. Empty string will return false. - - The string to test whether it is all white space. - - true if the string is all white space; otherwise, false. - - - - - Ensures the target string ends with the specified string. - - The target. - The value. - The target string with the value string at the end. - - - - Determines whether the SqlString is null or empty. - - The string. - - true if the SqlString is null or empty; otherwise, false. - - - - - Perform an action if the string is not null or empty. - - The value. - The action to perform. - - - - Indents the specified string. - - The string to indent. - The number of characters to indent by. - - - - - Indents the specified string. - - The string to indent. - The number of characters to indent by. - The indent character. - - - - - Numbers the lines. - - The string to number. - - - - - Nulls an empty string. - - The string. - Null if the string was null, otherwise the string unchanged. - - - - The exception thrown when an error occurs while reading Json text. + Represents a reader that provides fast, non-cached, forward-only access to serialized Json data. - + - Initializes a new instance of the class. + Represents a reader that provides fast, non-cached, forward-only access to serialized Json data. - + - Initializes a new instance of the class - with a specified error message. + Initializes a new instance of the class with the specified . - The error message that explains the reason for the exception. - + - Initializes a new instance of the class - with a specified error message and a reference to the inner exception that is the cause of this exception. + Reads the next JSON token from the stream. - The error message that explains the reason for the exception. - The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. + true if the next token was read successfully; false if there are no more tokens to read. - + - Gets the type of the typed list's items. + Reads the next JSON token from the stream as a . - The type. - The type of the typed list's items. + A or a null reference if the next JSON token is null. - + - Tests whether the list's items are their unitialized value. + Skips the children of the current token. - The list. - Whether the list's items are their unitialized value - + - Gets the member's underlying type. + Sets the current token. - The member. - The underlying type of the member. + The new token. - + - Determines whether the member is an indexed property. + Sets the current token and value. - The member. - - true if the member is an indexed property; otherwise, false. - - - - - Determines whether the property is an indexed property. - - The property. - - true if the property is an indexed property; otherwise, false. - - - - - Gets the member's value on the object. - - The member. - The target object. - The member's value on the object. - - - - Sets the member's value on the target object. - - The member. - The target. + The new token. The value. - + - Determines whether the specified MemberInfo can be read. + Sets the state based on current token type. + + + + + Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + + + + + Releases unmanaged and - optionally - managed resources + + true to release both managed and unmanaged resources; false to release only unmanaged resources. + + + + Changes the to Closed. + + + + + Gets the current reader state. + + The current reader state. + + + + Gets the quotation mark character used to enclose the value of a string. + + + + + Gets the type of the current Json token. + + + + + Gets the text value of the current Json token. + + + + + Gets The Common Language Runtime (CLR) type for the current Json token. + + + + + Gets the depth of the current token in the JSON document. + + The depth of the current token in the JSON document. + + + + Specifies the state of the reader. + + + + + The Read method has not been called. + + + + + The end of the file has been reached successfully. + + + + + Reader is at a property. + + + + + Reader is at the start of an object. + + + + + Reader is in an object. + + + + + Reader is at the start of an array. + + + + + Reader is in an array. + + + + + The Close method has been called. + + + + + Reader has just read a value. + + + + + Reader is at the start of a constructor. + + + + + Reader in a constructor. + + + + + An error occurred that prevents the read operation from continuing. + + + + + The end of the file has been reached successfully. + + + + + Initializes a new instance of the class. + + The stream. + + + + Initializes a new instance of the class. + + The stream. + if set to true the root object will be read as a JSON array. + The used when reading values from BSON. + + + + Reads the next JSON token from the stream as a . - The MemberInfo to determine whether can be read. - true if the specified MemberInfo can be read; otherwise, false. + A or a null reference if the next JSON token is null. - + - Determines whether the specified MemberInfo can be set. + Reads the next JSON token from the stream. - The MemberInfo to determine whether can be set. - true if the specified MemberInfo can be set; otherwise, false. + true if the next token was read successfully; false if there are no more tokens to read. - + - Specifies reference loop handling options for the . + Gets or sets a value indicating whether the root object will be read as a JSON array. + + true if the root object will be read as a JSON array; otherwise, false. + - + - Throw a when a loop is encountered. + Gets or sets the used when reading values from BSON. + The used when reading values from BSON. - + - Ignore loop references and do not serialize. + Represents a writer that provides a fast, non-cached, forward-only way of generating Json data. - + - Serialize loop references. - - - - - Serializes and deserializes objects into and from the Json format. - The enables you to control how objects are encoded into Json. - - - - - Initializes a new instance of the class. - - - - - Deserializes the Json structure contained by the specified . - - The that contains the Json structure to deserialize. - The being deserialized. - - - - Deserializes the Json structure contained by the specified - into an instance of the specified type. - - The type of object to create. - The of object being deserialized. - The instance of being deserialized. - - - - Serializes the specified and writes the Json structure - to a Stream using the specified . - - The used to write the Json structure. - The to serialize. - - - - Serializes the specified and writes the Json structure - to a Stream using the specified . - - The used to write the Json structure. - The to serialize. - - - - Get or set how reference loops (e.g. a class referencing itself) is handled. - - - - - Represents a JavaScript array. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class that - contains elements copied from the specified collection. - - The collection whose elements are copied to the new array. - - - - Initializes a new instance of the class that - is empty and has the specified initial capacity. - - The number of elements that the new array can initially store. - - - - Provides methods for converting between common language runtime types and JavaScript types. - - - - - Represents JavaScript's boolean value true as a string. This field is read-only. - - - - - Represents JavaScript's boolean value false as a string. This field is read-only. - - - - - Represents JavaScript's null as a string. This field is read-only. - - - - - Represents JavaScript's undefined as a string. This field is read-only. - - - - - Converts the to it's JavaScript string representation. - - The value to convert. - A Json string representation of the . - - - - Converts the to it's JavaScript string representation. - - The value to convert. - A Json string representation of the . - - - - Converts the to it's JavaScript string representation. - - The value to convert. - A Json string representation of the . - - - - Converts the to it's JavaScript string representation. - - The value to convert. - A Json string representation of the . - - - - Converts the to it's JavaScript string representation. - - The value to convert. - A Json string representation of the . - - - - Converts the to it's JavaScript string representation. - - The value to convert. - A Json string representation of the . - - - - Converts the to it's JavaScript string representation. - - The value to convert. - A Json string representation of the . - - - - Converts the to it's JavaScript string representation. - - The value to convert. - A Json string representation of the . - - - - Converts the to it's JavaScript string representation. - - The value to convert. - A Json string representation of the . - - - - Converts the to it's JavaScript string representation. - - The value to convert. - A Json string representation of the . - - - - Converts the to it's JavaScript string representation. - - The value to convert. - A Json string representation of the . - - - - Converts the to it's JavaScript string representation. - - The value to convert. - A Json string representation of the . - - - - Converts the to it's JavaScript string representation. - - The value to convert. - A Json string representation of the . - - - - Converts the to it's JavaScript string representation. - - The value to convert. - A Json string representation of the . - - - - Converts the to it's JavaScript string representation. - - The value to convert. - A Json string representation of the . - - - - Converts the to it's JavaScript string representation. - - The value to convert. - A Json string representation of the . - - - - Converts the to it's JavaScript string representation. - - The value to convert. - A Json string representation of the . - - - - Converts the to it's JavaScript string representation. - - The value to convert. - The string delimiter character. - A Json string representation of the . - - - - Converts the to it's JavaScript string representation. - - The value to convert. - A Json string representation of the . - - - - Serializes the specified object to a Json object. - - The object to serialize. - A Json string representation of the object. - - - - Deserializes the specified object to a Json object. - - The object to deserialize. - The deserialized object from the Json string. - - - - Deserializes the specified object to a Json object. - - The object to deserialize. - The of object being deserialized. - The deserialized object from the Json string. - - - - Deserializes the specified object to a Json object. - - The type of the object to deserialize. - The object to deserialize. - The deserialized object from the Json string. - - - - Deserializes the specified object to a Json object. - - The type of the object to deserialize. - The object to deserialize. - Converters to use while deserializing. - The deserialized object from the Json string. - - - - The exception thrown when an error occurs during Json serialization or deserialization. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class - with a specified error message. - - The error message that explains the reason for the exception. - - - - Initializes a new instance of the class - with a specified error message and a reference to the inner exception that is the cause of this exception. - - The error message that explains the reason for the exception. - The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. - - - - The exception thrown when an error occurs while reading Json text. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class - with a specified error message. - - The error message that explains the reason for the exception. - - - - Initializes a new instance of the class - with a specified error message and a reference to the inner exception that is the cause of this exception. - - The error message that explains the reason for the exception. - The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. - - - - Represents a JavaScript constructor. - - - - - Determines whether the collection is null or empty. - - The collection. - - true if the collection is null or empty; otherwise, false. - - - - - Determines whether the collection is null or empty. - - The collection. - - true if the collection is null or empty; otherwise, false. - - - - - Determines whether the collection is null, empty or its contents are uninitialized values. - - The list. - - true if the collection is null or empty or its contents are uninitialized values; otherwise, false. - - - - - Makes a slice of the specified list in between the start and end indexes. - - The list. - The start index. - The end index. - A slice of the list. - - - - Makes a slice of the specified list in between the start and end indexes, - getting every so many items based upon the step. - - The list. - The start index. - The end index. - The step. - A slice of the list. - - - - Group the collection using a function which returns the key. - - The source collection to group. - The key selector. - A Dictionary with each key relating to a list of objects in a list grouped under it. - - - - Adds the elements of the specified collection to the specified generic IList. - - The list to add to. - The collection of elements to add. - - - - Builds a string. Unlike StringBuilder this class lets you reuse it's internal buffer. - - - - - Represents a JavaScript object. - - - - - Initializes a new instance of the class. - - - - - Initializes a new instance of the class that - contains values copied from the specified . - - The whose elements are copied to the new object. - - - - Specifies the type of Json token. - - - - - This is returned by the if a method has not been called. - - - - - An object start token. - - - - - An array start token. - - - - - An object property name. - - - - - A comment. - - - - - An interger. - - - - - A float. - - - - - A string. - - - - - A boolean. - - - - - A null token. - - - - - An undefined token. - - - - - An object end token. - - - - - An array end token. - - - - - A JavaScript object constructor. - - - - - A Date. - - - - - Checks if the attributeName is a namespace attribute. - - Attribute name to test. - The attribute name prefix if it has one, otherwise an empty string. - True if attribute name is for a namespace attribute, otherwise false. - - - - Specifies the state of the . - - - - - An exception has been thrown, which has left the in an invalid state. - You may call the method to put the in the Closed state. - Any other method calls results in an being thrown. - - - - - The method has been called. - - - - - An object is being written. - - - - - A array is being written. - - - - - A property is being written. - - - - - A write method has not been called. - - - - - Specifies formatting options for the . - - - - - No special formatting is applied. This is the default. - - - - - Causes child objects to be indented according to the and settings. + Represents a writer that provides a fast, non-cached, forward-only way of generating Json data. @@ -749,11 +231,10 @@ Represents a writer that provides a fast, non-cached, forward-only way of generating Json data. - + - Creates an instance of the JsonWriter class using the specified . + Creates an instance of the JsonWriter class. - The TextWriter to write to. @@ -785,17 +266,55 @@ Writes the end of an array. + + + Writes the start of a constructor with the given name. + + The name of the constructor. + + + + Writes the end constructor. + + Writes the property name of a name/value pair on a Json object. - + The name of the property. Writes the end of the current Json object or array. + + + Writes the current token. + + The to read the token from. + + + + Writes the specified end token. + + The end token to write. + + + + Writes indent characters. + + + + + Writes the JSON value delimiter. + + + + + Writes an indent space. + + Writes a null value. @@ -808,9 +327,15 @@ - Writes raw JavaScript manually. + Writes raw JSON without changing the writer's state. - The raw JavaScript to write. + The raw JSON to write. + + + + Writes raw JSON where a value is expected and updates the writer's state. + + The raw JSON to write. @@ -902,6 +427,103 @@ The value to write. + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + An error will raised if the value cannot be written as a single JSON token. + + The value to write. + Writes out a comment /*...*/ containing the specified text. @@ -914,6 +536,12 @@ The string of white space characters. + + + Gets the top. + + The top. + Gets the state of the writer. @@ -924,67 +552,5276 @@ Indicates how the output is formatted. - + - Gets or sets how many IndentChars to write for each level in the hierarchy when is set to Formatting.Indented. + Initializes a new instance of the class writing to the given . + + The container being written to. + + + + Initializes a new instance of the class. - + - Gets or sets which character to use to quote attribute values. + Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream. - + - Gets or sets which character to use for indenting when is set to Formatting.Indented. + Closes this stream and the underlying stream. - + - Gets or sets a value indicating whether object names will be surrounded with quotes. + Writes the beginning of a Json object. - + + + Writes the beginning of a Json array. + + + + + Writes the start of a constructor with the given name. + + The name of the constructor. + + + + Writes the end. + + The token. + + + + Writes the property name of a name/value pair on a Json object. + + The name of the property. + + + + Writes a null value. + + + + + Writes an undefined value. + + + + + Writes raw JSON. + + The raw JSON to write. + + + + Writes out a comment /*...*/ containing the specified text. + + Text to place inside the comment. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Gets the token being writen. + + The token being writen. + + + + Initializes a new instance of the class. + + The stream. + + + + Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream. + + + + + Writes the end. + + The token. + + + + Writes out a comment /*...*/ containing the specified text. + + Text to place inside the comment. + + + + Writes the start of a constructor with the given name. + + The name of the constructor. + + + + Writes raw JSON. + + The raw JSON to write. + + + + Writes raw JSON where a value is expected and updates the writer's state. + + The raw JSON to write. + + + + Specifies how constructors are used when initializing objects during deserialization by the . + + + + + First attempt to use the public default constructor then fall back to single paramatized constructor. + + + + + Allow Json.NET to use a non-public default constructor. + + + + + Converts a binary value to and from a base 64 string value. + + + + + Converts an object to and from JSON. + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The calling serializer. + The object value. + + + + Determines whether this instance can convert the specified object type. + + Type of the object. + + true if this instance can convert the specified object type; otherwise, false. + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The calling serializer. + The object value. + + + + Determines whether this instance can convert the specified object type. + + Type of the object. + + true if this instance can convert the specified object type; otherwise, false. + + + + + Create a custom object + + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The calling serializer. + The object value. + + + + Creates an object which will then be populated by the serializer. + + Type of the object. + + + + + Determines whether this instance can convert the specified object type. + + Type of the object. + + true if this instance can convert the specified object type; otherwise, false. + + + + + Converts a to and from JSON. + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The calling serializer. + The object value. + + + + Determines whether this instance can convert the specified value type. + + Type of the value. + + true if this instance can convert the specified value type; otherwise, false. + + + + + Converts a to and from JSON. + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The calling serializer. + The object value. + + + + Determines whether this instance can convert the specified value type. + + Type of the value. + + true if this instance can convert the specified value type; otherwise, false. + + + + + Provides a base class for converting a to and from JSON. + + + + + Determines whether this instance can convert the specified object type. + + Type of the object. + + true if this instance can convert the specified object type; otherwise, false. + + + + + Converts an to and from its name string value. + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The calling serializer. + The object value. + + + + Determines whether this instance can convert the specified object type. + + Type of the object. + + true if this instance can convert the specified object type; otherwise, false. + + + + + Represents a view of a . + + + + + Initializes a new instance of the class. + + The name. + Type of the property. + + + + When overridden in a derived class, returns whether resetting an object changes its value. + + + true if resetting the component changes its value; otherwise, false. + + The component to test for reset capability. + + + + + When overridden in a derived class, gets the current value of the property on a component. + + + The value of a property for a given component. + + The component with the property for which to retrieve the value. + + + + + When overridden in a derived class, resets the value for this property of the component to the default value. + + The component with the property value that is to be reset to the default value. + + + + + When overridden in a derived class, sets the value of the component to a different value. + + The component with the property value that is to be set. + The new value. + + + + + When overridden in a derived class, determines a value indicating whether the value of this property needs to be persisted. + + + true if the property should be persisted; otherwise, false. + + The component with the property to be examined for persistence. + + + + + When overridden in a derived class, gets the type of the component this property is bound to. + + + A that represents the type of component this property is bound to. When the or methods are invoked, the object specified might be an instance of this type. + + + + + When overridden in a derived class, gets a value indicating whether this property is read-only. + + + true if the property is read-only; otherwise, false. + + + + + When overridden in a derived class, gets the type of the property. + + + A that represents the type of the property. + + + + + Gets the hash code for the name of the member. + + + + The hash code for the name of the member. + + + + + Represents a view of a . + + + + + Initializes a new instance of the class. + + The value. + + + + Returns the properties for this instance of a component. + + + A that represents the properties for this component instance. + + + + + Returns the properties for this instance of a component using the attribute array as a filter. + + An array of type that is used as a filter. + + A that represents the filtered properties for this component instance. + + + + + Returns a collection of custom attributes for this instance of a component. + + + An containing the attributes for this object. + + + + + Returns the class name of this instance of a component. + + + The class name of the object, or null if the class does not have a name. + + + + + Returns the name of this instance of a component. + + + The name of the object, or null if the object does not have a name. + + + + + Returns a type converter for this instance of a component. + + + A that is the converter for this object, or null if there is no for this object. + + + + + Returns the default event for this instance of a component. + + + An that represents the default event for this object, or null if this object does not have events. + + + + + Returns the default property for this instance of a component. + + + A that represents the default property for this object, or null if this object does not have properties. + + + + + Returns an editor of the specified type for this instance of a component. + + A that represents the editor for this object. + + An of the specified type that is the editor for this object, or null if the editor cannot be found. + + + + + Returns the events for this instance of a component using the specified attribute array as a filter. + + An array of type that is used as a filter. + + An that represents the filtered events for this component instance. + + + + + Returns the events for this instance of a component. + + + An that represents the events for this component instance. + + + + + Returns an object that contains the property described by the specified property descriptor. + + A that represents the property whose owner is to be found. + + An that represents the owner of the specified property. + + + + + Represents a raw JSON string. + + + + + Represents a value in JSON (string, integer, date, etc). + + + + + Represents an abstract JSON token. + + + + + Represents a collection of objects. + + The type of token + + + + Gets the with the specified key. + + + + + + Provides an interface to enable a class to return line and position information. + + + + + Gets a value indicating whether the class can return line information. + + + true if LineNumber and LinePosition can be provided; otherwise, false. + + + + + Gets the current line number. + + The current line number or 0 if no line information is available (for example, HasLineInfo returns false). + + + + Gets the current line position. + + The current line position or 0 if no line information is available (for example, HasLineInfo returns false). + + + + Compares the values of two tokens, including the values of all descendant tokens. + + The first to compare. + The second to compare. + true if the tokens are equal; otherwise false. + + + + Adds the specified content immediately after this token. + + A content object that contains simple content or a collection of content objects to be added after this token. + + + + Adds the specified content immediately before this token. + + A content object that contains simple content or a collection of content objects to be added before this token. + + + + Returns a collection of the ancestor tokens of this token. + + A collection of the ancestor tokens of this token. + + + + Returns a collection of the sibling tokens after this token, in document order. + + A collection of the sibling tokens after this tokens, in document order. + + + + Returns a collection of the sibling tokens before this token, in document order. + + A collection of the sibling tokens before this token, in document order. + + + + Gets the with the specified key converted to the specified type. + + The type to convert the token to. + The token key. + The converted token value. + + + + Returns a collection of the child tokens of this token, in document order. + + An of containing the child tokens of this , in document order. + + + + Returns a collection of the child tokens of this token, in document order, filtered by the specified type. + + The type to filter the child tokens on. + A containing the child tokens of this , in document order. + + + + Returns a collection of the child values of this token, in document order. + + The type to convert the values to. + A containing the child values of this , in document order. + + + + Removes this token from its parent. + + + + + Replaces this token with the specified token. + + The value. + + + + Writes this token to a . + + A into which this method will write. + A collection of which will be used when writing the token. + + + + Returns the indented JSON for this token. + + + The indented JSON for this token. + + + + + Returns the JSON for this token using the given formatting and converters. + + Indicates how the output is formatted. + A collection of which will be used when writing the token. + The JSON for this token using the given formatting and converters. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an explicit conversion from to . + + The value. + The result of the conversion. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Performs an implicit conversion from to . + + The value to create a from. + The initialized with the specified value. + + + + Creates an for this token. + + An that can be used to read this token and its descendants. + + + + Creates a from an object. + + The object that will be used to create . + A with the value of the specified object + + + + Creates a from an object using the specified . + + The object that will be used to create . + The that will be used when reading the object. + A with the value of the specified object + + + + Creates a from a . + + An positioned at the token to read into this . + + An that contains the token and its descendant tokens + that were read from the reader. The runtime type of the token is determined + by the token type of the first token encountered in the reader. + + + + + Selects the token that matches the object path. + + + The object path from the current to the + to be returned. This must be a string of property names or array indexes separated + by periods, such as Tables[0].DefaultView[0].Price in C# or + Tables(0).DefaultView(0).Price in Visual Basic. + + The that matches the object path or a null reference if no matching token is found. + + + + Selects the token that matches the object path. + + + The object path from the current to the + to be returned. This must be a string of property names or array indexes separated + by periods, such as Tables[0].DefaultView[0].Price in C# or + Tables(0).DefaultView(0).Price in Visual Basic. + + A flag to indicate whether an error should be thrown if no token is found. + The that matches the object path. + + + + Gets a comparer that can compare two tokens for value equality. + + A that can compare two nodes for value equality. + + + + Gets or sets the parent. + + The parent. + + + + Gets the root of this . + + The root of this . + + + + Gets the node type for this . + + The type. + + + + Gets a value indicating whether this token has childen tokens. + + + true if this token has child values; otherwise, false. + + + + + Gets the next sibling token of this node. + + The that contains the next sibling token. + + + + Gets the previous sibling token of this node. + + The that contains the previous sibling token. + + + + Gets the with the specified key. + + The with the specified key. + + + + Get the first child token of this token. + + A containing the first child token of the . + + + + Get the last child token of this token. + + A containing the last child token of the . + + + + Initializes a new instance of the class from another object. + + A object to copy from. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Initializes a new instance of the class with the given value. + + The value. + + + + Creates a comment with the given value. + + The value. + A comment with the given value. + + + + Creates a string with the given value. + + The value. + A string with the given value. + + + + Writes this token to a . + + A into which this method will write. + A collection of which will be used when writing the token. + + + + Indicates whether the current object is equal to another object of the same type. + + + true if the current object is equal to the parameter; otherwise, false. + + An object to compare with this object. + + + + Determines whether the specified is equal to the current . + + The to compare with the current . + + true if the specified is equal to the current ; otherwise, false. + + + The parameter is null. + + + + + Serves as a hash function for a particular type. + + + A hash code for the current . + + + + + Gets a value indicating whether this token has childen tokens. + + + true if this token has child values; otherwise, false. + + + + + Gets the node type for this . + + The type. + + + + Gets or sets the underlying token value. + + The underlying token value. + + + + Initializes a new instance of the class from another object. + + A object to copy from. + + + + Initializes a new instance of the class. + + The raw json. + + + + Creates an instance of with the content of the reader's current token. + + The reader. + An instance of with the content of the reader's current token. + + + + Indicating whether a property is required. + + + + + The property is not required. The default state. + + + + + The property must be defined in JSON but can be a null value. + + + + + The property must be defined in JSON and cannot be a null value. + + + + + Used to resolve references when serializing and deserializing JSON by the . + + + + + Resolves a reference to its object. + + The reference to resolve. + The object that + + + + Gets the reference for the sepecified object. + + The object to get a reference for. + The reference to the object. + + + + Determines whether the specified object is referenced. + + The object to test for a reference. + + true if the specified object is referenced; otherwise, false. + + + + + Adds a reference to the specified object. + + The reference. + The object to reference. + + + + Specifies reference handling options for the . + + + + + Do not preserve references when serializing types. + + + + + Preserve references when serializing into a JSON object structure. + + + + + Preserve references when serializing into a JSON array structure. + + + + + Preserve references when serializing. + + + + + Instructs the how to serialize the collection. + + + + + Instructs the how to serialize the object. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class with the specified container Id. + + The container Id. + + + + Gets or sets the id. + + The id. + + + + Gets or sets the title. + + The title. + + + + Gets or sets the description. + + The description. + + + + Gets or sets a value that indicates whether to preserve object reference data. + + + true to keep object reference; otherwise, false. The default is false. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class with a flag indicating whether the array can contain null items + + A flag indicating whether the array can contain null items. + + + + Initializes a new instance of the class with the specified container Id. + + The container Id. + + + + Gets or sets a value indicating whether null items are allowed in the collection. + + true if null items are allowed in the collection; otherwise, false. + + + + Specifies default value handling options for the . + + + + + Include null values when serializing and deserializing objects. + + + + + Ignore null values when serializing and deserializing objects. + + + + + Instructs the to use the specified when serializing the member or class. + + + + + Initializes a new instance of the class. + + Type of the converter. + + + + Gets the type of the converter. + + The type of the converter. + + + + Instructs the how to serialize the object. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class with the specified member serialization. + + The member serialization. + + + + Initializes a new instance of the class with the specified container Id. + + The container Id. + + + + Gets or sets the member serialization. + + The member serialization. + + + + Specifies the settings on a object. + + + + + Initializes a new instance of the class. + + + + + Gets or sets how reference loops (e.g. a class referencing itself) is handled. + + Reference loop handling. + + + + Gets or sets how missing members (e.g. JSON contains a property that isn't a member on the object) are handled during deserialization. + + Missing member handling. + + + + Gets or sets how objects are created during deserialization. + + The object creation handling. + + + + Gets or sets how null values are handled during serialization and deserialization. + + Null value handling. + + + + Gets or sets how null default are handled during serialization and deserialization. + + The default value handling. + + + + Gets or sets a collection that will be used during serialization. + + The converters. + + + + Gets or sets how object references are preserved by the serializer. + + The preserve references handling. + + + + Gets or sets how type name writing and reading is handled by the serializer. + + The type name handling. + + + + Gets or sets how constructors are used during deserialization. + + The constructor handling. + + + + Gets or sets the contract resolver used by the serializer when + serializing .NET objects to JSON and vice versa. + + The contract resolver. + + + + Gets or sets the used by the serializer when resolving references. + + The reference resolver. + + + + Gets or sets the used by the serializer when resolving type names. + + The binder. + + + + Gets or sets the error handler called during serialization and deserialization. + + The error handler called during serialization and deserialization. + + + + Gets or sets the used by the serializer when invoking serialization callback methods. + + The context. + + + + Represents a reader that provides validation. + + + + + Initializes a new instance of the class that + validates the content returned from the given . + + The to read from while validating. + + + + Reads the next JSON token from the stream as a . + + + A or a null reference if the next JSON token is null. + + + + + Reads the next JSON token from the stream. + + + true if the next token was read successfully; false if there are no more tokens to read. + + + + + Sets an event handler for receiving schema validation errors. + + + + + Gets the text value of the current Json token. + + + + + + Gets the depth of the current token in the JSON document. + + The depth of the current token in the JSON document. + + + + Gets the quotation mark character used to enclose the value of a string. + + + + + + Gets the type of the current Json token. + + + + + + Gets The Common Language Runtime (CLR) type for the current Json token. + + + + + + Gets or sets the schema. + + The schema. + + + + Gets the used to construct this . + + The specified in the constructor. + + + + Compares tokens to determine whether they are equal. + + + + + Determines whether the specified objects are equal. + + The first object of type to compare. + The second object of type to compare. + + true if the specified objects are equal; otherwise, false. + + + + + Returns a hash code for the specified object. + + The for which a hash code is to be returned. + A hash code for the specified object. + The type of is a reference type and is null. + + + + Specifies the member serialization options for the . + + + + + All members are serialized by default. Members can be excluded using the . + + + + + Only members must be marked with the are serialized. + + + + + Specifies how object creation is handled by the . + + + + + Reuse existing objects, create new objects when needed. + + + + + Only reuse existing objects. + + + + + Always create new objects. + + + + + Converts a to and from the ISO 8601 date format (e.g. 2008-04-12T12:53Z). + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The calling serializer. + The object value. + + + + Gets or sets the date time styles used when converting a date to and from JSON. + + The date time styles used when converting a date to and from JSON. + + + + Gets or sets the date time format used when converting a date to and from JSON. + + The date time format used when converting a date to and from JSON. + + + + Gets or sets the culture used when converting a date to and from JSON. + + The culture used when converting a date to and from JSON. + + + + Converts a to and from a JavaScript date constructor (e.g. new Date(52231943)). + + + + + Writes the JSON representation of the object. + + The to write to. + The value. + The calling serializer. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The calling serializer. + The object value. + + + + Specifies whether a DateTime object represents a local time, a Coordinated Universal Time (UTC), or is not specified as either local time or UTC. + + + + + The time represented is local time. + + + + + The time represented is UTC. + + + + + The time represented is not specified as either local time or Coordinated Universal Time (UTC). + + + + + Preserves the DateTimeKind field of a date when a DateTime object is converted to a string and the string is then converted back to a DateTime object. + + + + + Converts an to and from JSON. + + + + + Writes the JSON representation of the object. + + The to write to. + The calling serializer. + The value. + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The calling serializer. + The object value. + + + + Checks if the attributeName is a namespace attribute. + + Attribute name to test. + The attribute name prefix if it has one, otherwise an empty string. + True if attribute name is for a namespace attribute, otherwise false. + + + + Determines whether this instance can convert the specified value type. + + Type of the value. + + true if this instance can convert the specified value type; otherwise, false. + + + + + Gets or sets the name of the root element to insert when deserializing to XML if the JSON structure has produces multiple root elements. + + The name of the deserialize root element. + + + + Converts a object to and from JSON. + + + + + Writes the JSON representation of the object. + + The to write to. + The calling serializer. + The value. + + + + Determines whether this instance can convert the specified value type. + + Type of the value. + + true if this instance can convert the specified value type; otherwise, false. + + + + + Reads the JSON representation of the object. + + The to read from. + Type of the object. + The calling serializer. + The object value. + + Represents a reader that provides fast, non-cached, forward-only access to serialized Json data. - + Initializes a new instance of the class with the specified . The TextReader containing the XML data to read. - + - Reads the next Json token from the stream. + Reads the next JSON token from the stream. + + + true if the next token was read successfully; false if there are no more tokens to read. + + + + + Reads the next JSON token from the stream as a . + + + A or a null reference if the next JSON token is null. + + + + + Changes the state to closed. + + + + + Gets a value indicating whether the class can return line information. + + + true if LineNumber and LinePosition can be provided; otherwise, false. + + + + + Gets the current line number. + + + The current line number or 0 if no line information is available (for example, HasLineInfo returns false). + + + + + Gets the current line position. + + + The current line position or 0 if no line information is available (for example, HasLineInfo returns false). + + + + + Instructs the to always serialize the member with the specified name. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class with the specified name. + + Name of the property. + + + + Gets or sets the null value handling used when serializing this property. + + The null value handling. + + + + Gets or sets the default value handling used when serializing this property. + + The default value handling. + + + + Gets or sets the reference loop handling used when serializing this property. + + The reference loop handling. + + + + Gets or sets the object creation handling used when deserializing this property. + + The object creation handling. + + + + Gets or sets whether this property's value is serialized as a reference. + + Whether this property's value is serialized as a reference. + + + + Gets or sets the name of the property. + + The name of the property. + + + + Gets or sets a value indicating whether this property is required. + + + A value indicating whether this property is required. + + + + + Instructs the not to serialize the public field or public read/write property value. + + + + + Represents a writer that provides a fast, non-cached, forward-only way of generating Json data. + + + + + Creates an instance of the JsonWriter class using the specified . + + The TextWriter to write to. + + + + Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream. + + + + + Closes this stream and the underlying stream. + + + + + Writes the beginning of a Json object. + + + + + Writes the beginning of a Json array. + + + + + Writes the start of a constructor with the given name. + + The name of the constructor. + + + + Writes the specified end token. + + The end token to write. + + + + Writes the property name of a name/value pair on a Json object. + + The name of the property. + + + + Writes indent characters. + + + + + Writes the JSON value delimiter. + + + + + Writes an indent space. + + + + + Writes a null value. + + + + + Writes an undefined value. + + + + + Writes raw JSON. + + The raw JSON to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes a value. + + The value to write. + + + + Writes out a comment /*...*/ containing the specified text. + + Text to place inside the comment. + + + + Writes out the given white space. + + The string of white space characters. + + + + Gets or sets how many IndentChars to write for each level in the hierarchy when is set to Formatting.Indented. + + + + + Gets or sets which character to use to quote attribute values. + + + + + Gets or sets which character to use for indenting when is set to Formatting.Indented. + + + + + Gets or sets a value indicating whether object names will be surrounded with quotes. + + + + + The exception thrown when an error occurs while reading Json text. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class + with a specified error message. + + The error message that explains the reason for the exception. + + + + Initializes a new instance of the class + with a specified error message and a reference to the inner exception that is the cause of this exception. + + The error message that explains the reason for the exception. + The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. + + + + The exception thrown when an error occurs while reading Json text. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class + with a specified error message. + + The error message that explains the reason for the exception. + + + + Initializes a new instance of the class + with a specified error message and a reference to the inner exception that is the cause of this exception. + + The error message that explains the reason for the exception. + The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. + + + + Gets the line number indicating where the error occurred. + + The line number indicating where the error occurred. + + + + Gets the line position indicating where the error occurred. + + The line position indicating where the error occurred. + + + + Represents a collection of . + + + + + Provides methods for converting between common language runtime types and JSON types. + + + + + Represents JavaScript's boolean value true as a string. This field is read-only. + + + + + Represents JavaScript's boolean value false as a string. This field is read-only. + + + + + Represents JavaScript's null as a string. This field is read-only. + + + + + Represents JavaScript's undefined as a string. This field is read-only. + + + + + Represents JavaScript's positive infinity as a string. This field is read-only. + + + + + Represents JavaScript's negative infinity as a string. This field is read-only. + + + + + Represents JavaScript's NaN as a string. This field is read-only. + + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + The string delimiter character. + A JSON string representation of the . + + + + Converts the to its JSON string representation. + + The value to convert. + A JSON string representation of the . + + + + Serializes the specified object to a JSON string. + + The object to serialize. + A JSON string representation of the object. + + + + Serializes the specified object to a JSON string. + + The object to serialize. + Indicates how the output is formatted. + + A JSON string representation of the object. + + + + + Serializes the specified object to a JSON string using a collection of . + + The object to serialize. + A collection converters used while serializing. + A JSON string representation of the object. + + + + Serializes the specified object to a JSON string using a collection of . + + The object to serialize. + Indicates how the output is formatted. + A collection converters used while serializing. + A JSON string representation of the object. + + + + Serializes the specified object to a JSON string using a collection of . + + The object to serialize. + Indicates how the output is formatted. + The used to serialize the object. + If this is null, default serialization settings will be is used. + + A JSON string representation of the object. + + + + + Deserializes the specified object to a Json object. + + The object to deserialize. + The deserialized object from the Json string. + + + + Deserializes the specified object to a Json object. + + The object to deserialize. + The of object being deserialized. + The deserialized object from the Json string. + + + + Deserializes the specified object to a Json object. + + The type of the object to deserialize. + The object to deserialize. + The deserialized object from the Json string. + + + + Deserializes the specified JSON to the given anonymous type. + + + The anonymous type to deserialize to. This can't be specified + traditionally and must be infered from the anonymous type passed + as a parameter. + + The object to deserialize. + The anonymous type object. + The deserialized anonymous type from the JSON string. + + + + Deserializes the JSON string to the specified type. + + The type of the object to deserialize. + The object to deserialize. + Converters to use while deserializing. + The deserialized object from the JSON string. + + + + Deserializes the JSON string to the specified type. + + The type of the object to deserialize. + The object to deserialize. + + The used to deserialize the object. + If this is null, default serialization settings will be is used. + + The deserialized object from the JSON string. + + + + Deserializes the JSON string to the specified type. + + The object to deserialize. + The type of the object to deserialize. + Converters to use while deserializing. + The deserialized object from the JSON string. + + + + Deserializes the JSON string to the specified type. + + The JSON to deserialize. + The type of the object to deserialize. + + The used to deserialize the object. + If this is null, default serialization settings will be is used. + + The deserialized object from the JSON string. + + + + Populates the object with values from the JSON string. + + The JSON to populate values from. + The target object to populate values onto. + + + + Populates the object with values from the JSON string. + + The JSON to populate values from. + The target object to populate values onto. + + The used to deserialize the object. + If this is null, default serialization settings will be is used. + + + + + Serializes the XML node to a JSON string. + + The node to serialize. + A JSON string of the XmlNode. + + + + Deserializes the XmlNode from a JSON string. + + The JSON string. + The deserialized XmlNode + + + + Deserializes the XmlNode from a JSON string nested in a root elment. + + The JSON string. + The name of the root element to append when deserializing. + The deserialized XmlNode + + + + The exception thrown when an error occurs during Json serialization or deserialization. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class + with a specified error message. + + The error message that explains the reason for the exception. + + + + Initializes a new instance of the class + with a specified error message and a reference to the inner exception that is the cause of this exception. + + The error message that explains the reason for the exception. + The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. + + + + Serializes and deserializes objects into and from the JSON format. + The enables you to control how objects are encoded into JSON. + + + + + Initializes a new instance of the class. + + + + + Creates a new instance using the specified . + + The settings to be applied to the . + A new instance using the specified . + + + + Populates the JSON values onto the target object. + + The that contains the JSON structure to reader values from. + The target object to populate values onto. + + + + Populates the JSON values onto the target object. + + The that contains the JSON structure to reader values from. + The target object to populate values onto. + + + + Deserializes the Json structure contained by the specified . + + The that contains the JSON structure to deserialize. + The being deserialized. + + + + Deserializes the Json structure contained by the specified + into an instance of the specified type. + + The containing the object. + The of object being deserialized. + The instance of being deserialized. + + + + Deserializes the Json structure contained by the specified + into an instance of the specified type. + + The containing the object. + The type of the object to deserialize. + The instance of being deserialized. + + + + Deserializes the Json structure contained by the specified + into an instance of the specified type. + + The containing the object. + The of object being deserialized. + The instance of being deserialized. + + + + Serializes the specified and writes the Json structure + to a Stream using the specified . + + The used to write the Json structure. + The to serialize. + + + + Serializes the specified and writes the Json structure + to a Stream using the specified . + + The used to write the Json structure. + The to serialize. + + + + Occurs when the errors during serialization and deserialization. + + + + + Gets or sets the used by the serializer when resolving references. + + + + + Gets or sets the used by the serializer when resolving type names. + + + + + Gets or sets how type name writing and reading is handled by the serializer. + + + + + Gets or sets how object references are preserved by the serializer. + + + + + Get or set how reference loops (e.g. a class referencing itself) is handled. + + + + + Get or set how missing members (e.g. JSON contains a property that isn't a member on the object) are handled during deserialization. + + + + + Get or set how null values are handled during serialization and deserialization. + + + + + Get or set how null default are handled during serialization and deserialization. + + + + + Gets or sets how objects are created during deserialization. + + The object creation handling. + + + + Gets or sets how constructors are used during deserialization. + + The constructor handling. + + + + Gets a collection that will be used during serialization. + + Collection that will be used during serialization. + + + + Gets or sets the contract resolver used by the serializer when + serializing .NET objects to JSON and vice versa. + + + + + Gets or sets the used by the serializer when invoking serialization callback methods. + + The context. + + + + Contains the LINQ to JSON extension methods. + + + + + Returns a collection of tokens that contains the ancestors of every token in the source collection. + + The type of the objects in source, constrained to . + An of that contains the source collection. + An of that contains the ancestors of every node in the source collection. + + + + Returns a collection of tokens that contains the descendants of every token in the source collection. + + The type of the objects in source, constrained to . + An of that contains the source collection. + An of that contains the descendants of every node in the source collection. + + + + Returns a collection of child properties of every object in the source collection. + + An of that contains the source collection. + An of that contains the properties of every object in the source collection. + + + + Returns a collection of child values of every object in the source collection with the given key. + + An of that contains the source collection. + The token key. + An of that contains the values of every node in the source collection with the given key. + + + + Returns a collection of child values of every object in the source collection. + + An of that contains the source collection. + An of that contains the values of every node in the source collection. + + + + Returns a collection of converted child values of every object in the source collection with the given key. + + The type to convert the values to. + An of that contains the source collection. + The token key. + An that contains the converted values of every node in the source collection with the given key. + + + + Returns a collection of converted child values of every object in the source collection. + + The type to convert the values to. + An of that contains the source collection. + An that contains the converted values of every node in the source collection. + + + + Converts the value. + + The type to convert the value to. + A cast as a of . + A converted value. + + + + Converts the value. + + The source collection type. + The type to convert the value to. + A cast as a of . + A converted value. + + + + Returns a collection of child tokens of every array in the source collection. + + The source collection type. + An of that contains the source collection. + An of that contains the values of every node in the source collection. + + + + Returns a collection of converted child tokens of every array in the source collection. + + An of that contains the source collection. + The type to convert the values to. + The source collection type. + An that contains the converted values of every node in the source collection. + + + + Returns the input typed as . + + An of that contains the source collection. + The input typed as . + + + + Returns the input typed as . + + The source collection type. + An of that contains the source collection. + The input typed as . + + + + Represents a JSON constructor. + + + + + Represents a token that can contain other tokens. + + + + + Raises the event. + + The instance containing the event data. + + + + Raises the event. + + The instance containing the event data. + + + + Returns a collection of the child tokens of this token, in document order. + + + An of containing the child tokens of this , in document order. + + + + + Returns a collection of the child values of this token, in document order. + + The type to convert the values to. + + A containing the child values of this , in document order. + + + + + Returns a collection of the descendant tokens for this token in document order. + + An containing the descendant tokens of the . + + + + Adds the specified content as children of this . + + The content to be added. + + + + Adds the specified content as the first children of this . + + The content to be added. + + + + Creates an that can be used to add tokens to the . + + An that is ready to have content written to it. + + + + Replaces the children nodes of this token with the specified content. + + The content. + + + + Removes the child nodes from this token. + + + + + Occurs when the list changes or an item in the list changes. + + + + + Occurs before an item is added to the collection. + + + + + Gets a value indicating whether this token has childen tokens. + + + true if this token has child values; otherwise, false. + + + + + Get the first child token of this token. + + + A containing the first child token of the . + + + + + Get the last child token of this token. + + + A containing the last child token of the . + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class from another object. + + A object to copy from. + + + + Initializes a new instance of the class with the specified name and content. + + The constructor name. + The contents of the constructor. + + + + Initializes a new instance of the class with the specified name and content. + + The constructor name. + The contents of the constructor. + + + + Initializes a new instance of the class with the specified name. + + The constructor name. + + + + Writes this token to a . + + A into which this method will write. + A collection of which will be used when writing the token. + + + + Loads an from a . + + A that will be read for the content of the . + A that contains the JSON that was read from the specified . + + + + Gets or sets the name of this constructor. + + The constructor name. + + + + Gets the node type for this . + + The type. + + + + Gets the with the specified key. + + The with the specified key. + + + + Represents a collection of objects. + + The type of token + + + + An empty collection of objects. + + + + + Initializes a new instance of the struct. + + The enumerable. + + + + Returns an enumerator that iterates through the collection. + + + A that can be used to iterate through the collection. + + + + + Returns an enumerator that iterates through a collection. + + + An object that can be used to iterate through the collection. + + + + + Determines whether the specified is equal to this instance. + + The to compare with this instance. + + true if the specified is equal to this instance; otherwise, false. + + + + + Returns a hash code for this instance. + + + A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + + + + + Gets the with the specified key. + + + + + + Represents a JSON object. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class from another object. + + A object to copy from. + + + + Initializes a new instance of the class with the specified content. + + The contents of the object. + + + + Initializes a new instance of the class with the specified content. + + The contents of the object. + + + + Gets an of this object's properties. + + An of this object's properties. + + + + Gets a the specified name. + + The property name. + A with the specified name or null. + + + + Gets an of this object's property values. + + An of this object's property values. + + + + Loads an from a . + + A that will be read for the content of the . + A that contains the JSON that was read from the specified . + + + + Load a from a string that contains JSON. + + A that contains JSON. + A populated from the string that contains JSON. + + + + Creates a from an object. + + The object that will be used to create . + A with the values of the specified object + + + + Creates a from an object. + + The object that will be used to create . + The that will be used to read the object. + A with the values of the specified object + + + + Writes this token to a . + + A into which this method will write. + A collection of which will be used when writing the token. + + + + Adds the specified property name. + + Name of the property. + The value. + + + + Removes the property with the specified name. + + Name of the property. + true if item was successfully removed; otherwise, false. + + + + Tries the get value. + + Name of the property. + The value. + true if a value was successfully retrieved; otherwise, false. + + + + Returns an enumerator that iterates through the collection. + + + A that can be used to iterate through the collection. + + + + + Raises the event with the provided arguments. + + Name of the property. + + + + Occurs when a property value changes. + + + + + Gets the node type for this . + + The type. + + + + Gets the with the specified key. + + The with the specified key. + + + + Gets or sets the with the specified property name. + + + + + + Gets the number of elements contained in the . + + + The number of elements contained in the . + + + + Represents a JSON array. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class from another object. + + A object to copy from. + + + + Initializes a new instance of the class with the specified content. + + The contents of the array. + + + + Initializes a new instance of the class with the specified content. + + The contents of the array. + + + + Loads an from a . + + A that will be read for the content of the . + A that contains the JSON that was read from the specified . + + + + Load a from a string that contains JSON. + + A that contains JSON. + A populated from the string that contains JSON. + + + + Creates a from an object. + + The object that will be used to create . + A with the values of the specified object + + + + Creates a from an object. + + The object that will be used to create . + The that will be used to read the object. + A with the values of the specified object + + + + Writes this token to a . + + A into which this method will write. + A collection of which will be used when writing the token. + + + + Determines the index of a specific item in the . + + The object to locate in the . + + The index of if found in the list; otherwise, -1. + + + + + Inserts an item to the at the specified index. + + The zero-based index at which should be inserted. + The object to insert into the . + + is not a valid index in the . + The is read-only. + + + + Removes the item at the specified index. + + The zero-based index of the item to remove. + + is not a valid index in the . + The is read-only. + + + + Adds an item to the . + + The object to add to the . + The is read-only. + + + + Removes all items from the . + + The is read-only. + + + + Determines whether the contains a specific value. + + The object to locate in the . + + true if is found in the ; otherwise, false. + + + + + Removes the first occurrence of a specific object from the . + + The object to remove from the . + + true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . + + The is read-only. + + + + Gets the node type for this . + + The type. + + + + Gets the with the specified key. + + The with the specified key. + + + + Gets or sets the at the specified index. + + + + + + Gets the number of elements contained in the . + + + The number of elements contained in the . + + + + Represents a reader that provides fast, non-cached, forward-only access to serialized Json data. + + + + + Initializes a new instance of the class. + + The token to read from. + + + + Reads the next JSON token from the stream as a . + + + A or a null reference if the next JSON token is null. + + + + + Reads the next JSON token from the stream. + + + true if the next token was read successfully; false if there are no more tokens to read. + + + + + Represents a JSON property. + + + + + Initializes a new instance of the class from another object. + + A object to copy from. + + + + Returns a collection of the child tokens of this token, in document order. + + + An of containing the child tokens of this , in document order. + + + + + Initializes a new instance of the class. + + The property name. + The property content. + + + + Initializes a new instance of the class. + + The property name. + The property content. + + + + Writes this token to a . + + A into which this method will write. + A collection of which will be used when writing the token. + + + + Loads an from a . + + A that will be read for the content of the . + A that contains the JSON that was read from the specified . + + + + Gets the property name. + + The property name. + + + + Gets or sets the property value. + + The property value. + + + + Gets the node type for this . + + The type. + + + + Specifies the type of token. + + + + + No token type has been set. + + + + + A JSON object. + + + + + A JSON array. + + + + + A JSON constructor. + + + + + A JSON object property. + + + + + A comment. + + + + + An integer value. + + + + + A float value. + + + + + A string value. + + + + + A boolean value. + + + + + A null value. + + + + + An undefined value. + + + + + A date value. + + + + + A raw JSON value. + + + + + A collection of bytes value. + + + + + Contains the JSON schema extension methods. + + + + + Determines whether the is valid. + + The source to test. + The schema to test with. + + true if the specified is valid; otherwise, false. + + + + + Validates the specified . + + The source to test. + The schema to test with. + + + + Validates the specified . + + The source to test. + The schema to test with. + The validation event handler. + + + + Returns detailed information about the schema exception. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the class + with a specified error message. + + The error message that explains the reason for the exception. + + + + Initializes a new instance of the class + with a specified error message and a reference to the inner exception that is the cause of this exception. + + The error message that explains the reason for the exception. + The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. + + + + Gets the line number indicating where the error occurred. + + The line number indicating where the error occurred. + + + + Gets the line position indicating where the error occurred. + + The line position indicating where the error occurred. + + + + Resolves from an id. + + + + + Initializes a new instance of the class. + + + + + Gets a for the specified id. + + The id. + A for the specified id. + + + + Gets or sets the loaded schemas. + + The loaded schemas. + + + + Specifies undefined schema Id handling options for the . + + + + + Do not infer a schema Id. + + + + + Use the .NET type name as the schema Id. + + + + + Use the assembly qualified .NET type name as the schema Id. + + + + + Returns detailed information related to the . + + + + + Gets the associated with the validation event. + + The JsonSchemaException associated with the validation event. + + + + Gets the text description corresponding to the validation event. + + The text description. + + + + Represents the callback method that will handle JSON schema validation events and the . + + + + + Resolves member mappings for a type, camel casing property names. + + + + + Used by to resolves a for a given . + + + + + Used by to resolves a for a given . + + + + + Resolves the contract for a given type. + + The type to resolve a contract for. + The contract for a given type. + + + + Initializes a new instance of the class. + + + + + Resolves the contract for a given type. + + The type to resolve a contract for. + The contract for a given type. + + + + Gets the serializable members for the type. + + The type to get serializable members for. + The serializable members for the type. + + + + Creates a for the given type. + + Type of the object. + A for the given type. + + + + Resolves the default for the contract. + + Type of the object. + + + + + Creates a for the given type. + + Type of the object. + A for the given type. + + + + Creates a for the given type. + + Type of the object. + A for the given type. + + + + Creates a for the given type. + + Type of the object. + A for the given type. + + + + Creates a for the given type. + + Type of the object. + A for the given type. + + + + Creates properties for the given . + + The contract to create properties for. + Properties for the given . + + + + Creates the used by the serializer to get and set values from a member. + + The member. + The used by the serializer to get and set values from a member. + + + + Creates a for the given . + + The member's declaring types . + The member to create a for. + A created for the given . + + + + Resolves the name of the property. + + Name of the property. + Name of the property. + + + + Gets or sets the default members search flags. + + The default members search flags. + + + + Resolves the name of the property. + + Name of the property. + The property name camel cased. + + + + The default serialization binder used when resolving and loading classes from type names. + + + + + When overridden in a derived class, controls the binding of a serialized object to a type. + + Specifies the name of the serialized object. + Specifies the name of the serialized object. + + The type of the object the formatter creates a new instance of. + + + + + Get and set values for a using dynamic methods. + + + + + Provides methods to get and set values. + + + + + Sets the value. + + The target to set the value on. + The value to set on the target. + + + + Gets the value. + + The target to get the value from. + The value. + + + + Initializes a new instance of the class. + + The member info. + + + + Sets the value. + + The target to set the value on. + The value to set on the target. + + + + Gets the value. + + The target to get the value from. + The value. + + + + Provides information surrounding an error. + + + + + Gets or sets the error. + + The error. + + + + Gets the original object that caused the error. + + The original object that caused the error. + + + + Gets the member that caused the error. + + The member that caused the error. + + + + Gets or sets a value indicating whether this is handled. + + true if handled; otherwise, false. + + + + Provides data for the Error event. + + + + + Initializes a new instance of the class. + + The current object. + The error context. + + + + Gets the current object the error event is being raised against. + + The current object the error event is being raised against. + + + + Gets the error context. + + The error context. + + + + Contract details for a used by the . + + + + + Contract details for a used by the . + + + + + Gets the underlying type for the contract. + + The underlying type for the contract. + + + + Gets or sets the type created during deserialization. + + The type created during deserialization. + + + + Gets or sets whether this type contract is serialized as a reference. + + Whether this type contract is serialized as a reference. + + + + Gets or sets the default for this contract. + + The converter. + + + + Gets or sets the method called immediately after deserialization of the object. + + The method called immediately after deserialization of the object. + + + + Gets or sets the method called during deserialization of the object. + + The method called during deserialization of the object. + + + + Gets or sets the method called after serialization of the object graph. + + The method called after serialization of the object graph. + + + + Gets or sets the method called before serialization of the object. + + The method called before serialization of the object. + + + + Gets or sets the default creator. + + The default creator. + + + + Gets or sets a value indicating whether [default creator non public]. + + true if the default object creator is non-public; otherwise, false. + + + + Gets or sets the method called when an error is thrown during the serialization of the object. + + The method called when an error is thrown during the serialization of the object. + + + + Initializes a new instance of the class. + + The underlying type for the contract. + + + + Contract details for a used by the . + + + + + Initializes a new instance of the class. + + The underlying type for the contract. + + + + Contract details for a used by the . + + + + + Initializes a new instance of the class. + + The underlying type for the contract. + + + + Contract details for a used by the . + + + + + Initializes a new instance of the class. + + The underlying type for the contract. + + + + Maps a JSON property to a .NET member. + + + + + Gets the name of the property. + + The name of the property. + + + + Gets the that will get and set the during serialization. + + The that will get and set the during serialization. + + + + Gets or sets the type of the property. + + The type of the property. + + + + Gets or sets the for the property. + If set this converter takes presidence over the contract converter for the property type. + + The converter. + + + + Gets a value indicating whether this is ignored. + + true if ignored; otherwise, false. + + + + Gets a value indicating whether this is readable. + + true if readable; otherwise, false. + + + + Gets a value indicating whether this is writable. + + true if writable; otherwise, false. + + + + Gets the member converter. + + The member converter. + + + + Gets the default value. + + The default value. + + + + Gets a value indicating whether this is required. + + A value indicating whether this is required. + + + + Gets a value indicating whether this property preserves object references. + + + true if this instance is reference; otherwise, false. + + + + + Gets the property null value handling. + + The null value handling. + + + + Gets the property default value handling. + + The default value handling. + + + + Gets the property reference loop handling. + + The reference loop handling. + + + + Gets the property object creation handling. + + The object creation handling. + + + + A collection of objects. + + + + + Initializes a new instance of the class. + + The contract. + + + + When implemented in a derived class, extracts the key from the specified element. + + The element from which to extract the key. + The key for the specified element. + + + + Adds a object. + + The property to add to the collection. + + + + Gets the closest matching object. + First attempts to get an exact case match of propertyName and then + a case insensitive match. + + Name of the property. + A matching property if found. + + + + Gets a property by property name. + + The name of the property to get. + Type property name string comparison. + A matching property if found. + + + + Specifies missing member handling options for the . + + + + + Ignore a missing member and do not attempt to deserialize it. + + + + + Throw a when a missing member is encountered during deserialization. + + + + + Specifies null value handling options for the . + + + + + Include null values when serializing and deserializing objects. + + + + + Ignore null values when serializing and deserializing objects. + + + + + Specifies reference loop handling options for the . + + + + + Throw a when a loop is encountered. + + + + + Ignore loop references and do not serialize. + + + + + Serialize loop references. + + + + + An in-memory representation of a JSON Schema. + + + + + Initializes a new instance of the class. + + + + + Reads a from the specified . + + The containing the JSON Schema to read. + The object representing the JSON Schema. + + + + Reads a from the specified . + + The containing the JSON Schema to read. + The to use when resolving schema references. + The object representing the JSON Schema. + + + + Load a from a string that contains schema JSON. + + A that contains JSON. + A populated from the string that contains JSON. + + + + Parses the specified json. + + The json. + The resolver. + A populated from the string that contains JSON. + + + + Writes this schema to a . + + A into which this method will write. + + + + Writes this schema to a using the specified . + + A into which this method will write. + The resolver used. + + + + Returns a that represents the current . + + + A that represents the current . + + + + + Gets or sets the id. + + + + + Gets or sets the title. + + + + + Gets or sets whether the object is optional. + + + + + Gets or sets whether the object is read only. + + + + + Gets or sets whether the object is visible to users. + + + + + Gets or sets whether the object is transient. + + + + + Gets or sets the description of the object. + + + + + Gets or sets the types of values allowed by the object. + + The type. + + + + Gets or sets the pattern. + + The pattern. + + + + Gets or sets the minimum length. + + The minimum length. + + + + Gets or sets the maximum length. + + The maximum length. + + + + Gets or sets the maximum decimals. + + The maximum decimals. + + + + Gets or sets the minimum. + + The minimum. + + + + Gets or sets the maximum. + + The maximum. + + + + Gets or sets the minimum number of items. + + The minimum number of items. + + + + Gets or sets the maximum number of items. + + The maximum number of items. + + + + Gets or sets the of items. + + The of items. + + + + Gets or sets the of properties. + + The of properties. + + + + Gets or sets the of additional properties. + + The of additional properties. + + + + Gets or sets a value indicating whether additional properties are allowed. + + + true if additional properties are allowed; otherwise, false. + + + + + Gets or sets the required property if this property is present. + + The required property if this property is present. + + + + Gets or sets the identity. + + The identity. + + + + Gets or sets the a collection of valid enum values allowed. + + A collection of valid enum values allowed. + + + + Gets or sets a collection of options. + + A collection of options. + + + + Gets or sets disallowed types. + + The disallow types. + + + + Gets or sets the default value. + + The default value. + + + + Gets or sets the extend . + + The extended . + + + + Gets or sets the format. + + The format. + + + + Generates a from a specified . + + + + + Generate a from the specified type. + + The type to generate a from. + A generated from the specified type. + + + + Generate a from the specified type. + + The type to generate a from. + The used to resolve schema references. + A generated from the specified type. + + + + Generate a from the specified type. + + The type to generate a from. + Specify whether the generated root will be nullable. + A generated from the specified type. + + + + Generate a from the specified type. + + The type to generate a from. + The used to resolve schema references. + Specify whether the generated root will be nullable. + A generated from the specified type. + + + + Gets or sets how undefined schemas are handled by the serializer. + + + + + Gets or sets the contract resolver. + + The contract resolver. + + + + The value types allowed by the . + + + + + No type specified. + + + + + String type. + + + + + Float type. + + + + + Integer type. + + + + + Boolean type. + + + + + Object type. + + + + + Array type. + + + + + Null type. + + + + + Any type. + + + + + Contract details for a used by the . + + + + + Initializes a new instance of the class. + + The underlying type for the contract. + + + + Gets or sets the object member serialization. + + The member object serialization. + + + + Gets the object's properties. + + The object's properties. + + + + Gets or sets the parametrized constructor used to create the object. + + The parametrized constructor. + + + + When applied to a method, specifies that the method is called when an error occurs serializing an object. + + + + + Get and set values for a using reflection. + + + + + Initializes a new instance of the class. + + The member info. + + + + Sets the value. + + The target to set the value on. + The value to set on the target. + + + + Gets the value. + + The target to get the value from. + The value. + + + + Specifies type name handling options for the . + + + + + Do not include the .NET type name when serializing types. + + + + + Include the .NET type name when serializing into a JSON object structure. + + + + + Include the .NET type name when serializing into a JSON array structure. + + + + + Always include the .NET type name when serializing. + + + + + Converts the value to the specified type. + + The type to convert the value to. + The value to convert. + The converted type. + + + + Converts the value to the specified type. + + The type to convert the value to. + The value to convert. + The culture to use when converting. + The converted type. + + + + Converts the value to the specified type. + + The value to convert. + The culture to use when converting. + The type to convert the value to. + The converted type. + + + + Converts the value to the specified type. + + The type to convert the value to. + The value to convert. + The converted value if the conversion was successful or the default value of T if it failed. + + true if initialValue was converted successfully; otherwise, false. + + + + + Converts the value to the specified type. + + The type to convert the value to. + The value to convert. + The culture to use when converting. + The converted value if the conversion was successful or the default value of T if it failed. + + true if initialValue was converted successfully; otherwise, false. + + + + + Converts the value to the specified type. + + The value to convert. + The culture to use when converting. + The type to convert the value to. + The converted value if the conversion was successful or the default value of T if it failed. + + true if initialValue was converted successfully; otherwise, false. + + + + + Converts the value to the specified type. If the value is unable to be converted, the + value is checked whether it assignable to the specified type. + + The type to convert or cast the value to. + The value to convert. + The converted type. If conversion was unsuccessful, the initial value is returned if assignable to the target type + + + + Converts the value to the specified type. If the value is unable to be converted, the + value is checked whether it assignable to the specified type. + + The type to convert or cast the value to. + The value to convert. + The culture to use when converting. + The converted type. If conversion was unsuccessful, the initial value is returned if assignable to the target type + + + + Converts the value to the specified type. If the value is unable to be converted, the + value is checked whether it assignable to the specified type. + + The value to convert. + The culture to use when converting. + The type to convert or cast the value to. + + The converted type. If conversion was unsuccessful, the initial value + is returned if assignable to the target type. + + + + + Converts the value to the specified type. If the value is unable to be converted, the + value is checked whether it assignable to the specified type. + + The type to convert the value to. + The value to convert. + The converted value if the conversion was successful or the default value of T if it failed. + + true if initialValue was converted successfully or is assignable; otherwise, false. + + + + + Converts the value to the specified type. If the value is unable to be converted, the + value is checked whether it assignable to the specified type. + + The type to convert the value to. + The value to convert. + The culture to use when converting. + The converted value if the conversion was successful or the default value of T if it failed. + + true if initialValue was converted successfully or is assignable; otherwise, false. + + + + + Converts the value to the specified type. If the value is unable to be converted, the + value is checked whether it assignable to the specified type. + + The value to convert. + The culture to use when converting. + The type to convert the value to. + The converted value if the conversion was successful or the default value of T if it failed. + + true if initialValue was converted successfully or is assignable; otherwise, false. + + + + + Parses the specified enum member name, returning it's value. + + Name of the enum member. + + + + + Parses the specified enum member name, returning it's value. + + Name of the enum member. + If set to true ignore case. + + + + + Gets a dictionary of the names and values of an Enum type. - + - Changes the to Closed. + Gets a dictionary of the names and values of an Enum type. + + + + + + Gets a dictionary of the names and values of an Enum type. + + The enum type to get names and values for. + + + + + Gets the maximum valid value of an Enum type. Flags enums are ORed. + + The type of the returned value. Must be assignable from the enum's underlying value type. + The enum type to get the maximum value for. + + + + + Specifies the type of Json token. - + - Gets the quotation mark character used to enclose the value of a string. + This is returned by the if a method has not been called. - + - Gets the type of the current Json token. + An object start token. - + - Gets the text value of the current Json token. + An array start token. - + - Gets The Common Language Runtime (CLR) type for the current Json token. + A constructor start token. + + + An object property name. + + + + + A comment. + + + + + Raw JSON. + + + + + An interger. + + + + + A float. + + + + + A string. + + + + + A boolean. + + + + + A null token. + + + + + An undefined token. + + + + + An object end token. + + + + + An array end token. + + + + + A constructor end token. + + + + + A Date. + + + + + Byte data. + + + + + Specifies the state of the . + + + + + An exception has been thrown, which has left the in an invalid state. + You may call the method to put the in the Closed state. + Any other method calls results in an being thrown. + + + + + The method has been called. + + + + + An object is being written. + + + + + A array is being written. + + + + + A constructor is being written. + + + + + A property is being written. + + + + + A write method has not been called. + + + + + Specifies formatting options for the . + + + + + No special formatting is applied. This is the default. + + + + + Causes child objects to be indented according to the and settings. + + + + + Builds a string. Unlike StringBuilder this class lets you reuse it's internal buffer. + + + + + Determines whether the collection is null or empty. + + The collection. + + true if the collection is null or empty; otherwise, false. + + + + + Determines whether the collection is null or empty. + + The collection. + + true if the collection is null or empty; otherwise, false. + + + + + Determines whether the collection is null, empty or its contents are uninitialized values. + + The list. + + true if the collection is null or empty or its contents are uninitialized values; otherwise, false. + + + + + Makes a slice of the specified list in between the start and end indexes. + + The list. + The start index. + The end index. + A slice of the list. + + + + Makes a slice of the specified list in between the start and end indexes, + getting every so many items based upon the step. + + The list. + The start index. + The end index. + The step. + A slice of the list. + + + + Group the collection using a function which returns the key. + + The source collection to group. + The key selector. + A Dictionary with each key relating to a list of objects in a list grouped under it. + + + + Adds the elements of the specified collection to the specified generic IList. + + The list to add to. + The collection of elements to add. + + + + Gets the type of the typed collection's items. + + The type. + The type of the typed collection's items. + + + + Tests whether the list's items are their unitialized value. + + The list. + Whether the list's items are their unitialized value + + + + Gets the member's underlying type. + + The member. + The underlying type of the member. + + + + Determines whether the member is an indexed property. + + The member. + + true if the member is an indexed property; otherwise, false. + + + + + Determines whether the property is an indexed property. + + The property. + + true if the property is an indexed property; otherwise, false. + + + + + Gets the member's value on the object. + + The member. + The target object. + The member's value on the object. + + + + Sets the member's value on the target object. + + The member. + The target. + The value. + + + + Determines whether the specified MemberInfo can be read. + + The MemberInfo to determine whether can be read. + + true if the specified MemberInfo can be read; otherwise, false. + + + + + Determines whether the specified MemberInfo can be set. + + The MemberInfo to determine whether can be set. + + true if the specified MemberInfo can be set; otherwise, false. + + + + + Determines whether the string contains white space. + + The string to test for white space. + + true if the string contains white space; otherwise, false. + + + + + Determines whether the string is all white space. Empty string will return false. + + The string to test whether it is all white space. + + true if the string is all white space; otherwise, false. + + + + + Ensures the target string ends with the specified string. + + The target. + The value. + The target string with the value string at the end. + + + + Perform an action if the string is not null or empty. + + The value. + The action to perform. + + + + Indents the specified string. + + The string to indent. + The number of characters to indent by. + + + + + Indents the specified string. + + The string to indent. + The number of characters to indent by. + The indent character. + + + + + Numbers the lines. + + The string to number. + + + + + Nulls an empty string. + + The string. + Null if the string was null, otherwise the string unchanged. + diff --git a/bin/Newtonsoft.Json.dll b/bin/Newtonsoft.Json.dll index 3b14448814..177d9b5d94 100644 Binary files a/bin/Newtonsoft.Json.dll and b/bin/Newtonsoft.Json.dll differ diff --git a/bin/Newtonsoft.Json.pdb b/bin/Newtonsoft.Json.pdb index a8b8b8f27d..892546a3fc 100644 Binary files a/bin/Newtonsoft.Json.pdb and b/bin/Newtonsoft.Json.pdb differ diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example index 1a6059fee9..8d7c2cf467 100644 --- a/bin/OpenSim.ini.example +++ b/bin/OpenSim.ini.example @@ -1263,6 +1263,7 @@ ;Include-HGStandalone = "config-include/StandaloneHypergrid.ini" ;Include-Grid = "config-include/Grid.ini" ;Include-HGGrid = "config-include/GridHypergrid.ini" + ;Include-SimianGrid = "config-include/SimianGrid.ini" ; Then choose ; config-include/StandaloneCommon.ini.example (if you're in standlone) OR diff --git a/bin/config-include/SimianGrid.ini b/bin/config-include/SimianGrid.ini new file mode 100644 index 0000000000..58dcae9e24 --- /dev/null +++ b/bin/config-include/SimianGrid.ini @@ -0,0 +1,57 @@ +;; +;; Please don't change this file. +;; All optional settings are in GridCommon.ini.example, +;; which you can copy and change. +;; + +;; +;; In GridCommon.ini, these are the URLs you would use if SimianGrid is +;; installed at http://www.mygrid.com/Grid/ +;; +; AssetServerURI = "http://www.mygrid.com/Grid/?id=" +; InventoryServerURI = "http://www.mygrid.com/Grid/" +; AvatarServerURI = "http://www.mygrid.com/Grid/" +; PresenceServerURI = "http://www.mygrid.com/Grid/" +; UserAccountServerURI = "http://www.mygrid.com/Grid/" +; AuthenticationServerURI = "http://www.mygrid.com/Grid/" +; FriendsServerURI = "http://www.mygrid.com/Grid/" + +[Includes] + Include-Common = "config-include/GridCommon.ini" + +[Modules] + GridServices = "OpenSim.Services.Connectors.dll:SimianGridServiceConnector" + PresenceServices = "OpenSim.Services.Connectors.dll:SimianPresenceServiceConnector" + UserAccountServices = "OpenSim.Services.Connectors.dll:SimianGridUserAccountServiceConnector" + AuthenticationServices = "OpenSim.Services.Connectors.dll:SimianAuthenticationServiceConnector" + AssetServices = "OpenSim.Services.Connectors.dll:SimianAssetServiceConnector" + InventoryServices = "OpenSim.Services.Connectors.dll:SimianInventoryServiceConnector" + AvatarServices = "OpenSim.Services.Connectors.dll:SimianAvatarServiceConnector" + + NeighbourServices = "RemoteNeighbourServicesConnector" + SimulationServices = "RemoteSimulationConnectorModule" + EntityTransferModule = "BasicEntityTransferModule" + InventoryAccessModule = "BasicInventoryAccessModule" + + LandServiceInConnector = true + NeighbourServiceInConnector = true + SimulationServiceInConnector = true + LibraryModule = false + + AssetCaching = "FlotsamAssetCache" + +[Friends] + Connector = "OpenSim.Services.Connectors.dll:SimianFriendsServiceConnector" + +[GridService] + LocalServiceModule = "OpenSim.Services.GridService.dll:GridService" + StorageProvider = "OpenSim.Data.Null.dll:NullRegionData" + +[LibraryService] + LocalServiceModule = "OpenSim.Services.InventoryService.dll:LibraryService" + LibraryName = "OpenSim Library" + DefaultLibrary = "./inventory/Libraries.xml" + +[AssetService] + DefaultAssetLoader = "OpenSim.Framework.AssetLoader.Filesystem.dll" + AssetLoaderArgs = "assets/AssetSets.xml" diff --git a/bin/config-include/Standalone.ini b/bin/config-include/Standalone.ini index 30113708a1..92c215498e 100644 --- a/bin/config-include/Standalone.ini +++ b/bin/config-include/Standalone.ini @@ -5,17 +5,18 @@ ;; [Modules] - AssetServices = "LocalAssetServicesConnector" - InventoryServices = "LocalInventoryServicesConnector" - NeighbourServices = "LocalNeighbourServicesConnector" - AuthenticationServices = "LocalAuthenticationServicesConnector" - GridServices = "LocalGridServicesConnector" - PresenceServices = "LocalPresenceServicesConnector" - UserAccountServices = "LocalUserAccountServicesConnector" - SimulationServices = "LocalSimulationConnectorModule" - AvatarServices = "LocalAvatarServicesConnector" - EntityTransferModule = "BasicEntityTransferModule" - InventoryAccessModule = "BasicInventoryAccessModule" + AssetServices = "LocalAssetServicesConnector" + InventoryServices = "LocalInventoryServicesConnector" + NeighbourServices = "LocalNeighbourServicesConnector" + AuthenticationServices = "LocalAuthenticationServicesConnector" + GridServices = "LocalGridServicesConnector" + PresenceServices = "LocalPresenceServicesConnector" + UserAccountServices = "LocalUserAccountServicesConnector" + GridUserServices = "LocalGridUserServicesConnector" + SimulationServices = "LocalSimulationConnectorModule" + AvatarServices = "LocalAvatarServicesConnector" + EntityTransferModule = "BasicEntityTransferModule" + InventoryAccessModule = "BasicInventoryAccessModule" LibraryModule = true LLLoginServiceInConnector = true @@ -32,14 +33,12 @@ [AvatarService] LocalServiceModule = "OpenSim.Services.AvatarService.dll:AvatarService" - ConnectionString = "URI=file:avatars.db,version=3" [AuthorizationService] LocalServiceModule = "OpenSim.Services.AuthorizationService.dll:AuthorizationService" [AuthenticationService] LocalServiceModule = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" - ConnectionString = "URI=file:auth.db,version=3" [GridService] LocalServiceModule = "OpenSim.Services.GridService.dll:GridService" @@ -52,7 +51,6 @@ [UserAccountService] LocalServiceModule = "OpenSim.Services.UserAccountService.dll:UserAccountService" - ConnectionString = "URI=file:userprofiles.db,version=3" ;; These are for creating new accounts AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" @@ -60,22 +58,24 @@ GridService = "OpenSim.Services.GridService.dll:GridService" InventoryService = "OpenSim.Services.InventoryService.dll:InventoryService" +[GridUserService] + LocalServiceModule = "OpenSim.Services.UserAccountService.dll:GridUserService" + [FriendsService] LocalServiceModule = "OpenSim.Services.FriendsService.dll" - ConnectionString = "URI=file:friends.db,version=3" [Friends] Connector = "OpenSim.Services.FriendsService.dll" [LoginService] - LocalServiceModule = "OpenSim.Services.LLLoginService.dll:LLLoginService" - UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" + LocalServiceModule = "OpenSim.Services.LLLoginService.dll:LLLoginService" + UserAccountService = "OpenSim.Services.UserAccountService.dll:UserAccountService" AuthenticationService = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" - InventoryService = "OpenSim.Services.InventoryService.dll:InventoryService" - PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" - GridService = "OpenSim.Services.GridService.dll:GridService" - AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService" - FriendsService = "OpenSim.Services.FriendsService.dll:FriendsService" + InventoryService = "OpenSim.Services.InventoryService.dll:InventoryService" + PresenceService = "OpenSim.Services.PresenceService.dll:PresenceService" + GridService = "OpenSim.Services.GridService.dll:GridService" + AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService" + FriendsService = "OpenSim.Services.FriendsService.dll:FriendsService" WelcomeMessage = "Welcome, Avatar!" diff --git a/bin/config-include/StandaloneCommon.ini.example b/bin/config-include/StandaloneCommon.ini.example index 8e21a8c477..f89c67a779 100644 --- a/bin/config-include/StandaloneCommon.ini.example +++ b/bin/config-include/StandaloneCommon.ini.example @@ -1,19 +1,24 @@ +; This is the main configuration file for standalone OpenSim instances + [DatabaseService] ; - ;### Choose the DB + ; ### Choose the DB ; - ;--- For SQLite - StorageProvider = "OpenSim.Data.SQLite.dll" - ;--- For MySql + + ; SQLite + ; Uncomment this line if you want to use sqlite storage + Include-Storage = "config-include/storage/SQLiteStandalone.ini"; + + ; For MySql. + ; Uncomment these lines if you want to use mysql storage + ; Change the connection string to your db details ;StorageProvider = "OpenSim.Data.MySQL.dll" ;ConnectionString = "Data Source=localhost;Database=opensim;User ID=opensim;Password=***;" [AssetService] - DefaultAssetLoader = "OpenSim.Framework.AssetLoader.Filesystem.dll" AssetLoaderArgs = "assets/AssetSets.xml" - [Modules] ;; Choose 0 or 1 cache modules, and the corresponding config file, if it exists. ;; Copy the config .example file into your own .ini file and change configs there @@ -35,10 +40,10 @@ [GridService] ;; For in-memory region storage (default) - StorageProvider = "OpenSim.Data.Null.dll:NullRegionData" + StorageProvider = "OpenSim.Data.Null.dll:NullRegionData" ;;--- For MySql region storage (alternative) - ;StorageProvider = "OpenSim.Data.MySQL.dll:MySqlRegionData" - ;ConnectionString = "Data Source=localhost;Database=opensim;User ID=opensim;Password=***;" + ;StorageProvider = "OpenSim.Data.MySQL.dll:MySqlRegionData" + ;; Next, we can specify properties of regions, including default and fallback regions ;; The syntax is: Region_ = "" ;; where can be DefaultRegion, FallbackRegion, NoDirectLogin, Persistent, LockedOut diff --git a/bin/config-include/storage/SQLiteStandalone.ini b/bin/config-include/storage/SQLiteStandalone.ini new file mode 100644 index 0000000000..1ce03578f2 --- /dev/null +++ b/bin/config-include/storage/SQLiteStandalone.ini @@ -0,0 +1,16 @@ +; These are the initialization settings for running OpenSim Standalone with an SQLite database + +[DatabaseService] + StorageProvider = "OpenSim.Data.SQLite.dll" + +[AvatarService] + ConnectionString = "URI=file:avatars.db,version=3" + +[AuthenticationService] + ConnectionString = "URI=file:auth.db,version=3" + +[UserAccountService] + ConnectionString = "URI=file:userprofiles.db,version=3" + +[FriendsService] + ConnectionString = "URI=file:friends.db,version=3" diff --git a/bin/libbulletnet.dll b/bin/libbulletnet.dll index 3edd005cc8..8ec7c55da4 100644 Binary files a/bin/libbulletnet.dll and b/bin/libbulletnet.dll differ diff --git a/prebuild.xml b/prebuild.xml index 6850b50eb5..bfd6bbdfa6 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -959,7 +959,9 @@ + +