mirror of
https://github.com/RaindropViewer/RaindropViewer.git
synced 2026-07-27 22:11:39 +00:00
2301 lines
88 KiB
C#
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);
|
|
// }
|
|
// }
|
|
//}
|