/* * 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.IO; using System.Collections.Generic; using System.Reflection; using System.Runtime.InteropServices; using OpenMetaverse.Assets; using OpenMetaverse.StructuredData; // The common elements shared between rendering plugins are defined here namespace OpenMetaverse.Rendering { #region Enums public enum FaceType : ushort { PathBegin = 0x1 << 0, PathEnd = 0x1 << 1, InnerSide = 0x1 << 2, ProfileBegin = 0x1 << 3, ProfileEnd = 0x1 << 4, OuterSide0 = 0x1 << 5, OuterSide1 = 0x1 << 6, OuterSide2 = 0x1 << 7, OuterSide3 = 0x1 << 8 } [Flags] public enum FaceMask { Single = 0x0001, Cap = 0x0002, End = 0x0004, Side = 0x0008, Inner = 0x0010, Outer = 0x0020, Hollow = 0x0040, Open = 0x0080, Flat = 0x0100, Top = 0x0200, Bottom = 0x0400 } public enum DetailLevel { Low = 0, Medium = 1, High = 2, Highest = 3 } #endregion Enums #region Structs [StructLayout(LayoutKind.Explicit)] public struct Vertex : IEquatable { [FieldOffset(0)] public Vector3 Position; [FieldOffset(12)] public Vector3 Normal; [FieldOffset(24)] public Vector2 TexCoord; public override string ToString() { return String.Format("P: {0} N: {1} T: {2}", Position, Normal, TexCoord); } public override int GetHashCode() { int hash = Position.GetHashCode(); hash = hash * 31 + Normal.GetHashCode(); hash = hash * 31 + TexCoord.GetHashCode(); return hash; } public static bool operator ==(Vertex value1, Vertex value2) { return value1.Position == value2.Position && value1.Normal == value2.Normal && value1.TexCoord == value2.TexCoord; } public static bool operator !=(Vertex value1, Vertex value2) { return !(value1 == value2); } public override bool Equals(object obj) { return (obj is Vertex) ? this == (Vertex)obj : false; } public bool Equals(Vertex other) { return this == other; } } public struct ProfileFace { public int Index; public int Count; public float ScaleU; public bool Cap; public bool Flat; public FaceType Type; public override string ToString() { return Type.ToString(); } }; public struct Profile { public float MinX; public float MaxX; public bool Open; public bool Concave; public int TotalOutsidePoints; public List Positions; public List Faces; } public struct PathPoint { public Vector3 Position; public Vector2 Scale; public Quaternion Rotation; public float TexT; } public struct Path { public List Points; public bool Open; } public struct Face { // Only used for Inner/Outer faces public int BeginS; public int BeginT; public int NumS; public int NumT; public int ID; public Vector3 Center; public Vector3 MinExtent; public Vector3 MaxExtent; public List Vertices; public List Indices; public List Edge; public FaceMask Mask; public Primitive.TextureEntryFace TextureFace; public object UserData; public override string ToString() { return Mask.ToString(); } } #endregion Structs #region Exceptions public class RenderingException : Exception { public RenderingException(string message) : base(message) { } public RenderingException(string message, Exception innerException) : base(message, innerException) { } } #endregion Exceptions #region Mesh Classes public class Mesh { public Primitive Prim; public Path Path; public Profile Profile; public override string ToString() { if (Prim.Properties != null && !String.IsNullOrEmpty(Prim.Properties.Name)) { return Prim.Properties.Name; } else { return String.Format("{0} ({1})", Prim.LocalID, Prim.PrimData); } } } /// /// Contains all mesh faces that belong to a prim /// public class FacetedMesh : Mesh { /// List of primitive faces public List Faces; /// /// Decodes mesh asset into FacetedMesh /// /// Mesh primitive /// Asset retrieved from the asset server /// Level of detail /// Resulting decoded FacetedMesh /// True if mesh asset decoding was successful public static bool TryDecodeFromAsset(Primitive prim, AssetMesh meshAsset, DetailLevel LOD, out FacetedMesh mesh) { mesh = null; try { if (!meshAsset.Decode()) { return false; } OSDMap MeshData = meshAsset.MeshData; mesh = new FacetedMesh(); mesh.Faces = new List(); mesh.Prim = prim; mesh.Profile.Faces = new List(); mesh.Profile.Positions = new List(); mesh.Path.Points = new List(); OSD facesOSD = null; switch (LOD) { default: case DetailLevel.Highest: facesOSD = MeshData["high_lod"]; break; case DetailLevel.High: facesOSD = MeshData["medium_lod"]; break; case DetailLevel.Medium: facesOSD = MeshData["low_lod"]; break; case DetailLevel.Low: facesOSD = MeshData["lowest_lod"]; break; } if (facesOSD == null || !(facesOSD is OSDArray)) { return false; } OSDArray decodedMeshOsdArray = (OSDArray)facesOSD; for (int faceNr = 0; faceNr < decodedMeshOsdArray.Count; faceNr++) { OSD subMeshOsd = decodedMeshOsdArray[faceNr]; // Decode each individual face if (subMeshOsd is OSDMap) { Face oface = new Face(); oface.ID = faceNr; oface.Vertices = new List(); oface.Indices = new List(); oface.TextureFace = prim.Textures.GetFace((uint)faceNr); OSDMap subMeshMap = (OSDMap)subMeshOsd; Vector3 posMax; Vector3 posMin; // If PositionDomain is not specified, the default is from -0.5 to 0.5 if (subMeshMap.ContainsKey("PositionDomain")) { posMax = ((OSDMap)subMeshMap["PositionDomain"])["Max"]; posMin = ((OSDMap)subMeshMap["PositionDomain"])["Min"]; } else { posMax = new Vector3(0.5f, 0.5f, 0.5f); posMin = new Vector3(-0.5f, -0.5f, -0.5f); } // Vertex positions byte[] posBytes = subMeshMap["Position"]; // Normals byte[] norBytes = null; if (subMeshMap.ContainsKey("Normal")) { norBytes = subMeshMap["Normal"]; } // UV texture map Vector2 texPosMax = Vector2.Zero; Vector2 texPosMin = Vector2.Zero; byte[] texBytes = null; if (subMeshMap.ContainsKey("TexCoord0")) { texBytes = subMeshMap["TexCoord0"]; texPosMax = ((OSDMap)subMeshMap["TexCoord0Domain"])["Max"]; texPosMin = ((OSDMap)subMeshMap["TexCoord0Domain"])["Min"]; } // Extract the vertex position data // If present normals and texture coordinates too for (int i = 0; i < posBytes.Length; i += 6) { ushort uX = Utils.BytesToUInt16(posBytes, i); ushort uY = Utils.BytesToUInt16(posBytes, i + 2); ushort uZ = Utils.BytesToUInt16(posBytes, i + 4); Vertex vx = new Vertex(); vx.Position = new Vector3( Utils.UInt16ToFloat(uX, posMin.X, posMax.X), Utils.UInt16ToFloat(uY, posMin.Y, posMax.Y), Utils.UInt16ToFloat(uZ, posMin.Z, posMax.Z)); if (norBytes != null && norBytes.Length >= i + 4) { ushort nX = Utils.BytesToUInt16(norBytes, i); ushort nY = Utils.BytesToUInt16(norBytes, i + 2); ushort nZ = Utils.BytesToUInt16(norBytes, i + 4); vx.Normal = new Vector3( Utils.UInt16ToFloat(nX, posMin.X, posMax.X), Utils.UInt16ToFloat(nY, posMin.Y, posMax.Y), Utils.UInt16ToFloat(nZ, posMin.Z, posMax.Z)); } var vertexIndexOffset = oface.Vertices.Count * 4; if (texBytes != null && texBytes.Length >= vertexIndexOffset + 4) { ushort tX = Utils.BytesToUInt16(texBytes, vertexIndexOffset); ushort tY = Utils.BytesToUInt16(texBytes, vertexIndexOffset + 2); vx.TexCoord = new Vector2( Utils.UInt16ToFloat(tX, texPosMin.X, texPosMax.X), Utils.UInt16ToFloat(tY, texPosMin.Y, texPosMax.Y)); } oface.Vertices.Add(vx); } byte[] triangleBytes = subMeshMap["TriangleList"]; for (int i = 0; i < triangleBytes.Length; i += 6) { ushort v1 = (ushort)(Utils.BytesToUInt16(triangleBytes, i)); oface.Indices.Add(v1); ushort v2 = (ushort)(Utils.BytesToUInt16(triangleBytes, i + 2)); oface.Indices.Add(v2); ushort v3 = (ushort)(Utils.BytesToUInt16(triangleBytes, i + 4)); oface.Indices.Add(v3); } mesh.Faces.Add(oface); } } } catch (Exception ex) { Logger.Log("Failed to decode mesh asset: " + ex.Message, Helpers.LogLevel.Warning); return false; } return true; } } public class SimpleMesh : Mesh { public List Vertices; public List Indices; public SimpleMesh() { } public SimpleMesh(SimpleMesh mesh) { this.Indices = new List(mesh.Indices); this.Path.Open = mesh.Path.Open; this.Path.Points = new List(mesh.Path.Points); this.Prim = mesh.Prim; this.Profile.Concave = mesh.Profile.Concave; this.Profile.Faces = new List(mesh.Profile.Faces); this.Profile.MaxX = mesh.Profile.MaxX; this.Profile.MinX = mesh.Profile.MinX; this.Profile.Open = mesh.Profile.Open; this.Profile.Positions = new List(mesh.Profile.Positions); this.Profile.TotalOutsidePoints = mesh.Profile.TotalOutsidePoints; this.Vertices = new List(mesh.Vertices); } } #endregion Mesh Classes #region Plugin Loading public static class RenderingLoader { public static List ListRenderers(string path) { List plugins = new List(); string[] files = Directory.GetFiles(path, "OpenMetaverse.Rendering.*.dll"); foreach (string f in files) { try { Assembly a = Assembly.LoadFrom(f); System.Type[] types = a.GetTypes(); foreach (System.Type type in types) { if (type.GetInterface("IRendering") != null) { if (type.GetCustomAttributes(typeof(RendererNameAttribute), false).Length == 1) { plugins.Add(f); } else { Logger.Log("Rendering plugin does not support the [RendererName] attribute: " + f, Helpers.LogLevel.Warning); } break; } } } catch (Exception e) { Logger.Log(String.Format("Unrecognized rendering plugin {0}: {1}", f, e.Message), Helpers.LogLevel.Warning, e); } } return plugins; } public static IRendering LoadRenderer(string filename) { try { Assembly a = Assembly.LoadFrom(filename); System.Type[] types = a.GetTypes(); foreach (System.Type type in types) { if (type.GetInterface("IRendering") != null) { if (type.GetCustomAttributes(typeof(RendererNameAttribute), false).Length == 1) { return (IRendering)Activator.CreateInstance(type); } else { throw new RenderingException( "Rendering plugin does not support the [RendererName] attribute"); } } } throw new RenderingException( "Rendering plugin does not support the IRendering interface"); } catch (Exception e) { throw new RenderingException("Failed loading rendering plugin: " + e.Message, e); } } } #endregion Plugin Loading }