-using System;\r
-using System.Collections.Generic;\r
-using System.Linq;\r
-using System.Text;\r
-using System.IO;\r
-using System.Xml;\r
-using OpenTK.Graphics.OpenGL;\r
-using System.Runtime.InteropServices;\r
-using OpenMetaverse;\r
-using OpenMetaverse.Rendering;\r
-\r
-namespace Radegast.Rendering\r
-{\r
- public class FaceData\r
- {\r
- public float[] Vertices;\r
- public ushort[] Indices;\r
- public float[] TexCoords;\r
- public float[] Normals;\r
- public int PickingID = -1;\r
- public int VertexVBO = -1;\r
- public int IndexVBO = -1;\r
- public TextureInfo TextureInfo = new TextureInfo();\r
- public BoundingSphere BoundingSphere = new BoundingSphere();\r
- public static int VertexSize = 32; // sizeof (vertex), 2 x vector3 + 1 x vector2 = 8 floats x 4 bytes = 32 bytes \r
- \r
- public void CheckVBO(Face face)\r
- {\r
- if (VertexVBO == -1)\r
- {\r
- Vertex[] vArray = face.Vertices.ToArray();\r
- GL.GenBuffers(1, out VertexVBO);\r
- GL.BindBuffer(BufferTarget.ArrayBuffer, VertexVBO);\r
- GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vArray.Length * VertexSize), vArray, BufferUsageHint.StreamDraw);\r
- }\r
-\r
- if (IndexVBO == -1)\r
- {\r
- ushort[] iArray = face.Indices.ToArray();\r
- GL.GenBuffers(1, out IndexVBO);\r
- GL.BindBuffer(BufferTarget.ElementArrayBuffer, IndexVBO);\r
- GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(iArray.Length * sizeof(ushort)), iArray, BufferUsageHint.StreamDraw);\r
- }\r
- }\r
- }\r
-\r
- public class TextureInfo\r
- {\r
- public System.Drawing.Image Texture;\r
- public int TexturePointer;\r
- public bool HasAlpha;\r
- public UUID TextureID;\r
- }\r
-\r
- public class TextureLoadItem\r
- {\r
- public FaceData Data;\r
- public Primitive Prim;\r
- public Primitive.TextureEntryFace TeFace;\r
- }\r
-\r
- public enum RenderPass\r
- {\r
- Picking,\r
- Simple,\r
- Alpha\r
- }\r
-\r
- public static class Render\r
- {\r
- public static IRendering Plugin;\r
- }\r
-\r
- public static class RHelp\r
- {\r
- public static OpenTK.Vector2 TKVector3(Vector2 v)\r
- {\r
- return new OpenTK.Vector2(v.X, v.Y);\r
- }\r
-\r
- public static OpenTK.Vector3 TKVector3(Vector3 v)\r
- {\r
- return new OpenTK.Vector3(v.X, v.Y, v.Z);\r
- }\r
-\r
- public static OpenTK.Vector4 TKVector3(Vector4 v)\r
- {\r
- return new OpenTK.Vector4(v.X, v.Y, v.Z, v.W);\r
- }\r
- }\r
-\r
- /// <summary>\r
- /// Represents camera object\r
- /// </summary>\r
- public struct Camera\r
- {\r
- Vector3 mPosition;\r
- Vector3 mFocalPoint;\r
- bool mModified;\r
- \r
- /// <summary>Camera position</summary>\r
- public Vector3 Position { get { return mPosition; } set { mPosition = value; mModified = true; } }\r
- /// <summary>Camera target</summary>\r
- public Vector3 FocalPoint { get { return mFocalPoint; } set { mFocalPoint = value; mModified = true; } }\r
- /// <summary>Zoom level</summary>\r
- public float Zoom;\r
- /// <summary>Draw distance</summary>\r
- public float Far;\r
- /// <summary>Has camera been modified</summary>\r
- public bool Modified { get { return mModified; } set { mModified = value; } }\r
- }\r
-\r
- public static class MeshToOBJ\r
- {\r
- public static bool MeshesToOBJ(Dictionary<uint, FacetedMesh> meshes, string filename)\r
- {\r
- StringBuilder obj = new StringBuilder();\r
- StringBuilder mtl = new StringBuilder();\r
-\r
- FileInfo objFileInfo = new FileInfo(filename);\r
-\r
- string mtlFilename = objFileInfo.FullName.Substring(objFileInfo.DirectoryName.Length + 1,\r
- objFileInfo.FullName.Length - (objFileInfo.DirectoryName.Length + 1) - 4) + ".mtl";\r
-\r
- obj.AppendLine("# Created by libprimrender");\r
- obj.AppendLine("mtllib ./" + mtlFilename);\r
- obj.AppendLine();\r
-\r
- mtl.AppendLine("# Created by libprimrender");\r
- mtl.AppendLine();\r
-\r
- int primNr = 0;\r
- foreach (FacetedMesh mesh in meshes.Values)\r
- {\r
- for (int j = 0; j < mesh.Faces.Count; j++)\r
- {\r
- Face face = mesh.Faces[j];\r
-\r
- if (face.Vertices.Count > 2)\r
- {\r
- string mtlName = String.Format("material{0}-{1}", primNr, face.ID);\r
- Primitive.TextureEntryFace tex = face.TextureFace;\r
- string texName = tex.TextureID.ToString() + ".tga";\r
-\r
- // FIXME: Convert the source to TGA (if needed) and copy to the destination\r
-\r
- float shiny = 0.00f;\r
- switch (tex.Shiny)\r
- {\r
- case Shininess.High:\r
- shiny = 1.00f;\r
- break;\r
- case Shininess.Medium:\r
- shiny = 0.66f;\r
- break;\r
- case Shininess.Low:\r
- shiny = 0.33f;\r
- break;\r
- }\r
-\r
- obj.AppendFormat("g face{0}-{1}{2}", primNr, face.ID, Environment.NewLine);\r
-\r
- mtl.AppendLine("newmtl " + mtlName);\r
- mtl.AppendFormat("Ka {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);\r
- mtl.AppendFormat("Kd {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);\r
- //mtl.AppendFormat("Ks {0} {1} {2}{3}");\r
- mtl.AppendLine("Tr " + tex.RGBA.A);\r
- mtl.AppendLine("Ns " + shiny);\r
- mtl.AppendLine("illum 1");\r
- if (tex.TextureID != UUID.Zero && tex.TextureID != Primitive.TextureEntry.WHITE_TEXTURE)\r
- mtl.AppendLine("map_Kd ./" + texName);\r
- mtl.AppendLine();\r
-\r
- // Write the vertices, texture coordinates, and vertex normals for this side\r
- for (int k = 0; k < face.Vertices.Count; k++)\r
- {\r
- Vertex vertex = face.Vertices[k];\r
-\r
- #region Vertex\r
-\r
- Vector3 pos = vertex.Position;\r
-\r
- // Apply scaling\r
- pos *= mesh.Prim.Scale;\r
-\r
- // Apply rotation\r
- pos *= mesh.Prim.Rotation;\r
-\r
- // The root prim position is sim-relative, while child prim positions are\r
- // parent-relative. We want to apply parent-relative translations but not\r
- // sim-relative ones\r
- if (mesh.Prim.ParentID != 0)\r
- pos += mesh.Prim.Position;\r
-\r
- obj.AppendFormat("v {0} {1} {2}{3}", pos.X, pos.Y, pos.Z, Environment.NewLine);\r
-\r
- #endregion Vertex\r
-\r
- #region Texture Coord\r
-\r
- obj.AppendFormat("vt {0} {1}{2}", vertex.TexCoord.X, vertex.TexCoord.Y,\r
- Environment.NewLine);\r
-\r
- #endregion Texture Coord\r
-\r
- #region Vertex Normal\r
-\r
- // HACK: Sometimes normals are getting set to <NaN,NaN,NaN>\r
- if (!Single.IsNaN(vertex.Normal.X) && !Single.IsNaN(vertex.Normal.Y) && !Single.IsNaN(vertex.Normal.Z))\r
- obj.AppendFormat("vn {0} {1} {2}{3}", vertex.Normal.X, vertex.Normal.Y, vertex.Normal.Z,\r
- Environment.NewLine);\r
- else\r
- obj.AppendLine("vn 0.0 1.0 0.0");\r
-\r
- #endregion Vertex Normal\r
- }\r
-\r
- obj.AppendFormat("# {0} vertices{1}", face.Vertices.Count, Environment.NewLine);\r
- obj.AppendLine();\r
- obj.AppendLine("usemtl " + mtlName);\r
-\r
- #region Elements\r
-\r
- // Write all of the faces (triangles) for this side\r
- for (int k = 0; k < face.Indices.Count / 3; k++)\r
- {\r
- obj.AppendFormat("f -{0}/-{0}/-{0} -{1}/-{1}/-{1} -{2}/-{2}/-{2}{3}",\r
- face.Vertices.Count - face.Indices[k * 3 + 0],\r
- face.Vertices.Count - face.Indices[k * 3 + 1],\r
- face.Vertices.Count - face.Indices[k * 3 + 2],\r
- Environment.NewLine);\r
- }\r
-\r
- obj.AppendFormat("# {0} elements{1}", face.Indices.Count / 3, Environment.NewLine);\r
- obj.AppendLine();\r
-\r
- #endregion Elements\r
- }\r
- }\r
- primNr++;\r
- }\r
-\r
- try\r
- {\r
- File.WriteAllText(filename, obj.ToString());\r
- File.WriteAllText(mtlFilename, mtl.ToString());\r
- }\r
- catch (Exception)\r
- {\r
- return false;\r
- }\r
-\r
- return true;\r
- }\r
- }\r
-\r
- public static class Math3D\r
- {\r
- // Column-major:\r
- // | 0 4 8 12 |\r
- // | 1 5 9 13 |\r
- // | 2 6 10 14 |\r
- // | 3 7 11 15 |\r
-\r
- public static float[] CreateTranslationMatrix(Vector3 v)\r
- {\r
- float[] mat = new float[16];\r
-\r
- mat[12] = v.X;\r
- mat[13] = v.Y;\r
- mat[14] = v.Z;\r
- mat[0] = mat[5] = mat[10] = mat[15] = 1;\r
-\r
- return mat;\r
- }\r
-\r
- public static float[] CreateRotationMatrix(Quaternion q)\r
- {\r
- float[] mat = new float[16];\r
-\r
- // Transpose the quaternion (don't ask me why)\r
- q.X = q.X * -1f;\r
- q.Y = q.Y * -1f;\r
- q.Z = q.Z * -1f;\r
-\r
- float x2 = q.X + q.X;\r
- float y2 = q.Y + q.Y;\r
- float z2 = q.Z + q.Z;\r
- float xx = q.X * x2;\r
- float xy = q.X * y2;\r
- float xz = q.X * z2;\r
- float yy = q.Y * y2;\r
- float yz = q.Y * z2;\r
- float zz = q.Z * z2;\r
- float wx = q.W * x2;\r
- float wy = q.W * y2;\r
- float wz = q.W * z2;\r
-\r
- mat[0] = 1.0f - (yy + zz);\r
- mat[1] = xy - wz;\r
- mat[2] = xz + wy;\r
- mat[3] = 0.0f;\r
-\r
- mat[4] = xy + wz;\r
- mat[5] = 1.0f - (xx + zz);\r
- mat[6] = yz - wx;\r
- mat[7] = 0.0f;\r
-\r
- mat[8] = xz - wy;\r
- mat[9] = yz + wx;\r
- mat[10] = 1.0f - (xx + yy);\r
- mat[11] = 0.0f;\r
-\r
- mat[12] = 0.0f;\r
- mat[13] = 0.0f;\r
- mat[14] = 0.0f;\r
- mat[15] = 1.0f;\r
-\r
- return mat;\r
- }\r
-\r
- public static float[] CreateScaleMatrix(Vector3 v)\r
- {\r
- float[] mat = new float[16];\r
-\r
- mat[0] = v.X;\r
- mat[5] = v.Y;\r
- mat[10] = v.Z;\r
- mat[15] = 1;\r
-\r
- return mat;\r
- }\r
-\r
- public static bool GluProject(OpenTK.Vector3 objPos, OpenTK.Matrix4 modelMatrix, OpenTK.Matrix4 projMatrix, int[] viewport, out OpenTK.Vector3 screenPos)\r
- {\r
- OpenTK.Vector4 _in;\r
- OpenTK.Vector4 _out;\r
-\r
- _in.X = objPos.X;\r
- _in.Y = objPos.Y;\r
- _in.Z = objPos.Z;\r
- _in.W = 1.0f;\r
-\r
- _out = OpenTK.Vector4.Transform(_in, modelMatrix);\r
- _in = OpenTK.Vector4.Transform(_out, projMatrix);\r
-\r
- if (_in.W <= 0.0)\r
- {\r
- screenPos = OpenTK.Vector3.Zero;\r
- return false;\r
- }\r
-\r
- _in.X /= _in.W;\r
- _in.Y /= _in.W;\r
- _in.Z /= _in.W;\r
- /* Map x, y and z to range 0-1 */\r
- _in.X = _in.X * 0.5f + 0.5f;\r
- _in.Y = _in.Y * 0.5f + 0.5f;\r
- _in.Z = _in.Z * 0.5f + 0.5f;\r
-\r
- /* Map x,y to viewport */\r
- _in.X = _in.X * viewport[2] + viewport[0];\r
- _in.Y = _in.Y * viewport[3] + viewport[1];\r
-\r
- screenPos.X = _in.X;\r
- screenPos.Y = _in.Y;\r
- screenPos.Z = _in.Z;\r
-\r
- return true;\r
- }\r
- }\r
-\r
- public class attachment_point\r
- {\r
- public string name;\r
- public string joint;\r
- public Vector3 position;\r
- public Vector3 rotation;\r
- public int id;\r
- public int group;\r
-\r
- public GLMesh jointmesh;\r
- public int jointmeshindex;\r
-\r
- public Vector3 getposition()\r
- {\r
- Vector3 pos;\r
- pos = jointmesh.Position + position;\r
- return pos;\r
- }\r
-\r
- public Quaternion getrotation()\r
- {\r
- Vector3 rotvec = jointmesh.RotationAngles + rotation;\r
- rotvec.Normalize();\r
- Quaternion rot = new Quaternion(rotvec.X, rotvec.Y, rotvec.Z);\r
- return rot;\r
- }\r
- }\r
-\r
- /// <summary>\r
- /// Subclass of LindenMesh that adds vertex, index, and texture coordinate\r
- /// arrays suitable for pushing direct to OpenGL\r
- /// </summary>\r
- public class GLMesh : LindenMesh\r
- {\r
- /// <summary>\r
- /// Subclass of LODMesh that adds an index array suitable for pushing\r
- /// direct to OpenGL\r
- /// </summary>\r
- /// \r
-\r
- public int teFaceID;\r
-\r
- new public class LODMesh : LindenMesh.LODMesh\r
- {\r
- public ushort[] Indices;\r
-\r
- public override void LoadMesh(string filename)\r
- {\r
- base.LoadMesh(filename);\r
-\r
- // Generate the index array\r
- Indices = new ushort[_numFaces * 3];\r
- int current = 0;\r
- for (int i = 0; i < _numFaces; i++)\r
- {\r
- Indices[current++] = (ushort)_faces[i].Indices[0];\r
- Indices[current++] = (ushort)_faces[i].Indices[1];\r
- Indices[current++] = (ushort)_faces[i].Indices[2];\r
- }\r
- }\r
- }\r
-\r
- /// <summary>\r
- /// \r
- /// </summary>\r
- public struct GLData\r
- {\r
- public float[] Vertices;\r
- public ushort[] Indices;\r
- public float[] TexCoords;\r
- public Vector3 Center;\r
- }\r
-\r
- public static GLData baseRenderData;\r
- public GLData RenderData;\r
-\r
- public GLMesh(string name)\r
- : base(name)\r
- {\r
- }\r
-\r
- public void setMeshPos(Vector3 pos)\r
- {\r
- _position = pos;\r
- }\r
-\r
- public void setMeshRot(Vector3 rot)\r
- {\r
- _rotationAngles = rot;\r
- }\r
-\r
- public override void LoadMesh(string filename)\r
- {\r
- base.LoadMesh(filename);\r
-\r
- float minX, minY, minZ;\r
- minX = minY = minZ = Single.MaxValue;\r
- float maxX, maxY, maxZ;\r
- maxX = maxY = maxZ = Single.MinValue;\r
-\r
- // Generate the vertex array\r
- RenderData.Vertices = new float[_numVertices * 3];\r
- int current = 0;\r
- for (int i = 0; i < _numVertices; i++)\r
- {\r
- RenderData.Vertices[current++] = _vertices[i].Coord.X;\r
- RenderData.Vertices[current++] = _vertices[i].Coord.Y;\r
- RenderData.Vertices[current++] = _vertices[i].Coord.Z;\r
-\r
- if (_vertices[i].Coord.X < minX)\r
- minX = _vertices[i].Coord.X;\r
- else if (_vertices[i].Coord.X > maxX)\r
- maxX = _vertices[i].Coord.X;\r
-\r
- if (_vertices[i].Coord.Y < minY)\r
- minY = _vertices[i].Coord.Y;\r
- else if (_vertices[i].Coord.Y > maxY)\r
- maxY = _vertices[i].Coord.Y;\r
-\r
- if (_vertices[i].Coord.Z < minZ)\r
- minZ = _vertices[i].Coord.Z;\r
- else if (_vertices[i].Coord.Z > maxZ)\r
- maxZ = _vertices[i].Coord.Z;\r
- }\r
-\r
- // Calculate the center-point from the bounding box edges\r
- RenderData.Center = new Vector3((minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2);\r
-\r
- // Generate the index array\r
- RenderData.Indices = new ushort[_numFaces * 3];\r
- current = 0;\r
- for (int i = 0; i < _numFaces; i++)\r
- {\r
- RenderData.Indices[current++] = (ushort)_faces[i].Indices[0];\r
- RenderData.Indices[current++] = (ushort)_faces[i].Indices[1];\r
- RenderData.Indices[current++] = (ushort)_faces[i].Indices[2];\r
- }\r
-\r
- // Generate the texcoord array\r
- RenderData.TexCoords = new float[_numVertices * 2];\r
- current = 0;\r
- for (int i = 0; i < _numVertices; i++)\r
- {\r
- RenderData.TexCoords[current++] = _vertices[i].TexCoord.X;\r
- RenderData.TexCoords[current++] = _vertices[i].TexCoord.Y;\r
- }\r
- }\r
-\r
- public override void LoadLODMesh(int level, string filename)\r
- {\r
- LODMesh lod = new LODMesh();\r
- lod.LoadMesh(filename);\r
- _lodMeshes[level] = lod;\r
- }\r
- }\r
-\r
- public class GLAvatar\r
- {\r
- public static Dictionary<string, GLMesh> _meshes = new Dictionary<string, GLMesh>();\r
- public static bool _wireframe = true;\r
- public static bool _showSkirt = false;\r
- public static Dictionary<int, attachment_point> attachment_points = new Dictionary<int, attachment_point>();\r
-\r
- public static void loadlindenmeshes(string LODfilename)\r
- {\r
- Bone.loadbones("avatar_skeleton.xml");\r
-\r
- string basedir = Directory.GetCurrentDirectory() + System.IO.Path.DirectorySeparatorChar + "character" + System.IO.Path.DirectorySeparatorChar;\r
-\r
- // Parse through avatar_lad.xml to find all of the mesh references\r
- XmlDocument lad = new XmlDocument();\r
- lad.Load(basedir + LODfilename);\r
-\r
- attachment_points.Clear();\r
-\r
- XmlNodeList attach_points = lad.GetElementsByTagName("attachment_point");\r
- foreach (XmlNode apoint in attach_points)\r
- {\r
- attachment_point point = new attachment_point();\r
- point.name = apoint.Attributes.GetNamedItem("name").Value;\r
- point.joint = apoint.Attributes.GetNamedItem("joint").Value;\r
-\r
- string pos = apoint.Attributes.GetNamedItem("position").Value;\r
- string[] posparts = pos.Split(' ');\r
- point.position = new Vector3(float.Parse(posparts[0]), float.Parse(posparts[1]), float.Parse(posparts[2]));\r
-\r
- string rot = apoint.Attributes.GetNamedItem("rotation").Value;\r
- string[] rotparts = rot.Split(' ');\r
- point.rotation = new Vector3(float.Parse(rotparts[0]), float.Parse(rotparts[1]), float.Parse(rotparts[2]));\r
-\r
- point.id = Int32.Parse(apoint.Attributes.GetNamedItem("id").Value);\r
- point.group = Int32.Parse(apoint.Attributes.GetNamedItem("group").Value);\r
-\r
- attachment_points.Add(point.id, point);\r
-\r
- }\r
-\r
- XmlNodeList bones = lad.GetElementsByTagName("bone");\r
-\r
- XmlNodeList meshes = lad.GetElementsByTagName("mesh");\r
-\r
- foreach (XmlNode meshNode in meshes)\r
- {\r
- string type = meshNode.Attributes.GetNamedItem("type").Value;\r
- int lod = Int32.Parse(meshNode.Attributes.GetNamedItem("lod").Value);\r
- string fileName = meshNode.Attributes.GetNamedItem("file_name").Value;\r
- //string minPixelWidth = meshNode.Attributes.GetNamedItem("min_pixel_width").Value;\r
-\r
- GLMesh mesh = (_meshes.ContainsKey(type) ? _meshes[type] : new GLMesh(type));\r
-\r
- switch (mesh.Name)\r
- {\r
- case "lowerBodyMesh":\r
- mesh.teFaceID = (int)AvatarTextureIndex.LowerBaked;\r
- break;\r
-\r
- case "upperBodyMesh":\r
- mesh.teFaceID = (int)AvatarTextureIndex.UpperBaked;\r
- break;\r
-\r
- case "headMesh":\r
- mesh.teFaceID = (int)AvatarTextureIndex.HeadBaked;\r
- break;\r
-\r
- case "hairMesh":\r
- mesh.teFaceID = (int)AvatarTextureIndex.HairBaked;\r
- break;\r
-\r
- case "eyelashMesh":\r
- mesh.teFaceID = (int)AvatarTextureIndex.HeadBaked;\r
- break;\r
-\r
- case "eyeBallRightMesh":\r
- mesh.setMeshPos(Bone.getOffset("mEyeLeft"));\r
- mesh.setMeshRot(Bone.getRotation("mEyeLeft"));\r
- mesh.teFaceID = (int)AvatarTextureIndex.EyesBaked;\r
- break;\r
-\r
- case "eyeBallLeftMesh":\r
- mesh.setMeshPos(Bone.getOffset("mEyeRight"));\r
- mesh.setMeshRot(Bone.getRotation("mEyeRight"));\r
- mesh.teFaceID = (int)AvatarTextureIndex.EyesBaked;\r
- break;\r
-\r
- case "skirtMesh":\r
- mesh.teFaceID = (int)AvatarTextureIndex.SkirtBaked;\r
- break;\r
-\r
- default:\r
- mesh.teFaceID = 0;\r
- break;\r
- }\r
-\r
- if (lod == 0)\r
- {\r
- mesh.LoadMesh(basedir + fileName);\r
- }\r
- else\r
- {\r
- mesh.LoadLODMesh(lod, basedir + fileName);\r
- }\r
-\r
- _meshes[type] = mesh;\r
-\r
- }\r
- }\r
-\r
- }\r
-\r
- class RenderAvatar\r
- {\r
- public GLAvatar glavatar;\r
- public Avatar avatar;\r
- public FaceData[] data = new FaceData[32];\r
-\r
- }\r
-\r
- public class Bone\r
- {\r
- public string name;\r
- public Vector3 pos;\r
- public Vector3 rot;\r
- public Vector3 scale;\r
- public Vector3 piviot;\r
-\r
- public Bone parent;\r
-\r
- public static Dictionary<string, Bone> mBones = new Dictionary<string, Bone>();\r
-\r
- public static void loadbones(string skeletonfilename)\r
- {\r
- mBones.Clear();\r
- string basedir = Directory.GetCurrentDirectory() + System.IO.Path.DirectorySeparatorChar + "character" + System.IO.Path.DirectorySeparatorChar;\r
- XmlDocument skeleton = new XmlDocument();\r
- skeleton.Load(basedir + skeletonfilename);\r
- XmlNode boneslist = skeleton.GetElementsByTagName("linden_skeleton")[0]; \r
- addbone(boneslist.ChildNodes[0],null);\r
- }\r
-\r
- public static void addbone(XmlNode bone, Bone parent)\r
- {\r
- Bone b = new Bone();\r
- b.name = bone.Attributes.GetNamedItem("name").Value;\r
-\r
- string pos = bone.Attributes.GetNamedItem("pos").Value;\r
- string[] posparts = pos.Split(' ');\r
- b.pos = new Vector3(float.Parse(posparts[0]), float.Parse(posparts[1]), float.Parse(posparts[2]));\r
-\r
- string rot = bone.Attributes.GetNamedItem("rot").Value;\r
- string[] rotparts = pos.Split(' ');\r
- b.pos = new Vector3(float.Parse(rotparts[0]), float.Parse(rotparts[1]), float.Parse(rotparts[2]));\r
-\r
- string scale = bone.Attributes.GetNamedItem("scale").Value;\r
- string[] scaleparts = pos.Split(' ');\r
- b.scale = new Vector3(float.Parse(scaleparts[0]), float.Parse(scaleparts[1]), float.Parse(scaleparts[2]));\r
-\r
- //TODO piviot\r
-\r
- b.parent = parent;\r
-\r
- mBones.Add(b.name, b);\r
-\r
- Logger.Log("Found bone " + b.name, Helpers.LogLevel.Info);\r
-\r
- foreach (XmlNode childbone in bone.ChildNodes)\r
- {\r
- addbone(childbone,b);\r
- }\r
-\r
- }\r
-\r
- //TODO check offset and rot calcuations should each offset be multiplied by its parent rotation in\r
- // a standard child/parent rot/offset way?\r
- public static Vector3 getOffset(string bonename)\r
- {\r
- Bone b;\r
- if (mBones.TryGetValue(bonename, out b))\r
- {\r
- return (b.getOffset());\r
- }\r
- else\r
- {\r
- return Vector3.Zero;\r
- }\r
- }\r
-\r
- public Vector3 getOffset()\r
- {\r
- Vector3 totalpos = pos;\r
-\r
- if (parent != null)\r
- {\r
- totalpos = parent.getOffset() + pos;\r
- }\r
-\r
- return totalpos;\r
- }\r
-\r
- public static Vector3 getRotation(string bonename)\r
- {\r
- Bone b;\r
- if (mBones.TryGetValue(bonename, out b))\r
- {\r
- return (b.getRotation());\r
- }\r
- else\r
- {\r
- return Vector3.Zero;\r
- }\r
- }\r
-\r
- public Vector3 getRotation()\r
- {\r
- Vector3 totalrot = rot;\r
-\r
- if (parent != null)\r
- {\r
- totalrot = parent.getRotation() + rot;\r
- }\r
-\r
- return totalrot;\r
- }\r
- \r
- }\r
-\r
-}\r
+//
+// Radegast Metaverse Client
+// Copyright (c) 2009-2014, Radegast Development 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.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of the application "Radegast", nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior CreateReflectionTexture 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 HOLDER 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.
+//
+// $Id: RenderingHelpers.cs 1136 2011-09-05 22:45:11Z latifer $
+//
+
+using System;
+using System.Collections.Generic;
+using System.Collections;
+using System.Linq;
+using System.Text;
+using System.IO;
+using System.IO.Compression;
+using System.Xml;
+using System.Threading;
+using OpenTK.Graphics.OpenGL;
+using System.Runtime.InteropServices;
+using System.Drawing;
+using System.Drawing.Imaging;
+using OpenMetaverse;
+using OpenMetaverse.Rendering;
+
+namespace Radegast.Rendering
+{
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Color4b
+ {
+ public byte R;
+ public byte G;
+ public byte B;
+ public byte A;
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ public struct ColorVertex
+ {
+ [FieldOffset(0)]
+ public Vertex Vertex;
+ [FieldOffset(32)]
+ public Color4b Color;
+ public static int Size = 36;
+ }
+
+ public class TextureInfo
+ {
+ public System.Drawing.Image Texture;
+ public int TexturePointer;
+ public bool HasAlpha;
+ public bool FullAlpha;
+ public bool IsMask;
+ public bool IsInvisible;
+ public UUID TextureID;
+ public bool FetchFailed;
+ }
+
+ public class TextureLoadItem
+ {
+ public FaceData Data;
+ public Primitive Prim;
+ public Primitive.TextureEntryFace TeFace;
+ public byte[] TextureData = null;
+ public byte[] TGAData = null;
+ public bool LoadAssetFromCache = false;
+ public OpenMetaverse.ImageType ImageType = OpenMetaverse.ImageType.Normal;
+ public string BakeName = string.Empty;
+ public UUID AvatarID = UUID.Zero;
+ }
+
+ public enum RenderPass
+ {
+ Picking,
+ Simple,
+ Alpha,
+ Invisible
+ }
+
+ public enum SceneObjectType
+ {
+ None,
+ Primitive,
+ Avatar,
+ }
+
+ /// <summary>
+ /// Base class for all scene objects
+ /// </summary>
+ public abstract class SceneObject : IComparable, IDisposable
+ {
+ #region Public fields
+ /// <summary>Interpolated local position of the object</summary>
+ public Vector3 InterpolatedPosition;
+ /// <summary>Interpolated local rotation of the object/summary>
+ public Quaternion InterpolatedRotation;
+ /// <summary>Rendered position of the object in the region</summary>
+ public Vector3 RenderPosition;
+ /// <summary>Rendered rotationm of the object in the region</summary>
+ public Quaternion RenderRotation;
+ /// <summary>Per frame calculated square of the distance from camera</summary>
+ public float DistanceSquared;
+ /// <summary>Bounding volume of the object</summary>
+ public BoundingVolume BoundingVolume;
+ /// <summary>Was the sim position and distance from camera calculated during this frame</summary>
+ public bool PositionCalculated;
+ /// <summary>Scene object type</summary>
+ public SceneObjectType Type = SceneObjectType.None;
+ /// <summary>Libomv primitive</summary>
+ public virtual Primitive BasePrim { get; set; }
+ /// <summary>Were initial initialization tasks done</summary>
+ public bool Initialized;
+ /// <summary>Is this object disposed</summary>
+ public bool IsDisposed = false;
+ public int AlphaQueryID = -1;
+ public int SimpleQueryID = -1;
+ public bool HasAlphaFaces;
+ public bool HasSimpleFaces;
+ public bool HasInvisibleFaces;
+
+ #endregion Public fields
+
+ uint previousParent = uint.MaxValue;
+
+ /// <summary>
+ /// Cleanup resources used
+ /// </summary>
+ public virtual void Dispose()
+ {
+ IsDisposed = true;
+ }
+
+ /// <summary>
+ /// Task performed the fist time object is set for rendering
+ /// </summary>
+ public virtual void Initialize()
+ {
+ RenderPosition = InterpolatedPosition = BasePrim.Position;
+ RenderRotation = InterpolatedRotation = BasePrim.Rotation;
+ Initialized = true;
+ }
+
+ /// <summary>
+ /// Perform per frame tasks
+ /// </summary>
+ /// <param name="time">Time since the last call (last frame time in seconds)</param>
+ public virtual void Step(float time)
+ {
+ if (BasePrim == null) return;
+
+ // Don't interpolate when parent changes (sit/stand link/unlink)
+ if (previousParent != BasePrim.ParentID)
+ {
+ previousParent = BasePrim.ParentID;
+ InterpolatedPosition = BasePrim.Position;
+ InterpolatedRotation = BasePrim.Rotation;
+ return;
+ }
+
+ // Linear velocity and acceleration
+ if (BasePrim.Velocity != Vector3.Zero)
+ {
+ BasePrim.Position = InterpolatedPosition = BasePrim.Position + BasePrim.Velocity * time
+ * 0.98f * RadegastInstance.GlobalInstance.Client.Network.CurrentSim.Stats.Dilation;
+ BasePrim.Velocity += BasePrim.Acceleration * time;
+ }
+ else if (InterpolatedPosition != BasePrim.Position)
+ {
+ InterpolatedPosition = RHelp.Smoothed1stOrder(InterpolatedPosition, BasePrim.Position, time);
+ }
+
+ // Angular velocity (target omega)
+ if (BasePrim.AngularVelocity != Vector3.Zero)
+ {
+ Vector3 angVel = BasePrim.AngularVelocity;
+ float angle = time * angVel.Length();
+ Quaternion dQ = Quaternion.CreateFromAxisAngle(angVel, angle);
+ InterpolatedRotation = dQ * InterpolatedRotation;
+ }
+ else if (InterpolatedRotation != BasePrim.Rotation && !(this is RenderAvatar))
+ {
+ InterpolatedRotation = Quaternion.Slerp(InterpolatedRotation, BasePrim.Rotation, time * 10f);
+ if (1f - Math.Abs(Quaternion.Dot(InterpolatedRotation, BasePrim.Rotation)) < 0.0001)
+ InterpolatedRotation = BasePrim.Rotation;
+ }
+ else
+ {
+ InterpolatedRotation = BasePrim.Rotation;
+ }
+ }
+
+ /// <summary>
+ /// Render scene object
+ /// </summary>
+ /// <param name="pass">Which pass are we currently in</param>
+ /// <param name="pickingID">ID used to identify which object was picked</param>
+ /// <param name="scene">Main scene renderer</param>
+ /// <param name="time">Time it took to render the last frame</param>
+ public virtual void Render(RenderPass pass, int pickingID, SceneWindow scene, float time)
+ {
+ }
+
+ /// <summary>
+ /// Implementation of the IComparable interface
+ /// used for sorting by distance
+ /// </summary>
+ /// <param name="other">Object we are comparing to</param>
+ /// <returns>Result of the comparison</returns>
+ public virtual int CompareTo(object other)
+ {
+ SceneObject o = (SceneObject)other;
+ if (this.DistanceSquared < o.DistanceSquared)
+ return -1;
+ else if (this.DistanceSquared > o.DistanceSquared)
+ return 1;
+ else
+ return 0;
+ }
+
+ #region Occlusion queries
+ public void StartQuery(RenderPass pass)
+ {
+ if (!RenderSettings.OcclusionCullingEnabled) return;
+
+ if (pass == RenderPass.Simple)
+ {
+ StartSimpleQuery();
+ }
+ else if (pass == RenderPass.Alpha)
+ {
+ StartAlphaQuery();
+ }
+ }
+
+ public void EndQuery(RenderPass pass)
+ {
+ if (!RenderSettings.OcclusionCullingEnabled) return;
+
+ if (pass == RenderPass.Simple)
+ {
+ EndSimpleQuery();
+ }
+ else if (pass == RenderPass.Alpha)
+ {
+ EndAlphaQuery();
+ }
+ }
+
+ public void StartAlphaQuery()
+ {
+ if (!RenderSettings.OcclusionCullingEnabled) return;
+
+ if (AlphaQueryID == -1)
+ {
+ Compat.GenQueries(out AlphaQueryID);
+ }
+ if (AlphaQueryID > 0)
+ {
+ Compat.BeginQuery(QueryTarget.SamplesPassed, AlphaQueryID);
+ }
+ }
+
+ public void EndAlphaQuery()
+ {
+ if (!RenderSettings.OcclusionCullingEnabled) return;
+
+ if (AlphaQueryID > 0)
+ {
+ Compat.EndQuery(QueryTarget.SamplesPassed);
+ }
+ }
+
+ public void StartSimpleQuery()
+ {
+ if (!RenderSettings.OcclusionCullingEnabled) return;
+
+ if (SimpleQueryID == -1)
+ {
+ Compat.GenQueries(out SimpleQueryID);
+ }
+ if (SimpleQueryID > 0)
+ {
+ Compat.BeginQuery(QueryTarget.SamplesPassed, SimpleQueryID);
+ }
+ }
+
+ public void EndSimpleQuery()
+ {
+ if (!RenderSettings.OcclusionCullingEnabled) return;
+
+ if (SimpleQueryID > 0)
+ {
+ Compat.EndQuery(QueryTarget.SamplesPassed);
+ }
+ }
+
+ public bool Occluded()
+ {
+ if (!RenderSettings.OcclusionCullingEnabled) return false;
+
+ if (HasInvisibleFaces) return false;
+
+ if ((SimpleQueryID == -1 && AlphaQueryID == -1))
+ {
+ return false;
+ }
+
+ if ((!HasAlphaFaces && !HasSimpleFaces)) return true;
+
+ int samples = 1;
+ if (HasSimpleFaces && SimpleQueryID > 0)
+ {
+ Compat.GetQueryObject(SimpleQueryID, GetQueryObjectParam.QueryResult, out samples);
+ }
+ if (HasSimpleFaces && samples > 0)
+ {
+ return false;
+ }
+
+ samples = 1;
+ if (HasAlphaFaces && AlphaQueryID > 0)
+ {
+ Compat.GetQueryObject(AlphaQueryID, GetQueryObjectParam.QueryResult, out samples);
+ }
+ if (HasAlphaFaces && samples > 0)
+ {
+ return false;
+ }
+
+ return true;
+ }
+ #endregion Occlusion queries
+ }
+
+ public static class RHelp
+ {
+ public static readonly Vector3 InvalidPosition = new Vector3(99999f, 99999f, 99999f);
+ static float t1 = 0.075f;
+ static float t2 = t1 / 5.7f;
+
+ public static Vector3 Smoothed1stOrder(Vector3 curPos, Vector3 targetPos, float lastFrameTime)
+ {
+ int numIterations = (int)(lastFrameTime * 100);
+ do
+ {
+ curPos += (targetPos - curPos) * t1;
+ numIterations--;
+ }
+ while (numIterations > 0);
+ if (Vector3.DistanceSquared(curPos, targetPos) < 0.00001f)
+ {
+ curPos = targetPos;
+ }
+ return curPos;
+ }
+
+ public static Vector3 Smoothed2ndOrder(Vector3 curPos, Vector3 targetPos, ref Vector3 accel, float lastFrameTime)
+ {
+ int numIterations = (int)(lastFrameTime * 100);
+ do
+ {
+ accel += (targetPos - accel - curPos) * t1;
+ curPos += accel * t2;
+ numIterations--;
+ }
+ while (numIterations > 0);
+ if (Vector3.DistanceSquared(curPos, targetPos) < 0.00001f)
+ {
+ curPos = targetPos;
+ }
+ return curPos;
+ }
+
+ public static OpenTK.Vector2 TKVector3(Vector2 v)
+ {
+ return new OpenTK.Vector2(v.X, v.Y);
+ }
+
+ public static OpenTK.Vector3 TKVector3(Vector3 v)
+ {
+ return new OpenTK.Vector3(v.X, v.Y, v.Z);
+ }
+
+ public static OpenTK.Vector4 TKVector3(Vector4 v)
+ {
+ return new OpenTK.Vector4(v.X, v.Y, v.Z, v.W);
+ }
+
+ public static Vector2 OMVVector2(OpenTK.Vector2 v)
+ {
+ return new Vector2(v.X, v.Y);
+ }
+
+ public static Vector3 OMVVector3(OpenTK.Vector3 v)
+ {
+ return new Vector3(v.X, v.Y, v.Z);
+ }
+
+ public static Vector4 OMVVector4(OpenTK.Vector4 v)
+ {
+ return new Vector4(v.X, v.Y, v.Z, v.W);
+ }
+
+ public static Color WinColor(OpenTK.Graphics.Color4 color)
+ {
+ return Color.FromArgb((int)(color.A * 255), (int)(color.R * 255), (int)(color.G * 255), (int)(color.B * 255));
+ }
+
+ public static Color WinColor(Color4 color)
+ {
+ return Color.FromArgb((int)(color.A * 255), (int)(color.R * 255), (int)(color.G * 255), (int)(color.B * 255));
+ }
+
+ public static int NextPow2(int start)
+ {
+ int pow = 1;
+ while (pow < start) pow *= 2;
+ return pow;
+ }
+
+ #region Cached image save and load
+ public static readonly string RAD_IMG_MAGIC = "radegast_img";
+
+ public static bool LoadCachedImage(UUID textureID, out byte[] tgaData, out bool hasAlpha, out bool fullAlpha, out bool isMask)
+ {
+ tgaData = null;
+ hasAlpha = fullAlpha = isMask = false;
+
+ try
+ {
+ string fname = RadegastInstance.GlobalInstance.ComputeCacheName(RadegastInstance.GlobalInstance.Client.Settings.ASSET_CACHE_DIR, textureID) + ".rzi";
+
+ using (var f = File.Open(fname, FileMode.Open, FileAccess.Read, FileShare.Read))
+ {
+ byte[] header = new byte[36];
+ int i = 0;
+ f.Read(header, 0, header.Length);
+
+ // check if the file is starting with magic string
+ if (RAD_IMG_MAGIC != Utils.BytesToString(header, 0, RAD_IMG_MAGIC.Length))
+ return false;
+ i += RAD_IMG_MAGIC.Length;
+
+ if (header[i++] != 1) // check version
+ return false;
+
+ hasAlpha = header[i++] == 1;
+ fullAlpha = header[i++] == 1;
+ isMask = header[i++] == 1;
+
+ int uncompressedSize = Utils.BytesToInt(header, i);
+ i += 4;
+
+ textureID = new UUID(header, i);
+ i += 16;
+
+ tgaData = new byte[uncompressedSize];
+ using (var compressed = new DeflateStream(f, CompressionMode.Decompress))
+ {
+ int read = 0;
+ while ((read = compressed.Read(tgaData, read, uncompressedSize - read)) > 0) ;
+ }
+ }
+
+ return true;
+ }
+ catch (FileNotFoundException) { }
+ catch (Exception ex)
+ {
+ Logger.DebugLog(string.Format("Failed to load radegast cache file {0}: {1}", textureID, ex.Message));
+ }
+ return false;
+ }
+
+ public static bool SaveCachedImage(byte[] tgaData, UUID textureID, bool hasAlpha, bool fullAlpha, bool isMask)
+ {
+ try
+ {
+ string fname = RadegastInstance.GlobalInstance.ComputeCacheName(RadegastInstance.GlobalInstance.Client.Settings.ASSET_CACHE_DIR, textureID) + ".rzi";
+
+ using (var f = File.Open(fname, FileMode.Create, FileAccess.Write, FileShare.None))
+ {
+ int i = 0;
+ // magic header
+ f.Write(Utils.StringToBytes(RAD_IMG_MAGIC), 0, RAD_IMG_MAGIC.Length);
+ i += RAD_IMG_MAGIC.Length;
+
+ // version
+ f.WriteByte((byte)1);
+ i++;
+
+ // texture info
+ f.WriteByte(hasAlpha ? (byte)1 : (byte)0);
+ f.WriteByte(fullAlpha ? (byte)1 : (byte)0);
+ f.WriteByte(isMask ? (byte)1 : (byte)0);
+ i += 3;
+
+ // texture size
+ byte[] uncompressedSize = Utils.IntToBytes(tgaData.Length);
+ f.Write(uncompressedSize, 0, uncompressedSize.Length);
+ i += uncompressedSize.Length;
+
+ // texture id
+ byte[] id = new byte[16];
+ textureID.ToBytes(id, 0);
+ f.Write(id, 0, 16);
+ i += 16;
+
+ // compressed texture data
+ using (var compressed = new DeflateStream(f, CompressionMode.Compress))
+ {
+ compressed.Write(tgaData, 0, tgaData.Length);
+ }
+ }
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Logger.DebugLog(string.Format("Failed to save radegast cache file {0}: {1}", textureID, ex.Message));
+ return false;
+ }
+ }
+ #endregion Cached image save and load
+
+ #region Static vertices and indices for a cube (used for bounding box drawing)
+ /**********************************************
+ 5 --- 4
+ /| /|
+ 1 --- 0 |
+ | 6 --| 7
+ |/ |/
+ 2 --- 3
+ ***********************************************/
+ public static readonly float[] CubeVertices = new float[]
+ {
+ 0.5f, 0.5f, 0.5f, // 0
+ -0.5f, 0.5f, 0.5f, // 1
+ -0.5f, -0.5f, 0.5f, // 2
+ 0.5f, -0.5f, 0.5f, // 3
+ 0.5f, 0.5f, -0.5f, // 4
+ -0.5f, 0.5f, -0.5f, // 5
+ -0.5f, -0.5f, -0.5f, // 6
+ 0.5f, -0.5f, -0.5f // 7
+ };
+
+ public static readonly ushort[] CubeIndices = new ushort[]
+ {
+ 0, 1, 2, 3, // Front Face
+ 4, 5, 6, 7, // Back Face
+ 1, 2, 6, 5, // Left Face
+ 0, 3, 7, 4, // Right Face
+ 0, 1, 5, 4, // Top Face
+ 2, 3, 7, 6 // Bottom Face
+ };
+ #endregion Static vertices and indices for a cube (used for bounding box drawing)
+
+ public static int GLLoadImage(Bitmap bitmap, bool hasAlpha)
+ {
+ return GLLoadImage(bitmap, hasAlpha, true);
+ }
+
+ public static int GLLoadImage(Bitmap bitmap, bool hasAlpha, bool useMipmap)
+ {
+ useMipmap = useMipmap && RenderSettings.HasMipmap;
+ int ret = -1;
+ GL.GenTextures(1, out ret);
+ GL.BindTexture(TextureTarget.Texture2D, ret);
+
+ Rectangle rectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
+
+ BitmapData bitmapData =
+ bitmap.LockBits(
+ rectangle,
+ ImageLockMode.ReadOnly,
+ hasAlpha ? System.Drawing.Imaging.PixelFormat.Format32bppArgb : System.Drawing.Imaging.PixelFormat.Format24bppRgb);
+
+ GL.TexImage2D(
+ TextureTarget.Texture2D,
+ 0,
+ hasAlpha ? PixelInternalFormat.Rgba : PixelInternalFormat.Rgb8,
+ bitmap.Width,
+ bitmap.Height,
+ 0,
+ hasAlpha ? OpenTK.Graphics.OpenGL.PixelFormat.Bgra : OpenTK.Graphics.OpenGL.PixelFormat.Bgr,
+ PixelType.UnsignedByte,
+ bitmapData.Scan0);
+
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
+ if (useMipmap)
+ {
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.LinearMipmapLinear);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.GenerateMipmap, 1);
+ GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
+ }
+ else
+ {
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
+ }
+
+ bitmap.UnlockBits(bitmapData);
+ return ret;
+ }
+
+ public static void Draw2DBox(float x, float y, float width, float height, float depth)
+ {
+ GL.Begin(BeginMode.Quads);
+ {
+ GL.TexCoord2(0, 1);
+ GL.Vertex3(x, y, depth);
+ GL.TexCoord2(1, 1);
+ GL.Vertex3(x + width, y, depth);
+ GL.TexCoord2(1, 0);
+ GL.Vertex3(x + width, y + height, depth);
+ GL.TexCoord2(0, 0);
+ GL.Vertex3(x, y + height, depth);
+ }
+ GL.End();
+ }
+
+ public static void ResetMaterial()
+ {
+ GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Ambient, new float[] { 0.2f, 0.2f, 0.2f, 1.0f });
+ GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Diffuse, new float[] { 0.8f, 0.8f, 0.8f, 1.0f });
+ GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Specular, new float[] { 0f, 0f, 0f, 1.0f });
+ GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Emission, new float[] { 0f, 0f, 0f, 1.0f });
+ GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Shininess, 0f);
+ ShaderProgram.Stop();
+ }
+ }
+
+ /// <summary>
+ /// Represents camera object
+ /// </summary>
+ public class Camera
+ {
+ /// <summary>
+ /// Indicates that there was manual camera movement, stop tracking objects
+ /// </summary>
+ public bool Manual;
+ Vector3 mPosition;
+ Vector3 mFocalPoint;
+ bool mModified;
+
+ /// <summary>Camera position</summary>
+ public Vector3 Position
+ {
+ get
+ {
+ return mPosition;
+ }
+
+ set
+ {
+ if (mPosition != value)
+ {
+ mPosition = value;
+ Modify();
+ }
+ }
+ }
+
+ /// <summary>Camera target</summary>
+ public Vector3 FocalPoint
+ {
+ get
+ {
+ return mFocalPoint;
+ }
+
+ set
+ {
+ if (mFocalPoint != value)
+ {
+ mFocalPoint = value;
+ Modify();
+ }
+ }
+ }
+
+ /// <summary>Zoom level</summary>
+ public float Zoom;
+ /// <summary>Draw distance</summary>
+ public float Far;
+ /// <summary>Has camera been modified</summary>
+ public bool Modified { get { return mModified; } set { mModified = value; } }
+
+ public float TimeToTarget = 0f;
+
+ public Vector3 RenderPosition;
+ public Vector3 RenderFocalPoint;
+
+ void Modify()
+ {
+ mModified = true;
+ }
+
+ public void Step(float time)
+ {
+ if (RenderPosition != Position)
+ {
+ RenderPosition = RHelp.Smoothed1stOrder(RenderPosition, Position, time);
+ Modified = true;
+ }
+ if (RenderFocalPoint != FocalPoint)
+ {
+ RenderFocalPoint = RHelp.Smoothed1stOrder(RenderFocalPoint, FocalPoint, time);
+ Modified = true;
+ }
+ }
+
+#if OBSOLETE_CODE
+ [Obsolete("Use Step(), left in here for reference")]
+ public void Step2(float time)
+ {
+ TimeToTarget -= time;
+ if (TimeToTarget <= time)
+ {
+ EndMove();
+ return;
+ }
+
+ mModified = true;
+
+ float pctElapsed = time / TimeToTarget;
+
+ if (RenderPosition != Position)
+ {
+ float distance = Vector3.Distance(RenderPosition, Position);
+ RenderPosition = Vector3.Lerp(RenderPosition, Position, distance * pctElapsed);
+ }
+
+ if (RenderFocalPoint != FocalPoint)
+ {
+ RenderFocalPoint = Interpolate(RenderFocalPoint, FocalPoint, pctElapsed);
+ }
+ }
+
+ Vector3 Interpolate(Vector3 start, Vector3 end, float fraction)
+ {
+ float distance = Vector3.Distance(start, end);
+ Vector3 direction = end - start;
+ return start + direction * fraction;
+ }
+
+ public void EndMove()
+ {
+ mModified = true;
+ TimeToTarget = 0;
+ RenderPosition = Position;
+ RenderFocalPoint = FocalPoint;
+ }
+#endif
+
+ public void Pan(float deltaX, float deltaY)
+ {
+ Manual = true;
+ Vector3 direction = Position - FocalPoint;
+ direction.Normalize();
+ Vector3 vy = direction % Vector3.UnitZ;
+ Vector3 vx = vy % direction;
+ Vector3 vxy = vx * deltaY + vy * deltaX;
+ Position += vxy;
+ FocalPoint += vxy;
+ }
+
+ public void Rotate(float delta, bool horizontal)
+ {
+ Manual = true;
+ Vector3 direction = Position - FocalPoint;
+ if (horizontal)
+ {
+ Position = FocalPoint + direction * new Quaternion(0f, 0f, (float)Math.Sin(delta), (float)Math.Cos(delta));
+ }
+ else
+ {
+ Position = FocalPoint + direction * Quaternion.CreateFromAxisAngle(direction % Vector3.UnitZ, delta);
+ }
+ }
+
+ public void MoveToTarget(float delta)
+ {
+ Manual = true;
+ Position += (Position - FocalPoint) * delta;
+ }
+
+ /// <summary>
+ /// Sets the world in perspective of the camera
+ /// </summary>
+ public void LookAt()
+ {
+ OpenTK.Matrix4 lookAt = OpenTK.Matrix4.LookAt(
+ RenderPosition.X, RenderPosition.Y, RenderPosition.Z,
+ RenderFocalPoint.X, RenderFocalPoint.Y, RenderFocalPoint.Z,
+ 0f, 0f, 1f);
+ GL.MultMatrix(ref lookAt);
+ }
+ }
+
+ public static class MeshToOBJ
+ {
+ public static bool MeshesToOBJ(Dictionary<uint, FacetedMesh> meshes, string filename)
+ {
+ StringBuilder obj = new StringBuilder();
+ StringBuilder mtl = new StringBuilder();
+
+ FileInfo objFileInfo = new FileInfo(filename);
+
+ string mtlFilename = objFileInfo.FullName.Substring(objFileInfo.DirectoryName.Length + 1,
+ objFileInfo.FullName.Length - (objFileInfo.DirectoryName.Length + 1) - 4) + ".mtl";
+
+ obj.AppendLine("# Created by libprimrender");
+ obj.AppendLine("mtllib ./" + mtlFilename);
+ obj.AppendLine();
+
+ mtl.AppendLine("# Created by libprimrender");
+ mtl.AppendLine();
+
+ int primNr = 0;
+ foreach (FacetedMesh mesh in meshes.Values)
+ {
+ for (int j = 0; j < mesh.Faces.Count; j++)
+ {
+ Face face = mesh.Faces[j];
+
+ if (face.Vertices.Count > 2)
+ {
+ string mtlName = String.Format("material{0}-{1}", primNr, face.ID);
+ Primitive.TextureEntryFace tex = face.TextureFace;
+ string texName = tex.TextureID.ToString() + ".tga";
+
+ // FIXME: Convert the source to TGA (if needed) and copy to the destination
+
+ float shiny = 0.00f;
+ switch (tex.Shiny)
+ {
+ case Shininess.High:
+ shiny = 1.00f;
+ break;
+ case Shininess.Medium:
+ shiny = 0.66f;
+ break;
+ case Shininess.Low:
+ shiny = 0.33f;
+ break;
+ }
+
+ obj.AppendFormat("g face{0}-{1}{2}", primNr, face.ID, Environment.NewLine);
+
+ mtl.AppendLine("newmtl " + mtlName);
+ mtl.AppendFormat("Ka {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);
+ mtl.AppendFormat("Kd {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);
+ //mtl.AppendFormat("Ks {0} {1} {2}{3}");
+ mtl.AppendLine("Tr " + tex.RGBA.A);
+ mtl.AppendLine("Ns " + shiny);
+ mtl.AppendLine("illum 1");
+ if (tex.TextureID != UUID.Zero && tex.TextureID != Primitive.TextureEntry.WHITE_TEXTURE)
+ mtl.AppendLine("map_Kd ./" + texName);
+ mtl.AppendLine();
+
+ // Write the vertices, texture coordinates, and vertex normals for this side
+ for (int k = 0; k < face.Vertices.Count; k++)
+ {
+ Vertex vertex = face.Vertices[k];
+
+ #region Vertex
+
+ Vector3 pos = vertex.Position;
+
+ // Apply scaling
+ pos *= mesh.Prim.Scale;
+
+ // Apply rotation
+ pos *= mesh.Prim.Rotation;
+
+ // The root prim position is sim-relative, while child prim positions are
+ // parent-relative. We want to apply parent-relative translations but not
+ // sim-relative ones
+ if (mesh.Prim.ParentID != 0)
+ pos += mesh.Prim.Position;
+
+ obj.AppendFormat("v {0} {1} {2}{3}", pos.X, pos.Y, pos.Z, Environment.NewLine);
+
+ #endregion Vertex
+
+ #region Texture Coord
+
+ obj.AppendFormat("vt {0} {1}{2}", vertex.TexCoord.X, vertex.TexCoord.Y,
+ Environment.NewLine);
+
+ #endregion Texture Coord
+
+ #region Vertex Normal
+
+ // HACK: Sometimes normals are getting set to <NaN,NaN,NaN>
+ if (!Single.IsNaN(vertex.Normal.X) && !Single.IsNaN(vertex.Normal.Y) && !Single.IsNaN(vertex.Normal.Z))
+ obj.AppendFormat("vn {0} {1} {2}{3}", vertex.Normal.X, vertex.Normal.Y, vertex.Normal.Z,
+ Environment.NewLine);
+ else
+ obj.AppendLine("vn 0.0 1.0 0.0");
+
+ #endregion Vertex Normal
+ }
+
+ obj.AppendFormat("# {0} vertices{1}", face.Vertices.Count, Environment.NewLine);
+ obj.AppendLine();
+ obj.AppendLine("usemtl " + mtlName);
+
+ #region Elements
+
+ // Write all of the faces (triangles) for this side
+ for (int k = 0; k < face.Indices.Count / 3; k++)
+ {
+ obj.AppendFormat("f -{0}/-{0}/-{0} -{1}/-{1}/-{1} -{2}/-{2}/-{2}{3}",
+ face.Vertices.Count - face.Indices[k * 3 + 0],
+ face.Vertices.Count - face.Indices[k * 3 + 1],
+ face.Vertices.Count - face.Indices[k * 3 + 2],
+ Environment.NewLine);
+ }
+
+ obj.AppendFormat("# {0} elements{1}", face.Indices.Count / 3, Environment.NewLine);
+ obj.AppendLine();
+
+ #endregion Elements
+ }
+ }
+ primNr++;
+ }
+
+ try
+ {
+ File.WriteAllText(filename, obj.ToString());
+ File.WriteAllText(mtlFilename, mtl.ToString());
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ public static class Math3D
+ {
+ // Column-major:
+ // | 0 4 8 12 |
+ // | 1 5 9 13 |
+ // | 2 6 10 14 |
+ // | 3 7 11 15 |
+
+ public static float[] CreateTranslationMatrix(Vector3 v)
+ {
+ float[] mat = new float[16];
+
+ mat[12] = v.X;
+ mat[13] = v.Y;
+ mat[14] = v.Z;
+ mat[0] = mat[5] = mat[10] = mat[15] = 1;
+
+ return mat;
+ }
+
+ public static float[] CreateRotationMatrix(Quaternion q)
+ {
+ float[] mat = new float[16];
+
+ // Transpose the quaternion (don't ask me why)
+ q.X = q.X * -1f;
+ q.Y = q.Y * -1f;
+ q.Z = q.Z * -1f;
+
+ float x2 = q.X + q.X;
+ float y2 = q.Y + q.Y;
+ float z2 = q.Z + q.Z;
+ float xx = q.X * x2;
+ float xy = q.X * y2;
+ float xz = q.X * z2;
+ float yy = q.Y * y2;
+ float yz = q.Y * z2;
+ float zz = q.Z * z2;
+ float wx = q.W * x2;
+ float wy = q.W * y2;
+ float wz = q.W * z2;
+
+ mat[0] = 1.0f - (yy + zz);
+ mat[1] = xy - wz;
+ mat[2] = xz + wy;
+ mat[3] = 0.0f;
+
+ mat[4] = xy + wz;
+ mat[5] = 1.0f - (xx + zz);
+ mat[6] = yz - wx;
+ mat[7] = 0.0f;
+
+ mat[8] = xz - wy;
+ mat[9] = yz + wx;
+ mat[10] = 1.0f - (xx + yy);
+ mat[11] = 0.0f;
+
+ mat[12] = 0.0f;
+ mat[13] = 0.0f;
+ mat[14] = 0.0f;
+ mat[15] = 1.0f;
+
+ return mat;
+ }
+
+ public static float[] CreateSRTMatrix(Vector3 scale, Quaternion q, Vector3 pos)
+ {
+ float[] mat = new float[16];
+
+ // Transpose the quaternion (don't ask me why)
+ q.X = q.X * -1f;
+ q.Y = q.Y * -1f;
+ q.Z = q.Z * -1f;
+
+ float x2 = q.X + q.X;
+ float y2 = q.Y + q.Y;
+ float z2 = q.Z + q.Z;
+ float xx = q.X * x2;
+ float xy = q.X * y2;
+ float xz = q.X * z2;
+ float yy = q.Y * y2;
+ float yz = q.Y * z2;
+ float zz = q.Z * z2;
+ float wx = q.W * x2;
+ float wy = q.W * y2;
+ float wz = q.W * z2;
+
+ mat[0] = (1.0f - (yy + zz)) * scale.X;
+ mat[1] = (xy - wz) * scale.X;
+ mat[2] = (xz + wy) * scale.X;
+ mat[3] = 0.0f;
+
+ mat[4] = (xy + wz) * scale.Y;
+ mat[5] = (1.0f - (xx + zz)) * scale.Y;
+ mat[6] = (yz - wx) * scale.Y;
+ mat[7] = 0.0f;
+
+ mat[8] = (xz - wy) * scale.Z;
+ mat[9] = (yz + wx) * scale.Z;
+ mat[10] = (1.0f - (xx + yy)) * scale.Z;
+ mat[11] = 0.0f;
+
+ //Positional parts
+ mat[12] = pos.X;
+ mat[13] = pos.Y;
+ mat[14] = pos.Z;
+ mat[15] = 1.0f;
+
+ return mat;
+ }
+
+
+ public static float[] CreateScaleMatrix(Vector3 v)
+ {
+ float[] mat = new float[16];
+
+ mat[0] = v.X;
+ mat[5] = v.Y;
+ mat[10] = v.Z;
+ mat[15] = 1;
+
+ return mat;
+ }
+
+ public static float[] Lerp(float[] matrix1, float[] matrix2, float amount)
+ {
+
+ float[] lerp = new float[16];
+ //Probably not doing this as a loop is cheaper(unrolling)
+ //also for performance we probably should not create new objects
+ // but meh.
+ for (int x = 0; x < 16; x++)
+ {
+ lerp[x] = matrix1[x] + ((matrix2[x] - matrix1[x]) * amount);
+ }
+
+ return lerp;
+ }
+
+
+ public static bool GluProject(OpenTK.Vector3 objPos, OpenTK.Matrix4 modelMatrix, OpenTK.Matrix4 projMatrix, int[] viewport, out OpenTK.Vector3 screenPos)
+ {
+ OpenTK.Vector4 _in;
+ OpenTK.Vector4 _out;
+
+ _in.X = objPos.X;
+ _in.Y = objPos.Y;
+ _in.Z = objPos.Z;
+ _in.W = 1.0f;
+
+ _out = OpenTK.Vector4.Transform(_in, modelMatrix);
+ _in = OpenTK.Vector4.Transform(_out, projMatrix);
+
+ if (_in.W <= 0.0)
+ {
+ screenPos = OpenTK.Vector3.Zero;
+ return false;
+ }
+
+ _in.X /= _in.W;
+ _in.Y /= _in.W;
+ _in.Z /= _in.W;
+ /* Map x, y and z to range 0-1 */
+ _in.X = _in.X * 0.5f + 0.5f;
+ _in.Y = _in.Y * 0.5f + 0.5f;
+ _in.Z = _in.Z * 0.5f + 0.5f;
+
+ /* Map x,y to viewport */
+ _in.X = _in.X * viewport[2] + viewport[0];
+ _in.Y = _in.Y * viewport[3] + viewport[1];
+
+ screenPos.X = _in.X;
+ screenPos.Y = _in.Y;
+ screenPos.Z = _in.Z;
+
+ return true;
+ }
+
+ public static bool GluUnProject(float winx, float winy, float winz, OpenTK.Matrix4 modelMatrix, OpenTK.Matrix4 projMatrix, int[] viewport, out OpenTK.Vector3 pos)
+ {
+ OpenTK.Matrix4 finalMatrix;
+ OpenTK.Vector4 _in;
+ OpenTK.Vector4 _out;
+
+ finalMatrix = OpenTK.Matrix4.Mult(modelMatrix, projMatrix);
+
+ finalMatrix.Invert();
+
+ _in.X = winx;
+ _in.Y = winy;
+ _in.Z = winz;
+ _in.W = 1.0f;
+
+ /* Map x and y from window coordinates */
+ _in.X = (_in.X - viewport[0]) / viewport[2];
+ _in.Y = (_in.Y - viewport[1]) / viewport[3];
+
+ pos = OpenTK.Vector3.Zero;
+
+ /* Map to range -1 to 1 */
+ _in.X = _in.X * 2 - 1;
+ _in.Y = _in.Y * 2 - 1;
+ _in.Z = _in.Z * 2 - 1;
+
+ //__gluMultMatrixVecd(finalMatrix, _in, _out);
+ // check if this works:
+ _out = OpenTK.Vector4.Transform(_in, finalMatrix);
+
+ if (_out.W == 0.0f)
+ return false;
+ _out.X /= _out.W;
+ _out.Y /= _out.W;
+ _out.Z /= _out.W;
+ pos.X = _out.X;
+ pos.Y = _out.Y;
+ pos.Z = _out.Z;
+ return true;
+ }
+
+ public static double[] AbovePlane(double height)
+ {
+ return new double[] { 0, 0, 1, -height };
+ }
+
+ public static double[] BelowPlane(double height)
+ {
+ return new double[] { 0, 0, -1, height };
+ }
+ }
+
+ /*
+ * Helper classs for reading the static VFS file, call
+ * staticVFS.readVFSheaders() with the path to the static_data.db2 and static_index.db2 files
+ * and it will pass and dump in to openmetaverse_data for you
+ * This should only be needed to be used if LL update the static VFS in order to refresh our data
+ */
+
+ class VFSblock
+ {
+ public int mLocation;
+ public int mLength;
+ public int mAccessTime;
+ public UUID mFileID;
+ public int mSize;
+ public AssetType mAssetType;
+
+ public int readblock(byte[] blockdata, int offset)
+ {
+
+ BitPack input = new BitPack(blockdata, offset);
+ mLocation = input.UnpackInt();
+ mLength = input.UnpackInt();
+ mAccessTime = input.UnpackInt();
+ mFileID = input.UnpackUUID();
+ int filetype = input.UnpackShort();
+ mAssetType = (AssetType)filetype;
+ mSize = input.UnpackInt();
+ offset += 34;
+
+ Logger.Log(String.Format("Found header for {0} type {1} length {2} at {3}", mFileID, mAssetType, mSize, mLocation), Helpers.LogLevel.Info);
+
+ return offset;
+ }
+
+ }
+
+ public class staticVFS
+ {
+ public static void readVFSheaders(string datafile, string indexfile)
+ {
+ FileStream datastream;
+ FileStream indexstream;
+
+ datastream = File.Open(datafile, FileMode.Open);
+ indexstream = File.Open(indexfile, FileMode.Open);
+
+ int offset = 0;
+
+ byte[] blockdata = new byte[indexstream.Length];
+ indexstream.Read(blockdata, 0, (int)indexstream.Length);
+
+ while (offset < indexstream.Length)
+ {
+ VFSblock block = new VFSblock();
+ offset = block.readblock(blockdata, offset);
+
+ FileStream writer = File.Open(OpenMetaverse.Settings.RESOURCE_DIR + System.IO.Path.DirectorySeparatorChar + block.mFileID.ToString(), FileMode.Create);
+ byte[] data = new byte[block.mSize];
+ datastream.Seek(block.mLocation, SeekOrigin.Begin);
+ datastream.Read(data, 0, block.mSize);
+ writer.Write(data, 0, block.mSize);
+ writer.Close();
+ }
+
+ }
+ }
+}
\ No newline at end of file