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