/* * Copyright (c) 2006-2014, openmetaverse.org * 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 openmetaverse.org 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.IO; using OpenMetaverse.StructuredData; namespace OpenMetaverse { #region Enumerations /// /// The type of bump-mapping applied to a face /// public enum Bumpiness : byte { /// None = 0, /// Brightness = 1, /// Darkness = 2, /// Woodgrain = 3, /// Bark = 4, /// Bricks = 5, /// Checker = 6, /// Concrete = 7, /// Crustytile = 8, /// Cutstone = 9, /// Discs = 10, /// Gravel = 11, /// Petridish = 12, /// Siding = 13, /// Stonetile = 14, /// Stucco = 15, /// Suction = 16, /// Weave = 17 } /// /// The level of shininess applied to a face /// public enum Shininess : byte { /// None = 0, /// Low = 0x40, /// Medium = 0x80, /// High = 0xC0 } /// /// The texture mapping style used for a face /// public enum MappingType : byte { /// Default = 0, /// Planar = 2, /// Spherical = 4, /// Cylindrical = 6 } /// /// Flags in the TextureEntry block that describe which properties are /// set /// [Flags] public enum TextureAttributes : uint { /// None = 0, /// TextureID = 1 << 0, /// RGBA = 1 << 1, /// RepeatU = 1 << 2, /// RepeatV = 1 << 3, /// OffsetU = 1 << 4, /// OffsetV = 1 << 5, /// Rotation = 1 << 6, /// Material = 1 << 7, /// Media = 1 << 8, /// Glow = 1 << 9, /// MaterialID = 1 << 10, /// All = 0xFFFFFFFF } #endregion Enumerations public partial class Primitive { #region Enums /// /// Texture animation mode /// [Flags] public enum TextureAnimMode : uint { /// Disable texture animation ANIM_OFF = 0x00, /// Enable texture animation ANIM_ON = 0x01, /// Loop when animating textures LOOP = 0x02, /// Animate in reverse direction REVERSE = 0x04, /// Animate forward then reverse PING_PONG = 0x08, /// Slide texture smoothly instead of frame-stepping SMOOTH = 0x10, /// Rotate texture instead of using frames ROTATE = 0x20, /// Scale texture instead of using frames SCALE = 0x40, } #endregion Enums #region Subclasses /// /// A single textured face. Don't instantiate this class yourself, use the /// methods in TextureEntry /// public class TextureEntryFace : ICloneable { // +----------+ S = Shiny // | SSFBBBBB | F = Fullbright // | 76543210 | B = Bumpmap // +----------+ private const byte BUMP_MASK = 0x1F; private const byte FULLBRIGHT_MASK = 0x20; private const byte SHINY_MASK = 0xC0; // +----------+ M = Media Flags (web page) // | .....TTM | T = Texture Mapping // | 76543210 | . = Unused // +----------+ private const byte MEDIA_MASK = 0x01; private const byte TEX_MAP_MASK = 0x06; private Color4 rgba; private float repeatU; private float repeatV; private float offsetU; private float offsetV; private float rotation; private float glow; private byte materialb; private byte mediab; private TextureAttributes hasAttribute; private UUID textureID; private UUID materialID; private TextureEntryFace DefaultTexture; #region Properties /// internal byte material { get { if ((hasAttribute & TextureAttributes.Material) != 0) return materialb; else return DefaultTexture.material; } set { materialb = value; hasAttribute |= TextureAttributes.Material; } } /// internal byte media { get { if ((hasAttribute & TextureAttributes.Media) != 0) return mediab; else return DefaultTexture.media; } set { mediab = value; hasAttribute |= TextureAttributes.Media; } } /// public Color4 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 float Glow { get { if ((hasAttribute & TextureAttributes.Glow) != 0) return glow; else return DefaultTexture.glow; } set { glow = value; hasAttribute |= TextureAttributes.Glow; } } /// public Bumpiness Bump { get { if ((hasAttribute & TextureAttributes.Material) != 0) return (Bumpiness)(material & BUMP_MASK); else return DefaultTexture.Bump; } set { // Clear out the old material value material &= 0xE0; // Put the new bump value in the material byte material |= (byte)value; hasAttribute |= TextureAttributes.Material; } } public Shininess Shiny { get { if ((hasAttribute & TextureAttributes.Material) != 0) return (Shininess)(material & SHINY_MASK); else return DefaultTexture.Shiny; } set { // Clear out the old shiny value material &= 0x3F; // Put the new shiny value in the material byte material |= (byte)value; hasAttribute |= TextureAttributes.Material; } } public bool Fullbright { get { if ((hasAttribute & TextureAttributes.Material) != 0) return (material & FULLBRIGHT_MASK) != 0; else return DefaultTexture.Fullbright; } set { // Clear out the old fullbright value material &= 0xDF; if (value) { material |= 0x20; hasAttribute |= TextureAttributes.Material; } } } /// In the future this will specify whether a webpage is /// attached to this face public bool MediaFlags { get { if ((hasAttribute & TextureAttributes.Media) != 0) return (media & MEDIA_MASK) != 0; else return DefaultTexture.MediaFlags; } set { // Clear out the old mediaflags value media &= 0xFE; if (value) { media |= 0x01; hasAttribute |= TextureAttributes.Media; } } } public MappingType TexMapType { get { if ((hasAttribute & TextureAttributes.Media) != 0) return (MappingType)(media & TEX_MAP_MASK); else return DefaultTexture.TexMapType; } set { // Clear out the old texmap value media &= 0xF9; // Put the new texmap value in the media byte media |= (byte)value; hasAttribute |= TextureAttributes.Media; } } /// public UUID TextureID { get { if ((hasAttribute & TextureAttributes.TextureID) != 0) return textureID; else return DefaultTexture.textureID; } set { textureID = value; hasAttribute |= TextureAttributes.TextureID; } } /// public UUID MaterialID { get { if ((hasAttribute & TextureAttributes.MaterialID) != 0) return materialID; else return DefaultTexture.materialID; } set { materialID = value; hasAttribute |= TextureAttributes.MaterialID; } } #endregion Properties /// /// Contains the definition for individual faces /// /// public TextureEntryFace(TextureEntryFace defaultTexture) { rgba = Color4.White; repeatU = 1.0f; repeatV = 1.0f; DefaultTexture = defaultTexture; if (DefaultTexture == null) hasAttribute = TextureAttributes.All; else hasAttribute = TextureAttributes.None; } public OSD GetOSD(int faceNumber) { OSDMap tex = new OSDMap(10); if (faceNumber >= 0) tex["face_number"] = OSD.FromInteger(faceNumber); tex["colors"] = OSD.FromColor4(RGBA); tex["scales"] = OSD.FromReal(RepeatU); tex["scalet"] = OSD.FromReal(RepeatV); tex["offsets"] = OSD.FromReal(OffsetU); tex["offsett"] = OSD.FromReal(OffsetV); tex["imagerot"] = OSD.FromReal(Rotation); tex["bump"] = OSD.FromInteger((int)Bump); tex["shiny"] = OSD.FromInteger((int)Shiny); tex["fullbright"] = OSD.FromBoolean(Fullbright); tex["media_flags"] = OSD.FromInteger(Convert.ToInt32(MediaFlags)); tex["mapping"] = OSD.FromInteger((int)TexMapType); tex["glow"] = OSD.FromReal(Glow); if (TextureID != Primitive.TextureEntry.WHITE_TEXTURE) tex["imageid"] = OSD.FromUUID(TextureID); else tex["imageid"] = OSD.FromUUID(UUID.Zero); tex["materialid"] = OSD.FromUUID(materialID); return tex; } public static TextureEntryFace FromOSD(OSD osd, TextureEntryFace defaultFace, out int faceNumber) { OSDMap map = (OSDMap)osd; TextureEntryFace face = new TextureEntryFace(defaultFace); faceNumber = (map.ContainsKey("face_number")) ? map["face_number"].AsInteger() : -1; Color4 rgba = face.RGBA; rgba = ((OSDArray)map["colors"]).AsColor4(); face.RGBA = rgba; face.RepeatU = (float)map["scales"].AsReal(); face.RepeatV = (float)map["scalet"].AsReal(); face.OffsetU = (float)map["offsets"].AsReal(); face.OffsetV = (float)map["offsett"].AsReal(); face.Rotation = (float)map["imagerot"].AsReal(); face.Bump = (Bumpiness)map["bump"].AsInteger(); face.Shiny = (Shininess)map["shiny"].AsInteger(); face.Fullbright = map["fullbright"].AsBoolean(); face.MediaFlags = map["media_flags"].AsBoolean(); face.TexMapType = (MappingType)map["mapping"].AsInteger(); face.Glow = (float)map["glow"].AsReal(); face.TextureID = map["imageid"].AsUUID(); face.MaterialID = map["materialid"]; return face; } public object Clone() { TextureEntryFace ret = new TextureEntryFace(this.DefaultTexture == null ? null : (TextureEntryFace)this.DefaultTexture.Clone()); ret.rgba = rgba; ret.repeatU = repeatU; ret.repeatV = repeatV; ret.offsetU = offsetU; ret.offsetV = offsetV; ret.rotation = rotation; ret.glow = glow; ret.materialb = materialb; ret.mediab = mediab; ret.hasAttribute = hasAttribute; ret.textureID = textureID; ret.materialID = materialID; return ret; } public override int GetHashCode() { return RGBA.GetHashCode() ^ RepeatU.GetHashCode() ^ RepeatV.GetHashCode() ^ OffsetU.GetHashCode() ^ OffsetV.GetHashCode() ^ Rotation.GetHashCode() ^ Glow.GetHashCode() ^ Bump.GetHashCode() ^ Shiny.GetHashCode() ^ Fullbright.GetHashCode() ^ MediaFlags.GetHashCode() ^ TexMapType.GetHashCode() ^ TextureID.GetHashCode() ^ MaterialID.GetHashCode(); } /// /// /// /// public override string ToString() { return String.Format("Color: {0} RepeatU: {1} RepeatV: {2} OffsetU: {3} OffsetV: {4} " + "Rotation: {5} Bump: {6} Shiny: {7} Fullbright: {8} Mapping: {9} Media: {10} Glow: {11} ID: {12} MaterialID: {13}", RGBA, RepeatU, RepeatV, OffsetU, OffsetV, Rotation, Bump, Shiny, Fullbright, TexMapType, MediaFlags, Glow, TextureID, MaterialID); } } /// /// Represents all of the texturable faces for an object /// /// Grid objects have infinite faces, with each face /// using the properties of the default face unless set otherwise. So if /// you have a TextureEntry with a default texture uuid of X, and face 18 /// has a texture UUID of Y, every face would be textured with X except for /// face 18 that uses Y. In practice however, primitives utilize a maximum /// of nine faces public class TextureEntry { public const int MAX_FACES = 32; public static readonly UUID WHITE_TEXTURE = new UUID("5748decc-f629-461c-9a36-a35a221fe21f"); /// public TextureEntryFace DefaultTexture; /// public TextureEntryFace[] FaceTextures = new TextureEntryFace[MAX_FACES]; /// /// Constructor that takes a default texture UUID /// /// Texture UUID to use as the default texture public TextureEntry(UUID defaultTextureID) { DefaultTexture = new TextureEntryFace(null); DefaultTexture.TextureID = defaultTextureID; } /// /// Constructor that takes a TextureEntryFace for the /// default face /// /// Face to use as the default face public TextureEntry(TextureEntryFace defaultFace) { DefaultTexture = new TextureEntryFace(null); DefaultTexture.Bump = defaultFace.Bump; DefaultTexture.Fullbright = defaultFace.Fullbright; DefaultTexture.MediaFlags = defaultFace.MediaFlags; DefaultTexture.OffsetU = defaultFace.OffsetU; DefaultTexture.OffsetV = defaultFace.OffsetV; DefaultTexture.RepeatU = defaultFace.RepeatU; DefaultTexture.RepeatV = defaultFace.RepeatV; DefaultTexture.RGBA = defaultFace.RGBA; DefaultTexture.Rotation = defaultFace.Rotation; DefaultTexture.Glow = defaultFace.Glow; DefaultTexture.Shiny = defaultFace.Shiny; DefaultTexture.TexMapType = defaultFace.TexMapType; DefaultTexture.TextureID = defaultFace.TextureID; DefaultTexture.MaterialID = defaultFace.MaterialID; } /// /// Constructor that creates the TextureEntry class from a byte array /// /// Byte array containing the TextureEntry field /// Starting position of the TextureEntry field in /// the byte array /// Length of the TextureEntry field, in bytes public TextureEntry(byte[] data, int pos, int length) { FromBytes(data, pos, length); } /// /// This will either create a new face if a custom face for the given /// index is not defined, or return the custom face for that index if /// it already exists /// /// The index number of the face to create or /// retrieve /// A TextureEntryFace containing all the properties for that /// face public TextureEntryFace CreateFace(uint index) { if (index >= MAX_FACES) throw new Exception(index + " is outside the range of MAX_FACES"); if (FaceTextures[index] == null) FaceTextures[index] = new TextureEntryFace(this.DefaultTexture); return FaceTextures[index]; } /// /// /// /// /// public TextureEntryFace GetFace(uint index) { if (index >= MAX_FACES) throw new Exception(index + " is outside the range of MAX_FACES"); if (FaceTextures[index] != null) return FaceTextures[index]; else return DefaultTexture; } /// /// /// /// public OSD GetOSD() { OSDArray array = new OSDArray(); // If DefaultTexture is null, assume the whole TextureEntry is empty if (DefaultTexture == null) return array; // Otherwise, always add default texture array.Add(DefaultTexture.GetOSD(-1)); for (int i = 0; i < MAX_FACES; i++) { if (FaceTextures[i] != null) array.Add(FaceTextures[i].GetOSD(i)); } return array; } public static TextureEntry FromOSD(OSD osd) { if (osd.Type == OSDType.Array) { OSDArray array = (OSDArray)osd; OSDMap faceSD; if (array.Count > 0) { int faceNumber; faceSD = (OSDMap)array[0]; TextureEntryFace defaultFace = TextureEntryFace.FromOSD(faceSD, null, out faceNumber); TextureEntry te = new TextureEntry(defaultFace); for (int i = 1; i < array.Count; i++) { TextureEntryFace tex = TextureEntryFace.FromOSD(array[i], defaultFace, out faceNumber); if (faceNumber >= 0 && faceNumber < te.FaceTextures.Length) te.FaceTextures[faceNumber] = tex; } return te; } } return new TextureEntry(UUID.Zero); } private void FromBytes(byte[] data, int pos, int length) { if (length < 16) { // No TextureEntry to process DefaultTexture = null; return; } else { DefaultTexture = new TextureEntryFace(null); } uint bitfieldSize = 0; uint faceBits = 0; int i = pos; #region Texture DefaultTexture.TextureID = new UUID(data, i); i += 16; while (ReadFaceBitfield(data, ref i, ref faceBits, ref bitfieldSize)) { UUID tmpUUID = new UUID(data, i); i += 16; for (uint face = 0, bit = 1; face < bitfieldSize; face++, bit <<= 1) if ((faceBits & bit) != 0) CreateFace(face).TextureID = tmpUUID; } #endregion Texture #region Color DefaultTexture.RGBA = new Color4(data, i, true); i += 4; while (ReadFaceBitfield(data, ref i, ref faceBits, ref bitfieldSize)) { Color4 tmpColor = new Color4(data, i, true); i += 4; for (uint face = 0, bit = 1; face < bitfieldSize; face++, bit <<= 1) if ((faceBits & bit) != 0) CreateFace(face).RGBA = tmpColor; } #endregion Color #region RepeatU DefaultTexture.RepeatU = Utils.BytesToFloat(data, i); i += 4; while (ReadFaceBitfield(data, ref i, ref faceBits, ref bitfieldSize)) { float tmpFloat = Utils.BytesToFloat(data, i); i += 4; for (uint face = 0, bit = 1; face < bitfieldSize; face++, bit <<= 1) if ((faceBits & bit) != 0) CreateFace(face).RepeatU = tmpFloat; } #endregion RepeatU #region RepeatV DefaultTexture.RepeatV = Utils.BytesToFloat(data, i); i += 4; while (ReadFaceBitfield(data, ref i, ref faceBits, ref bitfieldSize)) { float tmpFloat = Utils.BytesToFloat(data, i); i += 4; for (uint face = 0, bit = 1; face < bitfieldSize; face++, bit <<= 1) if ((faceBits & bit) != 0) CreateFace(face).RepeatV = tmpFloat; } #endregion RepeatV #region OffsetU DefaultTexture.OffsetU = Helpers.TEOffsetFloat(data, i); i += 2; while (ReadFaceBitfield(data, ref i, ref faceBits, ref bitfieldSize)) { float tmpFloat = Helpers.TEOffsetFloat(data, i); i += 2; for (uint face = 0, bit = 1; face < bitfieldSize; face++, bit <<= 1) if ((faceBits & bit) != 0) CreateFace(face).OffsetU = tmpFloat; } #endregion OffsetU #region OffsetV DefaultTexture.OffsetV = Helpers.TEOffsetFloat(data, i); i += 2; while (ReadFaceBitfield(data, ref i, ref faceBits, ref bitfieldSize)) { float tmpFloat = Helpers.TEOffsetFloat(data, i); i += 2; for (uint face = 0, bit = 1; face < bitfieldSize; face++, bit <<= 1) if ((faceBits & bit) != 0) CreateFace(face).OffsetV = tmpFloat; } #endregion OffsetV #region Rotation DefaultTexture.Rotation = Helpers.TERotationFloat(data, i); i += 2; while (ReadFaceBitfield(data, ref i, ref faceBits, ref bitfieldSize)) { float tmpFloat = Helpers.TERotationFloat(data, i); i += 2; for (uint face = 0, bit = 1; face < bitfieldSize; face++, bit <<= 1) if ((faceBits & bit) != 0) CreateFace(face).Rotation = tmpFloat; } #endregion Rotation #region Material DefaultTexture.material = 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) CreateFace(face).material = tmpByte; } #endregion Material #region Media DefaultTexture.media = data[i]; i++; while (i - pos < length && 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) CreateFace(face).media = tmpByte; } #endregion Media #region Glow DefaultTexture.Glow = Helpers.TEGlowFloat(data, i); i++; while (ReadFaceBitfield(data, ref i, ref faceBits, ref bitfieldSize)) { float tmpFloat = Helpers.TEGlowFloat(data, i); i++; for (uint face = 0, bit = 1; face < bitfieldSize; face++, bit <<= 1) if ((faceBits & bit) != 0) CreateFace(face).Glow = tmpFloat; } #endregion Glow #region MaterialID if (i - pos + 16 <= length) { DefaultTexture.MaterialID = new UUID(data, i); i += 16; while (i - pos + 16 <= length && ReadFaceBitfield(data, ref i, ref faceBits, ref bitfieldSize)) { UUID tmpUUID = new UUID(data, i); i += 16; for (uint face = 0, bit = 1; face < bitfieldSize; face++, bit <<= 1) if ((faceBits & bit) != 0) CreateFace(face).MaterialID = tmpUUID; } } #endregion MaterialID } /// /// /// /// public byte[] GetBytes() { if (DefaultTexture == null) return Utils.EmptyBytes; using (MemoryStream memStream = new MemoryStream()) { using (BinaryWriter binWriter = new BinaryWriter(memStream)) { #region Bitfield Setup uint[] textures = new uint[FaceTextures.Length]; InitializeArray(ref textures); uint[] rgbas = new uint[FaceTextures.Length]; InitializeArray(ref rgbas); uint[] repeatus = new uint[FaceTextures.Length]; InitializeArray(ref repeatus); uint[] repeatvs = new uint[FaceTextures.Length]; InitializeArray(ref repeatvs); uint[] offsetus = new uint[FaceTextures.Length]; InitializeArray(ref offsetus); uint[] offsetvs = new uint[FaceTextures.Length]; InitializeArray(ref offsetvs); uint[] rotations = new uint[FaceTextures.Length]; InitializeArray(ref rotations); uint[] materials = new uint[FaceTextures.Length]; InitializeArray(ref materials); uint[] medias = new uint[FaceTextures.Length]; InitializeArray(ref medias); uint[] glows = new uint[FaceTextures.Length]; InitializeArray(ref glows); uint[] materialIDs = new uint[FaceTextures.Length]; InitializeArray(ref materialIDs); for (int i = 0; i < FaceTextures.Length; i++) { if (FaceTextures[i] == null) continue; if (FaceTextures[i].TextureID != DefaultTexture.TextureID) { if (textures[i] == UInt32.MaxValue) textures[i] = 0; textures[i] |= (uint)(1 << i); } if (FaceTextures[i].RGBA != DefaultTexture.RGBA) { if (rgbas[i] == UInt32.MaxValue) rgbas[i] = 0; rgbas[i] |= (uint)(1 << i); } if (FaceTextures[i].RepeatU != DefaultTexture.RepeatU) { if (repeatus[i] == UInt32.MaxValue) repeatus[i] = 0; repeatus[i] |= (uint)(1 << i); } if (FaceTextures[i].RepeatV != DefaultTexture.RepeatV) { if (repeatvs[i] == UInt32.MaxValue) repeatvs[i] = 0; repeatvs[i] |= (uint)(1 << i); } if (Helpers.TEOffsetShort(FaceTextures[i].OffsetU) != Helpers.TEOffsetShort(DefaultTexture.OffsetU)) { if (offsetus[i] == UInt32.MaxValue) offsetus[i] = 0; offsetus[i] |= (uint)(1 << i); } if (Helpers.TEOffsetShort(FaceTextures[i].OffsetV) != Helpers.TEOffsetShort(DefaultTexture.OffsetV)) { if (offsetvs[i] == UInt32.MaxValue) offsetvs[i] = 0; offsetvs[i] |= (uint)(1 << i); } if (Helpers.TERotationShort(FaceTextures[i].Rotation) != Helpers.TERotationShort(DefaultTexture.Rotation)) { if (rotations[i] == UInt32.MaxValue) rotations[i] = 0; rotations[i] |= (uint)(1 << i); } if (FaceTextures[i].material != DefaultTexture.material) { if (materials[i] == UInt32.MaxValue) materials[i] = 0; materials[i] |= (uint)(1 << i); } if (FaceTextures[i].media != DefaultTexture.media) { if (medias[i] == UInt32.MaxValue) medias[i] = 0; medias[i] |= (uint)(1 << i); } if (Helpers.TEGlowByte(FaceTextures[i].Glow) != Helpers.TEGlowByte(DefaultTexture.Glow)) { if (glows[i] == UInt32.MaxValue) glows[i] = 0; glows[i] |= (uint)(1 << i); } if (FaceTextures[i].MaterialID != DefaultTexture.MaterialID) { if (materialIDs[i] == UInt32.MaxValue) materialIDs[i] = 0; materialIDs[i] |= (uint)(1 << i); } } #endregion Bitfield Setup #region Texture binWriter.Write(DefaultTexture.TextureID.GetBytes()); for (int i = 0; i < textures.Length; i++) { if (textures[i] != UInt32.MaxValue) { binWriter.Write(GetFaceBitfieldBytes(textures[i])); binWriter.Write(FaceTextures[i].TextureID.GetBytes()); } } binWriter.Write((byte)0); #endregion Texture #region Color // Serialize the color bytes inverted to optimize for zerocoding binWriter.Write(DefaultTexture.RGBA.GetBytes(true)); for (int i = 0; i < rgbas.Length; i++) { if (rgbas[i] != UInt32.MaxValue) { binWriter.Write(GetFaceBitfieldBytes(rgbas[i])); // Serialize the color bytes inverted to optimize for zerocoding binWriter.Write(FaceTextures[i].RGBA.GetBytes(true)); } } binWriter.Write((byte)0); #endregion Color #region RepeatU binWriter.Write(DefaultTexture.RepeatU); for (int i = 0; i < repeatus.Length; i++) { if (repeatus[i] != UInt32.MaxValue) { binWriter.Write(GetFaceBitfieldBytes(repeatus[i])); binWriter.Write(FaceTextures[i].RepeatU); } } binWriter.Write((byte)0); #endregion RepeatU #region RepeatV binWriter.Write(DefaultTexture.RepeatV); for (int i = 0; i < repeatvs.Length; i++) { if (repeatvs[i] != UInt32.MaxValue) { binWriter.Write(GetFaceBitfieldBytes(repeatvs[i])); binWriter.Write(FaceTextures[i].RepeatV); } } binWriter.Write((byte)0); #endregion RepeatV #region OffsetU binWriter.Write(Helpers.TEOffsetShort(DefaultTexture.OffsetU)); for (int i = 0; i < offsetus.Length; i++) { if (offsetus[i] != UInt32.MaxValue) { binWriter.Write(GetFaceBitfieldBytes(offsetus[i])); binWriter.Write(Helpers.TEOffsetShort(FaceTextures[i].OffsetU)); } } binWriter.Write((byte)0); #endregion OffsetU #region OffsetV binWriter.Write(Helpers.TEOffsetShort(DefaultTexture.OffsetV)); for (int i = 0; i < offsetvs.Length; i++) { if (offsetvs[i] != UInt32.MaxValue) { binWriter.Write(GetFaceBitfieldBytes(offsetvs[i])); binWriter.Write(Helpers.TEOffsetShort(FaceTextures[i].OffsetV)); } } binWriter.Write((byte)0); #endregion OffsetV #region Rotation binWriter.Write(Helpers.TERotationShort(DefaultTexture.Rotation)); for (int i = 0; i < rotations.Length; i++) { if (rotations[i] != UInt32.MaxValue) { binWriter.Write(GetFaceBitfieldBytes(rotations[i])); binWriter.Write(Helpers.TERotationShort(FaceTextures[i].Rotation)); } } binWriter.Write((byte)0); #endregion Rotation #region Material binWriter.Write(DefaultTexture.material); for (int i = 0; i < materials.Length; i++) { if (materials[i] != UInt32.MaxValue) { binWriter.Write(GetFaceBitfieldBytes(materials[i])); binWriter.Write(FaceTextures[i].material); } } binWriter.Write((byte)0); #endregion Material #region Media binWriter.Write(DefaultTexture.media); for (int i = 0; i < medias.Length; i++) { if (medias[i] != UInt32.MaxValue) { binWriter.Write(GetFaceBitfieldBytes(medias[i])); binWriter.Write(FaceTextures[i].media); } } binWriter.Write((byte)0); #endregion Media #region Glow binWriter.Write(Helpers.TEGlowByte(DefaultTexture.Glow)); for (int i = 0; i < glows.Length; i++) { if (glows[i] != UInt32.MaxValue) { binWriter.Write(GetFaceBitfieldBytes(glows[i])); binWriter.Write(Helpers.TEGlowByte(FaceTextures[i].Glow)); } } binWriter.Write((byte)0); #endregion Glow #region MaterialID binWriter.Write(DefaultTexture.MaterialID.GetBytes()); for (int i = 0; i < materialIDs.Length; i++) { if (materialIDs[i] != UInt32.MaxValue) { binWriter.Write(GetFaceBitfieldBytes(materialIDs[i])); binWriter.Write(FaceTextures[i].MaterialID.GetBytes()); } } #endregion MaterialID return memStream.ToArray(); } } } public override int GetHashCode() { int hashCode = DefaultTexture != null ? DefaultTexture.GetHashCode() : 0; for (int i = 0; i < FaceTextures.Length; i++) { if (FaceTextures[i] != null) hashCode ^= FaceTextures[i].GetHashCode(); } return hashCode; } /// /// /// /// public override string ToString() { string output = String.Empty; output += "Default Face: " + DefaultTexture.ToString() + Environment.NewLine; for (int i = 0; i < FaceTextures.Length; i++) { if (FaceTextures[i] != null) output += "Face " + i + ": " + FaceTextures[i].ToString() + Environment.NewLine; } return output; } #region Helpers private void InitializeArray(ref uint[] array) { for (int i = 0; i < array.Length; i++) array[i] = UInt32.MaxValue; } 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 byte[] GetFaceBitfieldBytes(uint bitfield) { int byteLength = 0; uint tmpBitfield = bitfield; while (tmpBitfield != 0) { tmpBitfield >>= 7; byteLength++; } if (byteLength == 0) return new byte[1] { 0 }; byte[] bytes = new byte[byteLength]; for (int i = 0; i < byteLength; i++) { bytes[i] = (byte)((bitfield >> (7 * (byteLength - i - 1))) & 0x7F); if (i < byteLength - 1) bytes[i] |= 0x80; } return bytes; } #endregion Helpers } /// /// Controls the texture animation of a particular prim /// public struct TextureAnimation { /// public TextureAnimMode Flags; /// public uint Face; /// public uint SizeX; /// public uint SizeY; /// public float Start; /// public float Length; /// public float Rate; /// /// /// /// /// public TextureAnimation(byte[] data, int pos) { if (data.Length >= 16) { Flags = (TextureAnimMode)((uint)data[pos++]); Face = (uint)data[pos++]; SizeX = (uint)data[pos++]; SizeY = (uint)data[pos++]; Start = Utils.BytesToFloat(data, pos); Length = Utils.BytesToFloat(data, pos + 4); Rate = Utils.BytesToFloat(data, pos + 8); } else { Flags = 0; Face = 0; SizeX = 0; SizeY = 0; Start = 0.0f; Length = 0.0f; Rate = 0.0f; } } /// /// /// /// public byte[] GetBytes() { byte[] data = new byte[16]; int pos = 0; data[pos++] = (byte)Flags; data[pos++] = (byte)Face; data[pos++] = (byte)SizeX; data[pos++] = (byte)SizeY; Utils.FloatToBytes(Start).CopyTo(data, pos); Utils.FloatToBytes(Length).CopyTo(data, pos + 4); Utils.FloatToBytes(Rate).CopyTo(data, pos + 4); return data; } public OSD GetOSD() { OSDMap map = new OSDMap(); map["face"] = OSD.FromInteger(Face); map["flags"] = OSD.FromInteger((uint)Flags); map["length"] = OSD.FromReal(Length); map["rate"] = OSD.FromReal(Rate); map["size_x"] = OSD.FromInteger(SizeX); map["size_y"] = OSD.FromInteger(SizeY); map["start"] = OSD.FromReal(Start); return map; } public static TextureAnimation FromOSD(OSD osd) { TextureAnimation anim = new TextureAnimation(); OSDMap map = osd as OSDMap; if (map != null) { anim.Face = map["face"].AsUInteger(); anim.Flags = (TextureAnimMode)map["flags"].AsUInteger(); anim.Length = (float)map["length"].AsReal(); anim.Rate = (float)map["rate"].AsReal(); anim.SizeX = map["size_x"].AsUInteger(); anim.SizeY = map["size_y"].AsUInteger(); anim.Start = (float)map["start"].AsReal(); } return anim; } } #endregion Subclasses #region Public Members /// public TextureEntry Textures; /// public TextureAnimation TextureAnim; #endregion Public Members } }