/*
* Copyright (c) 2007, 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.IO;
namespace libsecondlife
{
public abstract partial class LLObject
{
#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
{
///
None = 0,
///
Low = 0x40,
///
Medium = 0x80,
///
High = 0xC0
}
///
/// The texture mapping style used for a face
///
public enum Mapping
{
///
Default = 0,
///
Planar = 2
}
///
/// 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,
///
All = 0xFFFFFFFF
}
#endregion Enumerations
///
/// A single textured face. Don't instantiate this class yourself, use the
/// methods in TextureEntry
///
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 LLColor rgba;
private float repeatU;
private float repeatV;
private float offsetU;
private float offsetV;
private float rotation;
private TextureAttributes hasAttribute;
private LLUUID textureID;
private TextureEntryFace DefaultTexture;
internal byte material;
internal byte media;
#region Properties
///
public LLColor 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 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 Mapping TexMapType
{
get
{
if ((hasAttribute & TextureAttributes.Media) != 0)
return (Mapping)(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 LLUUID TextureID
{
get
{
if ((hasAttribute & TextureAttributes.TextureID) != 0)
return textureID;
else
return DefaultTexture.textureID;
}
set
{
textureID = value;
hasAttribute |= TextureAttributes.TextureID;
}
}
#endregion Properties
///
/// Contains the definition for individual faces
///
///
public TextureEntryFace(TextureEntryFace defaultTexture)
{
repeatU = 1.0f;
repeatV = 1.0f;
DefaultTexture = defaultTexture;
if (DefaultTexture == null)
hasAttribute = TextureAttributes.All;
else
hasAttribute = TextureAttributes.None;
}
public Dictionary ToLLSD()
{
Dictionary tex = new Dictionary(10);
tex["bump"] = (int)Bump;
tex["colors"] = RGBA.ToLLSD();
tex["fullbright"] = Fullbright;
tex["imageid"] = TextureID;
tex["imagerot"] = Rotation;
tex["media_flags"] = MediaFlags;
tex["offsets"] = OffsetU;
tex["offsett"] = OffsetV;
tex["scales"] = RepeatU;
tex["scalet"] = RepeatV;
return tex;
}
///
///
///
///
public override string ToString()
{
return String.Format("RGBA: {0} RepeatU: {1} RepeatV: {2} OffsetU: {3} OffsetV: {4} Rotation: {5} " +
"TextureAttributes: {6} Material: {7} Media: {8} ID: {9}", rgba, repeatU, repeatV, offsetU,
offsetV, rotation, hasAttribute.ToString(), material, media, textureID.ToStringHyphenated());
}
}
///
/// Represents all of the texturable faces for an object
///
/// Objects in Second Life 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 TextureEntryFace DefaultTexture;
///
public TextureEntryFace[] FaceTextures = new TextureEntryFace[MAX_FACES];
public const int MAX_FACES = 32;
///
/// Constructor that takes a default texture UUID
///
/// Texture UUID to use as the default texture
public TextureEntry(LLUUID defaultTextureID)
{
DefaultTexture = new TextureEntryFace(null);
DefaultTexture.TextureID = defaultTextureID;
}
///
/// 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 List