Files
libremetaverse/OpenMetaverse/Primitives/TextureEntry.cs
John Hurliman 8106fccdd2 * Changed Primitive.TextureEntry.ToBytes() to GetBytes() to follow naming conventions
* Added Primitive.TreeSpecies and Primitive.ScratchPad
* Converted Primitive.SoundFlags to the new SoundFlags enum
* Added a Utils.BytesToString() overload that accepts index and count parameters
* Added Utils.FloatToUInt16()
[Simian]
* Lots of changes in Simian to use the new unified ISceneProvider.ObjectAddOrUpdate() function
* Update flags are checked to determine the minimum sized packet that needs to be sent out for an update. ImprovedTerseObjectUpdate is working, and started work on ObjectUpdateCached (updates using this will currently not send)
* Adding three new variables to SimulationObject to store attachment-related state

git-svn-id: http://libopenmetaverse.googlecode.com/svn/libopenmetaverse/trunk@2478 52acb1d6-8a22-11de-b505-999d5b087335
2009-03-10 01:54:45 +00:00

1227 lines
45 KiB
C#

/*
* Copyright (c) 2007-2008, 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
/// <summary>
/// The type of bump-mapping applied to a face
/// </summary>
public enum Bumpiness : byte
{
/// <summary></summary>
None = 0,
/// <summary></summary>
Brightness = 1,
/// <summary></summary>
Darkness = 2,
/// <summary></summary>
Woodgrain = 3,
/// <summary></summary>
Bark = 4,
/// <summary></summary>
Bricks = 5,
/// <summary></summary>
Checker = 6,
/// <summary></summary>
Concrete = 7,
/// <summary></summary>
Crustytile = 8,
/// <summary></summary>
Cutstone = 9,
/// <summary></summary>
Discs = 10,
/// <summary></summary>
Gravel = 11,
/// <summary></summary>
Petridish = 12,
/// <summary></summary>
Siding = 13,
/// <summary></summary>
Stonetile = 14,
/// <summary></summary>
Stucco = 15,
/// <summary></summary>
Suction = 16,
/// <summary></summary>
Weave = 17
}
/// <summary>
/// The level of shininess applied to a face
/// </summary>
public enum Shininess : byte
{
/// <summary></summary>
None = 0,
/// <summary></summary>
Low = 0x40,
/// <summary></summary>
Medium = 0x80,
/// <summary></summary>
High = 0xC0
}
/// <summary>
/// The texture mapping style used for a face
/// </summary>
public enum MappingType : byte
{
/// <summary></summary>
Default = 0,
/// <summary></summary>
Planar = 2
}
/// <summary>
/// Flags in the TextureEntry block that describe which properties are
/// set
/// </summary>
[Flags]
public enum TextureAttributes : uint
{
/// <summary></summary>
None = 0,
/// <summary></summary>
TextureID = 1 << 0,
/// <summary></summary>
RGBA = 1 << 1,
/// <summary></summary>
RepeatU = 1 << 2,
/// <summary></summary>
RepeatV = 1 << 3,
/// <summary></summary>
OffsetU = 1 << 4,
/// <summary></summary>
OffsetV = 1 << 5,
/// <summary></summary>
Rotation = 1 << 6,
/// <summary></summary>
Material = 1 << 7,
/// <summary></summary>
Media = 1 << 8,
/// <summary></summary>
Glow = 1 << 9,
/// <summary></summary>
All = 0xFFFFFFFF
}
#endregion Enumerations
public partial class Primitive
{
#region Enums
/// <summary>
/// Texture animation mode
/// </summary>
[Flags]
public enum TextureAnimMode : uint
{
/// <summary>Disable texture animation</summary>
ANIM_OFF = 0x00,
/// <summary>Enable texture animation</summary>
ANIM_ON = 0x01,
/// <summary>Loop when animating textures</summary>
LOOP = 0x02,
/// <summary>Animate in reverse direction</summary>
REVERSE = 0x04,
/// <summary>Animate forward then reverse</summary>
PING_PONG = 0x08,
/// <summary>Slide texture smoothly instead of frame-stepping</summary>
SMOOTH = 0x10,
/// <summary>Rotate texture instead of using frames</summary>
ROTATE = 0x20,
/// <summary>Scale texture instead of using frames</summary>
SCALE = 0x40,
}
#endregion Enums
#region Subclasses
/// <summary>
/// A single textured face. Don't instantiate this class yourself, use the
/// methods in TextureEntry
/// </summary>
public class TextureEntryFace
{
// +----------+ 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 TextureAttributes hasAttribute;
private UUID textureID;
private TextureEntryFace DefaultTexture;
internal byte material;
internal byte media;
#region Properties
/// <summary></summary>
public Color4 RGBA
{
get
{
if ((hasAttribute & TextureAttributes.RGBA) != 0)
return rgba;
else
return DefaultTexture.rgba;
}
set
{
rgba = value;
hasAttribute |= TextureAttributes.RGBA;
}
}
/// <summary></summary>
public float RepeatU
{
get
{
if ((hasAttribute & TextureAttributes.RepeatU) != 0)
return repeatU;
else
return DefaultTexture.repeatU;
}
set
{
repeatU = value;
hasAttribute |= TextureAttributes.RepeatU;
}
}
/// <summary></summary>
public float RepeatV
{
get
{
if ((hasAttribute & TextureAttributes.RepeatV) != 0)
return repeatV;
else
return DefaultTexture.repeatV;
}
set
{
repeatV = value;
hasAttribute |= TextureAttributes.RepeatV;
}
}
/// <summary></summary>
public float OffsetU
{
get
{
if ((hasAttribute & TextureAttributes.OffsetU) != 0)
return offsetU;
else
return DefaultTexture.offsetU;
}
set
{
offsetU = value;
hasAttribute |= TextureAttributes.OffsetU;
}
}
/// <summary></summary>
public float OffsetV
{
get
{
if ((hasAttribute & TextureAttributes.OffsetV) != 0)
return offsetV;
else
return DefaultTexture.offsetV;
}
set
{
offsetV = value;
hasAttribute |= TextureAttributes.OffsetV;
}
}
/// <summary></summary>
public float Rotation
{
get
{
if ((hasAttribute & TextureAttributes.Rotation) != 0)
return rotation;
else
return DefaultTexture.rotation;
}
set
{
rotation = value;
hasAttribute |= TextureAttributes.Rotation;
}
}
/// <summary></summary>
public float Glow
{
get
{
if ((hasAttribute & TextureAttributes.Glow) != 0)
return glow;
else
return DefaultTexture.glow;
}
set
{
glow = value;
hasAttribute |= TextureAttributes.Glow;
}
}
/// <summary></summary>
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;
}
}
}
/// <summary>In the future this will specify whether a webpage is
/// attached to this face</summary>
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;
}
}
/// <summary></summary>
public UUID TextureID
{
get
{
if ((hasAttribute & TextureAttributes.TextureID) != 0)
return textureID;
else
return DefaultTexture.textureID;
}
set
{
textureID = value;
hasAttribute |= TextureAttributes.TextureID;
}
}
#endregion Properties
/// <summary>
/// Contains the definition for individual faces
/// </summary>
/// <param name="defaultTexture"></param>
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);
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();
return face;
}
/// <summary>
///
/// </summary>
/// <returns></returns>
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}",
RGBA, RepeatU, RepeatV, OffsetU, OffsetV, Rotation, Bump, Shiny, Fullbright, TexMapType,
MediaFlags, Glow, TextureID);
}
}
/// <summary>
/// Represents all of the texturable faces for an object
/// </summary>
/// <remarks>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</remarks>
public class TextureEntry
{
public const int MAX_FACES = 32;
public static readonly UUID WHITE_TEXTURE = new UUID("5748decc-f629-461c-9a36-a35a221fe21f");
/// <summary></summary>
public TextureEntryFace DefaultTexture;
/// <summary></summary>
public TextureEntryFace[] FaceTextures = new TextureEntryFace[MAX_FACES];
/// <summary>
/// Constructor that takes a default texture UUID
/// </summary>
/// <param name="defaultTextureID">Texture UUID to use as the default texture</param>
public TextureEntry(UUID defaultTextureID)
{
DefaultTexture = new TextureEntryFace(null);
DefaultTexture.TextureID = defaultTextureID;
}
/// <summary>
/// Constructor that takes a <code>TextureEntryFace</code> for the
/// default face
/// </summary>
/// <param name="defaultFace">Face to use as the default face</param>
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;
}
/// <summary>
/// Constructor that creates the TextureEntry class from a byte array
/// </summary>
/// <param name="data">Byte array containing the TextureEntry field</param>
/// <param name="pos">Starting position of the TextureEntry field in
/// the byte array</param>
/// <param name="length">Length of the TextureEntry field, in bytes</param>
public TextureEntry(byte[] data, int pos, int length)
{
FromBytes(data, pos, length);
}
/// <summary>
/// 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
/// </summary>
/// <param name="index">The index number of the face to create or
/// retrieve</param>
/// <returns>A TextureEntryFace containing all the properties for that
/// face</returns>
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];
}
/// <summary>
///
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
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;
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public OSD GetOSD()
{
OSDArray array = new OSDArray();
// 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)
{
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;
}
else
{
throw new ArgumentException("SD contains no elements");
}
}
private void FromBytes(byte[] data, int pos, int length)
{
if (length <= 0)
{
// 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
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public byte[] GetBytes()
{
if (DefaultTexture == null)
return Utils.EmptyBytes;
MemoryStream memStream = new MemoryStream();
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);
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);
}
}
#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));
}
}
#endregion Glow
return memStream.ToArray();
}
/// <summary>
///
/// </summary>
/// <returns></returns>
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
}
/// <summary>
/// Controls the texture animation of a particular prim
/// </summary>
public struct TextureAnimation
{
/// <summary></summary>
public TextureAnimMode Flags;
/// <summary></summary>
public uint Face;
/// <summary></summary>
public uint SizeX;
/// <summary></summary>
public uint SizeY;
/// <summary></summary>
public float Start;
/// <summary></summary>
public float Length;
/// <summary></summary>
public float Rate;
/// <summary>
///
/// </summary>
/// <param name="data"></param>
/// <param name="pos"></param>
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;
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
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;
}
}
#endregion Subclasses
#region Public Members
/// <summary></summary>
public TextureEntry Textures;
/// <summary></summary>
public TextureAnimation TextureAnim;
#endregion Public Members
}
}