Files
RaindropViewer/Assets/Raindrop/Render/RenderAvatar.cs
T

2301 lines
88 KiB
C#

///**
// * Radegast Metaverse Client
// * Copyright(c) 2009-2014, Radegast Development Team
// * Copyright(c) 2016-2020, Sjofn, LLC
// * All rights reserved.
// *
// * Radegast is free software: you can redistribute it and/or modify
// * it under the terms of the GNU Lesser General Public License as published
// * by the Free Software Foundation, either version 3 of the License, or
// * (at your option) any later version.
// *
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
// * GNU General Public License for more details.
// *
// * You should have received a copy of the GNU Lesser General Public License
// * along with this program.If not, see<https://www.gnu.org/licenses/>.
// */
//using System;
//using System.Collections.Generic;
//using System.Collections;
//using System.IO;
//using System.Linq;
//using System.Threading;
//using System.Xml;
//#if (COGBOT_LIBOMV || USE_STHREADS)
//using ThreadPoolUtil;
//using Thread = ThreadPoolUtil.Thread;
//using ThreadPool = ThreadPoolUtil.ThreadPool;
//using Monitor = ThreadPoolUtil.Monitor;
//#endif
//using OpenMetaverse;
//using OpenMetaverse.Rendering;
//using Path = System.IO.Path;
//namespace Raindrop.Rendering
//{
// public class attachment_point
// {
// public string name;
// public string joint;
// public string location;
// 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;
// location = node.Attributes.GetNamedItem("location").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);
// }
// }
// /// <summary>
// /// Subclass of LindenMesh that adds vertex, index, and texture coordinate
// /// arrays suitable for pushing direct to OpenGL
// /// </summary>
// public class GLMesh : LindenMesh
// {
// /// <summary>
// /// Subclass of LODMesh that adds an index array suitable for pushing
// /// direct to OpenGL
// /// </summary>
// ///
// public int teFaceID;
// public Dictionary<int, VisualParamEx> _evp = new Dictionary<int, VisualParamEx>();
// public new class LODMesh : ReferenceMesh
// {
// 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 minY, minZ;
// var minX = minY = minZ = float.MaxValue;
// float maxY, maxZ;
// var maxX = maxY = maxZ = float.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 new 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];
// bb = jointname2 == "" ? null : 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<string, GLMesh> _defaultmeshes = new Dictionary<string, GLMesh>();
// public Dictionary<string, GLMesh> _meshes = new Dictionary<string, GLMesh>();
// public skeleton skel = new skeleton();
// public static Dictionary<int, attachment_point> attachment_points = new Dictionary<int, attachment_point>();
// public bool _wireframe = true;
// public bool _showSkirt = false;
// public VisualParamEx.EparamSex msex;
// public byte[] VisualAppearanceParameters = new byte[1024];
// static bool lindenMeshesLoaded = false;
// public GLAvatar()
// {
// lock (_defaultmeshes) foreach (var 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.TweakableParams.Count; x++)
// {
// VisualParamEx vpe = (VisualParamEx)VisualParamEx.TweakableParams.GetByIndex(x);
// Console.WriteLine($"{x} is {vpe.Name}");
// }
// }
// public static void loadlindenmeshes2(string LODfilename)
// {
// // Already have mashes loaded?
// if (lindenMeshesLoaded) return;
// attachment_points.Clear();
// string basedir = Path.Combine(Directory.GetCurrentDirectory(), "character");
// XmlDocument lad = new XmlDocument();
// lad.Load(Path.Combine(basedir, LODfilename));
// // First, 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 deduce 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 elements 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(Path.Combine(basedir, fileName));
// else
// mesh.LoadLODMesh(lod, Path.Combine(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;
// }
// }
// 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
// {
// driven_weight = child.max2 >= max_weight ? driven_max : 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 (var 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)
// {
// msex = av.VisualParameters[31] > 127 ? VisualParamEx.EparamSex.SEX_MALE : VisualParamEx.EparamSex.SEX_FEMALE;
// }
// foreach (GLMesh mesh in _meshes.Values)
// {
// mesh.resetallmorphs();
// }
// skel.resetbonescales();
// foreach (byte vpvalue in av.VisualParameters)
// {
// if (x >= VisualParamEx.TweakableParams.Count)
// {
// //Logger.Log("Two many visual paramaters in Avatar appearance", Helpers.LogLevel.Warning);
// break;
// }
// VisualParamEx vpe = (VisualParamEx)VisualParamEx.TweakableParams.GetByIndex(x);
// if (vpe.sex != VisualParamEx.EparamSex.SEX_BOTH && vpe.sex != msex)
// {
// x++;
// continue;
// }
// VisualAppearanceParameters[x] = vpvalue;
// float value = (vpvalue / 255.0f);
// applyMorph(av, vpe.ParamID, value);
// x++;
// }
// 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<string, Bone> mBones;
// private Dictionary<string, int> mPriority = new Dictionary<string, int>();
// private Dictionary<string, joint> jointdeforms = new Dictionary<string, joint>();
// public static Dictionary<int, string> mUpperMeshMapping = new Dictionary<int, string>();
// public static Dictionary<int, string> mLowerMeshMapping = new Dictionary<int, string>();
// public static Dictionary<int, string> mHeadMeshMapping = new Dictionary<int, string>();
// // public List<BinBVHAnimationReader> mAnimations = new List<BinBVHAnimationReader>();
// public Dictionary<UUID, animationwrapper> mAnimationsWrapper = new Dictionary<UUID, animationwrapper>();
// public static Dictionary<UUID, RenderAvatar> mAnimationTransactions = new Dictionary<UUID, RenderAvatar>();
// public static Dictionary<UUID, BinBVHAnimationReader> mAnimationCache = new Dictionary<UUID, BinBVHAnimationReader>();
// 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<string, Bone>();
// 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 (var 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;
// return mBones.TryGetValue(bonename, out b) ? (b.getTotalOffset()) : Vector3.Zero;
// }
// public Quaternion getRotation(string bonename)
// {
// Bone b;
// return mBones.TryGetValue(bonename, out b) ? (b.getTotalRotation()) : Quaternion.Identity;
// }
// public void flushanimations()
// {
// lock (mAnimationsWrapper)
// {
// List<UUID> kills = new List<UUID>();
// 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)
// {
// // 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, Helpers.LogLevel.Info);
// }
// if (!av.glavatar.skel.mAnimationsWrapper.ContainsKey(animKey))
// {
// Logger.Log($"Animation {animKey} is not in mAnimationsWrapper! ", 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)
// {
// 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 (var 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 joint();
// if (jointdeforms.TryGetValue(ar.anim.joints[jpos].Name, out jointstate))
// {
// jointstate.offset += (poslerp);
// if (ar.playstate != animationwrapper.animstate.STATE_EASEOUT)
// {
// jointstate.rotation = easeoutset
// ? Quaternion.Slerp(jointstate.rotation, state.easeoutrot, state.easeoutfactor)
// : rotlerp;
// }
// //jointstate.rotation= Quaternion.Slerp(jointstate.rotation, rotlerp, 0.5f);
// }
// else
// {
// jointstate = new joint
// {
// rotation = rotlerp,
// offset = poslerp
// };
// jointdeforms.Add(ar.anim.joints[jpos].Name, jointstate);
// }
// //warning struct copy non reference
// ar.anim.joints[jpos].Tag = state;
// jpos++;
// }
// }
// }
// foreach (var kvp in jointdeforms)
// {
// deformbone(kvp.Key, kvp.Value.offset, kvp.Value.rotation);
// }
// mNeedsMeshRebuild = true;
// }
// }
// public class Bone
// {
// public string name;
// public 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<Bone> children = new List<Bone>();
// public static Dictionary<string, Bone> mBones = new Dictionary<string, Bone>();
// public static Dictionary<int, Bone> mIndexedBones = new Dictionary<int, Bone>();
// 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;
// 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 (mBones) mBones.Clear();
// string basedir = Path.Combine(Directory.GetCurrentDirectory(), "character");
// XmlDocument skeleton = new XmlDocument();
// skeleton.Load(Path.Combine(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 {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 (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 (mBones) this.rot = mBones[name].orig_rot * rot;
// markdirty();
// }
// public void scalebone(Vector3 scale)
// {
// this.scale *= scale;
// markdirty();
// }
// public void offsetbone(Vector3 offset)
// {
// 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 (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()
// {
// return posdirty == false ? mTotalPos : getOffset();
// }
// public Vector3 getDeltaOffset()
// {
// if (posdirty == false)
// {
// return mDeltaPos;
// }
// 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 (mBones)
// {
// return mBones.TryGetValue(bonename, out b) ? (b.getRotation()) : 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()
// {
// return rotdirty == false ? mTotalRot : 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
// public static Dictionary<int, VisualParamEx> AllParams = new Dictionary<int, VisualParamEx>();
// // The sorted list of tweakable params, this matches the AvatarAppearance packet visual
// // parameters ordering
// public static SortedList TweakableParams = new SortedList();
// public Dictionary<string, BoneDeform> BoneDeforms = null;
// public Dictionary<string, VolumeDeform> VolumeDeforms = null;
// public List<driven> ChildParams = null;
// public List<VisualParamEx> IdenticalIds = new List<VisualParamEx>();
// public string MorphMesh = null;
// enum GroupType
// {
// VISUAL_PARAM_GROUP_TWEAKABLE = 0,
// VISUAL_PARAM_GROUP_ANIMATABLE,
// VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT,
// VISUAL_PARAM_GROUP_TRANSMIT_NOT_TWEAKABLE
// }
// public struct VolumeDeform
// {
// public string name;
// public Vector3 scale;
// public Vector3 pos;
// }
// public enum EparamSex
// {
// SEX_MALE = 0x1,
// SEX_FEMALE = 0x2,
// SEX_BOTH = 0x3
// }
// 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;
// /// <summary>Index of this visual param</summary>
// public int ParamID;
// /// <summary>Internal name</summary>
// public string Name;
// /// <summary>Group ID this parameter belongs to</summary>
// public int Group;
// /// <summary>Name of the wearable this parameter belongs to</summary>
// public string Wearable;
// /// <summary>Displayable label of this characteristic</summary>
// public string Label;
// /// <summary>Displayable label for the minimum value of this characteristic</summary>
// public string LabelMin;
// /// <summary>Displayable label for the maximum value of this characteristic</summary>
// public string LabelMax;
// /// <summary>Default value</summary>
// public float DefaultValue;
// /// <summary>Minimum value</summary>
// public float MinValue;
// /// <summary>Maximum value</summary>
// public float MaxValue;
// /// <summary>Is this param used for creation of bump layer?</summary>
// public bool IsBumpAttribute;
// /// <summary>Alpha blending/bump info</summary>
// public VisualAlphaParam? AlphaParams;
// /// <summary>Color information</summary>
// public VisualColorParam? ColorParams;
// /// <summary>Array of param IDs that are drivers for this parameter</summary>
// public int[] Drivers;
// /// <summary>The Avatar Sex that this parameter applies to</summary>
// public EparamSex sex;
// public ParamType pType;
// public static int count = 0;
// /// <summary>
// /// Set all the values through the constructor
// /// </summary>
// /// <param name="paramID">Index of this visual param</param>
// /// <param name="name">Internal name</param>
// /// <param name="group"></param>
// /// <param name="wearable"></param>
// /// <param name="label">Displayable label of this characteristic</param>
// /// <param name="labelMin">Displayable label for the minimum value of this characteristic</param>
// /// <param name="labelMax">Displayable label for the maximum value of this characteristic</param>
// /// <param name="def">Default value</param>
// /// <param name="min">Minimum value</param>
// /// <param name="max">Maximum value</param>
// /// <param name="isBumpAttribute">Is this param used for creation of bump layer?</param>
// /// <param name="drivers">Array of param IDs that are drivers for this parameter</param>
// /// <param name="alpha">Alpha blending/bump info</param>
// /// <param name="colorParams">Color information</param>
// 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)
// {
// return node.ChildNodes.Cast<XmlNode>().Any(n => n.Name == test);
// }
// 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)
// {
// switch (sexnode.Value)
// {
// case "male":
// sex = EparamSex.SEX_MALE;
// break;
// case "female":
// sex = EparamSex.SEX_FEMALE;
// break;
// default:
// sex = EparamSex.SEX_BOTH;
// break;
// }
// }
// if (node.ParentNode.Name == "mesh")
// {
// MorphMesh = node.ParentNode.Attributes.GetNamedItem("type").Value;
// }
// if (Group == (int)GroupType.VISUAL_PARAM_GROUP_TWEAKABLE)
// {
// if (!TweakableParams.ContainsKey(ParamID)) //stupid duplicate shared params
// {
// TweakableParams.Add(ParamID, this);
// }
// else
// {
// Logger.Log($"Warning duplicate tweakable parameter ID {count} {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<string, BoneDeform>();
// if (node.HasChildNodes && node.ChildNodes[0].HasChildNodes)
// {
// ParseBoneDeforms(node.ChildNodes[0].ChildNodes);
// }
// }
// if (matchchildnode("param_morph", node))
// {
// pType = ParamType.TYPE_MORPH;
// VolumeDeforms = new Dictionary<string, VolumeDeform>();
// if (node.HasChildNodes && node.ChildNodes[0].HasChildNodes)
// {
// ParseVolumeDeforms(node.ChildNodes[0].ChildNodes);
// }
// }
// if (matchchildnode("param_driver", node))
// {
// pType = ParamType.TYPE_DRIVER;
// ChildParams = new List<driven>();
// 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 <value color="50, 25, 5, 255" />
// }
// }
// }
// }
// }
// 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
// {
// name = node.Attributes.GetNamedItem("name").Value.ToLower(),
// scale =
// node.Attributes.GetNamedItem("scale") != null
// ? XmlParseVector(node.Attributes.GetNamedItem("scale").Value)
// : new Vector3(0, 0, 0),
// pos =
// node.Attributes.GetNamedItem("pos") != null
// ? XmlParseVector(node.Attributes.GetNamedItem("pos").Value)
// : 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 {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(new char[0], StringSplitOptions.RemoveEmptyEntries);
// 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<int, string> BakedTextures = new Dictionary<int, string>
// {
// { 8, "head" },
// { 9, "upper" },
// { 10, "lower" },
// { 11, "eyes" },
// { 19, "skirt" },
// { 20, "hair" }
// };
// public GLAvatar glavatar = new GLAvatar();
// public Avatar avatar;
// public FaceData[] data = new FaceData[32];
// public Dictionary<UUID, Animation> animlist = new Dictionary<UUID, Animation>();
// public Dictionary<WearableType, AppearanceManager.WearableData> Wearables = new Dictionary<WearableType, AppearanceManager.WearableData>();
// 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 => 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 float Height;
// public float PelvisToFoot;
// public void UpdateSize()
// {
// float F_SQRT2 = 1.4142135623730950488016887242097f;
// Vector3 pelvis_scale = glavatar.skel.mBones["mPelvis"].scale;
// Vector3 skull = glavatar.skel.mBones["mSkull"].pos;
// Vector3 skull_scale = glavatar.skel.mBones["mSkull"].scale;
// Vector3 neck = glavatar.skel.mBones["mNeck"].pos;
// Vector3 neck_scale = glavatar.skel.mBones["mNeck"].scale;
// Vector3 chest = glavatar.skel.mBones["mChest"].pos;
// Vector3 chest_scale = glavatar.skel.mBones["mChest"].scale;
// Vector3 head = glavatar.skel.mBones["mHead"].pos;
// Vector3 head_scale = glavatar.skel.mBones["mHead"].scale;
// Vector3 torso = glavatar.skel.mBones["mTorso"].pos;
// Vector3 torso_scale = glavatar.skel.mBones["mTorso"].scale;
// Vector3 hip = glavatar.skel.mBones["mHipLeft"].pos;
// Vector3 hip_scale = glavatar.skel.mBones["mHipLeft"].scale;
// Vector3 knee = glavatar.skel.mBones["mKneeLeft"].pos;
// Vector3 knee_scale = glavatar.skel.mBones["mKneeLeft"].scale;
// Vector3 ankle = glavatar.skel.mBones["mAnkleLeft"].pos;
// Vector3 ankle_scale = glavatar.skel.mBones["mAnkleLeft"].scale;
// Vector3 foot = glavatar.skel.mBones["mFootLeft"].pos;
// // mAvatarOffset.Z = getVisualParamWeight(11001);
// float mPelvisToFoot = hip.Z * pelvis_scale.Z -
// knee.Z * hip_scale.Z -
// ankle.Z * knee_scale.Z -
// foot.Z * ankle_scale.Z;
// Vector3 new_body_size;
// new_body_size.Z = mPelvisToFoot +
// // the sqrt(2) correction below is an approximate
// // correction to get to the top of the head
// F_SQRT2 * (skull.Z * head_scale.Z) +
// head.Z * neck_scale.Z +
// neck.Z * chest_scale.Z +
// chest.Z * torso_scale.Z +
// torso.Z * pelvis_scale.Z;
// Height = new_body_size.Z;
// PelvisToFoot = mPelvisToFoot;
// }
// public Vector3 AdjustedPosition(Vector3 source)
// {
// return new Vector3(source.X, source.Y, source.Z - Height + PelvisToFoot);
// }
// }
//}