diff --git a/libsecondlife-cs/ObjectManager.cs b/libsecondlife-cs/ObjectManager.cs
index 66ffccea..d5f124c2 100644
--- a/libsecondlife-cs/ObjectManager.cs
+++ b/libsecondlife-cs/ObjectManager.cs
@@ -182,6 +182,7 @@ namespace libsecondlife
request.AgentData.AgentID = Client.Network.AgentID;
request.AgentData.SessionID = Client.Network.SessionID;
request.ObjectData = new RequestMultipleObjectsPacket.ObjectDataBlock[1];
+ request.ObjectData[0] = new RequestMultipleObjectsPacket.ObjectDataBlock();
request.ObjectData[0].ID = localID;
request.ObjectData[0].CacheMissType = 0;
@@ -258,14 +259,13 @@ namespace libsecondlife
//block.Text Hovering text
//block.TextColor LLColor4U of the hovering text
//block.MediaURL Quicktime stream
- // TODO: Multi-texture support
- if (block.TextureEntry.Length >= 16)
+ if (block.TextureEntry.Length >= 40)
{
- prim.Texture = new LLUUID(block.TextureEntry, 0);
+ prim.Textures = new TextureEntry(block.TextureEntry, 0);
}
else
{
- prim.Texture = new LLUUID();
+ prim.Textures = new TextureEntry();
}
//block.TextureAnim ?
//block.JointType ?
@@ -461,6 +461,13 @@ namespace libsecondlife
i += 16;
prim.LocalID = (uint)(block.Data[i++] + (block.Data[i++] << 8) +
(block.Data[i++] << 16) + (block.Data[i++] << 24));
+
+ i++; //PCode
+ prim.State = (uint)block.Data[i++];
+ i += 4; //CRC
+ prim.Material = (uint)block.Data[i++];
+ i++; //ClickAction
+
prim.Scale = new LLVector3(block.Data, i);
i += 12;
prim.Position = new LLVector3(block.Data, i);
@@ -468,9 +475,62 @@ namespace libsecondlife
prim.Rotation = new LLQuaternion(block.Data, i, true);
i += 12;
- // FIXME: Fill in the rest of these fields
- prim.PathCurve = (uint)block.Data[69];
- prim.ProfileCurve = (uint)block.Data[83];
+ uint flags = (uint)(block.Data[i++] + (block.Data[i++] << 8) +
+ (block.Data[i++] << 16) + (block.Data[i++] << 24));
+
+ if ((flags & 0x20) != 0)
+ {
+ prim.ParentID = (uint)(block.Data[i++] + (block.Data[i++] << 8) +
+ (block.Data[i++] << 16) + (block.Data[i++] << 24));
+ }
+ else
+ {
+ prim.ParentID = 0;
+ }
+
+ //Unknown field
+ if ((flags & 0x80) != 0)
+ {
+ i += 12;
+ }
+
+ //FIXME: read this string
+ while (block.Data[i] != 0)
+ {
+ i++;
+ }
+ i++;
+
+ //Unknown field, possibly text color.
+ if ((flags & 0x04) != 0)
+ {
+ i += 5;
+ }
+
+ prim.PathCurve = (uint)block.Data[i++];
+ prim.PathBegin = PrimObject.PathBeginFloat(block.Data[i++]);
+ prim.PathEnd = PrimObject.PathEndFloat(block.Data[i++]);
+ prim.PathScaleX = PrimObject.PathScaleFloat(block.Data[i++]);
+ prim.PathScaleY = PrimObject.PathScaleFloat(block.Data[i++]);
+ prim.PathShearX = PrimObject.PathShearFloat(block.Data[i++]);
+ prim.PathShearY = PrimObject.PathShearFloat(block.Data[i++]);
+ prim.PathTwist = (int)block.Data[i++];
+ prim.PathTwistBegin = (int)block.Data[i++];
+ prim.PathRadiusOffset = PrimObject.PathRadiusOffsetFloat((sbyte)block.Data[i++]);
+ prim.PathTaperX = PrimObject.PathTaperFloat(block.Data[i++]);
+ prim.PathTaperY = PrimObject.PathTaperFloat(block.Data[i++]);
+ prim.PathRevolutions = PrimObject.PathRevolutionsFloat(block.Data[i++]);
+ prim.PathSkew = PrimObject.PathSkewFloat(block.Data[i++]);
+
+ prim.ProfileCurve = (uint)block.Data[i++];
+ prim.ProfileBegin = PrimObject.ProfileBeginFloat(block.Data[i++]);
+ prim.ProfileEnd = PrimObject.ProfileEndFloat(block.Data[i++]);
+ prim.ProfileHollow = (uint)block.Data[i++];
+
+ //Unknown field
+ i += 4;
+
+ prim.Textures = new TextureEntry(block.Data, i);
if (OnNewPrim != null)
{
diff --git a/libsecondlife-cs/Prims.cs b/libsecondlife-cs/Prims.cs
index 74528dea..e953ed08 100644
--- a/libsecondlife-cs/Prims.cs
+++ b/libsecondlife-cs/Prims.cs
@@ -84,7 +84,7 @@ namespace libsecondlife
///
public int PathTwist = 0;
///
- public LLUUID Texture = new LLUUID(); // TODO: Add multi-texture support
+ public TextureEntry Textures = new TextureEntry();
///
public uint ProfileHollow = 0;
///
@@ -99,7 +99,7 @@ namespace libsecondlife
///
public PrimObject()
{
- Texture = new LLUUID();
+
}
///
@@ -108,7 +108,7 @@ namespace libsecondlife
///
public PrimObject(LLUUID texture)
{
- Texture = texture;
+ Textures.DefaultTexture.TextureID = texture;
}
///
diff --git a/libsecondlife-cs/Textures.cs b/libsecondlife-cs/Textures.cs
new file mode 100644
index 00000000..11654a67
--- /dev/null
+++ b/libsecondlife-cs/Textures.cs
@@ -0,0 +1,409 @@
+/*
+ * Copyright (c) 2006, Second Life Reverse Engineering Team
+ * All rights reserved.
+ *
+ * - 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.
+ * - Neither the name of the Second Life Reverse Engineering Team 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR 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;
+
+namespace libsecondlife
+{
+ public class TextureEntry
+ {
+ private Dictionary Textures;
+ public TextureEntryFace DefaultTexture;
+
+ public TextureEntryFace GetFace(uint index)
+ {
+ if (Textures.ContainsKey(index))
+ return Textures[index];
+ else
+ return DefaultTexture;
+ }
+
+ public TextureEntryFace SetFace(uint index)
+ {
+ if (!Textures.ContainsKey(index))
+ Textures[index] = new TextureEntryFace(this.DefaultTexture);
+
+ return Textures[index];
+ }
+
+ public TextureEntry()
+ {
+ Textures = new Dictionary();
+ DefaultTexture = new TextureEntryFace(null);
+ }
+
+ public TextureEntry(byte[] data, int pos)
+ {
+ FromByte(data, pos);
+ }
+
+ private bool readFaceBitfield(byte[] data, ref int pos, ref uint faceBits, ref uint bitfieldSize)
+ {
+ faceBits = 0;
+ bitfieldSize = 0;
+
+ if (pos >= data.Length)
+ return false;
+
+ byte b = 0;
+ do
+ {
+ b = data[pos];
+ faceBits = (faceBits << 7) | (uint)(b & 0x7F);
+ bitfieldSize += 7;
+ pos++;
+ }
+ while ((b & 0x80) != 0);
+
+ return (faceBits != 0);
+ }
+
+ private float Dequantize(byte[] byteArray, int pos, float lower, float upper)
+ {
+ ushort value = (ushort)(byteArray[pos] + (byteArray[pos + 1] << 8));
+ float QV = (float)value;
+ float range = upper - lower;
+ float QF = range / 65536.0F;
+ return (float)((QV * QF - (0.5F * range)) + QF);
+ }
+
+ private void FromByte(byte[] data, int pos)
+ {
+ Textures = new Dictionary();
+ DefaultTexture = new TextureEntryFace(null);
+
+ uint BitfieldSize = 0;
+ uint faceBits = 0;
+ int i = pos;
+
+ //Read TextureID ---------------------------------------
+ DefaultTexture.TextureID = new LLUUID(data, i);
+ i += 16;
+
+ while (readFaceBitfield(data, ref i, ref faceBits, ref BitfieldSize))
+ {
+ LLUUID tmpUUID = new LLUUID(data, i);
+ i += 16;
+
+ for (uint face = 0, bit = 1; face < BitfieldSize; face++, bit <<= 1)
+ if ((faceBits & bit) != 0)
+ SetFace(face).TextureID = tmpUUID;
+ }
+ //Read RGBA --------------------------------------------
+ DefaultTexture.RGBA = (uint)(data[i] + (data[i + 1] << 8) + (data[i + 2] << 16) + (data[i + 3] << 24));
+ i += 4;
+
+ while (readFaceBitfield(data, ref i, ref faceBits, ref BitfieldSize))
+ {
+ uint tmpUint = (uint)(data[i] + (data[i + 1] << 8) + (data[i + 2] << 16) + (data[i + 3] << 24));
+ i += 4;
+
+ for (uint face = 0, bit = 1; face < BitfieldSize; face++, bit <<= 1)
+ if ((faceBits & bit) != 0)
+ SetFace(face).RGBA = tmpUint;
+ }
+ //Read RepeatU -----------------------------------------
+ DefaultTexture.RepeatU = Dequantize(data, i, -101.0F, 101.0F) + 1.0F;
+ i += 2;
+
+ while (readFaceBitfield(data, ref i, ref faceBits, ref BitfieldSize))
+ {
+ float tmpFloat = Dequantize(data, i, -101.0F, 101.0F) + 1.0F;
+ i += 2;
+
+ for (uint face = 0, bit = 1; face < BitfieldSize; face++, bit <<= 1)
+ if ((faceBits & bit) != 0)
+ SetFace(face).RepeatU = tmpFloat;
+ }
+ //Read RepeatV -----------------------------------------
+ DefaultTexture.RepeatV = Dequantize(data, i, -101.0F, 101.0F) + 1.0F;
+ i += 2;
+
+ while (readFaceBitfield(data, ref i, ref faceBits, ref BitfieldSize))
+ {
+ float tmpFloat = Dequantize(data, i, -101.0F, 101.0F) + 1.0F;
+ i += 2;
+
+ for (uint face = 0, bit = 1; face < BitfieldSize; face++, bit <<= 1)
+ if ((faceBits & bit) != 0)
+ SetFace(face).RepeatV = tmpFloat;
+ }
+ //Read OffsetU -----------------------------------------
+ DefaultTexture.OffsetU = Dequantize(data, i, -1.0F, 1.0F);
+ i += 2;
+
+ while (readFaceBitfield(data, ref i, ref faceBits, ref BitfieldSize))
+ {
+ float tmpFloat = Dequantize(data, i, -1.0F, 1.0F);
+ i += 2;
+
+ for (uint face = 0, bit = 1; face < BitfieldSize; face++, bit <<= 1)
+ if ((faceBits & bit) != 0)
+ SetFace(face).OffsetU = tmpFloat;
+ }
+ //Read OffsetV -----------------------------------------
+ DefaultTexture.OffsetV = Dequantize(data, i, -1.0F, 1.0F);
+ i += 2;
+
+ while (readFaceBitfield(data, ref i, ref faceBits, ref BitfieldSize))
+ {
+ float tmpFloat = Dequantize(data, i, -1.0F, 1.0F);
+ i += 2;
+
+ for (uint face = 0, bit = 1; face < BitfieldSize; face++, bit <<= 1)
+ if ((faceBits & bit) != 0)
+ SetFace(face).OffsetV = tmpFloat;
+ }
+ //Read Rotation ----------------------------------------
+ DefaultTexture.Rotation = Dequantize(data, i, -359.995F, 359.995F);
+ i += 2;
+
+ while (readFaceBitfield(data, ref i, ref faceBits, ref BitfieldSize))
+ {
+ float tmpFloat = Dequantize(data, i, -359.995F, 359.995F);
+ i += 2;
+
+ for (uint face = 0, bit = 1; face < BitfieldSize; face++, bit <<= 1)
+ if ((faceBits & bit) != 0)
+ SetFace(face).Rotation = tmpFloat;
+ }
+ //Read Flags1 ------------------------------------------
+ DefaultTexture.Flags1 = data[i];
+ i++;
+
+ while (readFaceBitfield(data, ref i, ref faceBits, ref BitfieldSize))
+ {
+ byte tmpByte = data[i];
+ i++;
+
+ for (uint face = 0, bit = 1; face < BitfieldSize; face++, bit <<= 1)
+ if ((faceBits & bit) != 0)
+ SetFace(face).Flags1 = tmpByte;
+ }
+ //Read Flags2 ------------------------------------------
+ DefaultTexture.Flags2 = data[i];
+ i++;
+
+ while (readFaceBitfield(data, ref i, ref faceBits, ref BitfieldSize))
+ {
+ byte tmpByte = data[i];
+ i++;
+
+ for (uint face = 0, bit = 1; face < BitfieldSize; face++, bit <<= 1)
+ if ((faceBits & bit) != 0)
+ SetFace(face).Flags2 = tmpByte;
+ }
+ }
+ }
+
+ public class TextureEntryFace
+ {
+ [Flags]
+ private enum TextureAttributes : uint
+ {
+ None,
+ TextureID,
+ RGBA,
+ RepeatU,
+ RepeatV,
+ OffsetU,
+ OffsetV,
+ Rotation,
+ Flags1,
+ Flags2,
+ All = 0xFFFFFFFF
+ }
+
+ private TextureAttributes hasAttribute;
+
+ private TextureEntryFace DefaultTexture;
+
+ private LLUUID _TextureID;
+ private uint _RGBA;
+ private float _RepeatU;
+ private float _RepeatV;
+ private float _OffsetU;
+ private float _OffsetV;
+ private float _Rotation;
+ private byte _Flags1;
+ private byte _Flags2;
+
+ public TextureEntryFace(TextureEntryFace defaultTexture)
+ {
+ DefaultTexture = defaultTexture;
+ if (DefaultTexture == null)
+ hasAttribute = TextureAttributes.All;
+ else
+ hasAttribute = TextureAttributes.None;
+ }
+
+ public LLUUID TextureID
+ {
+ get
+ {
+ if ((hasAttribute & TextureAttributes.TextureID) != 0)
+ return _TextureID;
+ else
+ return DefaultTexture._TextureID;
+ }
+ set
+ {
+ _TextureID = value;
+ hasAttribute |= TextureAttributes.TextureID;
+ }
+ }
+
+ public uint RGBA
+ {
+ get
+ {
+ if ((hasAttribute & TextureAttributes.RGBA) != 0)
+ return _RGBA;
+ else
+ return DefaultTexture._RGBA;
+ }
+ set
+ {
+ _RGBA = value;
+ hasAttribute |= TextureAttributes.RGBA;
+ }
+ }
+
+ public float RepeatU
+ {
+ get
+ {
+ if ((hasAttribute & TextureAttributes.RepeatU) != 0)
+ return _RepeatU;
+ else
+ return DefaultTexture._RepeatU;
+ }
+ set
+ {
+ _RepeatU = value;
+ hasAttribute |= TextureAttributes.RepeatU;
+ }
+ }
+
+ public float RepeatV
+ {
+ get
+ {
+ if ((hasAttribute & TextureAttributes.RepeatV) != 0)
+ return _RepeatV;
+ else
+ return DefaultTexture._RepeatV;
+ }
+ set
+ {
+ _RepeatV = value;
+ hasAttribute |= TextureAttributes.RepeatV;
+ }
+ }
+
+ public float OffsetU
+ {
+ get
+ {
+ if ((hasAttribute & TextureAttributes.OffsetU) != 0)
+ return _OffsetU;
+ else
+ return DefaultTexture._OffsetU;
+ }
+ set
+ {
+ _OffsetU = value;
+ hasAttribute |= TextureAttributes.OffsetU;
+ }
+ }
+
+ public float OffsetV
+ {
+ get
+ {
+ if ((hasAttribute & TextureAttributes.OffsetV) != 0)
+ return _OffsetV;
+ else
+ return DefaultTexture._OffsetV;
+ }
+ set
+ {
+ _OffsetV = value;
+ hasAttribute |= TextureAttributes.OffsetV;
+ }
+ }
+
+ public float Rotation
+ {
+ get
+ {
+ if ((hasAttribute & TextureAttributes.Rotation) != 0)
+ return _Rotation;
+ else
+ return DefaultTexture._Rotation;
+ }
+ set
+ {
+ _Rotation = value;
+ hasAttribute |= TextureAttributes.Rotation;
+ }
+ }
+
+ public byte Flags1
+ {
+ get
+ {
+ if ((hasAttribute & TextureAttributes.Flags1) != 0)
+ return _Flags1;
+ else
+ return DefaultTexture._Flags1;
+ }
+ set
+ {
+ _Flags1 = value;
+ hasAttribute |= TextureAttributes.Flags1;
+ }
+ }
+
+ public byte Flags2
+ {
+ get
+ {
+ if ((hasAttribute & TextureAttributes.Flags2) != 0)
+ return _Flags2;
+ else
+ return DefaultTexture._Flags2;
+ }
+ set
+ {
+ _Flags2 = value;
+ hasAttribute |= TextureAttributes.Flags2;
+ }
+ }
+ }
+}
diff --git a/libsecondlife-cs/libsecondlife.csproj b/libsecondlife-cs/libsecondlife.csproj
index 9fd8b433..3debd787 100644
--- a/libsecondlife-cs/libsecondlife.csproj
+++ b/libsecondlife-cs/libsecondlife.csproj
@@ -149,6 +149,9 @@
Code
+
+ Code
+
Code