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