diff --git a/Assets/Materials.meta b/Assets/Materials.meta new file mode 100644 index 0000000..b64c23c --- /dev/null +++ b/Assets/Materials.meta @@ -0,0 +1,4 @@ +fileFormatVersion: 2 +guid: 645a156f3902da1448c64094df7f8224 +DefaultImporter: + userData: diff --git a/Assets/Materials/skybox.meta b/Assets/Materials/skybox.meta new file mode 100644 index 0000000..083e192 --- /dev/null +++ b/Assets/Materials/skybox.meta @@ -0,0 +1,4 @@ +fileFormatVersion: 2 +guid: 119df583b9908444baf9d1bb86f6008c +DefaultImporter: + userData: diff --git a/Assets/Materials/skybox/forest_skybox.mat b/Assets/Materials/skybox/forest_skybox.mat new file mode 100644 index 0000000..60d497b Binary files /dev/null and b/Assets/Materials/skybox/forest_skybox.mat differ diff --git a/Assets/Materials/skybox/forest_skybox.mat.meta b/Assets/Materials/skybox/forest_skybox.mat.meta new file mode 100644 index 0000000..c0b61fe --- /dev/null +++ b/Assets/Materials/skybox/forest_skybox.mat.meta @@ -0,0 +1,4 @@ +fileFormatVersion: 2 +guid: cebdc449e3df4427c89249abb75ab7a2 +NativeFormatImporter: + userData: diff --git a/Assets/Plugins.meta b/Assets/Plugins.meta index 6341370..c599523 100644 --- a/Assets/Plugins.meta +++ b/Assets/Plugins.meta @@ -1,4 +1,4 @@ fileFormatVersion: 2 -guid: e9397f1cc72425345ae40b516554892b +guid: 43121e4fc44b64b47b464561b5a5e4c8 DefaultImporter: userData: diff --git a/Assets/Plugins/Accessibility.dll b/Assets/Plugins/Accessibility.dll new file mode 100644 index 0000000..bf5cb64 Binary files /dev/null and b/Assets/Plugins/Accessibility.dll differ diff --git a/Assets/Plugins/Accessibility.dll.meta b/Assets/Plugins/Accessibility.dll.meta new file mode 100644 index 0000000..cf62aa2 --- /dev/null +++ b/Assets/Plugins/Accessibility.dll.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: bb0563889a0b8b849b0e0e6ee82d5bb9 +MonoAssemblyImporter: + serializedVersion: 1 + iconMap: {} + executionOrder: {} + userData: diff --git a/Assets/Plugins/Mono.Posix.dll b/Assets/Plugins/Mono.Posix.dll new file mode 100644 index 0000000..a1ee72c Binary files /dev/null and b/Assets/Plugins/Mono.Posix.dll differ diff --git a/Assets/Plugins/Mono.Posix.dll.meta b/Assets/Plugins/Mono.Posix.dll.meta new file mode 100644 index 0000000..dac59ce --- /dev/null +++ b/Assets/Plugins/Mono.Posix.dll.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 502fc75f4ed83ab438df2742e48f9a05 +MonoAssemblyImporter: + serializedVersion: 1 + iconMap: {} + executionOrder: {} + userData: diff --git a/Assets/Plugins/Mono.Web.dll b/Assets/Plugins/Mono.Web.dll new file mode 100644 index 0000000..ea77964 Binary files /dev/null and b/Assets/Plugins/Mono.Web.dll differ diff --git a/Assets/Plugins/Mono.Web.dll.meta b/Assets/Plugins/Mono.Web.dll.meta new file mode 100644 index 0000000..bed6efa --- /dev/null +++ b/Assets/Plugins/Mono.Web.dll.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 47b3efd7d12758c43afec3d6a96d4d2d +MonoAssemblyImporter: + serializedVersion: 1 + iconMap: {} + executionOrder: {} + userData: diff --git a/Assets/Plugins/Mono.WebBrowser.dll b/Assets/Plugins/Mono.WebBrowser.dll new file mode 100644 index 0000000..192c9fc Binary files /dev/null and b/Assets/Plugins/Mono.WebBrowser.dll differ diff --git a/Assets/Plugins/Mono.WebBrowser.dll.meta b/Assets/Plugins/Mono.WebBrowser.dll.meta new file mode 100644 index 0000000..1107587 --- /dev/null +++ b/Assets/Plugins/Mono.WebBrowser.dll.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 0c838acf0293e61479a6bdbb29adc0e2 +MonoAssemblyImporter: + serializedVersion: 1 + iconMap: {} + executionOrder: {} + userData: diff --git a/Assets/Plugins/System.Configuration.dll b/Assets/Plugins/System.Configuration.dll new file mode 100644 index 0000000..61a0475 Binary files /dev/null and b/Assets/Plugins/System.Configuration.dll differ diff --git a/Assets/Plugins/System.Configuration.dll.meta b/Assets/Plugins/System.Configuration.dll.meta new file mode 100644 index 0000000..bad32e8 --- /dev/null +++ b/Assets/Plugins/System.Configuration.dll.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: ea3da258fbe548d409cd28f34bcf8681 +MonoAssemblyImporter: + serializedVersion: 1 + iconMap: {} + executionOrder: {} + userData: diff --git a/Assets/Plugins/System.EnterpriseServices.dll b/Assets/Plugins/System.EnterpriseServices.dll new file mode 100644 index 0000000..3757f7e Binary files /dev/null and b/Assets/Plugins/System.EnterpriseServices.dll differ diff --git a/Assets/Plugins/System.EnterpriseServices.dll.meta b/Assets/Plugins/System.EnterpriseServices.dll.meta new file mode 100644 index 0000000..566d7d7 --- /dev/null +++ b/Assets/Plugins/System.EnterpriseServices.dll.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 069ce17854431444f9b33b5ad95e2f04 +MonoAssemblyImporter: + serializedVersion: 1 + iconMap: {} + executionOrder: {} + userData: diff --git a/Assets/Plugins/System.Security.dll b/Assets/Plugins/System.Security.dll new file mode 100644 index 0000000..4fd68af Binary files /dev/null and b/Assets/Plugins/System.Security.dll differ diff --git a/Assets/Plugins/System.Security.dll.meta b/Assets/Plugins/System.Security.dll.meta new file mode 100644 index 0000000..44afbc2 --- /dev/null +++ b/Assets/Plugins/System.Security.dll.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 897bdd45df3315f4389e81f5d805e5fb +MonoAssemblyImporter: + serializedVersion: 1 + iconMap: {} + executionOrder: {} + userData: diff --git a/Assets/Plugins/System.Web.dll b/Assets/Plugins/System.Web.dll new file mode 100644 index 0000000..f021a79 Binary files /dev/null and b/Assets/Plugins/System.Web.dll differ diff --git a/Assets/Plugins/System.Web.dll.meta b/Assets/Plugins/System.Web.dll.meta new file mode 100644 index 0000000..8d65a2b --- /dev/null +++ b/Assets/Plugins/System.Web.dll.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 68379aac2a76c11459fec2559cf65d85 +MonoAssemblyImporter: + serializedVersion: 1 + iconMap: {} + executionOrder: {} + userData: diff --git a/Assets/Plugins/System.Windows.Forms.dll b/Assets/Plugins/System.Windows.Forms.dll new file mode 100644 index 0000000..074f379 Binary files /dev/null and b/Assets/Plugins/System.Windows.Forms.dll differ diff --git a/Assets/Plugins/System.Windows.Forms.dll.meta b/Assets/Plugins/System.Windows.Forms.dll.meta new file mode 100644 index 0000000..ca5041a --- /dev/null +++ b/Assets/Plugins/System.Windows.Forms.dll.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 834bec4d5f5fe12428755c20f9ffbb26 +MonoAssemblyImporter: + serializedVersion: 1 + iconMap: {} + executionOrder: {} + userData: diff --git a/Assets/Plugins/openjpeg-dotnet.dll.meta b/Assets/Plugins/openjpeg-dotnet.dll.meta index 7ae19c0..bbad5cd 100644 --- a/Assets/Plugins/openjpeg-dotnet.dll.meta +++ b/Assets/Plugins/openjpeg-dotnet.dll.meta @@ -1,4 +1,4 @@ fileFormatVersion: 2 -guid: 89c98088dfae49d4a851fa03b9336396 +guid: 1669161240056074ca0257dc0cb4fa67 DefaultImporter: userData: diff --git a/Assets/Scenes/login.unity b/Assets/Scenes/login.unity index ba52d25..bc7d678 100644 Binary files a/Assets/Scenes/login.unity and b/Assets/Scenes/login.unity differ diff --git a/Assets/Scripts/DummySceneWindow.cs b/Assets/Scripts/DummySceneWindow.cs new file mode 100644 index 0000000..2cfcc97 --- /dev/null +++ b/Assets/Scripts/DummySceneWindow.cs @@ -0,0 +1,13 @@ +using UnityEngine; +using System.Collections; +using OpenMetaverse; +using Radegast.Rendering; + +public class SceneWindow +{ + public void StartShiny(){} + public bool TryGetTextureInfo(UUID textureID, out TextureInfo info){ + info = new TextureInfo(); + return false;} + public void DownloadTexture(TextureLoadItem item, bool force){} +} diff --git a/Assets/Scripts/DummySceneWindow.cs.meta b/Assets/Scripts/DummySceneWindow.cs.meta new file mode 100644 index 0000000..84a005e --- /dev/null +++ b/Assets/Scripts/DummySceneWindow.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: bc2e13b17621a584b86a1c9278b59f28 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/Scripts/Frustum.cs b/Assets/Scripts/Frustum.cs new file mode 100644 index 0000000..93ed3d5 --- /dev/null +++ b/Assets/Scripts/Frustum.cs @@ -0,0 +1,232 @@ +#region --- MIT License --- +/* Licensed under the MIT/X11 license. + * Copyright (c) 2011 mjt + * This notice may not be removed from any source distribution. + * See license.txt for licensing details. + */ +#endregion +/* + * tutoriaali: + * http://www.crownandcutlass.com/features/technicaldetails/frustum.html + * + */ + +using System; +using OpenMetaverse; +//using OpenTK.Graphics.OpenGL; +//using OpenTK; + +namespace Radegast.Rendering +{ + public class Frustum + { + //static Matrix4 ProjMatrix, ModelMatrix; + static float[] ClipMatrix = new float[16]; + static float[,] frustum = new float[6, 4]; + const int RIGHT = 0, LEFT = 1, BOTTOM = 2, TOP = 3, BACK = 4, FRONT = 5; + + static void NormalizePlane(float[,] frustum, int side) + { + float magnitude = (float)Math.Sqrt((frustum[side, 0] * frustum[side, 0]) + (frustum[side, 1] * frustum[side, 1]) + + (frustum[side, 2] * frustum[side, 2])); + + frustum[side, 0] /= magnitude; + frustum[side, 1] /= magnitude; + frustum[side, 2] /= magnitude; + frustum[side, 3] /= magnitude; + } + + public static void CalculateFrustum(OpenTK.Matrix4 ProjMatrix, OpenTK.Matrix4 ModelMatrix) + { + ClipMatrix[0] = (ModelMatrix.M11 * ProjMatrix.M11) + (ModelMatrix.M12 * ProjMatrix.M21) + (ModelMatrix.M13 * ProjMatrix.M31) + (ModelMatrix.M14 * ProjMatrix.M41); + ClipMatrix[1] = (ModelMatrix.M11 * ProjMatrix.M12) + (ModelMatrix.M12 * ProjMatrix.M22) + (ModelMatrix.M13 * ProjMatrix.M32) + (ModelMatrix.M14 * ProjMatrix.M42); + ClipMatrix[2] = (ModelMatrix.M11 * ProjMatrix.M13) + (ModelMatrix.M12 * ProjMatrix.M23) + (ModelMatrix.M13 * ProjMatrix.M33) + (ModelMatrix.M14 * ProjMatrix.M43); + ClipMatrix[3] = (ModelMatrix.M11 * ProjMatrix.M14) + (ModelMatrix.M12 * ProjMatrix.M24) + (ModelMatrix.M13 * ProjMatrix.M34) + (ModelMatrix.M14 * ProjMatrix.M44); + + ClipMatrix[4] = (ModelMatrix.M21 * ProjMatrix.M11) + (ModelMatrix.M22 * ProjMatrix.M21) + (ModelMatrix.M23 * ProjMatrix.M31) + (ModelMatrix.M24 * ProjMatrix.M41); + ClipMatrix[5] = (ModelMatrix.M21 * ProjMatrix.M12) + (ModelMatrix.M22 * ProjMatrix.M22) + (ModelMatrix.M23 * ProjMatrix.M32) + (ModelMatrix.M24 * ProjMatrix.M42); + ClipMatrix[6] = (ModelMatrix.M21 * ProjMatrix.M13) + (ModelMatrix.M22 * ProjMatrix.M23) + (ModelMatrix.M23 * ProjMatrix.M33) + (ModelMatrix.M24 * ProjMatrix.M43); + ClipMatrix[7] = (ModelMatrix.M21 * ProjMatrix.M14) + (ModelMatrix.M22 * ProjMatrix.M24) + (ModelMatrix.M23 * ProjMatrix.M34) + (ModelMatrix.M24 * ProjMatrix.M44); + + ClipMatrix[8] = (ModelMatrix.M31 * ProjMatrix.M11) + (ModelMatrix.M32 * ProjMatrix.M21) + (ModelMatrix.M33 * ProjMatrix.M31) + (ModelMatrix.M34 * ProjMatrix.M41); + ClipMatrix[9] = (ModelMatrix.M31 * ProjMatrix.M12) + (ModelMatrix.M32 * ProjMatrix.M22) + (ModelMatrix.M33 * ProjMatrix.M32) + (ModelMatrix.M34 * ProjMatrix.M42); + ClipMatrix[10] = (ModelMatrix.M31 * ProjMatrix.M13) + (ModelMatrix.M32 * ProjMatrix.M23) + (ModelMatrix.M33 * ProjMatrix.M33) + (ModelMatrix.M34 * ProjMatrix.M43); + ClipMatrix[11] = (ModelMatrix.M31 * ProjMatrix.M14) + (ModelMatrix.M32 * ProjMatrix.M24) + (ModelMatrix.M33 * ProjMatrix.M34) + (ModelMatrix.M34 * ProjMatrix.M44); + + ClipMatrix[12] = (ModelMatrix.M41 * ProjMatrix.M11) + (ModelMatrix.M42 * ProjMatrix.M21) + (ModelMatrix.M43 * ProjMatrix.M31) + (ModelMatrix.M44 * ProjMatrix.M41); + ClipMatrix[13] = (ModelMatrix.M41 * ProjMatrix.M12) + (ModelMatrix.M42 * ProjMatrix.M22) + (ModelMatrix.M43 * ProjMatrix.M32) + (ModelMatrix.M44 * ProjMatrix.M42); + ClipMatrix[14] = (ModelMatrix.M41 * ProjMatrix.M13) + (ModelMatrix.M42 * ProjMatrix.M23) + (ModelMatrix.M43 * ProjMatrix.M33) + (ModelMatrix.M44 * ProjMatrix.M43); + ClipMatrix[15] = (ModelMatrix.M41 * ProjMatrix.M14) + (ModelMatrix.M42 * ProjMatrix.M24) + (ModelMatrix.M43 * ProjMatrix.M34) + (ModelMatrix.M44 * ProjMatrix.M44); + + // laske frustumin tasot ja normalisoi ne + frustum[RIGHT, 0] = ClipMatrix[3] - ClipMatrix[0]; + frustum[RIGHT, 1] = ClipMatrix[7] - ClipMatrix[4]; + frustum[RIGHT, 2] = ClipMatrix[11] - ClipMatrix[8]; + frustum[RIGHT, 3] = ClipMatrix[15] - ClipMatrix[12]; + NormalizePlane(frustum, RIGHT); + + frustum[LEFT, 0] = ClipMatrix[3] + ClipMatrix[0]; + frustum[LEFT, 1] = ClipMatrix[7] + ClipMatrix[4]; + frustum[LEFT, 2] = ClipMatrix[11] + ClipMatrix[8]; + frustum[LEFT, 3] = ClipMatrix[15] + ClipMatrix[12]; + NormalizePlane(frustum, LEFT); + + frustum[BOTTOM, 0] = ClipMatrix[3] + ClipMatrix[1]; + frustum[BOTTOM, 1] = ClipMatrix[7] + ClipMatrix[5]; + frustum[BOTTOM, 2] = ClipMatrix[11] + ClipMatrix[9]; + frustum[BOTTOM, 3] = ClipMatrix[15] + ClipMatrix[13]; + NormalizePlane(frustum, BOTTOM); + + frustum[TOP, 0] = ClipMatrix[3] - ClipMatrix[1]; + frustum[TOP, 1] = ClipMatrix[7] - ClipMatrix[5]; + frustum[TOP, 2] = ClipMatrix[11] - ClipMatrix[9]; + frustum[TOP, 3] = ClipMatrix[15] - ClipMatrix[13]; + NormalizePlane(frustum, TOP); + + frustum[BACK, 0] = ClipMatrix[3] - ClipMatrix[2]; + frustum[BACK, 1] = ClipMatrix[7] - ClipMatrix[6]; + frustum[BACK, 2] = ClipMatrix[11] - ClipMatrix[10]; + frustum[BACK, 3] = ClipMatrix[15] - ClipMatrix[14]; + NormalizePlane(frustum, BACK); + + frustum[FRONT, 0] = ClipMatrix[3] + ClipMatrix[2]; + frustum[FRONT, 1] = ClipMatrix[7] + ClipMatrix[6]; + frustum[FRONT, 2] = ClipMatrix[11] + ClipMatrix[10]; + frustum[FRONT, 3] = ClipMatrix[15] + ClipMatrix[14]; + NormalizePlane(frustum, FRONT); + } + + /// + /// tasojen normaalit osoittaa sisäänpäin joten jos testattava vertex on + /// kaikkien tasojen "edessä", se on ruudulla ja rendataan + /// + /// + /// + /// + /// + public static bool PointInFrustum(float x, float y, float z) + { + // tasoyhtälö: A*x + B*y + C*z + D = 0 + // ABC on normaalin X, Y ja Z + // D on tason etäisyys origosta + // =0 vertex on tasolla + // <0 tason takana + // >0 tason edessä + for (int a = 0; a < 6; a++) + { + // jos vertex jonkun tason takana, niin palauta false (ei rendata) + if (((frustum[a, 0] * x) + (frustum[a, 1] * y) + (frustum[a, 2] * z) + frustum[a, 3]) <= 0) + { + return false; + } + } + + // ruudulla + return true; + } + + /// + /// palauttaa etäisyyden kameraan jos pallo frustumissa, muuten 0. + /// + /// + /// + /// + /// + /// + public static float SphereInFrustum(float x, float y, float z, float radius) + { + float d = 0; + for (int p = 0; p < 6; p++) + { + d = frustum[p, 0] * x + frustum[p, 1] * y + frustum[p, 2] * z + frustum[p, 3]; + if (d <= -radius) // jos pallo ei ole ruudulla + { + return 0; + } + } + // kaikkien tasojen edessä eli näkyvissä. + return d + radius; // palauta matka kameraan + } + + public static bool ObjectInFrustum(Vector3 position, BoundingVolume bound) + { + return ObjectInFrustum(position.X, position.Y, position.Z, bound); + } + + public static bool ObjectInFrustum(float x, float y, float z, BoundingVolume bound) + { + if (bound == null) return true; + if (SphereInFrustum(x, y, z, bound.ScaledR) == 0) return false; + return true; + } + } + + public class BoundingVolume + { + Vector3 Min = new Vector3(99999, 99999, 99999); + Vector3 Max = new Vector3(-99999, -99999, -99999); + float R = 0f; + + public Vector3 ScaledMin = new Vector3(99999, 99999, 99999); + public Vector3 ScaledMax = new Vector3(-99999, -99999, -99999); + public float ScaledR = 0f; + + public void CalcScaled(Vector3 scale) + { + ScaledMin = Min * scale; + ScaledMax = Max * scale; + Vector3 dist = ScaledMax - ScaledMin; + ScaledR = dist.Length(); + } + + public void CreateBoundingVolume(OpenMetaverse.Rendering.Face mesh, Vector3 scale) + { + for (int q = 0; q < mesh.Vertices.Count; q++) + { + if (mesh.Vertices[q].Position.X < Min.X) Min.X = mesh.Vertices[q].Position.X; + if (mesh.Vertices[q].Position.Y < Min.Y) Min.Y = mesh.Vertices[q].Position.Y; + if (mesh.Vertices[q].Position.Z < Min.Z) Min.Z = mesh.Vertices[q].Position.Z; + + if (mesh.Vertices[q].Position.X > Max.X) Max.X = mesh.Vertices[q].Position.X; + if (mesh.Vertices[q].Position.Y > Max.Y) Max.Y = mesh.Vertices[q].Position.Y; + if (mesh.Vertices[q].Position.Z > Max.Z) Max.Z = mesh.Vertices[q].Position.Z; + } + + Vector3 dist = Max - Min; + R = dist.Length(); + mesh.Center = Min + (dist / 2); + CalcScaled(scale); + } + + public void FromScale(Vector3 scale) + { + ScaledMax = scale / 2f; + ScaledMin = -ScaledMax; + Vector3 dist = ScaledMax - ScaledMin; + ScaledR = dist.Length(); + } + + public void AddVolume(BoundingVolume vol, Vector3 scale) + { + if (vol.Min.X < this.Min.X) this.Min.X = vol.Min.X; + if (vol.Min.Y < this.Min.Y) this.Min.Y = vol.Min.Y; + if (vol.Min.Z < this.Min.Z) this.Min.Z = vol.Min.Z; + + if (vol.Max.X > this.Max.X) this.Max.X = vol.Max.X; + if (vol.Max.Y > this.Max.Y) this.Max.Y = vol.Max.Y; + if (vol.Max.Z > this.Max.Z) this.Max.Z = vol.Max.Z; + Vector3 dist = Max - Min; + R = dist.Length(); + CalcScaled(scale); + } + + //public void CreateBoundingVolume(Model mesh, Vector3 min, Vector3 max) + //{ + // Min = min; + // Max = max; + // Vector3 dist = Max - Min; + // R = dist.Length; + // mesh.ObjCenter = Min + (dist / 2); // objektin keskikohta + //} + } +} \ No newline at end of file diff --git a/Assets/Scripts/Frustum.cs.meta b/Assets/Scripts/Frustum.cs.meta new file mode 100644 index 0000000..8664ece --- /dev/null +++ b/Assets/Scripts/Frustum.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e19611bacca8d7146a836d68c7e01fe9 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/Scripts/Login.cs b/Assets/Scripts/Login.cs index 778ec3a..9760030 100644 Binary files a/Assets/Scripts/Login.cs and b/Assets/Scripts/Login.cs differ diff --git a/Assets/Scripts/RenderAvatar.cs b/Assets/Scripts/RenderAvatar.cs new file mode 100644 index 0000000..f596ae4 --- /dev/null +++ b/Assets/Scripts/RenderAvatar.cs @@ -0,0 +1,2340 @@ +// +// Radegast Metaverse Client +// Copyright (c) 2009-2013, 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: RenderAvatar.cs 1137 2011-09-05 22:53:46Z latifer $ +// + +using System; +using System.Collections.Generic; +using System.Collections; +using System.IO; +using System.Xml; +#if (COGBOT_LIBOMV || USE_STHREADS) +using ThreadPoolUtil; +using Thread = ThreadPoolUtil.Thread; +using ThreadPool = ThreadPoolUtil.ThreadPool; +using Monitor = ThreadPoolUtil.Monitor; +#endif +using System.Threading; + +using OpenMetaverse; +using OpenMetaverse.Rendering; + +namespace Radegast.Rendering +{ + public class attachment_point + { + public string name; + public string joint; + public Vector3 position; + public Quaternion rotation; + public int id; + public int group; + + public GLMesh jointmesh; + public int jointmeshindex; + + public attachment_point(XmlNode node) + { + name = node.Attributes.GetNamedItem("name").Value; + joint = node.Attributes.GetNamedItem("joint").Value; + position = VisualParamEx.XmlParseVector(node.Attributes.GetNamedItem("position").Value); + rotation = VisualParamEx.XmlParseRotation(node.Attributes.GetNamedItem("rotation").Value); + id = Int32.Parse(node.Attributes.GetNamedItem("id").Value); + group = Int32.Parse(node.Attributes.GetNamedItem("group").Value); + } + + } + + /// + /// Subclass of LindenMesh that adds vertex, index, and texture coordinate + /// arrays suitable for pushing direct to OpenGL + /// + public class GLMesh : LindenMesh + { + /// + /// Subclass of LODMesh that adds an index array suitable for pushing + /// direct to OpenGL + /// + /// + + public int teFaceID; + public Dictionary _evp = new Dictionary(); + + new public class LODMesh : LindenMesh.LODMesh + { + public ushort[] Indices; + + public override void LoadMesh(string filename) + { + base.LoadMesh(filename); + + // Generate the index array + Indices = new ushort[_numFaces * 3]; + int current = 0; + for (int i = 0; i < _numFaces; i++) + { + Indices[current++] = (ushort)_faces[i].Indices[0]; + Indices[current++] = (ushort)_faces[i].Indices[1]; + Indices[current++] = (ushort)_faces[i].Indices[2]; + } + } + } + + /// + /// + /// + public struct GLData + { + public float[] Vertices; + public float[] Normals; + public ushort[] Indices; + public float[] TexCoords; + public Vector3 Center; + public float[] weights; //strictly these are constant and don't need instancing with the GLMesh + public string[] skinJoints; //strictly these are constant and don't need instancing with the GLMesh + } + + public static GLData baseRenderData; + public GLData RenderData; + public GLData OrigRenderData; + public GLData MorphRenderData; + + public GLAvatar av; + + public GLMesh(string name) + : base(name) + { + } + + public GLMesh(GLMesh source, GLAvatar av) + : base(source.Name) + { + this.av = av; + // Make a new GLMesh copy from the supplied source + + RenderData.Vertices = new float[source.RenderData.Vertices.Length]; + RenderData.Normals = new float[source.RenderData.Normals.Length]; + RenderData.TexCoords = new float[source.RenderData.TexCoords.Length]; + RenderData.Indices = new ushort[source.RenderData.Indices.Length]; + + RenderData.weights = new float[source.RenderData.weights.Length]; + RenderData.skinJoints = new string[source.RenderData.skinJoints.Length]; + + Array.Copy(source.RenderData.Vertices, RenderData.Vertices, source.RenderData.Vertices.Length); + Array.Copy(source.RenderData.Normals, RenderData.Normals, source.RenderData.Normals.Length); + + Array.Copy(source.RenderData.TexCoords, RenderData.TexCoords, source.RenderData.TexCoords.Length); + Array.Copy(source.RenderData.Indices, RenderData.Indices, source.RenderData.Indices.Length); + Array.Copy(source.RenderData.weights, RenderData.weights, source.RenderData.weights.Length); + Array.Copy(source.RenderData.skinJoints, RenderData.skinJoints, source.RenderData.skinJoints.Length); + + RenderData.Center = new Vector3(source.RenderData.Center); + + teFaceID = source.teFaceID; + + _rotationAngles = new Vector3(source.RotationAngles); + _scale = new Vector3(source.Scale); + _position = new Vector3(source.Position); + + // We should not need to instance these the reference from the top should be constant + _evp = source._evp; + _morphs = source._morphs; + + OrigRenderData.Indices = new ushort[source.RenderData.Indices.Length]; + OrigRenderData.TexCoords = new float[source.RenderData.TexCoords.Length]; + OrigRenderData.Vertices = new float[source.RenderData.Vertices.Length]; + OrigRenderData.Normals = new float[source.RenderData.Normals.Length]; + + MorphRenderData.Vertices = new float[source.RenderData.Vertices.Length]; + MorphRenderData.Normals = new float[source.RenderData.Normals.Length]; + + Array.Copy(source.RenderData.Vertices, OrigRenderData.Vertices, source.RenderData.Vertices.Length); + Array.Copy(source.RenderData.Vertices, MorphRenderData.Vertices, source.RenderData.Vertices.Length); + + Array.Copy(source.RenderData.Normals, OrigRenderData.Normals, source.RenderData.Normals.Length); + Array.Copy(source.RenderData.Normals, MorphRenderData.Normals, source.RenderData.Normals.Length); + + Array.Copy(source.RenderData.TexCoords, OrigRenderData.TexCoords, source.RenderData.TexCoords.Length); + Array.Copy(source.RenderData.Indices, OrigRenderData.Indices, source.RenderData.Indices.Length); + + + + } + + public void setMeshPos(Vector3 pos) + { + _position = pos; + + // Force offset all vertices by this offset + // this is required to force some meshes into the default T bind pose + + for (int vert = 0; vert < RenderData.Vertices.Length; vert = vert + 3) + { + RenderData.Vertices[vert] += pos.X; + RenderData.Vertices[vert + 1] += pos.Y; + RenderData.Vertices[vert + 2] += pos.Z; + } + + + } + + public void setMeshRot(Vector3 rot) + { + _rotationAngles = rot; + } + + public override void LoadMesh(string filename) + { + base.LoadMesh(filename); + + float minX, minY, minZ; + minX = minY = minZ = Single.MaxValue; + float maxX, maxY, maxZ; + maxX = maxY = maxZ = Single.MinValue; + + // Generate the vertex array + RenderData.Vertices = new float[_numVertices * 3]; + RenderData.Normals = new float[_numVertices * 3]; + + Quaternion quat = Quaternion.CreateFromEulers(0, 0, (float)(Math.PI / 4.0)); + + int current = 0; + for (int i = 0; i < _numVertices; i++) + { + + RenderData.Normals[current] = _vertices[i].Normal.X; + RenderData.Vertices[current++] = _vertices[i].Coord.X; + RenderData.Normals[current] = _vertices[i].Normal.Y; + RenderData.Vertices[current++] = _vertices[i].Coord.Y; + RenderData.Normals[current] = _vertices[i].Normal.Z; + RenderData.Vertices[current++] = _vertices[i].Coord.Z; + + if (_vertices[i].Coord.X < minX) + minX = _vertices[i].Coord.X; + else if (_vertices[i].Coord.X > maxX) + maxX = _vertices[i].Coord.X; + + if (_vertices[i].Coord.Y < minY) + minY = _vertices[i].Coord.Y; + else if (_vertices[i].Coord.Y > maxY) + maxY = _vertices[i].Coord.Y; + + if (_vertices[i].Coord.Z < minZ) + minZ = _vertices[i].Coord.Z; + else if (_vertices[i].Coord.Z > maxZ) + maxZ = _vertices[i].Coord.Z; + } + + // Calculate the center-point from the bounding box edges + RenderData.Center = new Vector3((minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2); + + // Generate the index array + RenderData.Indices = new ushort[_numFaces * 3]; + current = 0; + for (int i = 0; i < _numFaces; i++) + { + RenderData.Indices[current++] = (ushort)_faces[i].Indices[0]; + RenderData.Indices[current++] = (ushort)_faces[i].Indices[1]; + RenderData.Indices[current++] = (ushort)_faces[i].Indices[2]; + } + + // Generate the texcoord array + RenderData.TexCoords = new float[_numVertices * 2]; + current = 0; + for (int i = 0; i < _numVertices; i++) + { + RenderData.TexCoords[current++] = _vertices[i].TexCoord.X; + RenderData.TexCoords[current++] = _vertices[i].TexCoord.Y; + } + + RenderData.weights = new float[_numVertices]; + for (int i = 0; i < _numVertices; i++) + { + RenderData.weights[i] = _vertices[i].Weight; + } + + RenderData.skinJoints = new string[_skinJoints.Length + 3]; + for (int i = 1; i < _skinJoints.Length; i++) + { + RenderData.skinJoints[i] = _skinJoints[i]; + } + + + } + + public override void LoadLODMesh(int level, string filename) + { + LODMesh lod = new LODMesh(); + lod.LoadMesh(filename); + _lodMeshes[level] = lod; + } + + public void applyjointweights() + { + + /*Each weight actually contains two pieces of information. + * The number to the left of the decimal point is the index of the joint and also + * implicitly indexes to the following joint. The actual weight is to the right of + * the decimal point and interpolates between these two joints. The index is into an + * "expanded" list of joints, not just a linear array of the joints as defined in the + * skeleton file. In particular, any joint that has more than one child will be repeated + * in the list for each of its children. + */ + + float weight = -9999; + int jointindex = 0; + float factor; + + Bone ba = null; + Bone bb = null; + + for (int v = 0, x = 0; v < RenderData.Vertices.Length; v = v + 3, x++) + { + if (weight != RenderData.weights[x]) + { + + jointindex = (int)Math.Floor(weight = RenderData.weights[x]); + factor = RenderData.weights[x] - jointindex; + weight = weight - jointindex; + + string jointname = "", jointname2 = ""; + + switch (Name) + { + case "upperBodyMesh": + jointname = skeleton.mUpperMeshMapping[jointindex]; + jointindex++; + jointname2 = skeleton.mUpperMeshMapping[jointindex]; + break; + + case "lowerBodyMesh": + jointname = skeleton.mLowerMeshMapping[jointindex]; + jointindex++; + jointname2 = skeleton.mLowerMeshMapping[jointindex]; + break; + + case "headMesh": + jointname = skeleton.mHeadMeshMapping[jointindex]; + jointindex++; + jointname2 = skeleton.mHeadMeshMapping[jointindex]; + break; + + case "eyeBallRightMesh": + jointname = "mHead"; + jointname2 = "mEyeRight"; + break; + + case "eyeBallLeftMesh": + jointname = "mHead"; + jointname2 = "mEyeLeft"; + break; + + case "eyelashMesh": + case "hairMesh": + jointname = "mHead"; + jointname2 = "mSkull"; + break; + + default: + return; + + } + + ba = av.skel.mBones[jointname]; + + + if (jointname2 == "") + { + bb = null; + } + else + { + bb = av.skel.mBones[jointname2]; + } + } + + //Special cases 0 is not used + // ON upper torso 5 and 10 are not used + // 4 is neck and 6 and 11 are the left and right collar bones + + + //TODO use the SRT matrix to do this! + + Vector3 lerpa; + Vector3 offseta; + Quaternion rota; + + Vector3 lerpb; + Vector3 offsetb; + Quaternion rotb; + + Vector3 pos; + + Vector3 posa = new Vector3(MorphRenderData.Vertices[v], MorphRenderData.Vertices[v + 1], MorphRenderData.Vertices[v + 2]); + + + if (bb != null) + { + lerpa = ba.getDeltaOffset(); + offseta = ba.getTotalOffset(); + rota = ba.getTotalRotation(); + + lerpb = bb.getDeltaOffset(); + offsetb = bb.getTotalOffset(); + rotb = bb.getTotalRotation(); + + Vector3 posb = new Vector3(MorphRenderData.Vertices[v], MorphRenderData.Vertices[v + 1], MorphRenderData.Vertices[v + 2]); + + //move back to mesh local coords + posa = posa - offseta; + + // apply rotated offset + posa = ((posa + lerpa) * ba.scale) * rota; + //move back to avatar local coords + posa = posa + offseta; + + //move back to mesh local coords + posb = posb - offsetb; + + // apply rotated offset + posb = ((posb + lerpb) * bb.scale) * rotb; + //move back to avatar local coords + posb = posb + offsetb; + + //LERP the two points to produce smooth skinning ;-) + pos = Vector3.Lerp(posa, posb, weight); + + } + else + { + lerpa = ba.getDeltaOffset(); + offseta = ba.getTotalOffset(); + rota = ba.getTotalRotation(); + + //move back to mesh local coords + posa = posa - offseta; + + // apply rotated offset + posa = ((posa + lerpa) * ba.scale) * rota; + //move back to avatar local coords + posa = posa + offseta; + + //Only one bone contributing so its 100% that one. + pos = posa; + + } + + RenderData.Vertices[v] = pos.X; + RenderData.Vertices[v + 1] = pos.Y; + RenderData.Vertices[v + 2] = pos.Z; + } + } + + public void resetallmorphs() + { + for (int i = 0; i < OrigRenderData.Vertices.Length / 3; i++) + { + + MorphRenderData.Vertices[i * 3] = OrigRenderData.Vertices[i * 3]; + MorphRenderData.Vertices[(i * 3) + 1] = OrigRenderData.Vertices[i * 3 + 1]; + MorphRenderData.Vertices[(i * 3) + 2] = OrigRenderData.Vertices[i * 3 + 2]; + + MorphRenderData.Normals[i * 3] = OrigRenderData.Normals[i * 3]; + MorphRenderData.Normals[(i * 3) + 1] = OrigRenderData.Normals[i * 3 + 1]; + MorphRenderData.Normals[(i * 3) + 2] = OrigRenderData.Normals[i * 3 + 2]; + + RenderData.TexCoords[i * 2] = OrigRenderData.TexCoords[i * 2]; + RenderData.TexCoords[(i * 2) + 1] = OrigRenderData.TexCoords[i * 2 + 1]; + + } + + } + + public void morphmesh(Morph morph, float weight) + { + // Logger.Log(String.Format("Applying morph {0} weight {1}",morph.Name,weight),Helpers.LogLevel.Debug); + + for (int v = 0; v < morph.NumVertices; v++) + { + MorphVertex mvx = morph.Vertices[v]; + + uint i = mvx.VertexIndex; + + MorphRenderData.Vertices[i * 3] = MorphRenderData.Vertices[i * 3] + mvx.Coord.X * weight; + MorphRenderData.Vertices[(i * 3) + 1] = MorphRenderData.Vertices[i * 3 + 1] + mvx.Coord.Y * weight; + MorphRenderData.Vertices[(i * 3) + 2] = MorphRenderData.Vertices[i * 3 + 2] + mvx.Coord.Z * weight; + + MorphRenderData.Normals[i * 3] = MorphRenderData.Normals[i * 3] + mvx.Normal.X * weight; + MorphRenderData.Normals[(i * 3) + 1] = MorphRenderData.Normals[(i * 3) + 1] + mvx.Normal.Y * weight; + MorphRenderData.Normals[(i * 3) + 2] = MorphRenderData.Normals[(i * 3) + 2] + mvx.Normal.Z * weight; + + RenderData.TexCoords[i * 2] = OrigRenderData.TexCoords[i * 2] + mvx.TexCoord.X * weight; + RenderData.TexCoords[(i * 2) + 1] = OrigRenderData.TexCoords[i * 2 + 1] + mvx.TexCoord.Y * weight; + + } + } + } + + public class GLAvatar + { + private static Dictionary _defaultmeshes = new Dictionary(); + public Dictionary _meshes = new Dictionary(); + + public skeleton skel = new skeleton(); + public static Dictionary attachment_points = new Dictionary(); + + public bool _wireframe = true; + public bool _showSkirt = false; + + public VisualParamEx.EparamSex msex; + + public byte[] VisualAppearanceParameters = new byte[1024]; + bool vpsent = false; + static bool lindenMeshesLoaded = false; + + public GLAvatar() + { + lock (_defaultmeshes) foreach (KeyValuePair kvp in _defaultmeshes) + { + GLMesh mesh = new GLMesh(kvp.Value, this); // Instance our meshes + _meshes.Add(kvp.Key, mesh); + + } + } + + public static void dumptweaks() + { + + for (int x = 0; x < VisualParamEx.tweakable_params.Count; x++) + { + VisualParamEx vpe = (VisualParamEx)VisualParamEx.tweakable_params.GetByIndex(x); + Console.WriteLine(string.Format("{0} is {1}", x, vpe.Name)); + } + + } + + public static void loadlindenmeshes2(string LODfilename) + { + // Already have mashes loaded? + if (lindenMeshesLoaded) return; + + attachment_points.Clear(); + + + string basedir = Directory.GetCurrentDirectory() + System.IO.Path.DirectorySeparatorChar + "character" + System.IO.Path.DirectorySeparatorChar; + + XmlDocument lad = new XmlDocument(); + lad.Load(basedir + LODfilename); + + //Firstly read the skeleton section this contains attachment point info and the bone deform info for visual params + // And load the skeleton file in to the bones class + + XmlNodeList skeleton = lad.GetElementsByTagName("skeleton"); + string skeletonfilename = skeleton[0].Attributes.GetNamedItem("file_name").Value; + Bone.loadbones(skeletonfilename); + + // Next read all the skeleton child nodes, we have attachment points and bone deform params + // attachment points are an offset and rotation from a bone location + // the name of the bone they reference is the joint paramater + // params in the skeleton nodes are bone deforms, eg leg length changes the scale of the leg bones + + foreach (XmlNode skeletonnode in skeleton[0].ChildNodes) + { + if (skeletonnode.Name == "attachment_point") + { + attachment_point point = new attachment_point(skeletonnode); + attachment_points.Add(point.id, point); + } + } + + // Parse all visual paramaters in one go + // we can dedue type on the fly + XmlNodeList paramss = lad.GetElementsByTagName("param"); + foreach (XmlNode paramNode in paramss) + { + VisualParamEx vp = new VisualParamEx(paramNode); + } + + //Now we parse the mesh nodes, mesh nodes reference a particular LLM file with a LOD + + XmlNodeList meshes = lad.GetElementsByTagName("mesh"); + foreach (XmlNode meshNode in meshes) + { + string type = meshNode.Attributes.GetNamedItem("type").Value; + int lod = Int32.Parse(meshNode.Attributes.GetNamedItem("lod").Value); + string fileName = meshNode.Attributes.GetNamedItem("file_name").Value; + + GLMesh mesh = null; + lock (_defaultmeshes) + mesh = (_defaultmeshes.ContainsKey(type) ? _defaultmeshes[type] : new GLMesh(type)); + + // Set up the texture elemenets for each mesh + // And hack the eyeball position + switch (mesh.Name) + { + case "lowerBodyMesh": + mesh.teFaceID = (int)AvatarTextureIndex.LowerBaked; + break; + + case "upperBodyMesh": + mesh.teFaceID = (int)AvatarTextureIndex.UpperBaked; + break; + + case "headMesh": + mesh.teFaceID = (int)AvatarTextureIndex.HeadBaked; + break; + + case "hairMesh": + mesh.teFaceID = (int)AvatarTextureIndex.HairBaked; + break; + + case "eyelashMesh": + mesh.teFaceID = (int)AvatarTextureIndex.HeadBaked; + break; + + case "eyeBallRightMesh": + mesh.teFaceID = (int)AvatarTextureIndex.EyesBaked; + break; + + case "eyeBallLeftMesh": + mesh.teFaceID = (int)AvatarTextureIndex.EyesBaked; + break; + + case "skirtMesh": + mesh.teFaceID = (int)AvatarTextureIndex.SkirtBaked; + break; + + default: + mesh.teFaceID = 0; + break; + } + + if (lod == 0) + mesh.LoadMesh(basedir + fileName); + else + mesh.LoadLODMesh(lod, basedir + fileName); + + if (lod == 0) + { + switch (mesh.Name) + { + case "eyeBallLeftMesh": + lock (Bone.mBones) mesh.setMeshPos(Bone.mBones["mEyeLeft"].getTotalOffset()); + break; + + case "eyeBallRightMesh": + lock (Bone.mBones) mesh.setMeshPos(Bone.mBones["mEyeRight"].getTotalOffset()); + break; + + case "eyelashMesh": + lock (Bone.mBones) mesh.setMeshPos(Bone.mBones["mHead"].getTotalOffset()); + break; + + case "hairMesh": + //mesh.setMeshPos(Bone.mBones["mHead"].getTotalOffset()); + break; + + default: + break; + } + } + + lock (_defaultmeshes) _defaultmeshes[type] = mesh; + + } + + lindenMeshesLoaded = true; + } + + public void applyMorph(Avatar av, int param, float weight) + { + VisualParamEx vpx; + + if (VisualParamEx.allParams.TryGetValue(param, out vpx)) + { + applyMorph(vpx, av, weight); + + // a morph ID may apply to more than one mesh (duplicate VP IDs) + // in this case also apply to all other identical IDs + foreach (VisualParamEx cvpx in vpx.identicalIds) + { + applyMorph(cvpx, av, weight); + } + } + } + + public void applyMorph(VisualParamEx vpx, Avatar av, float weight) + { + + weight = Utils.Clamp(weight, 0.0f, 1.0f); + float value = Utils.Lerp(vpx.MinValue, vpx.MaxValue, weight); + + // don't do anything for less than 1% change + if (value > -0.001 && value < 0.001) + return; + + // Morphs are mesh deforms + if (vpx.pType == VisualParamEx.ParamType.TYPE_MORPH) + { + // Its a morph + GLMesh mesh; + if (_meshes.TryGetValue(vpx.morphmesh, out mesh)) + { + foreach (LindenMesh.Morph morph in mesh.Morphs) //optimise me to a dictionary + { + if (morph.Name == vpx.Name) + { + if (mesh.Name == "skirtMesh" && _showSkirt == false) + return; + + // Logger.Log(String.Format("Applying morph {0} ID {2} weight {1} mesh {3}",morph.Name,weight,vpx.ParamID,mesh.Name),Helpers.LogLevel.Debug); + + mesh.morphmesh(morph, value); + + } + } + } + } + + + // Driver type + // A driver drives multiple slave visual paramaters + if (vpx.pType == VisualParamEx.ParamType.TYPE_DRIVER) + { + + foreach (VisualParamEx.driven child in vpx.childparams) + { + + /***** BEGIN UNGRACEFULL CODE STEALING ******/ + + // driven ________ + // ^ /| |\ ^ + // | / | | \ | + // | / | | \ | + // | / | | \ | + // | / | | \ | + //-------|----|-------|----|-------> driver + // | min1 max1 max2 min2 + + + if (child.hasMinMax == false) + { + applyMorph(av, child.id, weight); + continue; + } + + float driven_weight = vpx.DefaultValue; + float driven_max = VisualParamEx.allParams[child.id].MaxValue; + float driven_min = VisualParamEx.allParams[child.id].MinValue; + float input_weight = weight; + + float min_weight = vpx.MinValue; + float max_weight = vpx.MaxValue; + + if (input_weight <= child.min1) + { + if (child.min1 == child.max1 && + child.min1 <= min_weight) + { + driven_weight = driven_max; + } + else + { + driven_weight = driven_min; + } + } + else + if (input_weight <= child.max1) + { + float t = (input_weight - child.min1) / (child.max1 - child.min1); + driven_weight = driven_min + t * (driven_max - driven_min); + } + else + if (input_weight <= child.max2) + { + driven_weight = driven_max; + } + else + if (input_weight <= child.min2) + { + float t = (input_weight - child.max2) / (child.min2 - child.max2); + driven_weight = driven_max + t * (driven_min - driven_max); + } + else + { + if (child.max2 >= max_weight) + { + driven_weight = driven_max; + } + else + { + driven_weight = driven_min; + } + } + + + /***** END UNGRACEFULL CODE STEALING ******/ + + applyMorph(av, child.id, driven_weight); + + } + + return; + } + + //Is this a bone deform? + if (vpx.pType == VisualParamEx.ParamType.TYPE_BONEDEFORM) + { + // scale="0 0 .3" /> + // value_min="-1" + // value_max="1" + + foreach (KeyValuePair kvp in vpx.BoneDeforms) + { + skel.scalebone(kvp.Key, Vector3.One + (kvp.Value.scale * value)); + skel.offsetbone(kvp.Key, kvp.Value.offset * value); + } + } + } + + public void morph(Avatar av) + { + + if (av.VisualParameters == null) + return; + + ThreadPool.QueueUserWorkItem(sync => + { + int x = 0; + + // We need a lock here as we may get multiple packets thrown at us and we should at least + // process them in turn not 1/2 process one then start to process the next. + // That said av might not be the best lock object, but it will do for the moment + lock (av) + { + if (av.VisualParameters.Length > 123) + { + if (av.VisualParameters[31] > 127) + { + msex = VisualParamEx.EparamSex.SEX_MALE; + } + else + { + msex = VisualParamEx.EparamSex.SEX_FEMALE; + } + } + + foreach (GLMesh mesh in _meshes.Values) + { + mesh.resetallmorphs(); + } + + skel.resetbonescales(); + + foreach (byte vpvalue in av.VisualParameters) + { + if (x >= VisualParamEx.tweakable_params.Count) + { + //Logger.Log("Two many visual paramaters in Avatar appearance", Helpers.LogLevel.Warning); + break; + } + + VisualParamEx vpe = (VisualParamEx)VisualParamEx.tweakable_params.GetByIndex(x); + + if (vpe.sex != VisualParamEx.EparamSex.SEX_BOTH && vpe.sex != msex) + { + x++; + continue; + } + + VisualAppearanceParameters[x] = vpvalue; + + float value = (vpvalue / 255.0f); + this.applyMorph(av, vpe.ParamID, value); + + x++; + } + + vpsent = true; + this.skel.mNeedsMeshRebuild = true; + // Don't update actual meshes here anymore, we do it every frame because of animation anyway + + } + }); + } + } + + public class joint + { + public Vector3 offset; + public Quaternion rotation; + } + + public class animationwrapper + { + public BinBVHAnimationReader anim; + public float mRunTime; + public UUID mAnimation; + public bool mPotentialyDead = false; + + public enum animstate + { + STATE_WAITINGASSET, + STATE_EASEIN, + STATE_EASEOUT, + STATE_PLAY, + STATE_STOP + } + + public animstate playstate; + + public animationwrapper(BinBVHAnimationReader anim) + { + this.anim = anim; + playstate = animstate.STATE_EASEIN; + mRunTime = 0; + } + + public animationwrapper(UUID key) + { + mAnimation = key; + playstate = animstate.STATE_WAITINGASSET; + mRunTime = 0; + } + + public void stopanim() + { + //Logger.Log(string.Format("Animation {0} marked as stopped",mAnimation),Helpers.LogLevel.Info); + playstate = animstate.STATE_STOP; + } + } + + public class skeleton + { + public Dictionary mBones; + private Dictionary mPriority = new Dictionary(); + private Dictionary jointdeforms = new Dictionary(); + + public static Dictionary mUpperMeshMapping = new Dictionary(); + public static Dictionary mLowerMeshMapping = new Dictionary(); + public static Dictionary mHeadMeshMapping = new Dictionary(); + + // public List mAnimations = new List(); + public Dictionary mAnimationsWrapper = new Dictionary(); + + public static Dictionary mAnimationTransactions = new Dictionary(); + + public static Dictionary mAnimationCache = new Dictionary(); + + public bool mNeedsUpdate = false; + public bool mNeedsMeshRebuild = true; + + + public struct binBVHJointState + { + public float currenttime_rot; + public int lastkeyframe_rot; + public int nextkeyframe_rot; + + public float currenttime_pos; + public int lastkeyframe_pos; + public int nextkeyframe_pos; + + public int pos_loopinframe; + public int pos_loopoutframe; + + public int rot_loopinframe; + public int rot_loopoutframe; + + public Quaternion easeoutrot; + public float easeoutfactor; + + } + + + public skeleton() + { + + mBones = new Dictionary(); + + lock (Bone.mBones) foreach (Bone src in Bone.mBones.Values) + { + Bone newbone = new Bone(src); + mBones.Add(newbone.name, newbone); + } + + //rebuild the skeleton structure on the new copy + foreach (Bone src in mBones.Values) + { + if (src.mParentBone != null) + { + src.parent = mBones[src.mParentBone]; + src.parent.children.Add(src); + } + } + + //FUDGE + if (mUpperMeshMapping.Count == 0) + { + mUpperMeshMapping.Add(1, "mPelvis"); + mUpperMeshMapping.Add(2, "mTorso"); + mUpperMeshMapping.Add(3, "mChest"); + mUpperMeshMapping.Add(4, "mNeck"); + mUpperMeshMapping.Add(5, "mNeck"); + mUpperMeshMapping.Add(6, "mCollarLeft"); + mUpperMeshMapping.Add(7, "mShoulderLeft"); + mUpperMeshMapping.Add(8, "mElbowLeft"); + mUpperMeshMapping.Add(9, "mWristLeft"); + mUpperMeshMapping.Add(10, "mNeck"); // this case might fail for mWriteLeft and mNeck acting together? + mUpperMeshMapping.Add(11, "mCollarRight"); + mUpperMeshMapping.Add(12, "mShoulderRight"); + mUpperMeshMapping.Add(13, "mElbowRight"); + mUpperMeshMapping.Add(14, "mWristRight"); + mUpperMeshMapping.Add(15, ""); + + mLowerMeshMapping.Add(1, "mPelvis"); + mLowerMeshMapping.Add(2, "mHipRight"); + mLowerMeshMapping.Add(3, "mKneeRight"); + mLowerMeshMapping.Add(4, "mAnkleRight"); + mLowerMeshMapping.Add(5, "mPelvis"); + mLowerMeshMapping.Add(6, "mHipLeft"); + mLowerMeshMapping.Add(7, "mKneeLeft"); + mLowerMeshMapping.Add(8, "mAnkleLeft"); + mLowerMeshMapping.Add(9, ""); + + mHeadMeshMapping.Add(1, "mNeck"); + mHeadMeshMapping.Add(2, "mHead"); + mHeadMeshMapping.Add(3, ""); + + } + + } + + public void processAnimation(UUID mAnimID) + { + if (mAnimationsWrapper.ContainsKey(mAnimID)) + { + // Its an existing animation, we may need to do a seq update but we don't yet + // support that + mAnimationsWrapper[mAnimID].playstate = animationwrapper.animstate.STATE_WAITINGASSET; + mAnimationsWrapper[mAnimID].mPotentialyDead = false; + } + else + { + // Its a new animation do the decode dance + animationwrapper aw = new animationwrapper(mAnimID); + mAnimationsWrapper.Add(mAnimID, aw); + + } + } + + public void resetbonescales() + { + foreach (KeyValuePair src in mBones) + { + src.Value.scale = Vector3.One; + src.Value.offset_pos = Vector3.Zero; + } + } + + public void deformbone(string name, Vector3 pos, Quaternion rotation) + { + Bone bone; + if (mBones.TryGetValue(name, out bone)) + { + bone.deformbone(pos, rotation); + } + } + + public void scalebone(string name, Vector3 scale) + { + + Bone bone; + if (mBones.TryGetValue(name, out bone)) + { + // Logger.Log(String.Format("scalebone() {0} {1}", name, scale.ToString()),Helpers.LogLevel.Info); + bone.scalebone(scale); + } + } + + public void offsetbone(string name, Vector3 offset) + { + Bone bone; + if (mBones.TryGetValue(name, out bone)) + { + bone.offsetbone(offset); + } + } + + //TODO check offset and rot calcuations should each offset be multiplied by its parent rotation in + // a standard child/parent rot/offset way? + public Vector3 getOffset(string bonename) + { + Bone b; + if (mBones.TryGetValue(bonename, out b)) + { + return (b.getTotalOffset()); + } + else + { + return Vector3.Zero; + } + } + + public Quaternion getRotation(string bonename) + { + Bone b; + if (mBones.TryGetValue(bonename, out b)) + { + return (b.getTotalRotation()); + } + else + { + return Quaternion.Identity; + } + } + + + public void flushanimations() + { + lock (mAnimationsWrapper) + { + List kills = new List(); + foreach (animationwrapper ar in mAnimationsWrapper.Values) + { + if (ar.playstate == animationwrapper.animstate.STATE_STOP) + { + kills.Add(ar.mAnimation); + } + + ar.mPotentialyDead = true; + } + + foreach (UUID key in kills) + { + mAnimationsWrapper.Remove(key); + //Logger.Log(string.Format("Removing dead animation {0} from av", key), Helpers.LogLevel.Info); + } + } + } + + public void flushanimationsfinal() + { + lock (mAnimationsWrapper) + { + foreach (animationwrapper ar in mAnimationsWrapper.Values) + { + if (ar.mPotentialyDead == true) + { + // Logger.Log(string.Format("Animation {0} is being marked for easeout (dead)",ar.mAnimation.ToString()),Helpers.LogLevel.Info); + // Should we just stop dead? i think not it may get jerky + ar.playstate = animationwrapper.animstate.STATE_EASEOUT; + ar.mRunTime = 0; //fix me nasty hack + } + } + } + + } + + // Add animations to the global decoded list + // TODO garbage collect unused animations somehow + public static void addanimation(OpenMetaverse.Assets.Asset asset, UUID tid, BinBVHAnimationReader b, UUID animKey) + { + RenderAvatar av; + mAnimationTransactions.TryGetValue(tid, out av); + if (av == null) + return; + + mAnimationTransactions.Remove(tid); + + if (asset != null) + { + b = new BinBVHAnimationReader(asset.AssetData); + mAnimationCache[asset.AssetID] = b; + Logger.Log("Adding new decoded animaton known animations " + asset.AssetID.ToString(), Helpers.LogLevel.Info); + } + + if (!av.glavatar.skel.mAnimationsWrapper.ContainsKey(animKey)) + { + Logger.Log(String.Format("Animation {0} is not in mAnimationsWrapper! ", animKey), Helpers.LogLevel.Warning); + return; + } + + // This sets the anim in the wrapper class; + av.glavatar.skel.mAnimationsWrapper[animKey].anim = b; + + int pos = 0; + foreach (binBVHJoint joint in b.joints) + { + binBVHJointState state; + + state.lastkeyframe_rot = 0; + state.nextkeyframe_rot = 1; + + state.lastkeyframe_pos = 0; + state.nextkeyframe_pos = 1; + + state.currenttime_rot = 0; + state.currenttime_pos = 0; + + state.pos_loopinframe = 0; + state.pos_loopoutframe = joint.positionkeys.Length - 1; + + state.rot_loopinframe = 0; + state.rot_loopoutframe = joint.rotationkeys.Length - 1; + + state.easeoutfactor = 1.0f; + state.easeoutrot = Quaternion.Identity; + + if (b.Loop == true) + { + int frame = 0; + foreach (binBVHJointKey key in joint.rotationkeys) + { + if (key.time == b.InPoint) + { + state.rot_loopinframe = frame; + } + + if (key.time == b.OutPoint) + { + state.rot_loopoutframe = frame; + } + + frame++; + } + + frame = 0; + foreach (binBVHJointKey key in joint.positionkeys) + { + if (key.time == b.InPoint) + { + state.pos_loopinframe = frame; + } + + if (key.time == b.OutPoint) + { + state.pos_loopoutframe = frame; + } + + frame++; + } + + } + + b.joints[pos].Tag = state; + pos++; + } + + av.glavatar.skel.mAnimationsWrapper[animKey].playstate = animationwrapper.animstate.STATE_EASEIN; + recalcpriorities(av); + + } + + public static void recalcpriorities(RenderAvatar av) + { + + lock (av.glavatar.skel.mAnimationsWrapper) + { + //av.glavatar.skel.mAnimationsWrapper.Add(new animationwrapper(b)); + + //pre calculate all joint priorities here + av.glavatar.skel.mPriority.Clear(); + + foreach (KeyValuePair kvp in av.glavatar.skel.mAnimationsWrapper) + { + int jpos = 0; + animationwrapper ar = kvp.Value; + if (ar.anim == null) + continue; + + foreach (binBVHJoint joint in ar.anim.joints) + { + if (ar.anim == null) + continue; + + //warning struct copy non reference + binBVHJointState state = (binBVHJointState)ar.anim.joints[jpos].Tag; + + if (ar.playstate == animationwrapper.animstate.STATE_STOP || ar.playstate == animationwrapper.animstate.STATE_EASEOUT) + continue; + + //FIX ME need to consider ease out here on priorities somehow + + + int prio = 0; + //Quick hack to stack animations in the correct order + //TODO we need to do this per joint as they all have their own priorities as well ;-( + if (av.glavatar.skel.mPriority.TryGetValue(joint.Name, out prio)) + { + if (prio > (ar.anim.Priority)) + continue; + } + + av.glavatar.skel.mPriority[joint.Name] = ar.anim.Priority; + + jpos++; + + //av.glavatar.skel.mAnimationsWrapper[kvp.Key].playstate = animationwrapper.animstate.STATE_EASEIN; + + } + } + } + + } + + public void animate(float lastframetime) + { + + lock (mAnimationsWrapper) + { + + jointdeforms.Clear(); + + foreach (animationwrapper ar in mAnimationsWrapper.Values) + { + + if (ar.playstate == animationwrapper.animstate.STATE_WAITINGASSET) + continue; + + if (ar.anim == null) + continue; + + ar.mRunTime += lastframetime; + + // EASE FACTORS + // Caclulate ease factors now they are common to all joints in a given animation + float factor = 1.0f; + + if (ar.playstate == animationwrapper.animstate.STATE_EASEIN) + { + if (ar.mRunTime >= ar.anim.EaseInTime) + { + ar.playstate = animationwrapper.animstate.STATE_PLAY; + //Console.WriteLine(String.Format("{0} Now in STATE_PLAY", ar.mAnimation)); + } + else + { + factor = 1.0f - ((ar.anim.EaseInTime - ar.mRunTime) / ar.anim.EaseInTime); + } + + //Console.WriteLine(String.Format("EASE IN {0} {1}",factor.ToString(),ar.mAnimation)); + } + + if (ar.playstate == animationwrapper.animstate.STATE_EASEOUT) + { + + if (ar.mRunTime >= ar.anim.EaseOutTime) + { + ar.stopanim(); + factor = 0; + //Console.WriteLine(String.Format("{0} Now in STATE_STOP", ar.mAnimation)); + + } + else + { + factor = 1.0f - (ar.mRunTime / ar.anim.EaseOutTime); + + } + + //Console.WriteLine(String.Format("EASE OUT {0} {1}", factor.ToString(), ar.mAnimation)); + } + + // we should not need this, this implies bad math above + + + factor = Utils.Clamp(factor, 0.0f, 1.0f); + //factor = 1.0f; + + //END EASE FACTORS + + + int jpos = 0; + foreach (binBVHJoint joint in ar.anim.joints) + { + bool easeoutset = false; + //warning struct copy non reference + binBVHJointState state = (binBVHJointState)ar.anim.joints[jpos].Tag; + + if (ar.playstate == animationwrapper.animstate.STATE_STOP) + continue; + + int prio = 0; + //Quick hack to stack animations in the correct order + //TODO we need to do this per joint as they all have their own priorities as well ;-( + if (mPriority.TryGetValue(joint.Name, out prio)) + { + //if (prio > (ar.anim.Priority)) + //continue; + } + + Vector3 poslerp = Vector3.Zero; + Quaternion rotlerp = Quaternion.Identity; + + // Position + if (ar.anim.joints[jpos].positionkeys.Length >= 2 && joint.Name == "mPelvis") + { + + //Console.WriteLine("Animate time " + state.currenttime_pos.ToString()); + + state.currenttime_pos += lastframetime; + + float currentime = state.currenttime_pos; + bool overrun = false; + + if (state.currenttime_pos > ar.anim.OutPoint) + { + //overrun state + int itterations = (int)(state.currenttime_pos / ar.anim.OutPoint) + 1; + state.currenttime_pos = currentime = ar.anim.InPoint + ((ar.anim.OutPoint - ar.anim.InPoint) - (((ar.anim.OutPoint - ar.anim.InPoint) * itterations) - state.currenttime_pos)); + overrun = true; + } + + binBVHJointKey pos_next = ar.anim.joints[jpos].positionkeys[state.nextkeyframe_pos]; + binBVHJointKey pos_last = ar.anim.joints[jpos].positionkeys[state.lastkeyframe_pos]; + + // if the current time > than next key frame time we move keyframes + if (currentime >= pos_next.time || overrun) + { + + //Console.WriteLine("bump"); + state.lastkeyframe_pos++; + state.nextkeyframe_pos++; + + if (ar.anim.Loop) + { + if (state.nextkeyframe_pos > state.pos_loopoutframe) + state.nextkeyframe_pos = state.pos_loopinframe; + + if (state.lastkeyframe_pos > state.pos_loopoutframe) + state.lastkeyframe_pos = state.pos_loopinframe; + + + if (state.nextkeyframe_pos >= ar.anim.joints[jpos].positionkeys.Length) + state.nextkeyframe_pos = state.pos_loopinframe; + + if (state.lastkeyframe_pos >= ar.anim.joints[jpos].positionkeys.Length) + state.lastkeyframe_pos = state.pos_loopinframe; + + } + else + { + if (state.nextkeyframe_pos >= ar.anim.joints[jpos].positionkeys.Length) + state.nextkeyframe_pos = ar.anim.joints[jpos].positionkeys.Length - 1; + + if (state.lastkeyframe_pos >= ar.anim.joints[jpos].positionkeys.Length) + { + state.lastkeyframe_pos = ar.anim.joints[jpos].positionkeys.Length - 1; + + ar.playstate = animationwrapper.animstate.STATE_EASEOUT; + //animation over + } + } + } + + //if (pos_next.time == pos_last.time) + //{ + // ar.playstate = animationwrapper.animstate.STATE_EASEOUT; + //} + + // update the pointers incase they have been moved + pos_next = ar.anim.joints[jpos].positionkeys[state.nextkeyframe_pos]; + pos_last = ar.anim.joints[jpos].positionkeys[state.lastkeyframe_pos]; + + // TODO the lerp/delta is faulty + // it is not going to handle loop points when we wrap around as last will be > next + // it also fails when currenttime < last_time which occurs as keyframe[0] is not exactly at + // t(0). + float delta = (state.currenttime_pos - pos_last.time) / (pos_next.time - pos_last.time); + + delta = Utils.Clamp(delta, 0f, 1f); + poslerp = Vector3.Lerp(pos_last.key_element, pos_next.key_element, delta) * factor; + + //Console.WriteLine(string.Format("Time {0} {1} {2} {3} {4}", state.currenttime_pos, delta, poslerp.ToString(), state.lastkeyframe_pos, state.nextkeyframe_pos)); + + } + + // end of position + + //rotation + + if (ar.anim.joints[jpos].rotationkeys.Length >= 2) + { + + state.currenttime_rot += lastframetime; + + float currentime = state.currenttime_rot; + bool overrun = false; + + if (state.currenttime_rot > ar.anim.OutPoint) + { + //overrun state + int itterations = (int)(state.currenttime_rot / ar.anim.OutPoint) + 1; + state.currenttime_rot = currentime = ar.anim.InPoint + ((ar.anim.OutPoint - ar.anim.InPoint) - (((ar.anim.OutPoint - ar.anim.InPoint) * itterations) - state.currenttime_rot)); + overrun = true; + } + + binBVHJointKey rot_next = ar.anim.joints[jpos].rotationkeys[state.nextkeyframe_rot]; + binBVHJointKey rot_last = ar.anim.joints[jpos].rotationkeys[state.lastkeyframe_rot]; + + // if the current time > than next key frame time we move keyframes + if (currentime >= rot_next.time || overrun) + { + + state.lastkeyframe_rot++; + state.nextkeyframe_rot++; + + if (ar.anim.Loop) + { + if (state.nextkeyframe_rot > state.rot_loopoutframe) + state.nextkeyframe_rot = state.rot_loopinframe; + + if (state.lastkeyframe_rot > state.rot_loopoutframe) + state.lastkeyframe_rot = state.rot_loopinframe; + + } + else + { + if (state.nextkeyframe_rot >= ar.anim.joints[jpos].rotationkeys.Length) + state.nextkeyframe_rot = ar.anim.joints[jpos].rotationkeys.Length - 1; + + if (state.lastkeyframe_rot >= ar.anim.joints[jpos].rotationkeys.Length) + { + state.lastkeyframe_rot = ar.anim.joints[jpos].rotationkeys.Length - 1; + + ar.playstate = animationwrapper.animstate.STATE_EASEOUT; + //animation over + } + } + } + + // update the pointers incase they have been moved + rot_next = ar.anim.joints[jpos].rotationkeys[state.nextkeyframe_rot]; + rot_last = ar.anim.joints[jpos].rotationkeys[state.lastkeyframe_rot]; + + // TODO the lerp/delta is faulty + // it is not going to handle loop points when we wrap around as last will be > next + // it also fails when currenttime < last_time which occurs as keyframe[0] is not exactly at + // t(0). + float delta = state.currenttime_rot - rot_last.time / (rot_next.time - rot_last.time); + delta = Utils.Clamp(delta, 0f, 1f); + Vector3 rotlerpv = Vector3.Lerp(rot_last.key_element, rot_next.key_element, delta); + // rotlerp = Quaternion.Slerp(Quaternion.Identity,new Quaternion(rotlerpv.X, rotlerpv.Y, rotlerpv.Z), factor); + + if (easeoutset == false && ar.playstate == animationwrapper.animstate.STATE_EASEOUT) + { + easeoutset = true; + state.easeoutrot = new Quaternion(rotlerpv.X, rotlerpv.Y, rotlerpv.Z); + state.easeoutfactor = factor; + } + else + { + rotlerp = new Quaternion(rotlerpv.X, rotlerpv.Y, rotlerpv.Z); + } + } + + //end of rotation + + joint jointstate = new Rendering.joint(); + + if (jointdeforms.TryGetValue(ar.anim.joints[jpos].Name, out jointstate)) + { + jointstate.offset += (poslerp); + + if (ar.playstate != animationwrapper.animstate.STATE_EASEOUT) + { + if (easeoutset == true) + { + jointstate.rotation = Quaternion.Slerp(jointstate.rotation, state.easeoutrot, state.easeoutfactor); + } + else + { + jointstate.rotation = rotlerp; + } + } + + //jointstate.rotation= Quaternion.Slerp(jointstate.rotation, rotlerp, 0.5f); + } + else + { + jointstate = new joint(); + jointstate.rotation = rotlerp; + jointstate.offset = poslerp; + jointdeforms.Add(ar.anim.joints[jpos].Name, jointstate); + } + + //warning struct copy non reference + ar.anim.joints[jpos].Tag = state; + + jpos++; + } + } + } + + foreach (KeyValuePair kvp in jointdeforms) + { + deformbone(kvp.Key, kvp.Value.offset, kvp.Value.rotation); + } + mNeedsMeshRebuild = true; + } + + } + + public class Bone + { + public string name; + private Vector3 pos; + public Quaternion rot; + public Vector3 scale; + public Vector3 piviot; + + public Vector3 offset_pos; + + public Vector3 orig_pos; + public Quaternion orig_rot; + public Vector3 orig_scale; + public Vector3 orig_piviot; + + Matrix4 mDeformMatrix = Matrix4.Identity; + + public Vector3 animation_offset; + + public Bone parent; + + public List children = new List(); + + public static Dictionary mBones = new Dictionary(); + public static Dictionary mIndexedBones = new Dictionary(); + static int boneaddindex = 0; + + private bool rotdirty = true; + private bool posdirty = true; + + //Inverse bind matrix with bone scale + private Vector3 mTotalPos; + + private Quaternion mTotalRot; + + private Vector3 mDeltaPos; + private Quaternion mDeltaRot; + + public string mParentBone = null; + + public Bone() + { + } + + public Bone(Bone source) + { + name = String.Copy(source.name); + pos = new Vector3(source.pos); + rot = new Quaternion(source.rot); + scale = new Vector3(source.scale); + piviot = new Vector3(source.piviot); + offset_pos = new Vector3(source.offset_pos); + + orig_piviot = source.orig_piviot; + orig_pos = source.orig_pos; + orig_rot = source.orig_rot; + orig_scale = source.orig_scale; + + mParentBone = source.mParentBone; + + mDeformMatrix = new Matrix4(source.mDeformMatrix); + } + + public static void loadbones(string skeletonfilename) + { + lock (Bone.mBones) mBones.Clear(); + string basedir = Directory.GetCurrentDirectory() + System.IO.Path.DirectorySeparatorChar + "character" + System.IO.Path.DirectorySeparatorChar; + XmlDocument skeleton = new XmlDocument(); + skeleton.Load(basedir + skeletonfilename); + XmlNode boneslist = skeleton.GetElementsByTagName("linden_skeleton")[0]; + addbone(boneslist.ChildNodes[0], null); + } + + public static void addbone(XmlNode bone, Bone parent) + { + + if (bone.Name != "bone") + return; + + Bone b = new Bone(); + b.name = bone.Attributes.GetNamedItem("name").Value; + + string pos = bone.Attributes.GetNamedItem("pos").Value; + string[] posparts = pos.Split(' '); + b.pos = new Vector3(float.Parse(posparts[0], Utils.EnUsCulture), float.Parse(posparts[1], Utils.EnUsCulture), float.Parse(posparts[2], Utils.EnUsCulture)); + b.orig_pos = new Vector3(b.pos); + b.offset_pos = new Vector3(b.pos); + + string rot = bone.Attributes.GetNamedItem("rot").Value; + string[] rotparts = rot.Split(' '); + b.rot = Quaternion.CreateFromEulers((float)(float.Parse(rotparts[0], Utils.EnUsCulture) * Math.PI / 180f), (float)(float.Parse(rotparts[1], Utils.EnUsCulture) * Math.PI / 180f), (float)(float.Parse(rotparts[2], Utils.EnUsCulture) * Math.PI / 180f)); + b.orig_rot = new Quaternion(b.rot); + + string scale = bone.Attributes.GetNamedItem("scale").Value; + string[] scaleparts = scale.Split(' '); + b.scale = new Vector3(float.Parse(scaleparts[0], Utils.EnUsCulture), float.Parse(scaleparts[1], Utils.EnUsCulture), float.Parse(scaleparts[2], Utils.EnUsCulture)); + b.orig_scale = new Vector3(b.scale); + + + float[] deform = Math3D.CreateSRTMatrix(new Vector3(1, 1, 1), b.rot, b.orig_pos); + b.mDeformMatrix = new Matrix4(deform[0], deform[1], deform[2], deform[3], deform[4], deform[5], deform[6], deform[7], deform[8], deform[9], deform[10], deform[11], deform[12], deform[13], deform[14], deform[15]); + + //TODO piviot + + b.parent = parent; + if (parent != null) + { + b.mParentBone = parent.name; + parent.children.Add(b); + } + + lock (Bone.mBones) mBones.Add(b.name, b); + mIndexedBones.Add(boneaddindex++, b); + + Logger.Log("Found bone " + b.name, Helpers.LogLevel.Info); + + foreach (XmlNode childbone in bone.ChildNodes) + { + addbone(childbone, b); + } + + } + + public void deformbone(Vector3 dpos, Quaternion rot) + { + //float[] deform = Math3D.CreateSRTMatrix(scale, rot, this.orig_pos); + //mDeformMatrix = new Matrix4(deform[0], deform[1], deform[2], deform[3], deform[4], deform[5], deform[6], deform[7], deform[8], deform[9], deform[10], deform[11], deform[12], deform[13], deform[14], deform[15]); + + //this.offset_pos += pos; + //this.pos = pos; + // this.offset_pos = pos; + + animation_offset = dpos; + + //this.pos = Bone.mBones[name].offset_pos + dpos; + lock (Bone.mBones) this.rot = Bone.mBones[name].orig_rot * rot; + + markdirty(); + } + + public void scalebone(Vector3 scale) + { + this.scale *= scale; + markdirty(); + } + + public void offsetbone(Vector3 offset) + { + this.offset_pos += offset; + markdirty(); + } + + // If we deform a bone mark this bone and all its children as dirty. + public void markdirty() + { + rotdirty = true; + posdirty = true; + foreach (Bone childbone in children) + { + childbone.markdirty(); + } + } + + public Matrix4 getdeform() + { + if (this.parent != null) + { + return mDeformMatrix * parent.getdeform(); + } + else + { + return mDeformMatrix; + } + } + + private Vector3 getOffset() + { + if (parent != null) + { + Quaternion totalrot = getParentRot(); // we don't want this joints rotation included + Vector3 parento = parent.getOffset(); + mTotalPos = parento + pos * parent.scale * totalrot; + Vector3 orig = getOrigOffset(); + mDeltaPos = mTotalPos - orig; + + posdirty = false; + + return mTotalPos; + } + else + { + Vector3 orig = getOrigOffset(); + //mTotalPos = (pos * scale)+offset_pos; + //mTotalPos = (pos) + offset_pos; + mTotalPos = pos; + mDeltaPos = mTotalPos - orig; + posdirty = false; + return mTotalPos; + + } + } + + public Vector3 getMyOffset() + { + return pos * scale; + } + + // Try to save some cycles by not recalculating positions and rotations every time + public Vector3 getTotalOffset() + { + if (posdirty == false) + { + return mTotalPos; + } + else + { + return getOffset(); + } + } + + public Vector3 getDeltaOffset() + { + if (posdirty == false) + { + return mDeltaPos; + } + else + { + getOffset(); + return mDeltaPos; + } + } + + private Vector3 getOrigOffset() + { + if (parent != null) + { + return (parent.getOrigOffset() + orig_pos); + } + else + { + return orig_pos; + } + } + + private static Quaternion getRotation(string bonename) + { + Bone b; + lock (Bone.mBones) + { + if (mBones.TryGetValue(bonename, out b)) + { + return (b.getRotation()); + } + else + { + return Quaternion.Identity; + } + } + } + + + private Quaternion getParentRot() + { + Quaternion totalrot = Quaternion.Identity; + + if (parent != null) + { + totalrot = parent.getRotation(); + } + + return totalrot; + + } + + private Quaternion getRotation() + { + Quaternion totalrot = rot; + + if (parent != null) + { + totalrot = parent.getRotation() * rot; + } + + mTotalRot = totalrot; + rotdirty = false; + + return totalrot; + } + + public Quaternion getTotalRotation() + { + if (rotdirty == false) + { + return mTotalRot; + } + else + { + return getRotation(); + } + } + } + + public class BoneDeform + { + public BoneDeform(Vector3 scale, Vector3 offset) + { + this.scale = scale; + this.offset = offset; + } + + public Vector3 scale; + public Vector3 offset; + } + + public class VisualParamEx + { + //All visual params indexed by ID + static public Dictionary allParams = new Dictionary(); + + // The sorted list of tweakable params, this matches the AvatarAppearance packet visual + // parameters ordering + static public SortedList tweakable_params = new SortedList(); + + public Dictionary BoneDeforms = null; + + public Dictionary VolumeDeforms = null; + + public List childparams = null; + + public List identicalIds = new List(); + + public string morphmesh = null; + + enum GroupType + { + VISUAL_PARAM_GROUP_TWEAKABLE = 0, + VISUAL_PARAM_GROUP_ANIMATABLE, + VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT, + } + + public struct VolumeDeform + { + public string name; + public Vector3 scale; + public Vector3 pos; + } + + public enum EparamSex + { + SEX_BOTH = 0, + SEX_FEMALE = 1, + SEX_MALE = 2 + } + + public enum ParamType + { + TYPE_NOTSET = 0, + TYPE_BONEDEFORM, + TYPE_MORPH, + TYPE_DRIVER, + TYPE_COLOR, + TYPE_LAYER + } + + public struct driven + { + public int id; + public float max1; + public float max2; + public float min1; + public float min2; + public bool hasMinMax; + } + + public string meshname; + + /// Index of this visual param + public int ParamID; + /// Internal name + public string Name; + /// Group ID this parameter belongs to + public int Group; + /// Name of the wearable this parameter belongs to + public string Wearable; + /// Displayable label of this characteristic + public string Label; + /// Displayable label for the minimum value of this characteristic + public string LabelMin; + /// Displayable label for the maximum value of this characteristic + public string LabelMax; + /// Default value + public float DefaultValue; + /// Minimum value + public float MinValue; + /// Maximum value + public float MaxValue; + /// Is this param used for creation of bump layer? + public bool IsBumpAttribute; + /// Alpha blending/bump info + public VisualAlphaParam? AlphaParams; + /// Color information + public VisualColorParam? ColorParams; + /// Array of param IDs that are drivers for this parameter + public int[] Drivers; + /// The Avatar Sex that this parameter applies to + public EparamSex sex; + + public ParamType pType; + + public static int count = 0; + + /// + /// Set all the values through the constructor + /// + /// Index of this visual param + /// Internal name + /// + /// + /// Displayable label of this characteristic + /// Displayable label for the minimum value of this characteristic + /// Displayable label for the maximum value of this characteristic + /// Default value + /// Minimum value + /// Maximum value + /// Is this param used for creation of bump layer? + /// Array of param IDs that are drivers for this parameter + /// Alpha blending/bump info + /// Color information + public VisualParamEx(int paramID, string name, int group, string wearable, string label, string labelMin, string labelMax, float def, float min, float max, bool isBumpAttribute, int[] drivers, VisualAlphaParam? alpha, VisualColorParam? colorParams) + { + ParamID = paramID; + Name = name; + Group = group; + Wearable = wearable; + Label = label; + LabelMin = labelMin; + LabelMax = labelMax; + DefaultValue = def; + MaxValue = max; + MinValue = min; + IsBumpAttribute = isBumpAttribute; + Drivers = drivers; + AlphaParams = alpha; + ColorParams = colorParams; + sex = EparamSex.SEX_BOTH; + } + + public bool matchchildnode(string test, XmlNode node) + { + foreach (XmlNode n in node.ChildNodes) + { + if (n.Name == test) + return true; + } + + return false; + } + + public VisualParamEx(XmlNode node) + { + + ParamID = Int32.Parse(node.Attributes.GetNamedItem("id").Value); + Name = node.Attributes.GetNamedItem("name").Value; + Group = Int32.Parse(node.Attributes.GetNamedItem("group").Value); + + //These dont exist for facal expresion morphs + if (node.Attributes.GetNamedItem("wearable") != null) + Wearable = node.Attributes.GetNamedItem("wearable").Value; + + MinValue = float.Parse(node.Attributes.GetNamedItem("value_min").Value, Utils.EnUsCulture); + MaxValue = float.Parse(node.Attributes.GetNamedItem("value_max").Value, Utils.EnUsCulture); + + // These do not exists for driven parameters + if (node.Attributes.GetNamedItem("label_min") != null) + { + LabelMin = node.Attributes.GetNamedItem("label_min").Value; + } + + if (node.Attributes.GetNamedItem("label_max") != null) + { + LabelMax = node.Attributes.GetNamedItem("label_max").Value; + } + + XmlNode sexnode = node.Attributes.GetNamedItem("sex"); + + if (sexnode != null) + { + if (sexnode.Value == "male") + { + sex = EparamSex.SEX_MALE; + } + else + { + sex = EparamSex.SEX_FEMALE; + } + } + + if (node.ParentNode.Name == "mesh") + { + this.morphmesh = node.ParentNode.Attributes.GetNamedItem("type").Value; + } + + Group = int.Parse(node.Attributes.GetNamedItem("group").Value); + + if (Group == (int)GroupType.VISUAL_PARAM_GROUP_TWEAKABLE) + { + if (!tweakable_params.ContainsKey(ParamID)) //stupid duplicate shared params + { + tweakable_params.Add(this.ParamID, this); + } + else + { + Logger.Log(String.Format("Warning duplicate tweakable paramater ID {0} {1}", count, this.Name), Helpers.LogLevel.Warning); + } + count++; + } + + if (allParams.ContainsKey(ParamID)) + { + //Logger.Log("Shared VisualParam id " + ParamID.ToString() + " "+Name, Helpers.LogLevel.Info); + allParams[ParamID].identicalIds.Add(this); + } + else + { + //Logger.Log("VisualParam id " + ParamID.ToString() + " " + Name, Helpers.LogLevel.Info); + allParams.Add(ParamID, this); + } + + if (matchchildnode("param_skeleton", node)) + { + pType = ParamType.TYPE_BONEDEFORM; + // If we are in the skeleton section then we also have bone deforms to parse + BoneDeforms = new Dictionary(); + if (node.HasChildNodes && node.ChildNodes[0].HasChildNodes) + { + ParseBoneDeforms(node.ChildNodes[0].ChildNodes); + } + } + + if (matchchildnode("param_morph", node)) + { + pType = ParamType.TYPE_MORPH; + + VolumeDeforms = new Dictionary(); + if (node.HasChildNodes && node.ChildNodes[0].HasChildNodes) + { + ParseVolumeDeforms(node.ChildNodes[0].ChildNodes); + } + } + + if (matchchildnode("param_driver", node)) + { + pType = ParamType.TYPE_DRIVER; + childparams = new List(); + if (node.HasChildNodes && node.ChildNodes[0].HasChildNodes) //LAZY + { + ParseDrivers(node.ChildNodes[0].ChildNodes); + } + } + + if (matchchildnode("param_color", node)) + { + pType = ParamType.TYPE_COLOR; + if (node.HasChildNodes) + { + foreach (XmlNode colorchild in node.ChildNodes) + { + if (colorchild.Name == "param_color") + { + //TODO extract + } + } + } + } + + } + + void ParseBoneDeforms(XmlNodeList deforms) + { + foreach (XmlNode node in deforms) + { + if (node.Name == "bone") + { + string name = node.Attributes.GetNamedItem("name").Value; + Vector3 scale = Vector3.One; + Vector3 offset = Vector3.One; + + if (node.Attributes.GetNamedItem("scale") != null) + scale = XmlParseVector(node.Attributes.GetNamedItem("scale").Value); + + if (node.Attributes.GetNamedItem("offset") != null) + offset = XmlParseVector(node.Attributes.GetNamedItem("offset").Value); + + BoneDeform bd = new BoneDeform(scale, offset); + + BoneDeforms.Add(name, bd); + } + } + } + + void ParseVolumeDeforms(XmlNodeList deforms) + { + foreach (XmlNode node in deforms) + { + if (node.Name == "volume_morph") + { + VolumeDeform vd = new VolumeDeform(); + vd.name = node.Attributes.GetNamedItem("name").Value; + vd.name = vd.name.ToLower(); + + if (node.Attributes.GetNamedItem("scale") != null) + { + vd.scale = XmlParseVector(node.Attributes.GetNamedItem("scale").Value); + } + else + { + vd.scale = new Vector3(0, 0, 0); + } + + if (node.Attributes.GetNamedItem("pos") != null) + { + vd.pos = XmlParseVector(node.Attributes.GetNamedItem("pos").Value); + } + else + { + vd.pos = new Vector3(0f, 0f, 0f); + } + + VolumeDeforms.Add(vd.name, vd); + } + } + } + + void ParseDrivers(XmlNodeList drivennodes) + { + foreach (XmlNode node in drivennodes) + { + if (node.Name == "driven") + { + driven d = new driven(); + + d.id = Int32.Parse(node.Attributes.GetNamedItem("id").Value); + XmlNode param = node.Attributes.GetNamedItem("max1"); + if (param != null) + { + d.max1 = float.Parse(param.Value, Utils.EnUsCulture); + d.max2 = float.Parse(node.Attributes.GetNamedItem("max2").Value, Utils.EnUsCulture); + d.min1 = float.Parse(node.Attributes.GetNamedItem("min1").Value, Utils.EnUsCulture); + d.min2 = float.Parse(node.Attributes.GetNamedItem("min2").Value, Utils.EnUsCulture); + d.hasMinMax = true; + } + else + { + d.hasMinMax = false; + } + + childparams.Add(d); + + } + } + } + + public static Vector3 XmlParseVector(string data) + { + string[] posparts = data.Split(' '); + return new Vector3(float.Parse(posparts[0], Utils.EnUsCulture), float.Parse(posparts[1], Utils.EnUsCulture), float.Parse(posparts[2], Utils.EnUsCulture)); + } + + public static Quaternion XmlParseRotation(string data) + { + string[] rotparts = data.Split(' '); + return Quaternion.CreateFromEulers((float)(float.Parse(rotparts[0]) * Math.PI / 180f), (float)(float.Parse(rotparts[1]) * Math.PI / 180f), (float)(float.Parse(rotparts[2]) * Math.PI / 180f)); + } + } + + public class RenderAvatar : SceneObject + { + public static Dictionary BakedTextures = new Dictionary + { + { 8, "head" }, + { 9, "upper" }, + { 10, "lower" }, + { 11, "eyes" }, + { 19, "skirt" }, + { 20, "hair" } + }; + + public static readonly BoundingVolume AvatarBoundingVolume; + + // Static constructor + static RenderAvatar() + { + AvatarBoundingVolume = new BoundingVolume(); + AvatarBoundingVolume.FromScale(Vector3.One); + } + + // Default constructor + public RenderAvatar() + { + BoundingVolume = AvatarBoundingVolume; + Type = SceneObjectType.Avatar; + } + + public override Primitive BasePrim + { + get { return avatar; } + set + { + if (value is Avatar) + { + avatar = (Avatar)value; + AvatarBoundingVolume.CalcScaled(avatar.Scale); + } + } + } + + public override void Step(float time) + { + glavatar.skel.animate(time); + base.Step(time); + } + + public GLAvatar glavatar = new GLAvatar(); + public Avatar avatar; + public FaceData[] data = new FaceData[32]; + public Dictionary animlist = new Dictionary(); + public Dictionary Wearables = new Dictionary(); + + } + +} diff --git a/Assets/Scripts/RenderAvatar.cs.meta b/Assets/Scripts/RenderAvatar.cs.meta new file mode 100644 index 0000000..a027696 --- /dev/null +++ b/Assets/Scripts/RenderAvatar.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d243fd7b3fb9db640a3640c91d510f2f +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/Scripts/RenderPrimitive.cs b/Assets/Scripts/RenderPrimitive.cs new file mode 100644 index 0000000..81c5756 --- /dev/null +++ b/Assets/Scripts/RenderPrimitive.cs @@ -0,0 +1,665 @@ +// +// Radegast Metaverse Client +// Copyright (c) 2009-2013, 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$ +// + +using System; +using System.Collections.Generic; +using OpenTK.Graphics.OpenGL; +using OpenMetaverse; +using OpenMetaverse.Rendering; + +namespace Radegast.Rendering +{ + /// + /// Contains per primitive face data + /// + public class FaceData : IDisposable + { + public float[] Vertices; + public ushort[] Indices; + public float[] TexCoords; + public float[] Normals; + public int PickingID = -1; + public int VertexVBO = -1; + public int IndexVBO = -1; + public TextureInfo TextureInfo = new TextureInfo(); + public BoundingVolume BoundingVolume = new BoundingVolume(); + public static int VertexSize = 32; // sizeof (vertex), 2 x vector3 + 1 x vector2 = 8 floats x 4 bytes = 32 bytes + public TextureAnimationInfo AnimInfo; + public int QueryID = 0; + public bool VBOFailed = false; + + /// + /// Dispose VBOs if we have them in graphics card memory + /// + public void Dispose() + {//hack +// if (VertexVBO != -1) Compat.DeleteBuffer(VertexVBO); +// if (IndexVBO != -1) Compat.DeleteBuffer(IndexVBO); +// VertexVBO = -1; +// IndexVBO = -1; + } + + /// + /// Checks if VBOs are created, if they are, bind them, if not create new + /// + /// Which face's mesh is uploaded in this VBO + /// True, if face data was succesfully uploaded to the graphics card memory + public bool CheckVBO(Face face) + {//hack +// if (VertexVBO == -1) +// { +// Vertex[] vArray = face.Vertices.ToArray(); +// Compat.GenBuffers(out VertexVBO); +// Compat.BindBuffer(BufferTarget.ArrayBuffer, VertexVBO); +// Compat.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vArray.Length * VertexSize), vArray, BufferUsageHint.StaticDraw); +// if (Compat.BufferSize(BufferTarget.ArrayBuffer) != vArray.Length * VertexSize) +// { +// VBOFailed = true; +// Compat.BindBuffer(BufferTarget.ArrayBuffer, 0); +// Compat.DeleteBuffer(VertexVBO); +// VertexVBO = -1; +// return false; +// } +// } +// +// if (IndexVBO == -1) +// { +// ushort[] iArray = face.Indices.ToArray(); +// Compat.GenBuffers(out IndexVBO); +// Compat.BindBuffer(BufferTarget.ElementArrayBuffer, IndexVBO); +// Compat.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(iArray.Length * sizeof(ushort)), iArray, BufferUsageHint.StaticDraw); +// if (Compat.BufferSize(BufferTarget.ElementArrayBuffer) != iArray.Length * sizeof(ushort)) +// { +// VBOFailed = true; +// Compat.BindBuffer(BufferTarget.ElementArrayBuffer, 0); +// Compat.DeleteBuffer(IndexVBO); +// IndexVBO = -1; +// return false; +// } +// } + + return true; + } + } + + /// + /// Class handling texture animations + /// + public class TextureAnimationInfo + { + public Primitive.TextureAnimation PrimAnimInfo; + public float CurrentFrame; + public float CurrentTime; + public bool PingPong; + float LastTime = 0f; + float TotalTime = 0f; + + /// + /// Perform texture manupulation to implement texture animations + /// + /// Time passed since the last run (in seconds) + public void Step(float lastFrameTime) + { + float numFrames = 1f; + float fullLength = 1f; + + if (PrimAnimInfo.Length > 0) + { + numFrames = PrimAnimInfo.Length; + } + else + { + numFrames = Math.Max(1f, (float)(PrimAnimInfo.SizeX * PrimAnimInfo.SizeY)); + } + + if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.PING_PONG) != 0) + { + if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) != 0) + { + fullLength = 2f * numFrames; + } + else if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.LOOP) != 0) + { + fullLength = 2f * numFrames - 2f; + fullLength = Math.Max(1f, fullLength); + } + else + { + fullLength = 2f * numFrames - 1f; + fullLength = Math.Max(1f, fullLength); + } + } + else + { + fullLength = numFrames; + } + + float frameCounter; + if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) != 0) + { + frameCounter = lastFrameTime * PrimAnimInfo.Rate + LastTime; + } + else + { + TotalTime += lastFrameTime; + frameCounter = TotalTime * PrimAnimInfo.Rate; + } + LastTime = frameCounter; + + if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.LOOP) != 0) + { + frameCounter %= fullLength; + } + else + { + frameCounter = Math.Min(fullLength - 1f, frameCounter); + } + + if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) == 0) + { + frameCounter = (float)Math.Floor(frameCounter + 0.01f); + } + + if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.PING_PONG) != 0) + { + if (frameCounter > numFrames) + { + if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) != 0) + { + frameCounter = numFrames - (frameCounter - numFrames); + } + else + { + frameCounter = (numFrames - 1.99f) - (frameCounter - numFrames); + } + } + } + + if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.REVERSE) != 0) + { + if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) != 0) + { + frameCounter = numFrames - frameCounter; + } + else + { + frameCounter = (numFrames - 0.99f) - frameCounter; + } + } + + frameCounter += PrimAnimInfo.Start; + + if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) == 0) + { + frameCounter = (float)Math.Round(frameCounter); + } + + + GL.MatrixMode(MatrixMode.Texture); + GL.LoadIdentity(); + + if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.ROTATE) != 0) + { + GL.Translate(0.5f, 0.5f, 0f); + GL.Rotate(Utils.RAD_TO_DEG * frameCounter, OpenTK.Vector3d.UnitZ); + GL.Translate(-0.5f, -0.5f, 0f); + } + else if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SCALE) != 0) + { + GL.Scale(frameCounter, frameCounter, 0); + } + else // Translate + { + float sizeX = Math.Max(1f, (float)PrimAnimInfo.SizeX); + float sizeY = Math.Max(1f, (float)PrimAnimInfo.SizeY); + + GL.Scale(1f / sizeX, 1f / sizeY, 0); + GL.Translate(frameCounter % sizeX, Math.Floor(frameCounter / sizeY), 0); + } + + GL.MatrixMode(MatrixMode.Modelview); + } + + [Obsolete("Use Step() instead")] + public void ExperimentalStep(float time) + { + int reverseFactor = 1; + float rate = PrimAnimInfo.Rate; + + if (rate < 0) + { + rate = -rate; + reverseFactor = -reverseFactor; + } + + if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.REVERSE) != 0) + { + reverseFactor = -reverseFactor; + } + + CurrentTime += time; + float totalTime = 1 / rate; + + uint x = Math.Max(1, PrimAnimInfo.SizeX); + uint y = Math.Max(1, PrimAnimInfo.SizeY); + uint nrFrames = x * y; + + if (PrimAnimInfo.Length > 0 && PrimAnimInfo.Length < nrFrames) + { + nrFrames = (uint)PrimAnimInfo.Length; + } + + GL.MatrixMode(MatrixMode.Texture); + GL.LoadIdentity(); + + if (CurrentTime >= totalTime) + { + CurrentTime = 0; + CurrentFrame++; + if (CurrentFrame > nrFrames) CurrentFrame = (uint)PrimAnimInfo.Start; + if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.PING_PONG) != 0) + { + PingPong = !PingPong; + } + } + + float smoothOffset = 0f; + + if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) != 0) + { + smoothOffset = (CurrentTime / totalTime) * reverseFactor; + } + + float f = CurrentFrame; + if (reverseFactor < 0) + { + f = nrFrames - CurrentFrame; + } + + if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.ROTATE) == 0) // not rotating + { + GL.Scale(1f / x, 1f / y, 0f); + GL.Translate((f % x) + smoothOffset, f / y, 0); + } + else + { + smoothOffset = (CurrentTime * PrimAnimInfo.Rate); + float startAngle = PrimAnimInfo.Start; + float endAngle = PrimAnimInfo.Length; + float angle = startAngle + (endAngle - startAngle) * smoothOffset; + GL.Translate(0.5f, 0.5f, 0f); + GL.Rotate(Utils.RAD_TO_DEG * angle, OpenTK.Vector3d.UnitZ); + GL.Translate(-0.5f, -0.5f, 0f); + } + + GL.MatrixMode(MatrixMode.Modelview); + } + + } + + /// + /// Class that handle rendering of objects: simple primitives, scupties, and meshes + /// + public class RenderPrimitive : SceneObject + { + #region Public fields + /// Base simulator object + public Primitive Prim; + public List Faces; + /// Is this object attached to an avatar + public bool Attached; + /// Do we know if object is attached + public bool AttachedStateKnown; + /// Are meshes constructed and ready for this prim + public bool Meshed; + /// Process of creating a mesh is underway + public bool Meshing; + #endregion Public fields + + #region Private fields + int prevTEHash; + int prevSculptHash; + int prevShapeHash; + #endregion Private fields + + /// + /// Default constructor + /// + public RenderPrimitive() + { + Type = SceneObjectType.Primitive; + } + + /// + /// Remove any GL resource we may still have in use + /// + public override void Dispose() + { + if (Faces != null) + { + foreach (Face f in Faces) + { + if (f.UserData != null && f.UserData is FaceData) + { + FaceData data = (FaceData)f.UserData; + data.Dispose(); + data = null; + } + } + Faces = null; + } + base.Dispose(); + } + + /// + /// Simulator object that is basis for this SceneObject + /// + public override Primitive BasePrim + { + get { return Prim; } + set + { + Prim = value; + + int TEHash = Prim.Textures == null ? 0 : Prim.Textures.GetHashCode(); + int sculptHash, shapeHash; + + if (Meshed) + { + if (Prim.Type == PrimType.Sculpt || Prim.Type == PrimType.Mesh) + { + sculptHash = Prim.Sculpt.GetHashCode(); + if (Prim.Sculpt.GetHashCode() != prevSculptHash || TEHash != prevTEHash) + { + Meshed = false; + } + prevSculptHash = sculptHash; + } + else + { + shapeHash = Prim.PrimData.GetHashCode(); + if (shapeHash != prevShapeHash) + { + Meshed = false; + } + else if (TEHash != prevTEHash) + { + Meshed = false; + } + prevShapeHash = shapeHash; + } + } + prevTEHash = TEHash; + } + } + + /// + /// Set initial state of the object + /// + public override void Initialize() + { + AttachedStateKnown = false; + base.Initialize(); + } + + /// + /// Render Primitive + /// + /// Which pass are we currently in + /// ID used to identify which object was picked + /// Main scene renderer + /// Time it took to render the last frame + public override void Render(RenderPass pass, int pickingID, SceneWindow scene, float time) + { + //if (!RenderSettings.AvatarRenderingEnabled && Attached) return;//hack + + // Individual prim matrix + GL.PushMatrix(); + + // Prim roation and position and scale + GL.MultMatrix(Math3D.CreateSRTMatrix(Prim.Scale, RenderRotation, RenderPosition)); + + // Do we have animated texture on this face + bool animatedTexture = false; + + // Initialise flags tracking what type of faces this prim has + if (pass == RenderPass.Simple) + { + HasSimpleFaces = false; + } + else if (pass == RenderPass.Alpha) + { + HasAlphaFaces = false; + } + else if (pass == RenderPass.Invisible) + { + HasInvisibleFaces = false; + } + + // Draw the prim faces + for (int j = 0; j < Faces.Count; j++) + { + Primitive.TextureEntryFace teFace = Prim.Textures.GetFace((uint)j); + Face face = Faces[j]; + FaceData data = (FaceData)face.UserData; + + if (data == null) + continue; + + if (teFace == null) + continue; + + // Don't render transparent faces + Color4 RGBA = teFace.RGBA; + + if (data.TextureInfo.FullAlpha || RGBA.A <= 0.01f) continue; + + bool switchedLightsOff = false; + + if (pass == RenderPass.Picking) + { + data.PickingID = pickingID; + var primNrBytes = Utils.UInt16ToBytes((ushort)pickingID); + var faceColor = new byte[] { primNrBytes[0], primNrBytes[1], (byte)j, 255 }; + GL.Color4(faceColor); + } + else if (pass == RenderPass.Invisible) + { + if (!data.TextureInfo.IsInvisible) continue; + HasInvisibleFaces = true; + } + else + { + if (data.TextureInfo.IsInvisible) continue; + bool belongToAlphaPass = (RGBA.A < 0.99f) || (data.TextureInfo.HasAlpha && !data.TextureInfo.IsMask); + + if (belongToAlphaPass && pass != RenderPass.Alpha) continue; + if (!belongToAlphaPass && pass == RenderPass.Alpha) continue; + + if (pass == RenderPass.Simple) + { + HasSimpleFaces = true; + } + else if (pass == RenderPass.Alpha) + { + HasAlphaFaces = true; + } + + if (teFace.Fullbright) + { + GL.Disable(EnableCap.Lighting); + switchedLightsOff = true; + } + + float shiny = 0f; + switch (teFace.Shiny) + { + case Shininess.High: + shiny = 0.96f; + break; + + case Shininess.Medium: + shiny = 0.64f; + break; + + case Shininess.Low: + shiny = 0.24f; + break; + } + + if (shiny > 0f) + { + scene.StartShiny(); + } + GL.Material(MaterialFace.Front, MaterialParameter.Shininess, shiny); + var faceColor = new float[] { RGBA.R, RGBA.G, RGBA.B, RGBA.A }; + GL.Color4(faceColor); + + GL.Material(MaterialFace.Front, MaterialParameter.Specular, new float[] { 0.5f, 0.5f, 0.5f, 1f }); + + if (data.TextureInfo.TexturePointer == 0) + { + TextureInfo teInfo; + if (scene.TryGetTextureInfo(teFace.TextureID, out teInfo)) + { + data.TextureInfo = teInfo; + } + } + + if (data.TextureInfo.TexturePointer == 0) + { + GL.Disable(EnableCap.Texture2D); + if (!data.TextureInfo.FetchFailed) + { + scene.DownloadTexture(new TextureLoadItem() + { + Prim = this.Prim, + TeFace = teFace, + Data = data + }, false); + } + } + else + { + // Is this face using texture animation + if ((Prim.TextureAnim.Flags & Primitive.TextureAnimMode.ANIM_ON) != 0 + && (Prim.TextureAnim.Face == j || Prim.TextureAnim.Face == 255)) + { + if (data.AnimInfo == null) + { + data.AnimInfo = new TextureAnimationInfo(); + } + data.AnimInfo.PrimAnimInfo = Prim.TextureAnim; + data.AnimInfo.Step(time); + animatedTexture = true; + } + else if (data.AnimInfo != null) // Face texture not animated. Do we have previous anim setting? + { + data.AnimInfo = null; + } + + GL.Enable(EnableCap.Texture2D); + GL.BindTexture(TextureTarget.Texture2D, data.TextureInfo.TexturePointer); + } + } + //hack + /* + if (!RenderSettings.UseVBO || data.VBOFailed) + { + Vertex[] verts = face.Vertices.ToArray(); + ushort[] indices = face.Indices.ToArray(); + + unsafe + { + fixed (float* normalPtr = &verts[0].Normal.X) + fixed (float* texPtr = &verts[0].TexCoord.X) + { + GL.NormalPointer(NormalPointerType.Float, FaceData.VertexSize, (IntPtr)normalPtr); + GL.TexCoordPointer(2, TexCoordPointerType.Float, FaceData.VertexSize, (IntPtr)texPtr); + GL.VertexPointer(3, VertexPointerType.Float, FaceData.VertexSize, verts); + GL.DrawElements(BeginMode.Triangles, indices.Length, DrawElementsType.UnsignedShort, indices); + } + } + } + else + { + if (data.CheckVBO(face)) + { + Compat.BindBuffer(BufferTarget.ArrayBuffer, data.VertexVBO); + Compat.BindBuffer(BufferTarget.ElementArrayBuffer, data.IndexVBO); + GL.NormalPointer(NormalPointerType.Float, FaceData.VertexSize, (IntPtr)12); + GL.TexCoordPointer(2, TexCoordPointerType.Float, FaceData.VertexSize, (IntPtr)(24)); + GL.VertexPointer(3, VertexPointerType.Float, FaceData.VertexSize, (IntPtr)(0)); + + GL.DrawElements(BeginMode.Triangles, face.Indices.Count, DrawElementsType.UnsignedShort, IntPtr.Zero); + } + Compat.BindBuffer(BufferTarget.ArrayBuffer, 0); + Compat.BindBuffer(BufferTarget.ElementArrayBuffer, 0); + + } + */ + if (switchedLightsOff) + { + GL.Enable(EnableCap.Lighting); + switchedLightsOff = false; + } + + } + + GL.BindTexture(TextureTarget.Texture2D, 0); + RHelp.ResetMaterial(); + + // Reset texture coordinates if we modified them in texture animation + if (animatedTexture) + { + GL.MatrixMode(MatrixMode.Texture); + GL.LoadIdentity(); + GL.MatrixMode(MatrixMode.Modelview); + } + + // Pop the prim matrix + GL.PopMatrix(); + + base.Render(pass, pickingID, scene, time); + } + + /// + /// String representation of the object + /// + /// String containing local ID of the object and it's distance from the camera + public override string ToString() + { + uint id = Prim == null ? 0 : Prim.LocalID; + float distance = (float)Math.Sqrt(DistanceSquared); + return string.Format("LocalID: {0}, distance {0.00}", id, distance); + } + } +} diff --git a/Assets/Scripts/RenderPrimitive.cs.meta b/Assets/Scripts/RenderPrimitive.cs.meta new file mode 100644 index 0000000..6d4380c --- /dev/null +++ b/Assets/Scripts/RenderPrimitive.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a39f315ba92417d43a168eddc202ecaa +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/Scripts/Rendering.cs b/Assets/Scripts/Rendering.cs new file mode 100644 index 0000000..e69de29 diff --git a/Assets/Scripts/Rendering.cs.meta b/Assets/Scripts/Rendering.cs.meta new file mode 100644 index 0000000..083f490 --- /dev/null +++ b/Assets/Scripts/Rendering.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ce6bf542a1ac7004b867d58c64071494 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/Scripts/RenderingHelpers.cs b/Assets/Scripts/RenderingHelpers.cs new file mode 100644 index 0000000..3aef79f --- /dev/null +++ b/Assets/Scripts/RenderingHelpers.cs @@ -0,0 +1,1265 @@ +// +// Radegast Metaverse Client +// Copyright (c) 2009-2013, 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, + } + + /// + /// Base class for all scene objects + /// + public abstract class SceneObject : IComparable, IDisposable + { + #region Public fields + /// Interpolated local position of the object + public Vector3 InterpolatedPosition; + /// Interpolated local rotation of the object/summary> + public Quaternion InterpolatedRotation; + /// Rendered position of the object in the region + public Vector3 RenderPosition; + /// Rendered rotationm of the object in the region + public Quaternion RenderRotation; + /// Per frame calculated square of the distance from camera + public float DistanceSquared; + /// Bounding volume of the object + public BoundingVolume BoundingVolume; + /// Was the sim position and distance from camera calculated during this frame + public bool PositionCalculated; + /// Scene object type + public SceneObjectType Type = SceneObjectType.None; + /// Libomv primitive + public virtual Primitive BasePrim { get; set; } + /// Were initial initialization tasks done + public bool Initialized; + /// Is this object disposed + 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; + + /// + /// Cleanup resources used + /// + public virtual void Dispose() + { + IsDisposed = true; + } + + /// + /// Task performed the fist time object is set for rendering + /// + public virtual void Initialize() + { + RenderPosition = InterpolatedPosition = BasePrim.Position; + RenderRotation = InterpolatedRotation = BasePrim.Rotation; + Initialized = true; + } + + /// + /// Perform per frame tasks + /// + /// Time since the last call (last frame time in seconds) + 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 * Login.Client.Network.CurrentSim.Stats.Dilation; //hack: replace RadegastInstance + 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; + } + } + + /// + /// Render scene object + /// + /// Which pass are we currently in + /// ID used to identify which object was picked + /// Main scene renderer + /// Time it took to render the last frame + public virtual void Render(RenderPass pass, int pickingID, SceneWindow scene, float time) + { + } + + /// + /// Implementation of the IComparable interface + /// used for sorting by distance + /// + /// Object we are comparing to + /// Result of the comparison + 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; //hack + + if (pass == RenderPass.Simple) + { + StartSimpleQuery(); + } + else if (pass == RenderPass.Alpha) + { + StartAlphaQuery(); + } + } + + public void EndQuery(RenderPass pass) + { + //if (!RenderSettings.OcclusionCullingEnabled) return; //hack + + if (pass == RenderPass.Simple) + { + EndSimpleQuery(); + } + else if (pass == RenderPass.Alpha) + { + EndAlphaQuery(); + } + } + + public void StartAlphaQuery() + {//hack + //if (!RenderSettings.OcclusionCullingEnabled) return; + +// if (AlphaQueryID == -1) +// { +// Compat.GenQueries(out AlphaQueryID); +// } +// if (AlphaQueryID > 0) +// { +// Compat.BeginQuery(QueryTarget.SamplesPassed, AlphaQueryID); +// } + } + + public void EndAlphaQuery() + {//hack +// if (!RenderSettings.OcclusionCullingEnabled) return; +// +// if (AlphaQueryID > 0) +// { +// Compat.EndQuery(QueryTarget.SamplesPassed); +// } + } + + public void StartSimpleQuery() + {//hack +// if (!RenderSettings.OcclusionCullingEnabled) return; +// +// if (SimpleQueryID == -1) +// { +// Compat.GenQueries(out SimpleQueryID); +// } +// if (SimpleQueryID > 0) +// { +// Compat.BeginQuery(QueryTarget.SamplesPassed, SimpleQueryID); +// } + } + + public void EndSimpleQuery() + {//hack +// if (!RenderSettings.OcclusionCullingEnabled) return; +// +// if (SimpleQueryID > 0) +// { +// Compat.EndQuery(QueryTarget.SamplesPassed); +// } + } + + public bool Occluded() + {//hack +// 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 = System.IO.Path.Combine(Login.Client.Settings.ASSET_CACHE_DIR, string.Format("{0}.rzi", textureID)); + //string fname = System.IO.Path.Combine(".", string.Format("{0}.rzi", textureID)); + + 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 = System.IO.Path.Combine(Login.Client.Settings.ASSET_CACHE_DIR, string.Format("{0}.rzi", textureID)); + //string fname = System.IO.Path.Combine(".", string.Format("{0}.rzi", textureID)); + + 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(); + } + } + + /// + /// Represents camera object + /// + public class Camera + { + /// + /// Indicates that there was manual camera movement, stop tracking objects + /// + public bool Manual; + Vector3 mPosition; + Vector3 mFocalPoint; + bool mModified; + + /// Camera position + public Vector3 Position + { + get + { + return mPosition; + } + + set + { + if (mPosition != value) + { + mPosition = value; + Modify(); + } + } + } + + /// Camera target + public Vector3 FocalPoint + { + get + { + return mFocalPoint; + } + + set + { + if (mFocalPoint != value) + { + mFocalPoint = value; + Modify(); + } + } + } + + /// Zoom level + public float Zoom; + /// Draw distance + public float Far; + /// Has camera been modified + 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; + } + + /// + /// Sets the world in perspective of the camera + /// + 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 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 + 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 diff --git a/Assets/Scripts/RenderingHelpers.cs.meta b/Assets/Scripts/RenderingHelpers.cs.meta new file mode 100644 index 0000000..2a0f133 --- /dev/null +++ b/Assets/Scripts/RenderingHelpers.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 07e0dbb908f9e9941a7484343bd3b3b1 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Assets/Textures.meta b/Assets/Textures.meta new file mode 100644 index 0000000..3306a1f --- /dev/null +++ b/Assets/Textures.meta @@ -0,0 +1,4 @@ +fileFormatVersion: 2 +guid: af545c64be0618b4086c843e2287aef3 +DefaultImporter: + userData: diff --git a/Assets/Textures/skybox_forest.meta b/Assets/Textures/skybox_forest.meta new file mode 100644 index 0000000..03f1290 --- /dev/null +++ b/Assets/Textures/skybox_forest.meta @@ -0,0 +1,4 @@ +fileFormatVersion: 2 +guid: eccc668e6519a6e428cfcac8c6644358 +DefaultImporter: + userData: diff --git a/Assets/Textures/skybox_forest/forest_sky_back.psd b/Assets/Textures/skybox_forest/forest_sky_back.psd new file mode 100644 index 0000000..aa00d4c Binary files /dev/null and b/Assets/Textures/skybox_forest/forest_sky_back.psd differ diff --git a/Assets/Textures/skybox_forest/forest_sky_back.psd.meta b/Assets/Textures/skybox_forest/forest_sky_back.psd.meta new file mode 100644 index 0000000..2de16ea --- /dev/null +++ b/Assets/Textures/skybox_forest/forest_sky_back.psd.meta @@ -0,0 +1,35 @@ +fileFormatVersion: 2 +guid: bc86e1f68d31db0448a4bff74807e574 +TextureImporter: + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + linearTexture: 0 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + seamlessCubemap: 0 + textureFormat: -1 + maxTextureSize: 512 + textureSettings: + filterMode: 1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + textureType: 0 + buildTargetSettings: [] + userData: diff --git a/Assets/Textures/skybox_forest/forest_sky_bottom.JPG b/Assets/Textures/skybox_forest/forest_sky_bottom.JPG new file mode 100644 index 0000000..bd8397c Binary files /dev/null and b/Assets/Textures/skybox_forest/forest_sky_bottom.JPG differ diff --git a/Assets/Textures/skybox_forest/forest_sky_bottom.JPG.meta b/Assets/Textures/skybox_forest/forest_sky_bottom.JPG.meta new file mode 100644 index 0000000..e73397c --- /dev/null +++ b/Assets/Textures/skybox_forest/forest_sky_bottom.JPG.meta @@ -0,0 +1,35 @@ +fileFormatVersion: 2 +guid: 935cf88c386d9cc4ca156b2a5fd9c992 +TextureImporter: + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + linearTexture: 0 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + seamlessCubemap: 0 + textureFormat: -1 + maxTextureSize: 1024 + textureSettings: + filterMode: 1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + textureType: -1 + buildTargetSettings: [] + userData: diff --git a/Assets/Textures/skybox_forest/forest_sky_front.psd b/Assets/Textures/skybox_forest/forest_sky_front.psd new file mode 100644 index 0000000..4ea0b7e Binary files /dev/null and b/Assets/Textures/skybox_forest/forest_sky_front.psd differ diff --git a/Assets/Textures/skybox_forest/forest_sky_front.psd.meta b/Assets/Textures/skybox_forest/forest_sky_front.psd.meta new file mode 100644 index 0000000..5ba6b0b --- /dev/null +++ b/Assets/Textures/skybox_forest/forest_sky_front.psd.meta @@ -0,0 +1,35 @@ +fileFormatVersion: 2 +guid: c768927e55bffbe4cb64587121132353 +TextureImporter: + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + linearTexture: 0 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + seamlessCubemap: 0 + textureFormat: -1 + maxTextureSize: 512 + textureSettings: + filterMode: 1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + textureType: 0 + buildTargetSettings: [] + userData: diff --git a/Assets/Textures/skybox_forest/forest_sky_left.psd b/Assets/Textures/skybox_forest/forest_sky_left.psd new file mode 100644 index 0000000..86746e1 Binary files /dev/null and b/Assets/Textures/skybox_forest/forest_sky_left.psd differ diff --git a/Assets/Textures/skybox_forest/forest_sky_left.psd.meta b/Assets/Textures/skybox_forest/forest_sky_left.psd.meta new file mode 100644 index 0000000..8b8d5d2 --- /dev/null +++ b/Assets/Textures/skybox_forest/forest_sky_left.psd.meta @@ -0,0 +1,35 @@ +fileFormatVersion: 2 +guid: 709280674f0ca3c4491270f799ff59f5 +TextureImporter: + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + linearTexture: 0 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + seamlessCubemap: 0 + textureFormat: -1 + maxTextureSize: 1024 + textureSettings: + filterMode: 1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + textureType: -1 + buildTargetSettings: [] + userData: diff --git a/Assets/Textures/skybox_forest/forest_sky_right.psd b/Assets/Textures/skybox_forest/forest_sky_right.psd new file mode 100644 index 0000000..72a2773 Binary files /dev/null and b/Assets/Textures/skybox_forest/forest_sky_right.psd differ diff --git a/Assets/Textures/skybox_forest/forest_sky_right.psd.meta b/Assets/Textures/skybox_forest/forest_sky_right.psd.meta new file mode 100644 index 0000000..f1f7dc0 --- /dev/null +++ b/Assets/Textures/skybox_forest/forest_sky_right.psd.meta @@ -0,0 +1,35 @@ +fileFormatVersion: 2 +guid: dc88bc4dfec18ec429107ce352ce5144 +TextureImporter: + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + linearTexture: 0 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + seamlessCubemap: 0 + textureFormat: -1 + maxTextureSize: 1024 + textureSettings: + filterMode: 1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + textureType: -1 + buildTargetSettings: [] + userData: diff --git a/Assets/Textures/skybox_forest/forest_sky_top.psd b/Assets/Textures/skybox_forest/forest_sky_top.psd new file mode 100644 index 0000000..e63aa82 Binary files /dev/null and b/Assets/Textures/skybox_forest/forest_sky_top.psd differ diff --git a/Assets/Textures/skybox_forest/forest_sky_top.psd.meta b/Assets/Textures/skybox_forest/forest_sky_top.psd.meta new file mode 100644 index 0000000..5133b5d --- /dev/null +++ b/Assets/Textures/skybox_forest/forest_sky_top.psd.meta @@ -0,0 +1,35 @@ +fileFormatVersion: 2 +guid: 18b19e7d34063ae4dbef0c911511e1fc +TextureImporter: + serializedVersion: 2 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + linearTexture: 0 + correctGamma: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: .25 + normalMapFilter: 0 + isReadable: 0 + grayScaleToAlpha: 0 + generateCubemap: 0 + seamlessCubemap: 0 + textureFormat: -1 + maxTextureSize: 1024 + textureSettings: + filterMode: 1 + aniso: 1 + mipBias: -1 + wrapMode: 1 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + textureType: -1 + buildTargetSettings: [] + userData: diff --git a/Assets/dlls/OpenTK.GLControl.dll b/Assets/dlls/OpenTK.GLControl.dll new file mode 100644 index 0000000..c74b6e8 Binary files /dev/null and b/Assets/dlls/OpenTK.GLControl.dll differ diff --git a/Assets/dlls/OpenTK.GLControl.dll.meta b/Assets/dlls/OpenTK.GLControl.dll.meta new file mode 100644 index 0000000..a72f5c8 --- /dev/null +++ b/Assets/dlls/OpenTK.GLControl.dll.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 48ebc8f37c0643742a40533aa83e3ba9 +MonoAssemblyImporter: + serializedVersion: 1 + iconMap: {} + executionOrder: {} + userData: diff --git a/Assets/dlls/OpenTK.dll b/Assets/dlls/OpenTK.dll new file mode 100644 index 0000000..68b396c Binary files /dev/null and b/Assets/dlls/OpenTK.dll differ diff --git a/Assets/dlls/OpenTK.dll.meta b/Assets/dlls/OpenTK.dll.meta new file mode 100644 index 0000000..9de08e2 --- /dev/null +++ b/Assets/dlls/OpenTK.dll.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 03f10af4406b42a47a96951cb7129f03 +MonoAssemblyImporter: + serializedVersion: 1 + iconMap: {} + executionOrder: {} + userData: diff --git a/ProjectSettings/InputManager.asset b/ProjectSettings/InputManager.asset index f666ba8..2710438 100644 Binary files a/ProjectSettings/InputManager.asset and b/ProjectSettings/InputManager.asset differ diff --git a/character/attentions.xml b/character/attentions.xml new file mode 100644 index 0000000..1b14c0c --- /dev/null +++ b/character/attentions.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/character/attentionsN.xml b/character/attentionsN.xml new file mode 100644 index 0000000..7c02415 --- /dev/null +++ b/character/attentionsN.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/character/avatar_eye.llm b/character/avatar_eye.llm new file mode 100644 index 0000000..8c6e74e Binary files /dev/null and b/character/avatar_eye.llm differ diff --git a/character/avatar_eye_1.llm b/character/avatar_eye_1.llm new file mode 100644 index 0000000..7a3b0d6 Binary files /dev/null and b/character/avatar_eye_1.llm differ diff --git a/character/avatar_eyelashes.llm b/character/avatar_eyelashes.llm new file mode 100644 index 0000000..99995b5 Binary files /dev/null and b/character/avatar_eyelashes.llm differ diff --git a/character/avatar_hair.llm b/character/avatar_hair.llm new file mode 100644 index 0000000..df99de8 Binary files /dev/null and b/character/avatar_hair.llm differ diff --git a/character/avatar_hair_1.llm b/character/avatar_hair_1.llm new file mode 100644 index 0000000..6de31fd Binary files /dev/null and b/character/avatar_hair_1.llm differ diff --git a/character/avatar_hair_2.llm b/character/avatar_hair_2.llm new file mode 100644 index 0000000..47d02ba Binary files /dev/null and b/character/avatar_hair_2.llm differ diff --git a/character/avatar_hair_3.llm b/character/avatar_hair_3.llm new file mode 100644 index 0000000..107f9e2 Binary files /dev/null and b/character/avatar_hair_3.llm differ diff --git a/character/avatar_hair_4.llm b/character/avatar_hair_4.llm new file mode 100644 index 0000000..1b9a12a Binary files /dev/null and b/character/avatar_hair_4.llm differ diff --git a/character/avatar_hair_5.llm b/character/avatar_hair_5.llm new file mode 100644 index 0000000..1b9a12a Binary files /dev/null and b/character/avatar_hair_5.llm differ diff --git a/character/avatar_head.llm b/character/avatar_head.llm new file mode 100644 index 0000000..8d8b5e0 Binary files /dev/null and b/character/avatar_head.llm differ diff --git a/character/avatar_head_1.llm b/character/avatar_head_1.llm new file mode 100644 index 0000000..26291e6 Binary files /dev/null and b/character/avatar_head_1.llm differ diff --git a/character/avatar_head_2.llm b/character/avatar_head_2.llm new file mode 100644 index 0000000..c2b808b Binary files /dev/null and b/character/avatar_head_2.llm differ diff --git a/character/avatar_head_3.llm b/character/avatar_head_3.llm new file mode 100644 index 0000000..a0676b1 Binary files /dev/null and b/character/avatar_head_3.llm differ diff --git a/character/avatar_head_4.llm b/character/avatar_head_4.llm new file mode 100644 index 0000000..5035585 Binary files /dev/null and b/character/avatar_head_4.llm differ diff --git a/character/avatar_lad.xml b/character/avatar_lad.xml new file mode 100644 index 0000000..c7d3323 --- /dev/null +++ b/character/avatar_lad.xml @@ -0,0 +1,11687 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/character/avatar_lower_body.llm b/character/avatar_lower_body.llm new file mode 100644 index 0000000..0420899 Binary files /dev/null and b/character/avatar_lower_body.llm differ diff --git a/character/avatar_lower_body_1.llm b/character/avatar_lower_body_1.llm new file mode 100644 index 0000000..1394eb8 Binary files /dev/null and b/character/avatar_lower_body_1.llm differ diff --git a/character/avatar_lower_body_2.llm b/character/avatar_lower_body_2.llm new file mode 100644 index 0000000..0da9c12 Binary files /dev/null and b/character/avatar_lower_body_2.llm differ diff --git a/character/avatar_lower_body_3.llm b/character/avatar_lower_body_3.llm new file mode 100644 index 0000000..f3c49a1 Binary files /dev/null and b/character/avatar_lower_body_3.llm differ diff --git a/character/avatar_lower_body_4.llm b/character/avatar_lower_body_4.llm new file mode 100644 index 0000000..e717210 Binary files /dev/null and b/character/avatar_lower_body_4.llm differ diff --git a/character/avatar_skeleton.xml b/character/avatar_skeleton.xml new file mode 100644 index 0000000..2c37095 --- /dev/null +++ b/character/avatar_skeleton.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/character/avatar_skirt.llm b/character/avatar_skirt.llm new file mode 100644 index 0000000..08ce3d1 Binary files /dev/null and b/character/avatar_skirt.llm differ diff --git a/character/avatar_skirt_1.llm b/character/avatar_skirt_1.llm new file mode 100644 index 0000000..88076c3 Binary files /dev/null and b/character/avatar_skirt_1.llm differ diff --git a/character/avatar_skirt_2.llm b/character/avatar_skirt_2.llm new file mode 100644 index 0000000..73b3eff Binary files /dev/null and b/character/avatar_skirt_2.llm differ diff --git a/character/avatar_skirt_3.llm b/character/avatar_skirt_3.llm new file mode 100644 index 0000000..ded546f Binary files /dev/null and b/character/avatar_skirt_3.llm differ diff --git a/character/avatar_skirt_4.llm b/character/avatar_skirt_4.llm new file mode 100644 index 0000000..b9d5cb9 Binary files /dev/null and b/character/avatar_skirt_4.llm differ diff --git a/character/avatar_upper_body.llm b/character/avatar_upper_body.llm new file mode 100644 index 0000000..da7d990 Binary files /dev/null and b/character/avatar_upper_body.llm differ diff --git a/character/avatar_upper_body_1.llm b/character/avatar_upper_body_1.llm new file mode 100644 index 0000000..31e104c Binary files /dev/null and b/character/avatar_upper_body_1.llm differ diff --git a/character/avatar_upper_body_2.llm b/character/avatar_upper_body_2.llm new file mode 100644 index 0000000..c1f4199 Binary files /dev/null and b/character/avatar_upper_body_2.llm differ diff --git a/character/avatar_upper_body_3.llm b/character/avatar_upper_body_3.llm new file mode 100644 index 0000000..9e89ed8 Binary files /dev/null and b/character/avatar_upper_body_3.llm differ diff --git a/character/avatar_upper_body_4.llm b/character/avatar_upper_body_4.llm new file mode 100644 index 0000000..ec836d1 Binary files /dev/null and b/character/avatar_upper_body_4.llm differ diff --git a/character/genepool.xml b/character/genepool.xml new file mode 100644 index 0000000..e88dc56 --- /dev/null +++ b/character/genepool.xml @@ -0,0 +1,7107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/openmetaverse_data/cache/04b85e9d-7039-42c4-8f9b-be3c8b043f8e b/openmetaverse_data/cache/04b85e9d-7039-42c4-8f9b-be3c8b043f8e new file mode 100644 index 0000000..a21385c Binary files /dev/null and b/openmetaverse_data/cache/04b85e9d-7039-42c4-8f9b-be3c8b043f8e differ diff --git a/openmetaverse_data/cache/04b9edb9-9341-4205-ad1e-371cf0de1d5b b/openmetaverse_data/cache/04b9edb9-9341-4205-ad1e-371cf0de1d5b new file mode 100644 index 0000000..816607b Binary files /dev/null and b/openmetaverse_data/cache/04b9edb9-9341-4205-ad1e-371cf0de1d5b differ diff --git a/openmetaverse_data/cache/05aa815f-a6ce-4682-b734-8211c9b6ce8c b/openmetaverse_data/cache/05aa815f-a6ce-4682-b734-8211c9b6ce8c new file mode 100644 index 0000000..858a596 Binary files /dev/null and b/openmetaverse_data/cache/05aa815f-a6ce-4682-b734-8211c9b6ce8c differ diff --git a/openmetaverse_data/cache/05b5e3ba-caf5-4e79-b108-6ce7a2a30dde b/openmetaverse_data/cache/05b5e3ba-caf5-4e79-b108-6ce7a2a30dde new file mode 100644 index 0000000..858a596 Binary files /dev/null and b/openmetaverse_data/cache/05b5e3ba-caf5-4e79-b108-6ce7a2a30dde differ diff --git a/openmetaverse_data/cache/0714fdbe-1dba-44dc-bd4f-254701b8218b b/openmetaverse_data/cache/0714fdbe-1dba-44dc-bd4f-254701b8218b new file mode 100644 index 0000000..a21385c Binary files /dev/null and b/openmetaverse_data/cache/0714fdbe-1dba-44dc-bd4f-254701b8218b differ diff --git a/openmetaverse_data/cache/11ba9988-376e-4609-981a-7d29a21cb6e7 b/openmetaverse_data/cache/11ba9988-376e-4609-981a-7d29a21cb6e7 new file mode 100644 index 0000000..b9cf02c Binary files /dev/null and b/openmetaverse_data/cache/11ba9988-376e-4609-981a-7d29a21cb6e7 differ diff --git a/openmetaverse_data/cache/12c9a5f3-ce82-4533-9742-3b928424d556 b/openmetaverse_data/cache/12c9a5f3-ce82-4533-9742-3b928424d556 new file mode 100644 index 0000000..7409e77 Binary files /dev/null and b/openmetaverse_data/cache/12c9a5f3-ce82-4533-9742-3b928424d556 differ diff --git a/openmetaverse_data/cache/1847ea44-4345-4a8f-b88e-32f39d253437 b/openmetaverse_data/cache/1847ea44-4345-4a8f-b88e-32f39d253437 new file mode 100644 index 0000000..816607b Binary files /dev/null and b/openmetaverse_data/cache/1847ea44-4345-4a8f-b88e-32f39d253437 differ diff --git a/openmetaverse_data/cache/1a5aa75c-ded8-4088-b4c7-e4ee87db0263 b/openmetaverse_data/cache/1a5aa75c-ded8-4088-b4c7-e4ee87db0263 new file mode 100644 index 0000000..816607b Binary files /dev/null and b/openmetaverse_data/cache/1a5aa75c-ded8-4088-b4c7-e4ee87db0263 differ diff --git a/openmetaverse_data/cache/1d281b84-10ef-4cb6-8cac-5b977398e7ed b/openmetaverse_data/cache/1d281b84-10ef-4cb6-8cac-5b977398e7ed new file mode 100644 index 0000000..c16e79a Binary files /dev/null and b/openmetaverse_data/cache/1d281b84-10ef-4cb6-8cac-5b977398e7ed differ diff --git a/openmetaverse_data/cache/1e67a9b5-516a-49c2-b0c7-43f1aa19b572 b/openmetaverse_data/cache/1e67a9b5-516a-49c2-b0c7-43f1aa19b572 new file mode 100644 index 0000000..50a2737 Binary files /dev/null and b/openmetaverse_data/cache/1e67a9b5-516a-49c2-b0c7-43f1aa19b572 differ diff --git a/openmetaverse_data/cache/22e12b70-fb63-4959-acdc-8fdd3f93f1d7 b/openmetaverse_data/cache/22e12b70-fb63-4959-acdc-8fdd3f93f1d7 new file mode 100644 index 0000000..6df9e9a Binary files /dev/null and b/openmetaverse_data/cache/22e12b70-fb63-4959-acdc-8fdd3f93f1d7 differ diff --git a/openmetaverse_data/cache/290d94b7-d81e-431d-8525-890e5bf99a26 b/openmetaverse_data/cache/290d94b7-d81e-431d-8525-890e5bf99a26 new file mode 100644 index 0000000..b9cf02c Binary files /dev/null and b/openmetaverse_data/cache/290d94b7-d81e-431d-8525-890e5bf99a26 differ diff --git a/openmetaverse_data/cache/2b0b6279-48cc-4437-a999-d04e502cd34e b/openmetaverse_data/cache/2b0b6279-48cc-4437-a999-d04e502cd34e new file mode 100644 index 0000000..6df9e9a Binary files /dev/null and b/openmetaverse_data/cache/2b0b6279-48cc-4437-a999-d04e502cd34e differ diff --git a/openmetaverse_data/cache/2cb92f23-27eb-436e-ab78-5f3ae6808e8a b/openmetaverse_data/cache/2cb92f23-27eb-436e-ab78-5f3ae6808e8a new file mode 100644 index 0000000..7409e77 Binary files /dev/null and b/openmetaverse_data/cache/2cb92f23-27eb-436e-ab78-5f3ae6808e8a differ diff --git a/openmetaverse_data/cache/2fb52a5b-928b-4709-9e82-8aac1c44faec b/openmetaverse_data/cache/2fb52a5b-928b-4709-9e82-8aac1c44faec new file mode 100644 index 0000000..a21385c Binary files /dev/null and b/openmetaverse_data/cache/2fb52a5b-928b-4709-9e82-8aac1c44faec differ diff --git a/openmetaverse_data/cache/31f9f824-33ce-4f4b-89ed-055cb66983f4 b/openmetaverse_data/cache/31f9f824-33ce-4f4b-89ed-055cb66983f4 new file mode 100644 index 0000000..a21385c Binary files /dev/null and b/openmetaverse_data/cache/31f9f824-33ce-4f4b-89ed-055cb66983f4 differ diff --git a/openmetaverse_data/cache/332e1265-ab28-444b-a9c8-af5150aa89ae b/openmetaverse_data/cache/332e1265-ab28-444b-a9c8-af5150aa89ae new file mode 100644 index 0000000..7409e77 Binary files /dev/null and b/openmetaverse_data/cache/332e1265-ab28-444b-a9c8-af5150aa89ae differ diff --git a/openmetaverse_data/cache/3a47d9e0-b1ca-44d9-9263-abee5c0f53b4 b/openmetaverse_data/cache/3a47d9e0-b1ca-44d9-9263-abee5c0f53b4 new file mode 100644 index 0000000..816607b Binary files /dev/null and b/openmetaverse_data/cache/3a47d9e0-b1ca-44d9-9263-abee5c0f53b4 differ diff --git a/openmetaverse_data/cache/3ab5d9e6-dff8-44ff-a5fb-84b69b808cd8 b/openmetaverse_data/cache/3ab5d9e6-dff8-44ff-a5fb-84b69b808cd8 new file mode 100644 index 0000000..e5d7f62 Binary files /dev/null and b/openmetaverse_data/cache/3ab5d9e6-dff8-44ff-a5fb-84b69b808cd8 differ diff --git a/openmetaverse_data/cache/3c7d4d6d-d4c2-4e32-9fe6-e8b3d3333813 b/openmetaverse_data/cache/3c7d4d6d-d4c2-4e32-9fe6-e8b3d3333813 new file mode 100644 index 0000000..50a2737 Binary files /dev/null and b/openmetaverse_data/cache/3c7d4d6d-d4c2-4e32-9fe6-e8b3d3333813 differ diff --git a/openmetaverse_data/cache/48833cd8-96db-4057-931c-5483d287c3a0 b/openmetaverse_data/cache/48833cd8-96db-4057-931c-5483d287c3a0 new file mode 100644 index 0000000..6df9e9a Binary files /dev/null and b/openmetaverse_data/cache/48833cd8-96db-4057-931c-5483d287c3a0 differ diff --git a/openmetaverse_data/cache/4c656873-4ba6-4962-bd96-99369f3ae377 b/openmetaverse_data/cache/4c656873-4ba6-4962-bd96-99369f3ae377 new file mode 100644 index 0000000..a21385c Binary files /dev/null and b/openmetaverse_data/cache/4c656873-4ba6-4962-bd96-99369f3ae377 differ diff --git a/openmetaverse_data/cache/4ec142ab-13a7-4eeb-ad70-12a87fcad6cc b/openmetaverse_data/cache/4ec142ab-13a7-4eeb-ad70-12a87fcad6cc new file mode 100644 index 0000000..e5d7f62 Binary files /dev/null and b/openmetaverse_data/cache/4ec142ab-13a7-4eeb-ad70-12a87fcad6cc differ diff --git a/openmetaverse_data/cache/53bf35d1-d587-4d56-968d-3c7118b07219 b/openmetaverse_data/cache/53bf35d1-d587-4d56-968d-3c7118b07219 new file mode 100644 index 0000000..c16e79a Binary files /dev/null and b/openmetaverse_data/cache/53bf35d1-d587-4d56-968d-3c7118b07219 differ diff --git a/openmetaverse_data/cache/628953a7-62e5-430c-9649-260767df5cb8 b/openmetaverse_data/cache/628953a7-62e5-430c-9649-260767df5cb8 new file mode 100644 index 0000000..a21385c Binary files /dev/null and b/openmetaverse_data/cache/628953a7-62e5-430c-9649-260767df5cb8 differ diff --git a/openmetaverse_data/cache/6433b7b5-1aea-45e5-bcb0-0ae2dd3403f0 b/openmetaverse_data/cache/6433b7b5-1aea-45e5-bcb0-0ae2dd3403f0 new file mode 100644 index 0000000..c16e79a Binary files /dev/null and b/openmetaverse_data/cache/6433b7b5-1aea-45e5-bcb0-0ae2dd3403f0 differ diff --git a/openmetaverse_data/cache/6579d33b-2d42-4927-b611-38c024fc59ac b/openmetaverse_data/cache/6579d33b-2d42-4927-b611-38c024fc59ac new file mode 100644 index 0000000..a21385c Binary files /dev/null and b/openmetaverse_data/cache/6579d33b-2d42-4927-b611-38c024fc59ac differ diff --git a/openmetaverse_data/cache/6767a2e9-03e3-4e56-892d-4be1ea489a5f b/openmetaverse_data/cache/6767a2e9-03e3-4e56-892d-4be1ea489a5f new file mode 100644 index 0000000..a21385c Binary files /dev/null and b/openmetaverse_data/cache/6767a2e9-03e3-4e56-892d-4be1ea489a5f differ diff --git a/openmetaverse_data/cache/6cdb18c0-856e-496c-967b-3476edceab8f b/openmetaverse_data/cache/6cdb18c0-856e-496c-967b-3476edceab8f new file mode 100644 index 0000000..6df9e9a Binary files /dev/null and b/openmetaverse_data/cache/6cdb18c0-856e-496c-967b-3476edceab8f differ diff --git a/openmetaverse_data/cache/74192e73-e947-467a-b4ad-8fe2e857106e b/openmetaverse_data/cache/74192e73-e947-467a-b4ad-8fe2e857106e new file mode 100644 index 0000000..be8a744 Binary files /dev/null and b/openmetaverse_data/cache/74192e73-e947-467a-b4ad-8fe2e857106e differ diff --git a/openmetaverse_data/cache/78eacc88-d466-4db7-8e03-81a5ac8d20ea b/openmetaverse_data/cache/78eacc88-d466-4db7-8e03-81a5ac8d20ea new file mode 100644 index 0000000..6df9e9a Binary files /dev/null and b/openmetaverse_data/cache/78eacc88-d466-4db7-8e03-81a5ac8d20ea differ diff --git a/openmetaverse_data/cache/7f42ba4a-340b-4b13-a8e0-22ab5aa94645 b/openmetaverse_data/cache/7f42ba4a-340b-4b13-a8e0-22ab5aa94645 new file mode 100644 index 0000000..764bbbc Binary files /dev/null and b/openmetaverse_data/cache/7f42ba4a-340b-4b13-a8e0-22ab5aa94645 differ diff --git a/openmetaverse_data/cache/80f838a8-d60f-49ef-81e1-7e78d4f02a4f b/openmetaverse_data/cache/80f838a8-d60f-49ef-81e1-7e78d4f02a4f new file mode 100644 index 0000000..b9cf02c Binary files /dev/null and b/openmetaverse_data/cache/80f838a8-d60f-49ef-81e1-7e78d4f02a4f differ diff --git a/openmetaverse_data/cache/82fcfa3b-3974-4d34-a938-9adfa2ecbc7a b/openmetaverse_data/cache/82fcfa3b-3974-4d34-a938-9adfa2ecbc7a new file mode 100644 index 0000000..a21385c Binary files /dev/null and b/openmetaverse_data/cache/82fcfa3b-3974-4d34-a938-9adfa2ecbc7a differ diff --git a/openmetaverse_data/cache/87451b24-8581-4a9c-863c-a5ce0e6c0489 b/openmetaverse_data/cache/87451b24-8581-4a9c-863c-a5ce0e6c0489 new file mode 100644 index 0000000..816607b Binary files /dev/null and b/openmetaverse_data/cache/87451b24-8581-4a9c-863c-a5ce0e6c0489 differ diff --git a/openmetaverse_data/cache/89556747-24cb-43ed-920b-47caed15465f b/openmetaverse_data/cache/89556747-24cb-43ed-920b-47caed15465f new file mode 100644 index 0000000..2053675 Binary files /dev/null and b/openmetaverse_data/cache/89556747-24cb-43ed-920b-47caed15465f differ diff --git a/openmetaverse_data/cache/89f7af70-76b4-4d93-a6f4-56064518b45c b/openmetaverse_data/cache/89f7af70-76b4-4d93-a6f4-56064518b45c new file mode 100644 index 0000000..764bbbc Binary files /dev/null and b/openmetaverse_data/cache/89f7af70-76b4-4d93-a6f4-56064518b45c differ diff --git a/openmetaverse_data/cache/8b30ead1-a791-4028-9d34-a6fdef23c383 b/openmetaverse_data/cache/8b30ead1-a791-4028-9d34-a6fdef23c383 new file mode 100644 index 0000000..be8a744 Binary files /dev/null and b/openmetaverse_data/cache/8b30ead1-a791-4028-9d34-a6fdef23c383 differ diff --git a/openmetaverse_data/cache/8f05459c-18c8-422e-ad8f-ae4e349cd978 b/openmetaverse_data/cache/8f05459c-18c8-422e-ad8f-ae4e349cd978 new file mode 100644 index 0000000..a21385c Binary files /dev/null and b/openmetaverse_data/cache/8f05459c-18c8-422e-ad8f-ae4e349cd978 differ diff --git a/openmetaverse_data/cache/91ad0389-4a0e-43ce-9747-5713e0e6173a b/openmetaverse_data/cache/91ad0389-4a0e-43ce-9747-5713e0e6173a new file mode 100644 index 0000000..b9cf02c Binary files /dev/null and b/openmetaverse_data/cache/91ad0389-4a0e-43ce-9747-5713e0e6173a differ diff --git a/openmetaverse_data/cache/9297e6aa-db72-4661-b259-0632959ab0a4 b/openmetaverse_data/cache/9297e6aa-db72-4661-b259-0632959ab0a4 new file mode 100644 index 0000000..e5d7f62 Binary files /dev/null and b/openmetaverse_data/cache/9297e6aa-db72-4661-b259-0632959ab0a4 differ diff --git a/openmetaverse_data/cache/962749d8-675a-47ff-89fe-8ad107f0ba27 b/openmetaverse_data/cache/962749d8-675a-47ff-89fe-8ad107f0ba27 new file mode 100644 index 0000000..c16e79a Binary files /dev/null and b/openmetaverse_data/cache/962749d8-675a-47ff-89fe-8ad107f0ba27 differ diff --git a/openmetaverse_data/cache/9881ca43-aa0a-41a5-9fbe-3eb16c9ce888 b/openmetaverse_data/cache/9881ca43-aa0a-41a5-9fbe-3eb16c9ce888 new file mode 100644 index 0000000..b9cf02c Binary files /dev/null and b/openmetaverse_data/cache/9881ca43-aa0a-41a5-9fbe-3eb16c9ce888 differ diff --git a/openmetaverse_data/cache/991fe249-beba-4511-8e3f-1bb2db6fd1e9 b/openmetaverse_data/cache/991fe249-beba-4511-8e3f-1bb2db6fd1e9 new file mode 100644 index 0000000..6df9e9a Binary files /dev/null and b/openmetaverse_data/cache/991fe249-beba-4511-8e3f-1bb2db6fd1e9 differ diff --git a/openmetaverse_data/cache/9cb47536-a9c9-4d6a-abce-b8e3ec56cbe1 b/openmetaverse_data/cache/9cb47536-a9c9-4d6a-abce-b8e3ec56cbe1 new file mode 100644 index 0000000..a21385c Binary files /dev/null and b/openmetaverse_data/cache/9cb47536-a9c9-4d6a-abce-b8e3ec56cbe1 differ diff --git a/openmetaverse_data/cache/9d07596f-c36c-4bc7-a4d6-deaad0a1fda6 b/openmetaverse_data/cache/9d07596f-c36c-4bc7-a4d6-deaad0a1fda6 new file mode 100644 index 0000000..6df9e9a Binary files /dev/null and b/openmetaverse_data/cache/9d07596f-c36c-4bc7-a4d6-deaad0a1fda6 differ diff --git a/openmetaverse_data/cache/9fd546dc-76e9-4f35-aacd-cc8c097ff074 b/openmetaverse_data/cache/9fd546dc-76e9-4f35-aacd-cc8c097ff074 new file mode 100644 index 0000000..81857d6 Binary files /dev/null and b/openmetaverse_data/cache/9fd546dc-76e9-4f35-aacd-cc8c097ff074 differ diff --git a/openmetaverse_data/cache/9ff37e48-8cea-44c6-b43e-4578d9b582ec b/openmetaverse_data/cache/9ff37e48-8cea-44c6-b43e-4578d9b582ec new file mode 100644 index 0000000..81857d6 Binary files /dev/null and b/openmetaverse_data/cache/9ff37e48-8cea-44c6-b43e-4578d9b582ec differ diff --git a/openmetaverse_data/cache/a5f67c65-c057-40dc-9ace-c76c1e2c2462 b/openmetaverse_data/cache/a5f67c65-c057-40dc-9ace-c76c1e2c2462 new file mode 100644 index 0000000..50a2737 Binary files /dev/null and b/openmetaverse_data/cache/a5f67c65-c057-40dc-9ace-c76c1e2c2462 differ diff --git a/openmetaverse_data/cache/a6f4006b-07bc-44d5-8fe8-4b7c77503a6e b/openmetaverse_data/cache/a6f4006b-07bc-44d5-8fe8-4b7c77503a6e new file mode 100644 index 0000000..c16e79a Binary files /dev/null and b/openmetaverse_data/cache/a6f4006b-07bc-44d5-8fe8-4b7c77503a6e differ diff --git a/openmetaverse_data/cache/addfbac8-3825-4821-8ee9-34a6451e578c b/openmetaverse_data/cache/addfbac8-3825-4821-8ee9-34a6451e578c new file mode 100644 index 0000000..6df9e9a Binary files /dev/null and b/openmetaverse_data/cache/addfbac8-3825-4821-8ee9-34a6451e578c differ diff --git a/openmetaverse_data/cache/b2d57292-9d87-4df7-be7e-b400fca43e8f b/openmetaverse_data/cache/b2d57292-9d87-4df7-be7e-b400fca43e8f new file mode 100644 index 0000000..b9cf02c Binary files /dev/null and b/openmetaverse_data/cache/b2d57292-9d87-4df7-be7e-b400fca43e8f differ diff --git a/openmetaverse_data/cache/b3ecba3a-6bd0-4173-ae94-477cd6d80e5d b/openmetaverse_data/cache/b3ecba3a-6bd0-4173-ae94-477cd6d80e5d new file mode 100644 index 0000000..b9cf02c Binary files /dev/null and b/openmetaverse_data/cache/b3ecba3a-6bd0-4173-ae94-477cd6d80e5d differ diff --git a/openmetaverse_data/cache/b6c078d5-a135-49a2-a462-1a793b1da398 b/openmetaverse_data/cache/b6c078d5-a135-49a2-a462-1a793b1da398 new file mode 100644 index 0000000..b9cf02c Binary files /dev/null and b/openmetaverse_data/cache/b6c078d5-a135-49a2-a462-1a793b1da398 differ diff --git a/openmetaverse_data/cache/b748af88-58e2-995b-cf26-9486dea8e830 b/openmetaverse_data/cache/b748af88-58e2-995b-cf26-9486dea8e830 new file mode 100644 index 0000000..69ee4f1 Binary files /dev/null and b/openmetaverse_data/cache/b748af88-58e2-995b-cf26-9486dea8e830 differ diff --git a/openmetaverse_data/cache/ba8f6148-2914-46a6-9f28-de41d5d34d3b b/openmetaverse_data/cache/ba8f6148-2914-46a6-9f28-de41d5d34d3b new file mode 100644 index 0000000..764bbbc Binary files /dev/null and b/openmetaverse_data/cache/ba8f6148-2914-46a6-9f28-de41d5d34d3b differ diff --git a/openmetaverse_data/cache/c0188c97-d79e-4764-93f1-9dc3b8faed6b b/openmetaverse_data/cache/c0188c97-d79e-4764-93f1-9dc3b8faed6b new file mode 100644 index 0000000..be8a744 Binary files /dev/null and b/openmetaverse_data/cache/c0188c97-d79e-4764-93f1-9dc3b8faed6b differ diff --git a/openmetaverse_data/cache/c228d1cf-4b5d-4ba8-84f4-899a0796aa97 b/openmetaverse_data/cache/c228d1cf-4b5d-4ba8-84f4-899a0796aa97 new file mode 100644 index 0000000..116b860 Binary files /dev/null and b/openmetaverse_data/cache/c228d1cf-4b5d-4ba8-84f4-899a0796aa97 differ diff --git a/openmetaverse_data/cache/c37b4e05-0261-4f1b-bf43-9df1cb6b79ff b/openmetaverse_data/cache/c37b4e05-0261-4f1b-bf43-9df1cb6b79ff new file mode 100644 index 0000000..81857d6 Binary files /dev/null and b/openmetaverse_data/cache/c37b4e05-0261-4f1b-bf43-9df1cb6b79ff differ diff --git a/openmetaverse_data/cache/cbedcfbc-d570-4cd9-a8e0-de0578a67cfd b/openmetaverse_data/cache/cbedcfbc-d570-4cd9-a8e0-de0578a67cfd new file mode 100644 index 0000000..858a596 Binary files /dev/null and b/openmetaverse_data/cache/cbedcfbc-d570-4cd9-a8e0-de0578a67cfd differ diff --git a/openmetaverse_data/cache/cd5f8a01-3d44-4928-a908-66b42e4a5723 b/openmetaverse_data/cache/cd5f8a01-3d44-4928-a908-66b42e4a5723 new file mode 100644 index 0000000..816607b Binary files /dev/null and b/openmetaverse_data/cache/cd5f8a01-3d44-4928-a908-66b42e4a5723 differ diff --git a/openmetaverse_data/cache/d938ffc9-9c14-4c85-9304-abdd4cb7c7a7 b/openmetaverse_data/cache/d938ffc9-9c14-4c85-9304-abdd4cb7c7a7 new file mode 100644 index 0000000..c16e79a Binary files /dev/null and b/openmetaverse_data/cache/d938ffc9-9c14-4c85-9304-abdd4cb7c7a7 differ diff --git a/openmetaverse_data/cache/dbeae443-e975-4830-bbd8-baa5f9ebbef7 b/openmetaverse_data/cache/dbeae443-e975-4830-bbd8-baa5f9ebbef7 new file mode 100644 index 0000000..a21385c Binary files /dev/null and b/openmetaverse_data/cache/dbeae443-e975-4830-bbd8-baa5f9ebbef7 differ diff --git a/openmetaverse_data/cache/dcd877ef-5bd8-4d23-b19f-e8121d31da67 b/openmetaverse_data/cache/dcd877ef-5bd8-4d23-b19f-e8121d31da67 new file mode 100644 index 0000000..816607b Binary files /dev/null and b/openmetaverse_data/cache/dcd877ef-5bd8-4d23-b19f-e8121d31da67 differ diff --git a/openmetaverse_data/cache/e2eb95fb-771d-42fc-a0a8-7773c5503056 b/openmetaverse_data/cache/e2eb95fb-771d-42fc-a0a8-7773c5503056 new file mode 100644 index 0000000..6df9e9a Binary files /dev/null and b/openmetaverse_data/cache/e2eb95fb-771d-42fc-a0a8-7773c5503056 differ diff --git a/openmetaverse_data/cache/e5276987-5843-4374-9554-25dd44263384 b/openmetaverse_data/cache/e5276987-5843-4374-9554-25dd44263384 new file mode 100644 index 0000000..c16e79a Binary files /dev/null and b/openmetaverse_data/cache/e5276987-5843-4374-9554-25dd44263384 differ diff --git a/openmetaverse_data/cache/ec1e9fdd-0cf2-4202-9a06-1b184b3c0f26 b/openmetaverse_data/cache/ec1e9fdd-0cf2-4202-9a06-1b184b3c0f26 new file mode 100644 index 0000000..816607b Binary files /dev/null and b/openmetaverse_data/cache/ec1e9fdd-0cf2-4202-9a06-1b184b3c0f26 differ diff --git a/openmetaverse_data/cache/ed4dcd91-0957-4475-b65e-5e4e3a322c00 b/openmetaverse_data/cache/ed4dcd91-0957-4475-b65e-5e4e3a322c00 new file mode 100644 index 0000000..6df9e9a Binary files /dev/null and b/openmetaverse_data/cache/ed4dcd91-0957-4475-b65e-5e4e3a322c00 differ diff --git a/openmetaverse_data/cache/f14dded4-c757-441e-ba76-4a9117fe9b28 b/openmetaverse_data/cache/f14dded4-c757-441e-ba76-4a9117fe9b28 new file mode 100644 index 0000000..6df9e9a Binary files /dev/null and b/openmetaverse_data/cache/f14dded4-c757-441e-ba76-4a9117fe9b28 differ diff --git a/openmetaverse_data/cache/f1faabf1-0468-46e2-a909-162feddee6a8 b/openmetaverse_data/cache/f1faabf1-0468-46e2-a909-162feddee6a8 new file mode 100644 index 0000000..c16e79a Binary files /dev/null and b/openmetaverse_data/cache/f1faabf1-0468-46e2-a909-162feddee6a8 differ diff --git a/openmetaverse_data/cache/fc5fd7e5-d87b-40ec-80af-d7d8c2138fb8 b/openmetaverse_data/cache/fc5fd7e5-d87b-40ec-80af-d7d8c2138fb8 new file mode 100644 index 0000000..816607b Binary files /dev/null and b/openmetaverse_data/cache/fc5fd7e5-d87b-40ec-80af-d7d8c2138fb8 differ diff --git a/openmetaverse_data/cache/fc62f92a-d27b-4ad0-8252-3e8ce6ec1e6b b/openmetaverse_data/cache/fc62f92a-d27b-4ad0-8252-3e8ce6ec1e6b new file mode 100644 index 0000000..c16e79a Binary files /dev/null and b/openmetaverse_data/cache/fc62f92a-d27b-4ad0-8252-3e8ce6ec1e6b differ diff --git a/openmetaverse_data/cache/fcbee9d0-c2d2-4bf3-bff3-d3ca474b3529 b/openmetaverse_data/cache/fcbee9d0-c2d2-4bf3-bff3-d3ca474b3529 new file mode 100644 index 0000000..6df9e9a Binary files /dev/null and b/openmetaverse_data/cache/fcbee9d0-c2d2-4bf3-bff3-d3ca474b3529 differ diff --git a/openmetaverse_data/cache/ff11fe5e-f51f-45da-b6ad-46e5c5934242 b/openmetaverse_data/cache/ff11fe5e-f51f-45da-b6ad-46e5c5934242 new file mode 100644 index 0000000..b9cf02c Binary files /dev/null and b/openmetaverse_data/cache/ff11fe5e-f51f-45da-b6ad-46e5c5934242 differ