From f44c012f60e3b264a4e405b23149bfc9b577e41d Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Mon, 9 Apr 2007 08:03:12 +0000 Subject: [PATCH] * Client.Self.Position now does acceleration/velocity interpolation * Added LLVector3 * float operator * Adding Oven.cs to Baker (will be moved in to libsecondlife later) that does the first baby steps of baking * Added the missing Helpers.GetResourceStream() function * Changed readonly Settings values to const git-svn-id: http://libopenmetaverse.googlecode.com/svn/trunk@1110 52acb1d6-8a22-11de-b505-999d5b087335 --- libsecondlife/Helpers.cs | 11 +++ libsecondlife/LLObject.cs | 12 +++- libsecondlife/MainAvatar.cs | 1 + libsecondlife/MainAvatarStatus.cs | 2 +- libsecondlife/NetworkManager.cs | 2 +- libsecondlife/ObjectManager.cs | 38 +++++++++- libsecondlife/Settings.cs | 16 +++-- libsecondlife/Simulator.cs | 8 +-- libsecondlife/Types.cs | 28 +++++++- libsecondlife/examples/Baker/Baker.csproj | 3 +- libsecondlife/examples/Baker/Oven.cs | 69 +++++++++++++++++++ .../examples/Baker/frmBaker.Designer.cs | 4 ++ libsecondlife/examples/Baker/frmBaker.cs | 3 +- .../libsecondlife.Utilities/Assets.cs | 2 +- 14 files changed, 176 insertions(+), 23 deletions(-) create mode 100644 libsecondlife/examples/Baker/Oven.cs diff --git a/libsecondlife/Helpers.cs b/libsecondlife/Helpers.cs index 3c8e95b2..9950d80e 100644 --- a/libsecondlife/Helpers.cs +++ b/libsecondlife/Helpers.cs @@ -958,5 +958,16 @@ namespace libsecondlife object list = serializer.Deserialize(reader); return (List)list; } + + /// + /// + /// + /// + /// + public static System.IO.Stream GetResourceStream(string resourceName) + { + System.Reflection.Assembly a = System.Reflection.Assembly.GetExecutingAssembly(); + return a.GetManifestResourceStream("libsecondlife.Resources." + resourceName); + } } } diff --git a/libsecondlife/LLObject.cs b/libsecondlife/LLObject.cs index fdc02a6e..d560e1d0 100644 --- a/libsecondlife/LLObject.cs +++ b/libsecondlife/LLObject.cs @@ -346,9 +346,15 @@ namespace libsecondlife #endregion Public Properties - internal ObjectData data = new ObjectData(); + internal ObjectData data; + internal DateTime lastInterpolation; + /// + /// + /// + /// + /// public override bool Equals(object obj) { LLObject llobj = obj as LLObject; @@ -357,6 +363,10 @@ namespace libsecondlife return ID.Equals(llobj.ID); } + /// + /// + /// + /// public override int GetHashCode() { return ID.GetHashCode(); diff --git a/libsecondlife/MainAvatar.cs b/libsecondlife/MainAvatar.cs index de73de25..383a6ff9 100644 --- a/libsecondlife/MainAvatar.cs +++ b/libsecondlife/MainAvatar.cs @@ -699,6 +699,7 @@ namespace libsecondlife internal uint sittingOn = 0; internal string teleportMessage = String.Empty; + internal DateTime lastInterpolation; private SecondLife Client; private TeleportStatus TeleportStat = TeleportStatus.None; diff --git a/libsecondlife/MainAvatarStatus.cs b/libsecondlife/MainAvatarStatus.cs index c8bd64e9..b9125b32 100644 --- a/libsecondlife/MainAvatarStatus.cs +++ b/libsecondlife/MainAvatarStatus.cs @@ -334,7 +334,7 @@ namespace libsecondlife Camera.CameraUpAxis = new LLVector3(0, 0, 0.9999f); Camera.Far = 384.0f; - UpdateTimer = new Timer(Client.Settings.AGENT_UPDATE_INTERVAL); + UpdateTimer = new Timer(Settings.AGENT_UPDATE_INTERVAL); UpdateTimer.Elapsed += new ElapsedEventHandler(UpdateTimer_Elapsed); UpdateTimer.Enabled = Client.Settings.SEND_AGENT_UPDATES; } diff --git a/libsecondlife/NetworkManager.cs b/libsecondlife/NetworkManager.cs index e3f0f2e1..27ce1e4a 100644 --- a/libsecondlife/NetworkManager.cs +++ b/libsecondlife/NetworkManager.cs @@ -528,7 +528,7 @@ namespace libsecondlife else { // Keep the Inbox size within a certain capacity - while (simulator.PacketArchive.Count >= Client.Settings.PACKET_ARCHIVE_SIZE) + while (simulator.PacketArchive.Count >= Settings.PACKET_ARCHIVE_SIZE) { simulator.PacketArchive.Dequeue(); simulator.PacketArchive.Dequeue(); simulator.PacketArchive.Dequeue(); simulator.PacketArchive.Dequeue(); diff --git a/libsecondlife/ObjectManager.cs b/libsecondlife/ObjectManager.cs index 7c8e5150..a33653f4 100644 --- a/libsecondlife/ObjectManager.cs +++ b/libsecondlife/ObjectManager.cs @@ -451,12 +451,18 @@ namespace libsecondlife #endregion + private const float HAVOK_TIMESTEP = 1.0f / 45.0f; + + /// /// Reference to the SecondLife client /// protected SecondLife Client; + private System.Timers.Timer InterpolationTimer; + + /// /// Instantiates a new ObjectManager class. This class should only be accessed /// through SecondLife.Objects, client applications should never create their own @@ -495,6 +501,12 @@ namespace libsecondlife Client.Network.RegisterCallback(PacketType.ObjectUpdateCached, new NetworkManager.PacketCallback(CachedUpdateHandler)); Client.Network.RegisterCallback(PacketType.KillObject, new NetworkManager.PacketCallback(KillObjectHandler)); Client.Network.RegisterCallback(PacketType.ObjectPropertiesFamily, new NetworkManager.PacketCallback(ObjectPropertiesFamilyHandler)); + + // If the callbacks aren't registered there's not point in doing client-side path prediction, + // so we set it up here + InterpolationTimer = new System.Timers.Timer(Settings.INTERPOLATION_UPDATE); + InterpolationTimer.Elapsed += new System.Timers.ElapsedEventHandler(InterpolationTimer_Elapsed); + InterpolationTimer.Start(); } #region Action Methods @@ -1962,7 +1974,6 @@ namespace libsecondlife #endregion - #region Utility Functions protected void SetAvatarSelfSittingOn(uint localid) @@ -1980,8 +1991,30 @@ namespace libsecondlife s.Dilation = (float)dilation / 65535.0f; } - #endregion + protected void InterpolationTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) + { + if (Client.Network.Connected) + { + // For now we only update Client.Self since there is no object tracking + // built in to the library + // Only do movement interpolation (extrapolation) when there is a non-zero velocity but + // no acceleration + if (Client.Self.Velocity != LLVector3.Zero || Client.Self.Acceleration != LLVector3.Zero) + { + TimeSpan interval = DateTime.Now - Client.Self.lastInterpolation; + float adjSeconds = (float)interval.TotalSeconds * Client.Network.CurrentSim.Dilation; + + Client.Self.Position += (Client.Self.Velocity + (0.5f * (adjSeconds - HAVOK_TIMESTEP)) * + Client.Self.Acceleration) * adjSeconds; + } + } + + // Make sure the last interpolated time is always updated + Client.Self.lastInterpolation = DateTime.Now; + } + + #endregion #region Event Notification @@ -2068,7 +2101,6 @@ namespace libsecondlife #endregion - #region Subclass Indirection /// diff --git a/libsecondlife/Settings.cs b/libsecondlife/Settings.cs index 448611b9..36416cc2 100644 --- a/libsecondlife/Settings.cs +++ b/libsecondlife/Settings.cs @@ -47,22 +47,24 @@ namespace libsecondlife /// The initial size of the packet inbox, where packets are /// stored before processing public const int PACKET_INBOX_SIZE = 100; - /// Maximum size of packet that we want to send over the wire - public readonly int MAX_PACKET_SIZE = 1200; + public const int MAX_PACKET_SIZE = 1200; /// Millisecond interval between ticks, where all ACKs are /// sent out and the age of unACKed packets is checked - public readonly int NETWORK_TICK_LENGTH = 500; + public const int NETWORK_TICK_LENGTH = 500; /// The maximum value of a packet sequence number before it /// rolls over back to one - public readonly int MAX_SEQUENCE = 0xFFFFFF; + public const int MAX_SEQUENCE = 0xFFFFFF; /// The maximum size of the sequence number archive, used to /// check for resent and/or duplicate packets - public readonly int PACKET_ARCHIVE_SIZE = 50; + public const int PACKET_ARCHIVE_SIZE = 50; /// Number of milliseconds between sending pings to each sim - public readonly int PING_INTERVAL = 2200; + public const int PING_INTERVAL = 2200; /// Number of milliseconds between sending camera updates - public readonly int AGENT_UPDATE_INTERVAL = 500; + public const int AGENT_UPDATE_INTERVAL = 500; + /// Number of milliseconds between updating the current + /// positions of moving, non-accelerating and non-colliding objects + public const int INTERPOLATION_UPDATE = 250; /// Number of milliseconds before a teleport attempt will time /// out diff --git a/libsecondlife/Simulator.cs b/libsecondlife/Simulator.cs index 51220335..cbc7fdea 100644 --- a/libsecondlife/Simulator.cs +++ b/libsecondlife/Simulator.cs @@ -294,7 +294,7 @@ namespace libsecondlife Estate = new EstateTools(Client); Network = client.Network; - PacketArchive = new Queue(Client.Settings.PACKET_ARCHIVE_SIZE); + PacketArchive = new Queue(Settings.PACKET_ARCHIVE_SIZE); InBytes = new Queue(Client.Settings.STATS_QUEUE_SIZE); OutBytes = new Queue(Client.Settings.STATS_QUEUE_SIZE); @@ -306,13 +306,13 @@ namespace libsecondlife // Initialize the callback for receiving a new packet ReceivedData = new AsyncCallback(OnReceivedData); - AckTimer = new System.Timers.Timer(Client.Settings.NETWORK_TICK_LENGTH); + AckTimer = new System.Timers.Timer(Settings.NETWORK_TICK_LENGTH); AckTimer.Elapsed += new System.Timers.ElapsedEventHandler(AckTimer_Elapsed); StatsTimer = new System.Timers.Timer(1000); StatsTimer.Elapsed += new System.Timers.ElapsedEventHandler(StatsTimer_Elapsed); - PingTimer = new System.Timers.Timer(Client.Settings.PING_INTERVAL); + PingTimer = new System.Timers.Timer(Settings.PING_INTERVAL); PingTimer.Elapsed += new System.Timers.ElapsedEventHandler(PingTimer_Elapsed); } @@ -425,7 +425,7 @@ namespace libsecondlife // Set the sequence number lock (SequenceLock) { - if (Sequence > Client.Settings.MAX_SEQUENCE) + if (Sequence > Settings.MAX_SEQUENCE) Sequence = 1; else Sequence++; diff --git a/libsecondlife/Types.cs b/libsecondlife/Types.cs index 21e9c1df..0a6796ed 100644 --- a/libsecondlife/Types.cs +++ b/libsecondlife/Types.cs @@ -310,11 +310,11 @@ namespace libsecondlife public struct LLVector3 { /// X value - [XmlAttribute("x"), DefaultValue(0)] public float X; + public float X; /// Y value - [XmlAttribute("y"), DefaultValue(0)] public float Y; + public float Y; /// Z value - [XmlAttribute("z"), DefaultValue(0)] public float Z; + public float Z; /// /// Constructor, builds a single-precision vector from a @@ -509,6 +509,28 @@ namespace libsecondlife return new LLVector3(lhs.X - rhs.X,lhs.Y - rhs.Y, lhs.Z - rhs.Z); } + /// + /// + /// + /// + /// + /// + public static LLVector3 operator *(LLVector3 vec, float val) + { + return new LLVector3(vec.X * val, vec.Y * val, vec.Z * val); + } + + /// + /// + /// + /// + /// + /// + public static LLVector3 operator *(float val, LLVector3 vec) + { + return new LLVector3(vec.X * val, vec.Y * val, vec.Z * val); + } + public static LLVector3 operator *(LLVector3 vec, LLQuaternion quat) { LLQuaternion vq = new LLQuaternion(vec.X, vec.Y, vec.Z, 0); diff --git a/libsecondlife/examples/Baker/Baker.csproj b/libsecondlife/examples/Baker/Baker.csproj index 061e40c4..dd5d9c5d 100644 --- a/libsecondlife/examples/Baker/Baker.csproj +++ b/libsecondlife/examples/Baker/Baker.csproj @@ -41,6 +41,7 @@ frmBaker.cs Designer + @@ -67,4 +68,4 @@ libsecondlife - \ No newline at end of file + diff --git a/libsecondlife/examples/Baker/Oven.cs b/libsecondlife/examples/Baker/Oven.cs new file mode 100644 index 00000000..2684f800 --- /dev/null +++ b/libsecondlife/examples/Baker/Oven.cs @@ -0,0 +1,69 @@ +using System; +using System.Drawing; +using System.Drawing.Imaging; +using System.Runtime.InteropServices; + +namespace Baker +{ + public static class Oven + { + public static Bitmap ModifyAlphaMask(Bitmap alpha, byte weight, float ramp) + { + // Create the new modifiable image (our canvas) + int width = alpha.Width; + int height = alpha.Height; + int pixelFormatSize = Image.GetPixelFormatSize(alpha.PixelFormat) / 8; + int stride = width * pixelFormatSize; + byte[] data = new byte[stride * height]; + GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned); + IntPtr pointer = Marshal.UnsafeAddrOfPinnedArrayElement(data, 0); + Bitmap modified = new Bitmap(width, height, stride, alpha.PixelFormat, pointer); + + // Copy the existing alpha mask to the canvas + Graphics g = Graphics.FromImage(modified); + g.DrawImageUnscaledAndClipped(alpha, new Rectangle(0, 0, width, height)); + g.Dispose(); + + // Modify the canvas based on the input weight and ramp values + // TODO: use the ramp + // TODO: only bother with the alpha values + for (int i = 0; i < data.Length; i++) + { + if (data[i] < weight) data[i] = 0; + } + + return modified; + } + + public static Bitmap ApplyAlphaMask(Bitmap source, Bitmap alpha) + { + // Create the new modifiable image (our canvas) + int width = source.Width; + int height = source.Height; + + if (alpha.Width != width || alpha.Height != height || + alpha.PixelFormat != source.PixelFormat) + { + throw new Exception("Source image and alpha mask formats do not match"); + } + + int pixelFormatSize = Image.GetPixelFormatSize(source.PixelFormat) / 8; + int stride = width * pixelFormatSize; + byte[] data = new byte[stride * height]; + GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned); + IntPtr pointer = Marshal.UnsafeAddrOfPinnedArrayElement(data, 0); + Bitmap modified = new Bitmap(width, height, stride, source.PixelFormat, pointer); + + // Copy the source image to the canvas + Graphics g = Graphics.FromImage(modified); + g.DrawImageUnscaledAndClipped(source, new Rectangle(0, 0, width, height)); + g.Dispose(); + + // Get access to the pixel data for the alpha mask (probably using lockbits) + + // Combine the alpha mask alpha bytes in to the canvas + + return modified; + } + } +} diff --git a/libsecondlife/examples/Baker/frmBaker.Designer.cs b/libsecondlife/examples/Baker/frmBaker.Designer.cs index c47847be..1e6f03cc 100644 --- a/libsecondlife/examples/Baker/frmBaker.Designer.cs +++ b/libsecondlife/examples/Baker/frmBaker.Designer.cs @@ -47,6 +47,7 @@ namespace Baker this.pic1.Size = new System.Drawing.Size(256, 256); this.pic1.TabIndex = 0; this.pic1.TabStop = false; + this.pic1.AutoSize = true; // // cmdLoadPic1 // @@ -66,6 +67,7 @@ namespace Baker this.pic2.Size = new System.Drawing.Size(256, 256); this.pic2.TabIndex = 2; this.pic2.TabStop = false; + this.pic1.AutoSize = true; // // pic3 // @@ -75,6 +77,7 @@ namespace Baker this.pic3.Size = new System.Drawing.Size(256, 256); this.pic3.TabIndex = 3; this.pic3.TabStop = false; + this.pic1.AutoSize = true; // // cmdLoadPic2 // @@ -130,3 +133,4 @@ namespace Baker } } + diff --git a/libsecondlife/examples/Baker/frmBaker.cs b/libsecondlife/examples/Baker/frmBaker.cs index 298632fb..ab81af26 100644 --- a/libsecondlife/examples/Baker/frmBaker.cs +++ b/libsecondlife/examples/Baker/frmBaker.cs @@ -23,7 +23,8 @@ namespace Baker if (stream != null) { - pic1.Image = OpenJPEGNet.LoadTGAClass.LoadTGA(stream); + Bitmap alphaMask = OpenJPEGNet.LoadTGAClass.LoadTGA(stream); + pic1.Image = Oven.ModifyAlphaMask(alphaMask, 245, 0.0f); } else { diff --git a/libsecondlife/libsecondlife.Utilities/Assets.cs b/libsecondlife/libsecondlife.Utilities/Assets.cs index bc2deef8..7a407cec 100644 --- a/libsecondlife/libsecondlife.Utilities/Assets.cs +++ b/libsecondlife/libsecondlife.Utilities/Assets.cs @@ -404,7 +404,7 @@ namespace libsecondlife.Utilities.Assets request.AssetBlock.TransactionID = upload.ID; request.AssetBlock.Type = (sbyte)type; - if (data.Length + 100 < Client.Settings.MAX_PACKET_SIZE) + if (data.Length + 100 < Settings.MAX_PACKET_SIZE) { // The whole asset will fit in this packet, makes things easy request.AssetBlock.AssetData = data;