2 // Radegast Metaverse Client
3 // Copyright (c) 2009-2014, Radegast Development Team
4 // All rights reserved.
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
9 // * Redistributions of source code must retain the above copyright notice,
10 // this list of conditions and the following disclaimer.
11 // * Redistributions in binary form must reproduce the above copyright
12 // notice, this list of conditions and the following disclaimer in the
13 // documentation and/or other materials provided with the distribution.
14 // * Neither the name of the application "Radegast", nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior CreateReflectionTexture permission.
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 // $Id: RenderAvatar.cs 1137 2011-09-05 22:53:46Z latifer $
33 using System.Collections.Generic;
34 using System.Collections;
37 #if (COGBOT_LIBOMV || USE_STHREADS)
39 using Thread = ThreadPoolUtil.Thread;
40 using ThreadPool = ThreadPoolUtil.ThreadPool;
41 using Monitor = ThreadPoolUtil.Monitor;
43 using System.Threading;
46 using OpenMetaverse.Rendering;
48 namespace Radegast.Rendering
50 public class attachment_point
54 public Vector3 position;
55 public Quaternion rotation;
59 public GLMesh jointmesh;
60 public int jointmeshindex;
62 public attachment_point(XmlNode node)
64 name = node.Attributes.GetNamedItem("name").Value;
65 joint = node.Attributes.GetNamedItem("joint").Value;
66 position = VisualParamEx.XmlParseVector(node.Attributes.GetNamedItem("position").Value);
67 rotation = VisualParamEx.XmlParseRotation(node.Attributes.GetNamedItem("rotation").Value);
68 id = Int32.Parse(node.Attributes.GetNamedItem("id").Value);
69 group = Int32.Parse(node.Attributes.GetNamedItem("group").Value);
75 /// Subclass of LindenMesh that adds vertex, index, and texture coordinate
76 /// arrays suitable for pushing direct to OpenGL
78 public class GLMesh : LindenMesh
81 /// Subclass of LODMesh that adds an index array suitable for pushing
87 public Dictionary<int, VisualParamEx> _evp = new Dictionary<int, VisualParamEx>();
89 new public class LODMesh : LindenMesh.LODMesh
91 public ushort[] Indices;
93 public override void LoadMesh(string filename)
95 base.LoadMesh(filename);
97 // Generate the index array
98 Indices = new ushort[_numFaces * 3];
100 for (int i = 0; i < _numFaces; i++)
102 Indices[current++] = (ushort)_faces[i].Indices[0];
103 Indices[current++] = (ushort)_faces[i].Indices[1];
104 Indices[current++] = (ushort)_faces[i].Indices[2];
114 public float[] Vertices;
115 public float[] Normals;
116 public ushort[] Indices;
117 public float[] TexCoords;
118 public Vector3 Center;
119 public float[] weights; //strictly these are constant and don't need instancing with the GLMesh
120 public string[] skinJoints; //strictly these are constant and don't need instancing with the GLMesh
123 public static GLData baseRenderData;
124 public GLData RenderData;
125 public GLData OrigRenderData;
126 public GLData MorphRenderData;
130 public GLMesh(string name)
135 public GLMesh(GLMesh source, GLAvatar av)
139 // Make a new GLMesh copy from the supplied source
141 RenderData.Vertices = new float[source.RenderData.Vertices.Length];
142 RenderData.Normals = new float[source.RenderData.Normals.Length];
143 RenderData.TexCoords = new float[source.RenderData.TexCoords.Length];
144 RenderData.Indices = new ushort[source.RenderData.Indices.Length];
146 RenderData.weights = new float[source.RenderData.weights.Length];
147 RenderData.skinJoints = new string[source.RenderData.skinJoints.Length];
149 Array.Copy(source.RenderData.Vertices, RenderData.Vertices, source.RenderData.Vertices.Length);
150 Array.Copy(source.RenderData.Normals, RenderData.Normals, source.RenderData.Normals.Length);
152 Array.Copy(source.RenderData.TexCoords, RenderData.TexCoords, source.RenderData.TexCoords.Length);
153 Array.Copy(source.RenderData.Indices, RenderData.Indices, source.RenderData.Indices.Length);
154 Array.Copy(source.RenderData.weights, RenderData.weights, source.RenderData.weights.Length);
155 Array.Copy(source.RenderData.skinJoints, RenderData.skinJoints, source.RenderData.skinJoints.Length);
157 RenderData.Center = new Vector3(source.RenderData.Center);
159 teFaceID = source.teFaceID;
161 RotationAngles = new Vector3(source.RotationAngles);
162 Scale = new Vector3(source.Scale);
163 Position = new Vector3(source.Position);
165 // We should not need to instance these the reference from the top should be constant
167 Morphs = source.Morphs;
169 OrigRenderData.Indices = new ushort[source.RenderData.Indices.Length];
170 OrigRenderData.TexCoords = new float[source.RenderData.TexCoords.Length];
171 OrigRenderData.Vertices = new float[source.RenderData.Vertices.Length];
172 OrigRenderData.Normals = new float[source.RenderData.Normals.Length];
174 MorphRenderData.Vertices = new float[source.RenderData.Vertices.Length];
175 MorphRenderData.Normals = new float[source.RenderData.Normals.Length];
177 Array.Copy(source.RenderData.Vertices, OrigRenderData.Vertices, source.RenderData.Vertices.Length);
178 Array.Copy(source.RenderData.Vertices, MorphRenderData.Vertices, source.RenderData.Vertices.Length);
180 Array.Copy(source.RenderData.Normals, OrigRenderData.Normals, source.RenderData.Normals.Length);
181 Array.Copy(source.RenderData.Normals, MorphRenderData.Normals, source.RenderData.Normals.Length);
183 Array.Copy(source.RenderData.TexCoords, OrigRenderData.TexCoords, source.RenderData.TexCoords.Length);
184 Array.Copy(source.RenderData.Indices, OrigRenderData.Indices, source.RenderData.Indices.Length);
190 public void setMeshPos(Vector3 pos)
194 // Force offset all vertices by this offset
195 // this is required to force some meshes into the default T bind pose
197 for (int vert = 0; vert < RenderData.Vertices.Length; vert = vert + 3)
199 RenderData.Vertices[vert] += pos.X;
200 RenderData.Vertices[vert + 1] += pos.Y;
201 RenderData.Vertices[vert + 2] += pos.Z;
207 public void setMeshRot(Vector3 rot)
209 RotationAngles = rot;
212 public override void LoadMesh(string filename)
214 base.LoadMesh(filename);
216 float minX, minY, minZ;
217 minX = minY = minZ = Single.MaxValue;
218 float maxX, maxY, maxZ;
219 maxX = maxY = maxZ = Single.MinValue;
221 // Generate the vertex array
222 RenderData.Vertices = new float[NumVertices * 3];
223 RenderData.Normals = new float[NumVertices * 3];
225 Quaternion quat = Quaternion.CreateFromEulers(0, 0, (float)(Math.PI / 4.0));
228 for (int i = 0; i < NumVertices; i++)
231 RenderData.Normals[current] = Vertices[i].Normal.X;
232 RenderData.Vertices[current++] = Vertices[i].Coord.X;
233 RenderData.Normals[current] = Vertices[i].Normal.Y;
234 RenderData.Vertices[current++] = Vertices[i].Coord.Y;
235 RenderData.Normals[current] = Vertices[i].Normal.Z;
236 RenderData.Vertices[current++] = Vertices[i].Coord.Z;
238 if (Vertices[i].Coord.X < minX)
239 minX = Vertices[i].Coord.X;
240 else if (Vertices[i].Coord.X > maxX)
241 maxX = Vertices[i].Coord.X;
243 if (Vertices[i].Coord.Y < minY)
244 minY = Vertices[i].Coord.Y;
245 else if (Vertices[i].Coord.Y > maxY)
246 maxY = Vertices[i].Coord.Y;
248 if (Vertices[i].Coord.Z < minZ)
249 minZ = Vertices[i].Coord.Z;
250 else if (Vertices[i].Coord.Z > maxZ)
251 maxZ = Vertices[i].Coord.Z;
254 // Calculate the center-point from the bounding box edges
255 RenderData.Center = new Vector3((minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2);
257 // Generate the index array
258 RenderData.Indices = new ushort[NumFaces * 3];
260 for (int i = 0; i < NumFaces; i++)
262 RenderData.Indices[current++] = (ushort)Faces[i].Indices[0];
263 RenderData.Indices[current++] = (ushort)Faces[i].Indices[1];
264 RenderData.Indices[current++] = (ushort)Faces[i].Indices[2];
267 // Generate the texcoord array
268 RenderData.TexCoords = new float[NumVertices * 2];
270 for (int i = 0; i < NumVertices; i++)
272 RenderData.TexCoords[current++] = Vertices[i].TexCoord.X;
273 RenderData.TexCoords[current++] = Vertices[i].TexCoord.Y;
276 RenderData.weights = new float[NumVertices];
277 for (int i = 0; i < NumVertices; i++)
279 RenderData.weights[i] = Vertices[i].Weight;
282 RenderData.skinJoints = new string[SkinJoints.Length + 3];
283 for (int i = 1; i < SkinJoints.Length; i++)
285 RenderData.skinJoints[i] = SkinJoints[i];
291 public override void LoadLODMesh(int level, string filename)
293 LODMesh lod = new LODMesh();
294 lod.LoadMesh(filename);
295 LodMeshes[level] = lod;
298 public void applyjointweights()
301 /*Each weight actually contains two pieces of information.
302 * The number to the left of the decimal point is the index of the joint and also
303 * implicitly indexes to the following joint. The actual weight is to the right of
304 * the decimal point and interpolates between these two joints. The index is into an
305 * "expanded" list of joints, not just a linear array of the joints as defined in the
306 * skeleton file. In particular, any joint that has more than one child will be repeated
307 * in the list for each of its children.
310 float weight = -9999;
317 for (int v = 0, x = 0; v < RenderData.Vertices.Length; v = v + 3, x++)
319 if (weight != RenderData.weights[x])
322 jointindex = (int)Math.Floor(weight = RenderData.weights[x]);
323 factor = RenderData.weights[x] - jointindex;
324 weight = weight - jointindex;
326 string jointname = "", jointname2 = "";
330 case "upperBodyMesh":
331 jointname = skeleton.mUpperMeshMapping[jointindex];
333 jointname2 = skeleton.mUpperMeshMapping[jointindex];
336 case "lowerBodyMesh":
337 jointname = skeleton.mLowerMeshMapping[jointindex];
339 jointname2 = skeleton.mLowerMeshMapping[jointindex];
343 jointname = skeleton.mHeadMeshMapping[jointindex];
345 jointname2 = skeleton.mHeadMeshMapping[jointindex];
348 case "eyeBallRightMesh":
350 jointname2 = "mEyeRight";
353 case "eyeBallLeftMesh":
355 jointname2 = "mEyeLeft";
361 jointname2 = "mSkull";
369 ba = av.skel.mBones[jointname];
372 if (jointname2 == "")
378 bb = av.skel.mBones[jointname2];
382 //Special cases 0 is not used
383 // ON upper torso 5 and 10 are not used
384 // 4 is neck and 6 and 11 are the left and right collar bones
387 //TODO use the SRT matrix to do this!
399 Vector3 posa = new Vector3(MorphRenderData.Vertices[v], MorphRenderData.Vertices[v + 1], MorphRenderData.Vertices[v + 2]);
404 lerpa = ba.getDeltaOffset();
405 offseta = ba.getTotalOffset();
406 rota = ba.getTotalRotation();
408 lerpb = bb.getDeltaOffset();
409 offsetb = bb.getTotalOffset();
410 rotb = bb.getTotalRotation();
412 Vector3 posb = new Vector3(MorphRenderData.Vertices[v], MorphRenderData.Vertices[v + 1], MorphRenderData.Vertices[v + 2]);
414 //move back to mesh local coords
415 posa = posa - offseta;
417 // apply rotated offset
418 posa = ((posa + lerpa) * ba.scale) * rota;
419 //move back to avatar local coords
420 posa = posa + offseta;
422 //move back to mesh local coords
423 posb = posb - offsetb;
425 // apply rotated offset
426 posb = ((posb + lerpb) * bb.scale) * rotb;
427 //move back to avatar local coords
428 posb = posb + offsetb;
430 //LERP the two points to produce smooth skinning ;-)
431 pos = Vector3.Lerp(posa, posb, weight);
436 lerpa = ba.getDeltaOffset();
437 offseta = ba.getTotalOffset();
438 rota = ba.getTotalRotation();
440 //move back to mesh local coords
441 posa = posa - offseta;
443 // apply rotated offset
444 posa = ((posa + lerpa) * ba.scale) * rota;
445 //move back to avatar local coords
446 posa = posa + offseta;
448 //Only one bone contributing so its 100% that one.
453 RenderData.Vertices[v] = pos.X;
454 RenderData.Vertices[v + 1] = pos.Y;
455 RenderData.Vertices[v + 2] = pos.Z;
459 public void resetallmorphs()
461 for (int i = 0; i < OrigRenderData.Vertices.Length / 3; i++)
464 MorphRenderData.Vertices[i * 3] = OrigRenderData.Vertices[i * 3];
465 MorphRenderData.Vertices[(i * 3) + 1] = OrigRenderData.Vertices[i * 3 + 1];
466 MorphRenderData.Vertices[(i * 3) + 2] = OrigRenderData.Vertices[i * 3 + 2];
468 MorphRenderData.Normals[i * 3] = OrigRenderData.Normals[i * 3];
469 MorphRenderData.Normals[(i * 3) + 1] = OrigRenderData.Normals[i * 3 + 1];
470 MorphRenderData.Normals[(i * 3) + 2] = OrigRenderData.Normals[i * 3 + 2];
472 RenderData.TexCoords[i * 2] = OrigRenderData.TexCoords[i * 2];
473 RenderData.TexCoords[(i * 2) + 1] = OrigRenderData.TexCoords[i * 2 + 1];
479 public void morphmesh(Morph morph, float weight)
481 // Logger.Log(String.Format("Applying morph {0} weight {1}",morph.Name,weight),Helpers.LogLevel.Debug);
483 for (int v = 0; v < morph.NumVertices; v++)
485 MorphVertex mvx = morph.Vertices[v];
487 uint i = mvx.VertexIndex;
489 MorphRenderData.Vertices[i * 3] = MorphRenderData.Vertices[i * 3] + mvx.Coord.X * weight;
490 MorphRenderData.Vertices[(i * 3) + 1] = MorphRenderData.Vertices[i * 3 + 1] + mvx.Coord.Y * weight;
491 MorphRenderData.Vertices[(i * 3) + 2] = MorphRenderData.Vertices[i * 3 + 2] + mvx.Coord.Z * weight;
493 MorphRenderData.Normals[i * 3] = MorphRenderData.Normals[i * 3] + mvx.Normal.X * weight;
494 MorphRenderData.Normals[(i * 3) + 1] = MorphRenderData.Normals[(i * 3) + 1] + mvx.Normal.Y * weight;
495 MorphRenderData.Normals[(i * 3) + 2] = MorphRenderData.Normals[(i * 3) + 2] + mvx.Normal.Z * weight;
497 RenderData.TexCoords[i * 2] = OrigRenderData.TexCoords[i * 2] + mvx.TexCoord.X * weight;
498 RenderData.TexCoords[(i * 2) + 1] = OrigRenderData.TexCoords[i * 2 + 1] + mvx.TexCoord.Y * weight;
504 public class GLAvatar
506 private static Dictionary<string, GLMesh> _defaultmeshes = new Dictionary<string, GLMesh>();
507 public Dictionary<string, GLMesh> _meshes = new Dictionary<string, GLMesh>();
509 public skeleton skel = new skeleton();
510 public static Dictionary<int, attachment_point> attachment_points = new Dictionary<int, attachment_point>();
512 public bool _wireframe = true;
513 public bool _showSkirt = false;
515 public VisualParamEx.EparamSex msex;
517 public byte[] VisualAppearanceParameters = new byte[1024];
519 static bool lindenMeshesLoaded = false;
523 lock (_defaultmeshes) foreach (KeyValuePair<string, GLMesh> kvp in _defaultmeshes)
525 GLMesh mesh = new GLMesh(kvp.Value, this); // Instance our meshes
526 _meshes.Add(kvp.Key, mesh);
531 public static void dumptweaks()
534 for (int x = 0; x < VisualParamEx.tweakable_params.Count; x++)
536 VisualParamEx vpe = (VisualParamEx)VisualParamEx.tweakable_params.GetByIndex(x);
537 Console.WriteLine(string.Format("{0} is {1}", x, vpe.Name));
542 public static void loadlindenmeshes2(string LODfilename)
544 // Already have mashes loaded?
545 if (lindenMeshesLoaded) return;
547 attachment_points.Clear();
550 string basedir = Directory.GetCurrentDirectory() + System.IO.Path.DirectorySeparatorChar + "character" + System.IO.Path.DirectorySeparatorChar;
552 XmlDocument lad = new XmlDocument();
553 lad.Load(basedir + LODfilename);
555 //Firstly read the skeleton section this contains attachment point info and the bone deform info for visual params
556 // And load the skeleton file in to the bones class
558 XmlNodeList skeleton = lad.GetElementsByTagName("skeleton");
559 string skeletonfilename = skeleton[0].Attributes.GetNamedItem("file_name").Value;
560 Bone.loadbones(skeletonfilename);
562 // Next read all the skeleton child nodes, we have attachment points and bone deform params
563 // attachment points are an offset and rotation from a bone location
564 // the name of the bone they reference is the joint paramater
565 // params in the skeleton nodes are bone deforms, eg leg length changes the scale of the leg bones
567 foreach (XmlNode skeletonnode in skeleton[0].ChildNodes)
569 if (skeletonnode.Name == "attachment_point")
571 attachment_point point = new attachment_point(skeletonnode);
572 attachment_points.Add(point.id, point);
576 // Parse all visual paramaters in one go
577 // we can dedue type on the fly
578 XmlNodeList paramss = lad.GetElementsByTagName("param");
579 foreach (XmlNode paramNode in paramss)
581 VisualParamEx vp = new VisualParamEx(paramNode);
584 //Now we parse the mesh nodes, mesh nodes reference a particular LLM file with a LOD
586 XmlNodeList meshes = lad.GetElementsByTagName("mesh");
587 foreach (XmlNode meshNode in meshes)
589 string type = meshNode.Attributes.GetNamedItem("type").Value;
590 int lod = Int32.Parse(meshNode.Attributes.GetNamedItem("lod").Value);
591 string fileName = meshNode.Attributes.GetNamedItem("file_name").Value;
594 lock (_defaultmeshes)
595 mesh = (_defaultmeshes.ContainsKey(type) ? _defaultmeshes[type] : new GLMesh(type));
597 // Set up the texture elemenets for each mesh
598 // And hack the eyeball position
601 case "lowerBodyMesh":
602 mesh.teFaceID = (int)AvatarTextureIndex.LowerBaked;
605 case "upperBodyMesh":
606 mesh.teFaceID = (int)AvatarTextureIndex.UpperBaked;
610 mesh.teFaceID = (int)AvatarTextureIndex.HeadBaked;
614 mesh.teFaceID = (int)AvatarTextureIndex.HairBaked;
618 mesh.teFaceID = (int)AvatarTextureIndex.HeadBaked;
621 case "eyeBallRightMesh":
622 mesh.teFaceID = (int)AvatarTextureIndex.EyesBaked;
625 case "eyeBallLeftMesh":
626 mesh.teFaceID = (int)AvatarTextureIndex.EyesBaked;
630 mesh.teFaceID = (int)AvatarTextureIndex.SkirtBaked;
639 mesh.LoadMesh(basedir + fileName);
641 mesh.LoadLODMesh(lod, basedir + fileName);
647 case "eyeBallLeftMesh":
648 lock (Bone.mBones) mesh.setMeshPos(Bone.mBones["mEyeLeft"].getTotalOffset());
651 case "eyeBallRightMesh":
652 lock (Bone.mBones) mesh.setMeshPos(Bone.mBones["mEyeRight"].getTotalOffset());
656 lock (Bone.mBones) mesh.setMeshPos(Bone.mBones["mHead"].getTotalOffset());
660 //mesh.setMeshPos(Bone.mBones["mHead"].getTotalOffset());
668 lock (_defaultmeshes) _defaultmeshes[type] = mesh;
672 lindenMeshesLoaded = true;
675 public void applyMorph(Avatar av, int param, float weight)
679 if (VisualParamEx.allParams.TryGetValue(param, out vpx))
681 applyMorph(vpx, av, weight);
683 // a morph ID may apply to more than one mesh (duplicate VP IDs)
684 // in this case also apply to all other identical IDs
685 foreach (VisualParamEx cvpx in vpx.identicalIds)
687 applyMorph(cvpx, av, weight);
692 public void applyMorph(VisualParamEx vpx, Avatar av, float weight)
695 weight = Utils.Clamp(weight, 0.0f, 1.0f);
696 float value = Utils.Lerp(vpx.MinValue, vpx.MaxValue, weight);
698 // don't do anything for less than 1% change
699 if (value > -0.001 && value < 0.001)
702 // Morphs are mesh deforms
703 if (vpx.pType == VisualParamEx.ParamType.TYPE_MORPH)
707 if (_meshes.TryGetValue(vpx.morphmesh, out mesh))
709 foreach (LindenMesh.Morph morph in mesh.Morphs) //optimise me to a dictionary
711 if (morph.Name == vpx.Name)
713 if (mesh.Name == "skirtMesh" && _showSkirt == false)
716 // Logger.Log(String.Format("Applying morph {0} ID {2} weight {1} mesh {3}",morph.Name,weight,vpx.ParamID,mesh.Name),Helpers.LogLevel.Debug);
718 mesh.morphmesh(morph, value);
727 // A driver drives multiple slave visual paramaters
728 if (vpx.pType == VisualParamEx.ParamType.TYPE_DRIVER)
731 foreach (VisualParamEx.driven child in vpx.childparams)
734 /***** BEGIN UNGRACEFULL CODE STEALING ******/
742 //-------|----|-------|----|-------> driver
743 // | min1 max1 max2 min2
746 if (child.hasMinMax == false)
748 applyMorph(av, child.id, weight);
752 float driven_weight = vpx.DefaultValue;
753 float driven_max = VisualParamEx.allParams[child.id].MaxValue;
754 float driven_min = VisualParamEx.allParams[child.id].MinValue;
755 float input_weight = weight;
757 float min_weight = vpx.MinValue;
758 float max_weight = vpx.MaxValue;
760 if (input_weight <= child.min1)
762 if (child.min1 == child.max1 &&
763 child.min1 <= min_weight)
765 driven_weight = driven_max;
769 driven_weight = driven_min;
773 if (input_weight <= child.max1)
775 float t = (input_weight - child.min1) / (child.max1 - child.min1);
776 driven_weight = driven_min + t * (driven_max - driven_min);
779 if (input_weight <= child.max2)
781 driven_weight = driven_max;
784 if (input_weight <= child.min2)
786 float t = (input_weight - child.max2) / (child.min2 - child.max2);
787 driven_weight = driven_max + t * (driven_min - driven_max);
791 if (child.max2 >= max_weight)
793 driven_weight = driven_max;
797 driven_weight = driven_min;
802 /***** END UNGRACEFULL CODE STEALING ******/
804 applyMorph(av, child.id, driven_weight);
811 //Is this a bone deform?
812 if (vpx.pType == VisualParamEx.ParamType.TYPE_BONEDEFORM)
818 foreach (KeyValuePair<string, BoneDeform> kvp in vpx.BoneDeforms)
820 skel.scalebone(kvp.Key, Vector3.One + (kvp.Value.scale * value));
821 skel.offsetbone(kvp.Key, kvp.Value.offset * value);
826 public void morph(Avatar av)
829 if (av.VisualParameters == null)
832 WorkPool.QueueUserWorkItem(sync =>
836 // We need a lock here as we may get multiple packets thrown at us and we should at least
837 // process them in turn not 1/2 process one then start to process the next.
838 // That said av might not be the best lock object, but it will do for the moment
841 if (av.VisualParameters.Length > 123)
843 if (av.VisualParameters[31] > 127)
845 msex = VisualParamEx.EparamSex.SEX_MALE;
849 msex = VisualParamEx.EparamSex.SEX_FEMALE;
853 foreach (GLMesh mesh in _meshes.Values)
855 mesh.resetallmorphs();
858 skel.resetbonescales();
860 foreach (byte vpvalue in av.VisualParameters)
862 if (x >= VisualParamEx.tweakable_params.Count)
864 //Logger.Log("Two many visual paramaters in Avatar appearance", Helpers.LogLevel.Warning);
868 VisualParamEx vpe = (VisualParamEx)VisualParamEx.tweakable_params.GetByIndex(x);
870 if (vpe.sex != VisualParamEx.EparamSex.SEX_BOTH && vpe.sex != msex)
876 VisualAppearanceParameters[x] = vpvalue;
878 float value = (vpvalue / 255.0f);
879 this.applyMorph(av, vpe.ParamID, value);
885 this.skel.mNeedsMeshRebuild = true;
886 // Don't update actual meshes here anymore, we do it every frame because of animation anyway
895 public Vector3 offset;
896 public Quaternion rotation;
899 public class animationwrapper
901 public BinBVHAnimationReader anim;
902 public float mRunTime;
903 public UUID mAnimation;
904 public bool mPotentialyDead = false;
906 public enum animstate
915 public animstate playstate;
917 public animationwrapper(BinBVHAnimationReader anim)
920 playstate = animstate.STATE_EASEIN;
924 public animationwrapper(UUID key)
927 playstate = animstate.STATE_WAITINGASSET;
931 public void stopanim()
933 //Logger.Log(string.Format("Animation {0} marked as stopped",mAnimation),Helpers.LogLevel.Info);
934 playstate = animstate.STATE_STOP;
938 public class skeleton
940 public Dictionary<string, Bone> mBones;
941 private Dictionary<string, int> mPriority = new Dictionary<string, int>();
942 private Dictionary<string, joint> jointdeforms = new Dictionary<string, joint>();
944 public static Dictionary<int, string> mUpperMeshMapping = new Dictionary<int, string>();
945 public static Dictionary<int, string> mLowerMeshMapping = new Dictionary<int, string>();
946 public static Dictionary<int, string> mHeadMeshMapping = new Dictionary<int, string>();
948 // public List<BinBVHAnimationReader> mAnimations = new List<BinBVHAnimationReader>();
949 public Dictionary<UUID, animationwrapper> mAnimationsWrapper = new Dictionary<UUID, animationwrapper>();
951 public static Dictionary<UUID, RenderAvatar> mAnimationTransactions = new Dictionary<UUID, RenderAvatar>();
953 public static Dictionary<UUID, BinBVHAnimationReader> mAnimationCache = new Dictionary<UUID, BinBVHAnimationReader>();
955 public bool mNeedsUpdate = false;
956 public bool mNeedsMeshRebuild = true;
959 public struct binBVHJointState
961 public float currenttime_rot;
962 public int lastkeyframe_rot;
963 public int nextkeyframe_rot;
965 public float currenttime_pos;
966 public int lastkeyframe_pos;
967 public int nextkeyframe_pos;
969 public int pos_loopinframe;
970 public int pos_loopoutframe;
972 public int rot_loopinframe;
973 public int rot_loopoutframe;
975 public Quaternion easeoutrot;
976 public float easeoutfactor;
984 mBones = new Dictionary<string, Bone>();
986 lock (Bone.mBones) foreach (Bone src in Bone.mBones.Values)
988 Bone newbone = new Bone(src);
989 mBones.Add(newbone.name, newbone);
992 //rebuild the skeleton structure on the new copy
993 foreach (Bone src in mBones.Values)
995 if (src.mParentBone != null)
997 src.parent = mBones[src.mParentBone];
998 src.parent.children.Add(src);
1003 if (mUpperMeshMapping.Count == 0)
1005 mUpperMeshMapping.Add(1, "mPelvis");
1006 mUpperMeshMapping.Add(2, "mTorso");
1007 mUpperMeshMapping.Add(3, "mChest");
1008 mUpperMeshMapping.Add(4, "mNeck");
1009 mUpperMeshMapping.Add(5, "mNeck");
1010 mUpperMeshMapping.Add(6, "mCollarLeft");
1011 mUpperMeshMapping.Add(7, "mShoulderLeft");
1012 mUpperMeshMapping.Add(8, "mElbowLeft");
1013 mUpperMeshMapping.Add(9, "mWristLeft");
1014 mUpperMeshMapping.Add(10, "mNeck"); // this case might fail for mWriteLeft and mNeck acting together?
1015 mUpperMeshMapping.Add(11, "mCollarRight");
1016 mUpperMeshMapping.Add(12, "mShoulderRight");
1017 mUpperMeshMapping.Add(13, "mElbowRight");
1018 mUpperMeshMapping.Add(14, "mWristRight");
1019 mUpperMeshMapping.Add(15, "");
1021 mLowerMeshMapping.Add(1, "mPelvis");
1022 mLowerMeshMapping.Add(2, "mHipRight");
1023 mLowerMeshMapping.Add(3, "mKneeRight");
1024 mLowerMeshMapping.Add(4, "mAnkleRight");
1025 mLowerMeshMapping.Add(5, "mPelvis");
1026 mLowerMeshMapping.Add(6, "mHipLeft");
1027 mLowerMeshMapping.Add(7, "mKneeLeft");
1028 mLowerMeshMapping.Add(8, "mAnkleLeft");
1029 mLowerMeshMapping.Add(9, "");
1031 mHeadMeshMapping.Add(1, "mNeck");
1032 mHeadMeshMapping.Add(2, "mHead");
1033 mHeadMeshMapping.Add(3, "");
1039 public void processAnimation(UUID mAnimID)
1041 if (mAnimationsWrapper.ContainsKey(mAnimID))
1043 // Its an existing animation, we may need to do a seq update but we don't yet
1045 mAnimationsWrapper[mAnimID].playstate = animationwrapper.animstate.STATE_WAITINGASSET;
1046 mAnimationsWrapper[mAnimID].mPotentialyDead = false;
1050 // Its a new animation do the decode dance
1051 animationwrapper aw = new animationwrapper(mAnimID);
1052 mAnimationsWrapper.Add(mAnimID, aw);
1057 public void resetbonescales()
1059 foreach (KeyValuePair<string, Bone> src in mBones)
1061 src.Value.scale = Vector3.One;
1062 src.Value.offset_pos = Vector3.Zero;
1066 public void deformbone(string name, Vector3 pos, Quaternion rotation)
1069 if (mBones.TryGetValue(name, out bone))
1071 bone.deformbone(pos, rotation);
1075 public void scalebone(string name, Vector3 scale)
1079 if (mBones.TryGetValue(name, out bone))
1081 // Logger.Log(String.Format("scalebone() {0} {1}", name, scale.ToString()),Helpers.LogLevel.Info);
1082 bone.scalebone(scale);
1086 public void offsetbone(string name, Vector3 offset)
1089 if (mBones.TryGetValue(name, out bone))
1091 bone.offsetbone(offset);
1095 //TODO check offset and rot calcuations should each offset be multiplied by its parent rotation in
1096 // a standard child/parent rot/offset way?
1097 public Vector3 getOffset(string bonename)
1100 if (mBones.TryGetValue(bonename, out b))
1102 return (b.getTotalOffset());
1106 return Vector3.Zero;
1110 public Quaternion getRotation(string bonename)
1113 if (mBones.TryGetValue(bonename, out b))
1115 return (b.getTotalRotation());
1119 return Quaternion.Identity;
1124 public void flushanimations()
1126 lock (mAnimationsWrapper)
1128 List<UUID> kills = new List<UUID>();
1129 foreach (animationwrapper ar in mAnimationsWrapper.Values)
1131 if (ar.playstate == animationwrapper.animstate.STATE_STOP)
1133 kills.Add(ar.mAnimation);
1136 ar.mPotentialyDead = true;
1139 foreach (UUID key in kills)
1141 mAnimationsWrapper.Remove(key);
1142 //Logger.Log(string.Format("Removing dead animation {0} from av", key), Helpers.LogLevel.Info);
1147 public void flushanimationsfinal()
1149 lock (mAnimationsWrapper)
1151 foreach (animationwrapper ar in mAnimationsWrapper.Values)
1153 if (ar.mPotentialyDead == true)
1155 // Logger.Log(string.Format("Animation {0} is being marked for easeout (dead)",ar.mAnimation.ToString()),Helpers.LogLevel.Info);
1156 // Should we just stop dead? i think not it may get jerky
1157 ar.playstate = animationwrapper.animstate.STATE_EASEOUT;
1158 ar.mRunTime = 0; //fix me nasty hack
1165 // Add animations to the global decoded list
1166 // TODO garbage collect unused animations somehow
1167 public static void addanimation(OpenMetaverse.Assets.Asset asset, UUID tid, BinBVHAnimationReader b, UUID animKey)
1170 mAnimationTransactions.TryGetValue(tid, out av);
1174 mAnimationTransactions.Remove(tid);
1178 b = new BinBVHAnimationReader(asset.AssetData);
1179 mAnimationCache[asset.AssetID] = b;
1180 Logger.Log("Adding new decoded animaton known animations " + asset.AssetID.ToString(), Helpers.LogLevel.Info);
1183 if (!av.glavatar.skel.mAnimationsWrapper.ContainsKey(animKey))
1185 Logger.Log(String.Format("Animation {0} is not in mAnimationsWrapper! ", animKey), Helpers.LogLevel.Warning);
1189 // This sets the anim in the wrapper class;
1190 av.glavatar.skel.mAnimationsWrapper[animKey].anim = b;
1193 foreach (binBVHJoint joint in b.joints)
1195 binBVHJointState state;
1197 state.lastkeyframe_rot = 0;
1198 state.nextkeyframe_rot = 1;
1200 state.lastkeyframe_pos = 0;
1201 state.nextkeyframe_pos = 1;
1203 state.currenttime_rot = 0;
1204 state.currenttime_pos = 0;
1206 state.pos_loopinframe = 0;
1207 state.pos_loopoutframe = joint.positionkeys.Length - 1;
1209 state.rot_loopinframe = 0;
1210 state.rot_loopoutframe = joint.rotationkeys.Length - 1;
1212 state.easeoutfactor = 1.0f;
1213 state.easeoutrot = Quaternion.Identity;
1218 foreach (binBVHJointKey key in joint.rotationkeys)
1220 if (key.time == b.InPoint)
1222 state.rot_loopinframe = frame;
1225 if (key.time == b.OutPoint)
1227 state.rot_loopoutframe = frame;
1234 foreach (binBVHJointKey key in joint.positionkeys)
1236 if (key.time == b.InPoint)
1238 state.pos_loopinframe = frame;
1241 if (key.time == b.OutPoint)
1243 state.pos_loopoutframe = frame;
1251 b.joints[pos].Tag = state;
1255 av.glavatar.skel.mAnimationsWrapper[animKey].playstate = animationwrapper.animstate.STATE_EASEIN;
1256 recalcpriorities(av);
1260 public static void recalcpriorities(RenderAvatar av)
1263 lock (av.glavatar.skel.mAnimationsWrapper)
1265 //av.glavatar.skel.mAnimationsWrapper.Add(new animationwrapper(b));
1267 //pre calculate all joint priorities here
1268 av.glavatar.skel.mPriority.Clear();
1270 foreach (KeyValuePair<UUID, animationwrapper> kvp in av.glavatar.skel.mAnimationsWrapper)
1273 animationwrapper ar = kvp.Value;
1274 if (ar.anim == null)
1277 foreach (binBVHJoint joint in ar.anim.joints)
1279 if (ar.anim == null)
1282 //warning struct copy non reference
1283 binBVHJointState state = (binBVHJointState)ar.anim.joints[jpos].Tag;
1285 if (ar.playstate == animationwrapper.animstate.STATE_STOP || ar.playstate == animationwrapper.animstate.STATE_EASEOUT)
1288 //FIX ME need to consider ease out here on priorities somehow
1292 //Quick hack to stack animations in the correct order
1293 //TODO we need to do this per joint as they all have their own priorities as well ;-(
1294 if (av.glavatar.skel.mPriority.TryGetValue(joint.Name, out prio))
1296 if (prio > (ar.anim.Priority))
1300 av.glavatar.skel.mPriority[joint.Name] = ar.anim.Priority;
1304 //av.glavatar.skel.mAnimationsWrapper[kvp.Key].playstate = animationwrapper.animstate.STATE_EASEIN;
1312 public void animate(float lastframetime)
1315 lock (mAnimationsWrapper)
1318 jointdeforms.Clear();
1320 foreach (animationwrapper ar in mAnimationsWrapper.Values)
1323 if (ar.playstate == animationwrapper.animstate.STATE_WAITINGASSET)
1326 if (ar.anim == null)
1329 ar.mRunTime += lastframetime;
1332 // Caclulate ease factors now they are common to all joints in a given animation
1333 float factor = 1.0f;
1335 if (ar.playstate == animationwrapper.animstate.STATE_EASEIN)
1337 if (ar.mRunTime >= ar.anim.EaseInTime)
1339 ar.playstate = animationwrapper.animstate.STATE_PLAY;
1340 //Console.WriteLine(String.Format("{0} Now in STATE_PLAY", ar.mAnimation));
1344 factor = 1.0f - ((ar.anim.EaseInTime - ar.mRunTime) / ar.anim.EaseInTime);
1347 //Console.WriteLine(String.Format("EASE IN {0} {1}",factor.ToString(),ar.mAnimation));
1350 if (ar.playstate == animationwrapper.animstate.STATE_EASEOUT)
1353 if (ar.mRunTime >= ar.anim.EaseOutTime)
1357 //Console.WriteLine(String.Format("{0} Now in STATE_STOP", ar.mAnimation));
1362 factor = 1.0f - (ar.mRunTime / ar.anim.EaseOutTime);
1366 //Console.WriteLine(String.Format("EASE OUT {0} {1}", factor.ToString(), ar.mAnimation));
1369 // we should not need this, this implies bad math above
1372 factor = Utils.Clamp(factor, 0.0f, 1.0f);
1379 foreach (binBVHJoint joint in ar.anim.joints)
1381 bool easeoutset = false;
1382 //warning struct copy non reference
1383 binBVHJointState state = (binBVHJointState)ar.anim.joints[jpos].Tag;
1385 if (ar.playstate == animationwrapper.animstate.STATE_STOP)
1389 //Quick hack to stack animations in the correct order
1390 //TODO we need to do this per joint as they all have their own priorities as well ;-(
1391 if (mPriority.TryGetValue(joint.Name, out prio))
1393 //if (prio > (ar.anim.Priority))
1397 Vector3 poslerp = Vector3.Zero;
1398 Quaternion rotlerp = Quaternion.Identity;
1401 if (ar.anim.joints[jpos].positionkeys.Length >= 2 && joint.Name == "mPelvis")
1404 //Console.WriteLine("Animate time " + state.currenttime_pos.ToString());
1406 state.currenttime_pos += lastframetime;
1408 float currentime = state.currenttime_pos;
1409 bool overrun = false;
1411 if (state.currenttime_pos > ar.anim.OutPoint)
1414 int itterations = (int)(state.currenttime_pos / ar.anim.OutPoint) + 1;
1415 state.currenttime_pos = currentime = ar.anim.InPoint + ((ar.anim.OutPoint - ar.anim.InPoint) - (((ar.anim.OutPoint - ar.anim.InPoint) * itterations) - state.currenttime_pos));
1419 binBVHJointKey pos_next = ar.anim.joints[jpos].positionkeys[state.nextkeyframe_pos];
1420 binBVHJointKey pos_last = ar.anim.joints[jpos].positionkeys[state.lastkeyframe_pos];
1422 // if the current time > than next key frame time we move keyframes
1423 if (currentime >= pos_next.time || overrun)
1426 //Console.WriteLine("bump");
1427 state.lastkeyframe_pos++;
1428 state.nextkeyframe_pos++;
1432 if (state.nextkeyframe_pos > state.pos_loopoutframe)
1433 state.nextkeyframe_pos = state.pos_loopinframe;
1435 if (state.lastkeyframe_pos > state.pos_loopoutframe)
1436 state.lastkeyframe_pos = state.pos_loopinframe;
1439 if (state.nextkeyframe_pos >= ar.anim.joints[jpos].positionkeys.Length)
1440 state.nextkeyframe_pos = state.pos_loopinframe;
1442 if (state.lastkeyframe_pos >= ar.anim.joints[jpos].positionkeys.Length)
1443 state.lastkeyframe_pos = state.pos_loopinframe;
1448 if (state.nextkeyframe_pos >= ar.anim.joints[jpos].positionkeys.Length)
1449 state.nextkeyframe_pos = ar.anim.joints[jpos].positionkeys.Length - 1;
1451 if (state.lastkeyframe_pos >= ar.anim.joints[jpos].positionkeys.Length)
1453 state.lastkeyframe_pos = ar.anim.joints[jpos].positionkeys.Length - 1;
1455 ar.playstate = animationwrapper.animstate.STATE_EASEOUT;
1461 //if (pos_next.time == pos_last.time)
1463 // ar.playstate = animationwrapper.animstate.STATE_EASEOUT;
1466 // update the pointers incase they have been moved
1467 pos_next = ar.anim.joints[jpos].positionkeys[state.nextkeyframe_pos];
1468 pos_last = ar.anim.joints[jpos].positionkeys[state.lastkeyframe_pos];
1470 // TODO the lerp/delta is faulty
1471 // it is not going to handle loop points when we wrap around as last will be > next
1472 // it also fails when currenttime < last_time which occurs as keyframe[0] is not exactly at
1474 float delta = (state.currenttime_pos - pos_last.time) / (pos_next.time - pos_last.time);
1476 delta = Utils.Clamp(delta, 0f, 1f);
1477 poslerp = Vector3.Lerp(pos_last.key_element, pos_next.key_element, delta) * factor;
1479 //Console.WriteLine(string.Format("Time {0} {1} {2} {3} {4}", state.currenttime_pos, delta, poslerp.ToString(), state.lastkeyframe_pos, state.nextkeyframe_pos));
1487 if (ar.anim.joints[jpos].rotationkeys.Length >= 2)
1490 state.currenttime_rot += lastframetime;
1492 float currentime = state.currenttime_rot;
1493 bool overrun = false;
1495 if (state.currenttime_rot > ar.anim.OutPoint)
1498 int itterations = (int)(state.currenttime_rot / ar.anim.OutPoint) + 1;
1499 state.currenttime_rot = currentime = ar.anim.InPoint + ((ar.anim.OutPoint - ar.anim.InPoint) - (((ar.anim.OutPoint - ar.anim.InPoint) * itterations) - state.currenttime_rot));
1503 binBVHJointKey rot_next = ar.anim.joints[jpos].rotationkeys[state.nextkeyframe_rot];
1504 binBVHJointKey rot_last = ar.anim.joints[jpos].rotationkeys[state.lastkeyframe_rot];
1506 // if the current time > than next key frame time we move keyframes
1507 if (currentime >= rot_next.time || overrun)
1510 state.lastkeyframe_rot++;
1511 state.nextkeyframe_rot++;
1515 if (state.nextkeyframe_rot > state.rot_loopoutframe)
1516 state.nextkeyframe_rot = state.rot_loopinframe;
1518 if (state.lastkeyframe_rot > state.rot_loopoutframe)
1519 state.lastkeyframe_rot = state.rot_loopinframe;
1524 if (state.nextkeyframe_rot >= ar.anim.joints[jpos].rotationkeys.Length)
1525 state.nextkeyframe_rot = ar.anim.joints[jpos].rotationkeys.Length - 1;
1527 if (state.lastkeyframe_rot >= ar.anim.joints[jpos].rotationkeys.Length)
1529 state.lastkeyframe_rot = ar.anim.joints[jpos].rotationkeys.Length - 1;
1531 ar.playstate = animationwrapper.animstate.STATE_EASEOUT;
1537 // update the pointers incase they have been moved
1538 rot_next = ar.anim.joints[jpos].rotationkeys[state.nextkeyframe_rot];
1539 rot_last = ar.anim.joints[jpos].rotationkeys[state.lastkeyframe_rot];
1541 // TODO the lerp/delta is faulty
1542 // it is not going to handle loop points when we wrap around as last will be > next
1543 // it also fails when currenttime < last_time which occurs as keyframe[0] is not exactly at
1545 float delta = state.currenttime_rot - rot_last.time / (rot_next.time - rot_last.time);
1546 delta = Utils.Clamp(delta, 0f, 1f);
1547 Vector3 rotlerpv = Vector3.Lerp(rot_last.key_element, rot_next.key_element, delta);
1548 // rotlerp = Quaternion.Slerp(Quaternion.Identity,new Quaternion(rotlerpv.X, rotlerpv.Y, rotlerpv.Z), factor);
1550 if (easeoutset == false && ar.playstate == animationwrapper.animstate.STATE_EASEOUT)
1553 state.easeoutrot = new Quaternion(rotlerpv.X, rotlerpv.Y, rotlerpv.Z);
1554 state.easeoutfactor = factor;
1558 rotlerp = new Quaternion(rotlerpv.X, rotlerpv.Y, rotlerpv.Z);
1564 joint jointstate = new Rendering.joint();
1566 if (jointdeforms.TryGetValue(ar.anim.joints[jpos].Name, out jointstate))
1568 jointstate.offset += (poslerp);
1570 if (ar.playstate != animationwrapper.animstate.STATE_EASEOUT)
1572 if (easeoutset == true)
1574 jointstate.rotation = Quaternion.Slerp(jointstate.rotation, state.easeoutrot, state.easeoutfactor);
1578 jointstate.rotation = rotlerp;
1582 //jointstate.rotation= Quaternion.Slerp(jointstate.rotation, rotlerp, 0.5f);
1586 jointstate = new joint();
1587 jointstate.rotation = rotlerp;
1588 jointstate.offset = poslerp;
1589 jointdeforms.Add(ar.anim.joints[jpos].Name, jointstate);
1592 //warning struct copy non reference
1593 ar.anim.joints[jpos].Tag = state;
1600 foreach (KeyValuePair<string, joint> kvp in jointdeforms)
1602 deformbone(kvp.Key, kvp.Value.offset, kvp.Value.rotation);
1604 mNeedsMeshRebuild = true;
1613 public Quaternion rot;
1614 public Vector3 scale;
1615 public Vector3 piviot;
1617 public Vector3 offset_pos;
1619 public Vector3 orig_pos;
1620 public Quaternion orig_rot;
1621 public Vector3 orig_scale;
1622 public Vector3 orig_piviot;
1624 Matrix4 mDeformMatrix = Matrix4.Identity;
1626 public Vector3 animation_offset;
1630 public List<Bone> children = new List<Bone>();
1632 public static Dictionary<string, Bone> mBones = new Dictionary<string, Bone>();
1633 public static Dictionary<int, Bone> mIndexedBones = new Dictionary<int, Bone>();
1634 static int boneaddindex = 0;
1636 private bool rotdirty = true;
1637 private bool posdirty = true;
1639 //Inverse bind matrix with bone scale
1640 private Vector3 mTotalPos;
1642 private Quaternion mTotalRot;
1644 private Vector3 mDeltaPos;
1645 private Quaternion mDeltaRot;
1647 public string mParentBone = null;
1653 public Bone(Bone source)
1655 name = String.Copy(source.name);
1656 pos = new Vector3(source.pos);
1657 rot = new Quaternion(source.rot);
1658 scale = new Vector3(source.scale);
1659 piviot = new Vector3(source.piviot);
1660 offset_pos = new Vector3(source.offset_pos);
1662 orig_piviot = source.orig_piviot;
1663 orig_pos = source.orig_pos;
1664 orig_rot = source.orig_rot;
1665 orig_scale = source.orig_scale;
1667 mParentBone = source.mParentBone;
1669 mDeformMatrix = new Matrix4(source.mDeformMatrix);
1672 public static void loadbones(string skeletonfilename)
1674 lock (Bone.mBones) mBones.Clear();
1675 string basedir = Directory.GetCurrentDirectory() + System.IO.Path.DirectorySeparatorChar + "character" + System.IO.Path.DirectorySeparatorChar;
1676 XmlDocument skeleton = new XmlDocument();
1677 skeleton.Load(basedir + skeletonfilename);
1678 XmlNode boneslist = skeleton.GetElementsByTagName("linden_skeleton")[0];
1679 addbone(boneslist.ChildNodes[0], null);
1682 public static void addbone(XmlNode bone, Bone parent)
1685 if (bone.Name != "bone")
1688 Bone b = new Bone();
1689 b.name = bone.Attributes.GetNamedItem("name").Value;
1691 string pos = bone.Attributes.GetNamedItem("pos").Value;
1692 string[] posparts = pos.Split(' ');
1693 b.pos = new Vector3(float.Parse(posparts[0], Utils.EnUsCulture), float.Parse(posparts[1], Utils.EnUsCulture), float.Parse(posparts[2], Utils.EnUsCulture));
1694 b.orig_pos = new Vector3(b.pos);
1695 b.offset_pos = new Vector3(b.pos);
1697 string rot = bone.Attributes.GetNamedItem("rot").Value;
1698 string[] rotparts = rot.Split(' ');
1699 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));
1700 b.orig_rot = new Quaternion(b.rot);
1702 string scale = bone.Attributes.GetNamedItem("scale").Value;
1703 string[] scaleparts = scale.Split(' ');
1704 b.scale = new Vector3(float.Parse(scaleparts[0], Utils.EnUsCulture), float.Parse(scaleparts[1], Utils.EnUsCulture), float.Parse(scaleparts[2], Utils.EnUsCulture));
1705 b.orig_scale = new Vector3(b.scale);
1708 float[] deform = Math3D.CreateSRTMatrix(new Vector3(1, 1, 1), b.rot, b.orig_pos);
1709 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]);
1716 b.mParentBone = parent.name;
1717 parent.children.Add(b);
1720 lock (Bone.mBones) mBones.Add(b.name, b);
1721 mIndexedBones.Add(boneaddindex++, b);
1723 Logger.Log("Found bone " + b.name, Helpers.LogLevel.Info);
1725 foreach (XmlNode childbone in bone.ChildNodes)
1727 addbone(childbone, b);
1732 public void deformbone(Vector3 dpos, Quaternion rot)
1734 //float[] deform = Math3D.CreateSRTMatrix(scale, rot, this.orig_pos);
1735 //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]);
1737 //this.offset_pos += pos;
1739 // this.offset_pos = pos;
1741 animation_offset = dpos;
1743 //this.pos = Bone.mBones[name].offset_pos + dpos;
1744 lock (Bone.mBones) this.rot = Bone.mBones[name].orig_rot * rot;
1749 public void scalebone(Vector3 scale)
1751 this.scale *= scale;
1755 public void offsetbone(Vector3 offset)
1757 this.offset_pos += offset;
1761 // If we deform a bone mark this bone and all its children as dirty.
1762 public void markdirty()
1766 foreach (Bone childbone in children)
1768 childbone.markdirty();
1772 public Matrix4 getdeform()
1774 if (this.parent != null)
1776 return mDeformMatrix * parent.getdeform();
1780 return mDeformMatrix;
1784 private Vector3 getOffset()
1788 Quaternion totalrot = getParentRot(); // we don't want this joints rotation included
1789 Vector3 parento = parent.getOffset();
1790 mTotalPos = parento + pos * parent.scale * totalrot;
1791 Vector3 orig = getOrigOffset();
1792 mDeltaPos = mTotalPos - orig;
1800 Vector3 orig = getOrigOffset();
1801 //mTotalPos = (pos * scale)+offset_pos;
1802 //mTotalPos = (pos) + offset_pos;
1804 mDeltaPos = mTotalPos - orig;
1811 public Vector3 getMyOffset()
1816 // Try to save some cycles by not recalculating positions and rotations every time
1817 public Vector3 getTotalOffset()
1819 if (posdirty == false)
1829 public Vector3 getDeltaOffset()
1831 if (posdirty == false)
1842 private Vector3 getOrigOffset()
1846 return (parent.getOrigOffset() + orig_pos);
1854 private static Quaternion getRotation(string bonename)
1859 if (mBones.TryGetValue(bonename, out b))
1861 return (b.getRotation());
1865 return Quaternion.Identity;
1871 private Quaternion getParentRot()
1873 Quaternion totalrot = Quaternion.Identity;
1877 totalrot = parent.getRotation();
1884 private Quaternion getRotation()
1886 Quaternion totalrot = rot;
1890 totalrot = parent.getRotation() * rot;
1893 mTotalRot = totalrot;
1899 public Quaternion getTotalRotation()
1901 if (rotdirty == false)
1907 return getRotation();
1912 public class BoneDeform
1914 public BoneDeform(Vector3 scale, Vector3 offset)
1917 this.offset = offset;
1920 public Vector3 scale;
1921 public Vector3 offset;
1924 public class VisualParamEx
1926 //All visual params indexed by ID
1927 static public Dictionary<int, VisualParamEx> allParams = new Dictionary<int, VisualParamEx>();
1929 // The sorted list of tweakable params, this matches the AvatarAppearance packet visual
1930 // parameters ordering
1931 static public SortedList tweakable_params = new SortedList();
1933 public Dictionary<string, BoneDeform> BoneDeforms = null;
1935 public Dictionary<string, VolumeDeform> VolumeDeforms = null;
1937 public List<driven> childparams = null;
1939 public List<VisualParamEx> identicalIds = new List<VisualParamEx>();
1941 public string morphmesh = null;
1945 VISUAL_PARAM_GROUP_TWEAKABLE = 0,
1946 VISUAL_PARAM_GROUP_ANIMATABLE,
1947 VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT,
1950 public struct VolumeDeform
1953 public Vector3 scale;
1957 public enum EparamSex
1964 public enum ParamType
1974 public struct driven
1981 public bool hasMinMax;
1984 public string meshname;
1986 /// <summary>Index of this visual param</summary>
1988 /// <summary>Internal name</summary>
1990 /// <summary>Group ID this parameter belongs to</summary>
1992 /// <summary>Name of the wearable this parameter belongs to</summary>
1993 public string Wearable;
1994 /// <summary>Displayable label of this characteristic</summary>
1995 public string Label;
1996 /// <summary>Displayable label for the minimum value of this characteristic</summary>
1997 public string LabelMin;
1998 /// <summary>Displayable label for the maximum value of this characteristic</summary>
1999 public string LabelMax;
2000 /// <summary>Default value</summary>
2001 public float DefaultValue;
2002 /// <summary>Minimum value</summary>
2003 public float MinValue;
2004 /// <summary>Maximum value</summary>
2005 public float MaxValue;
2006 /// <summary>Is this param used for creation of bump layer?</summary>
2007 public bool IsBumpAttribute;
2008 /// <summary>Alpha blending/bump info</summary>
2009 public VisualAlphaParam? AlphaParams;
2010 /// <summary>Color information</summary>
2011 public VisualColorParam? ColorParams;
2012 /// <summary>Array of param IDs that are drivers for this parameter</summary>
2013 public int[] Drivers;
2014 /// <summary>The Avatar Sex that this parameter applies to</summary>
2015 public EparamSex sex;
2017 public ParamType pType;
2019 public static int count = 0;
2022 /// Set all the values through the constructor
2024 /// <param name="paramID">Index of this visual param</param>
2025 /// <param name="name">Internal name</param>
2026 /// <param name="group"></param>
2027 /// <param name="wearable"></param>
2028 /// <param name="label">Displayable label of this characteristic</param>
2029 /// <param name="labelMin">Displayable label for the minimum value of this characteristic</param>
2030 /// <param name="labelMax">Displayable label for the maximum value of this characteristic</param>
2031 /// <param name="def">Default value</param>
2032 /// <param name="min">Minimum value</param>
2033 /// <param name="max">Maximum value</param>
2034 /// <param name="isBumpAttribute">Is this param used for creation of bump layer?</param>
2035 /// <param name="drivers">Array of param IDs that are drivers for this parameter</param>
2036 /// <param name="alpha">Alpha blending/bump info</param>
2037 /// <param name="colorParams">Color information</param>
2038 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)
2043 Wearable = wearable;
2045 LabelMin = labelMin;
2046 LabelMax = labelMax;
2050 IsBumpAttribute = isBumpAttribute;
2052 AlphaParams = alpha;
2053 ColorParams = colorParams;
2054 sex = EparamSex.SEX_BOTH;
2057 public bool matchchildnode(string test, XmlNode node)
2059 foreach (XmlNode n in node.ChildNodes)
2068 public VisualParamEx(XmlNode node)
2071 ParamID = Int32.Parse(node.Attributes.GetNamedItem("id").Value);
2072 Name = node.Attributes.GetNamedItem("name").Value;
2073 Group = Int32.Parse(node.Attributes.GetNamedItem("group").Value);
2075 //These dont exist for facal expresion morphs
2076 if (node.Attributes.GetNamedItem("wearable") != null)
2077 Wearable = node.Attributes.GetNamedItem("wearable").Value;
2079 MinValue = float.Parse(node.Attributes.GetNamedItem("value_min").Value, Utils.EnUsCulture);
2080 MaxValue = float.Parse(node.Attributes.GetNamedItem("value_max").Value, Utils.EnUsCulture);
2082 // These do not exists for driven parameters
2083 if (node.Attributes.GetNamedItem("label_min") != null)
2085 LabelMin = node.Attributes.GetNamedItem("label_min").Value;
2088 if (node.Attributes.GetNamedItem("label_max") != null)
2090 LabelMax = node.Attributes.GetNamedItem("label_max").Value;
2093 XmlNode sexnode = node.Attributes.GetNamedItem("sex");
2095 if (sexnode != null)
2097 if (sexnode.Value == "male")
2099 sex = EparamSex.SEX_MALE;
2103 sex = EparamSex.SEX_FEMALE;
2107 if (node.ParentNode.Name == "mesh")
2109 this.morphmesh = node.ParentNode.Attributes.GetNamedItem("type").Value;
2112 Group = int.Parse(node.Attributes.GetNamedItem("group").Value);
2114 if (Group == (int)GroupType.VISUAL_PARAM_GROUP_TWEAKABLE)
2116 if (!tweakable_params.ContainsKey(ParamID)) //stupid duplicate shared params
2118 tweakable_params.Add(this.ParamID, this);
2122 Logger.Log(String.Format("Warning duplicate tweakable paramater ID {0} {1}", count, this.Name), Helpers.LogLevel.Warning);
2127 if (allParams.ContainsKey(ParamID))
2129 //Logger.Log("Shared VisualParam id " + ParamID.ToString() + " "+Name, Helpers.LogLevel.Info);
2130 allParams[ParamID].identicalIds.Add(this);
2134 //Logger.Log("VisualParam id " + ParamID.ToString() + " " + Name, Helpers.LogLevel.Info);
2135 allParams.Add(ParamID, this);
2138 if (matchchildnode("param_skeleton", node))
2140 pType = ParamType.TYPE_BONEDEFORM;
2141 // If we are in the skeleton section then we also have bone deforms to parse
2142 BoneDeforms = new Dictionary<string, BoneDeform>();
2143 if (node.HasChildNodes && node.ChildNodes[0].HasChildNodes)
2145 ParseBoneDeforms(node.ChildNodes[0].ChildNodes);
2149 if (matchchildnode("param_morph", node))
2151 pType = ParamType.TYPE_MORPH;
2153 VolumeDeforms = new Dictionary<string, VolumeDeform>();
2154 if (node.HasChildNodes && node.ChildNodes[0].HasChildNodes)
2156 ParseVolumeDeforms(node.ChildNodes[0].ChildNodes);
2160 if (matchchildnode("param_driver", node))
2162 pType = ParamType.TYPE_DRIVER;
2163 childparams = new List<driven>();
2164 if (node.HasChildNodes && node.ChildNodes[0].HasChildNodes) //LAZY
2166 ParseDrivers(node.ChildNodes[0].ChildNodes);
2170 if (matchchildnode("param_color", node))
2172 pType = ParamType.TYPE_COLOR;
2173 if (node.HasChildNodes)
2175 foreach (XmlNode colorchild in node.ChildNodes)
2177 if (colorchild.Name == "param_color")
2179 //TODO extract <value color="50, 25, 5, 255" />
2187 void ParseBoneDeforms(XmlNodeList deforms)
2189 foreach (XmlNode node in deforms)
2191 if (node.Name == "bone")
2193 string name = node.Attributes.GetNamedItem("name").Value;
2194 Vector3 scale = Vector3.One;
2195 Vector3 offset = Vector3.One;
2197 if (node.Attributes.GetNamedItem("scale") != null)
2198 scale = XmlParseVector(node.Attributes.GetNamedItem("scale").Value);
2200 if (node.Attributes.GetNamedItem("offset") != null)
2201 offset = XmlParseVector(node.Attributes.GetNamedItem("offset").Value);
2203 BoneDeform bd = new BoneDeform(scale, offset);
2205 BoneDeforms.Add(name, bd);
2210 void ParseVolumeDeforms(XmlNodeList deforms)
2212 foreach (XmlNode node in deforms)
2214 if (node.Name == "volume_morph")
2216 VolumeDeform vd = new VolumeDeform();
2217 vd.name = node.Attributes.GetNamedItem("name").Value;
2218 vd.name = vd.name.ToLower();
2220 if (node.Attributes.GetNamedItem("scale") != null)
2222 vd.scale = XmlParseVector(node.Attributes.GetNamedItem("scale").Value);
2226 vd.scale = new Vector3(0, 0, 0);
2229 if (node.Attributes.GetNamedItem("pos") != null)
2231 vd.pos = XmlParseVector(node.Attributes.GetNamedItem("pos").Value);
2235 vd.pos = new Vector3(0f, 0f, 0f);
2238 VolumeDeforms.Add(vd.name, vd);
2243 void ParseDrivers(XmlNodeList drivennodes)
2245 foreach (XmlNode node in drivennodes)
2247 if (node.Name == "driven")
2249 driven d = new driven();
2251 d.id = Int32.Parse(node.Attributes.GetNamedItem("id").Value);
2252 XmlNode param = node.Attributes.GetNamedItem("max1");
2255 d.max1 = float.Parse(param.Value, Utils.EnUsCulture);
2256 d.max2 = float.Parse(node.Attributes.GetNamedItem("max2").Value, Utils.EnUsCulture);
2257 d.min1 = float.Parse(node.Attributes.GetNamedItem("min1").Value, Utils.EnUsCulture);
2258 d.min2 = float.Parse(node.Attributes.GetNamedItem("min2").Value, Utils.EnUsCulture);
2263 d.hasMinMax = false;
2272 public static Vector3 XmlParseVector(string data)
2274 string[] posparts = data.Split(' ');
2275 return new Vector3(float.Parse(posparts[0], Utils.EnUsCulture), float.Parse(posparts[1], Utils.EnUsCulture), float.Parse(posparts[2], Utils.EnUsCulture));
2278 public static Quaternion XmlParseRotation(string data)
2280 string[] rotparts = data.Split(' ');
2281 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));
2285 public class RenderAvatar : SceneObject
2287 public static Dictionary<int, string> BakedTextures = new Dictionary<int, string>
2297 public GLAvatar glavatar = new GLAvatar();
2298 public Avatar avatar;
2299 public FaceData[] data = new FaceData[32];
2300 public Dictionary<UUID, Animation> animlist = new Dictionary<UUID, Animation>();
2301 public Dictionary<WearableType, AppearanceManager.WearableData> Wearables = new Dictionary<WearableType, AppearanceManager.WearableData>();
2302 public static readonly BoundingVolume AvatarBoundingVolume;
2304 // Static constructor
2305 static RenderAvatar()
2307 AvatarBoundingVolume = new BoundingVolume();
2308 AvatarBoundingVolume.FromScale(Vector3.One);
2311 // Default constructor
2312 public RenderAvatar()
2314 BoundingVolume = AvatarBoundingVolume;
2315 Type = SceneObjectType.Avatar;
2318 public override Primitive BasePrim
2320 get { return avatar; }
2323 if (value is Avatar)
2325 avatar = (Avatar)value;
2326 AvatarBoundingVolume.CalcScaled(avatar.Scale);
2331 public override void Step(float time)
2333 glavatar.skel.animate(time);
2337 public float Height;
2338 public float PelvisToFoot;
2340 public void UpdateSize()
2342 float F_SQRT2 = 1.4142135623730950488016887242097f;
2344 Vector3 pelvis_scale = glavatar.skel.mBones["mPelvis"].scale;
2346 Vector3 skull = glavatar.skel.mBones["mSkull"].pos;
2347 Vector3 skull_scale = glavatar.skel.mBones["mSkull"].scale;
2349 Vector3 neck = glavatar.skel.mBones["mNeck"].pos;
2350 Vector3 neck_scale = glavatar.skel.mBones["mNeck"].scale;
2352 Vector3 chest = glavatar.skel.mBones["mChest"].pos;
2353 Vector3 chest_scale = glavatar.skel.mBones["mChest"].scale;
2355 Vector3 head = glavatar.skel.mBones["mHead"].pos;
2356 Vector3 head_scale = glavatar.skel.mBones["mHead"].scale;
2358 Vector3 torso = glavatar.skel.mBones["mTorso"].pos;
2359 Vector3 torso_scale = glavatar.skel.mBones["mTorso"].scale;
2361 Vector3 hip = glavatar.skel.mBones["mHipLeft"].pos;
2362 Vector3 hip_scale = glavatar.skel.mBones["mHipLeft"].scale;
2364 Vector3 knee = glavatar.skel.mBones["mKneeLeft"].pos;
2365 Vector3 knee_scale = glavatar.skel.mBones["mKneeLeft"].scale;
2367 Vector3 ankle = glavatar.skel.mBones["mAnkleLeft"].pos;
2368 Vector3 ankle_scale = glavatar.skel.mBones["mAnkleLeft"].scale;
2370 Vector3 foot = glavatar.skel.mBones["mFootLeft"].pos;
2372 // mAvatarOffset.Z = getVisualParamWeight(11001);
2374 float mPelvisToFoot = hip.Z * pelvis_scale.Z -
2375 knee.Z * hip_scale.Z -
2376 ankle.Z * knee_scale.Z -
2377 foot.Z * ankle_scale.Z;
2379 Vector3 new_body_size;
2380 new_body_size.Z = mPelvisToFoot +
2381 // the sqrt(2) correction below is an approximate
2382 // correction to get to the top of the head
2383 F_SQRT2 * (skull.Z * head_scale.Z) +
2384 head.Z * neck_scale.Z +
2385 neck.Z * chest_scale.Z +
2386 chest.Z * torso_scale.Z +
2387 torso.Z * pelvis_scale.Z;
2389 Height = new_body_size.Z;
2390 PelvisToFoot = mPelvisToFoot;
2393 public Vector3 AdjustedPosition(Vector3 source)
2395 return new Vector3(source.X, source.Y, source.Z - Height + PelvisToFoot);