OSDN Git Service

Very basic animation code shows last key frame, no priorities
[radegast/radegast.git] / Radegast / GUI / Rendering / RenderingHelpers.cs
1 using System;\r
2 using System.Collections.Generic;\r
3 using System.Collections;\r
4 using System.Linq;\r
5 using System.Text;\r
6 using System.IO;\r
7 using System.Xml;\r
8 using System.Threading;\r
9 using OpenTK.Graphics.OpenGL;\r
10 using System.Runtime.InteropServices;\r
11 using OpenMetaverse;\r
12 using OpenMetaverse.Rendering;\r
13 \r
14 namespace Radegast.Rendering\r
15 {\r
16     public class FaceData\r
17     {\r
18         public float[] Vertices;\r
19         public ushort[] Indices;\r
20         public float[] TexCoords;\r
21         public float[] Normals;\r
22         public int PickingID = -1;\r
23         public int VertexVBO = -1;\r
24         public int IndexVBO = -1;\r
25         public TextureInfo TextureInfo = new TextureInfo();\r
26         public BoundingVolume BoundingVolume = new BoundingVolume();\r
27         public static int VertexSize = 32; // sizeof (vertex), 2  x vector3 + 1 x vector2 = 8 floats x 4 bytes = 32 bytes \r
28         public TextureAnimationInfo AnimInfo;\r
29         public int QueryID = 0;\r
30 \r
31         public void CheckVBO(Face face)\r
32         {\r
33             if (VertexVBO == -1)\r
34             {\r
35                 Vertex[] vArray = face.Vertices.ToArray();\r
36                 GL.GenBuffers(1, out VertexVBO);\r
37                 GL.BindBuffer(BufferTarget.ArrayBuffer, VertexVBO);\r
38                 GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vArray.Length * VertexSize), vArray, BufferUsageHint.StaticDraw);\r
39             }\r
40 \r
41             if (IndexVBO == -1)\r
42             {\r
43                 ushort[] iArray = face.Indices.ToArray();\r
44                 GL.GenBuffers(1, out IndexVBO);\r
45                 GL.BindBuffer(BufferTarget.ElementArrayBuffer, IndexVBO);\r
46                 GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(iArray.Length * sizeof(ushort)), iArray, BufferUsageHint.StaticDraw);\r
47             }\r
48         }\r
49     }\r
50 \r
51     public class TextureAnimationInfo\r
52     {\r
53         public Primitive.TextureAnimation PrimAnimInfo;\r
54         public float CurrentFrame;\r
55         public double CurrentTime;\r
56         public bool PingPong;\r
57         float LastTime = 0f;\r
58         float TotalTime = 0f;\r
59 \r
60         public void Step(double lastFrameTime)\r
61         {\r
62             float numFrames = 1f;\r
63             float fullLength = 1f;\r
64 \r
65             if (PrimAnimInfo.Length > 0)\r
66             {\r
67                 numFrames = PrimAnimInfo.Length;\r
68             }\r
69             else\r
70             {\r
71                 numFrames = Math.Max(1f, (float)(PrimAnimInfo.SizeX * PrimAnimInfo.SizeY));\r
72             }\r
73 \r
74             if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.PING_PONG) != 0)\r
75             {\r
76                 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) != 0)\r
77                 {\r
78                     fullLength = 2f * numFrames;\r
79                 }\r
80                 else if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.LOOP) != 0)\r
81                 {\r
82                     fullLength = 2f * numFrames - 2f;\r
83                     fullLength = Math.Max(1f, fullLength);\r
84                 }\r
85                 else\r
86                 {\r
87                     fullLength = 2f * numFrames - 1f;\r
88                     fullLength = Math.Max(1f, fullLength);\r
89                 }\r
90             }\r
91             else\r
92             {\r
93                 fullLength = numFrames;\r
94             }\r
95 \r
96             float frameCounter;\r
97             if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) != 0)\r
98             {\r
99                 frameCounter = (float)lastFrameTime * PrimAnimInfo.Rate + LastTime;\r
100             }\r
101             else\r
102             {\r
103                 TotalTime += (float)lastFrameTime;\r
104                 frameCounter = TotalTime * PrimAnimInfo.Rate;\r
105             }\r
106             LastTime = frameCounter;\r
107 \r
108             if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.LOOP) != 0)\r
109             {\r
110                 frameCounter %= fullLength;\r
111             }\r
112             else\r
113             {\r
114                 frameCounter = Math.Min(fullLength - 1f, frameCounter);\r
115             }\r
116 \r
117             if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) == 0)\r
118             {\r
119                 frameCounter = (float)Math.Floor(frameCounter + 0.01f);\r
120             }\r
121 \r
122             if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.PING_PONG) != 0)\r
123             {\r
124                 if (frameCounter > numFrames)\r
125                 {\r
126                     if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) != 0)\r
127                     {\r
128                         frameCounter = numFrames - (frameCounter - numFrames);\r
129                     }\r
130                     else\r
131                     {\r
132                         frameCounter = (numFrames - 1.99f) - (frameCounter - numFrames);\r
133                     }\r
134                 }\r
135             }\r
136 \r
137             if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.REVERSE) != 0)\r
138             {\r
139                 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) != 0)\r
140                 {\r
141                     frameCounter = numFrames - frameCounter;\r
142                 }\r
143                 else\r
144                 {\r
145                     frameCounter = (numFrames - 0.99f) - frameCounter;\r
146                 }\r
147             }\r
148 \r
149             frameCounter += PrimAnimInfo.Start;\r
150 \r
151             if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) == 0)\r
152             {\r
153                 frameCounter = (float)Math.Round(frameCounter);\r
154             }\r
155 \r
156 \r
157             GL.MatrixMode(MatrixMode.Texture);\r
158             GL.LoadIdentity();\r
159 \r
160             if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.ROTATE) != 0)\r
161             {\r
162                 GL.Translate(0.5f, 0.5f, 0f);\r
163                 GL.Rotate(Utils.RAD_TO_DEG * frameCounter, OpenTK.Vector3d.UnitZ);\r
164                 GL.Translate(-0.5f, -0.5f, 0f);\r
165             }\r
166             else if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SCALE) != 0)\r
167             {\r
168                 GL.Scale(frameCounter, frameCounter, 0);\r
169             }\r
170             else // Translate\r
171             {\r
172                 float sizeX = Math.Max(1f, (float)PrimAnimInfo.SizeX);\r
173                 float sizeY = Math.Max(1f, (float)PrimAnimInfo.SizeY);\r
174 \r
175                 GL.Scale(1f / sizeX, 1f / sizeY, 0);\r
176                 GL.Translate(frameCounter % sizeX, Math.Floor(frameCounter / sizeY), 0);\r
177             }\r
178 \r
179             GL.MatrixMode(MatrixMode.Modelview);\r
180         }\r
181 \r
182         [Obsolete("Use Step() instead")]\r
183         public void ExperimentalStep(double time)\r
184         {\r
185             int reverseFactor = 1;\r
186             float rate = PrimAnimInfo.Rate;\r
187 \r
188             if (rate < 0)\r
189             {\r
190                 rate = -rate;\r
191                 reverseFactor = -reverseFactor;\r
192             }\r
193 \r
194             if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.REVERSE) != 0)\r
195             {\r
196                 reverseFactor = -reverseFactor;\r
197             }\r
198 \r
199             CurrentTime += time;\r
200             double totalTime = 1 / rate;\r
201 \r
202             uint x = Math.Max(1, PrimAnimInfo.SizeX);\r
203             uint y = Math.Max(1, PrimAnimInfo.SizeY);\r
204             uint nrFrames = x * y;\r
205 \r
206             if (PrimAnimInfo.Length > 0 && PrimAnimInfo.Length < nrFrames)\r
207             {\r
208                 nrFrames = (uint)PrimAnimInfo.Length;\r
209             }\r
210 \r
211             GL.MatrixMode(MatrixMode.Texture);\r
212             GL.LoadIdentity();\r
213 \r
214             if (CurrentTime >= totalTime)\r
215             {\r
216                 CurrentTime = 0;\r
217                 CurrentFrame++;\r
218                 if (CurrentFrame > nrFrames) CurrentFrame = (uint)PrimAnimInfo.Start;\r
219                 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.PING_PONG) != 0)\r
220                 {\r
221                     PingPong = !PingPong;\r
222                 }\r
223             }\r
224 \r
225             float smoothOffset = 0f;\r
226 \r
227             if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) != 0)\r
228             {\r
229                 smoothOffset = (float)(CurrentTime / totalTime) * reverseFactor;\r
230             }\r
231 \r
232             float f = CurrentFrame;\r
233             if (reverseFactor < 0)\r
234             {\r
235                 f = nrFrames - CurrentFrame;\r
236             }\r
237 \r
238             if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.ROTATE) == 0) // not rotaating\r
239             {\r
240                 GL.Scale(1f / x, 1f / y, 0f);\r
241                 GL.Translate((f % x) + smoothOffset, f / y, 0);\r
242             }\r
243             else\r
244             {\r
245                 smoothOffset = (float)(CurrentTime * PrimAnimInfo.Rate);\r
246                 double startAngle = PrimAnimInfo.Start;\r
247                 double endAngle = PrimAnimInfo.Length;\r
248                 double angle = startAngle + (endAngle - startAngle) * smoothOffset;\r
249                 GL.Translate(0.5f, 0.5f, 0f);\r
250                 GL.Rotate(Utils.RAD_TO_DEG * angle, OpenTK.Vector3d.UnitZ);\r
251                 GL.Translate(-0.5f, -0.5f, 0f);\r
252             }\r
253 \r
254             GL.MatrixMode(MatrixMode.Modelview);\r
255         }\r
256 \r
257     }\r
258 \r
259 \r
260     public class TextureInfo\r
261     {\r
262         public System.Drawing.Image Texture;\r
263         public int TexturePointer;\r
264         public bool HasAlpha;\r
265         public bool FullAlpha;\r
266         public bool IsMask;\r
267         public UUID TextureID;\r
268     }\r
269 \r
270     public class TextureLoadItem\r
271     {\r
272         public FaceData Data;\r
273         public Primitive Prim;\r
274         public Primitive.TextureEntryFace TeFace;\r
275         public byte[] TextureData = null;\r
276     }\r
277 \r
278     public enum RenderPass\r
279     {\r
280         Picking,\r
281         Simple,\r
282         Alpha\r
283     }\r
284 \r
285     public enum SceneObjectType\r
286     {\r
287         None,\r
288         Primitive,\r
289         Avatar,\r
290     }\r
291 \r
292     /// <summary>\r
293     /// Base class for all scene objects\r
294     /// </summary>\r
295     public abstract class SceneObject : IComparable, IDisposable\r
296     {\r
297         /// <summary>Actual position of the object in the region</summary>\r
298         public Vector3 SimPosition;\r
299         /// <summary>Actual rotation of the object in the region</summary>\r
300         public Quaternion SimRotation;\r
301         /// <summary>Rendered position of the object in the region</summary>\r
302         public Vector3 RenderPosition;\r
303         /// <summary>Rendered rotationm of the object in the region</summary>\r
304         public Quaternion RenderRotation;\r
305         /// <summary>Per frame calculated square of the distance from camera</summary>\r
306         public float DistanceSquared;\r
307         /// <summary>Bounding volume of the object</summary>\r
308         public BoundingVolume BoundingVolume;\r
309         /// <summary>Was the sim position and distance from camera calculated during this frame</summary>\r
310         public bool PositionCalculated;\r
311         /// <summary>Scene object type</summary>\r
312         public SceneObjectType Type = SceneObjectType.None;\r
313         /// <summary>Libomv primitive</summary>\r
314         public virtual Primitive BasePrim { get; set; }\r
315         /// <summary>Were initial initialization tasks done</summary>\r
316         public bool Initialized;\r
317 \r
318         /// <summary>\r
319         /// Cleanup resources used\r
320         /// </summary>\r
321         public virtual void Dispose()\r
322         {\r
323         }\r
324 \r
325         /// <summary>\r
326         /// Task performed the fist time object is set for rendering\r
327         /// </summary>\r
328         public virtual void Initialize()\r
329         {\r
330             RenderPosition = SimPosition;\r
331             RenderRotation = SimRotation;\r
332             Initialized = true;\r
333         }\r
334 \r
335         /// <summary>\r
336         /// Perform per frame tasks\r
337         /// </summary>\r
338         /// <param name="time">Time since the last call (last frame time in seconds)</param>\r
339         public virtual void Step(double time)\r
340         {\r
341         }\r
342 \r
343         /// <summary>\r
344         /// Implementation of the IComparable interface\r
345         /// used for sorting by distance\r
346         /// </summary>\r
347         /// <param name="other">Object we are comparing to</param>\r
348         /// <returns>Result of the comparison</returns>\r
349         public virtual int CompareTo(object other)\r
350         {\r
351             SceneObject o = (SceneObject)other;\r
352             if (this.DistanceSquared < o.DistanceSquared)\r
353                 return -1;\r
354             else if (this.DistanceSquared > o.DistanceSquared)\r
355                 return 1;\r
356             else\r
357                 return 0;\r
358         }\r
359     }\r
360 \r
361     public class RenderPrimitive : SceneObject\r
362     {\r
363         public Primitive Prim;\r
364         public List<Face> Faces;\r
365         /// <summary>Is this object attached to an avatar</summary>\r
366         public bool Attached;\r
367         /// <summary>Do we know if object is attached</summary>\r
368         public bool AttachedStateKnown;\r
369 \r
370         public RenderPrimitive()\r
371         {\r
372             Type = SceneObjectType.Primitive;\r
373         }\r
374 \r
375         public override Primitive BasePrim\r
376         {\r
377             get { return Prim; }\r
378             set { Prim = value; }\r
379         }\r
380 \r
381         public override void Initialize()\r
382         {\r
383             AttachedStateKnown = false;\r
384             base.Initialize();\r
385         }\r
386 \r
387         public override void Step(double time)\r
388         {\r
389             // Don't interpolate positions of attached objects\r
390             if (Attached)\r
391             {\r
392                 RenderPosition = SimPosition;\r
393                 RenderRotation = SimRotation;\r
394                 return;\r
395             }\r
396 \r
397             if (RenderPosition != SimPosition)\r
398             {\r
399                 RenderPosition = RHelp.Smoothed1stOrder(RenderPosition, SimPosition, time);\r
400             }\r
401             if (Prim.AngularVelocity != Vector3.Zero)\r
402             {\r
403                 Vector3 angVel = Prim.AngularVelocity;\r
404                 float angle = (float)time * angVel.Length();\r
405                 Quaternion dQ = Quaternion.CreateFromAxisAngle(angVel, angle);\r
406                 RenderRotation = dQ * RenderRotation;\r
407             }\r
408             else if (RenderRotation != SimRotation)\r
409             {\r
410                 RenderRotation = SimRotation;\r
411             }\r
412         }\r
413 \r
414         public override string ToString()\r
415         {\r
416             uint id = Prim == null ? 0 : Prim.LocalID;\r
417             double distance = Math.Sqrt(DistanceSquared);\r
418             return string.Format("LocalID: {0}, distance {0.00}", id, distance);\r
419         }\r
420     }\r
421 \r
422     public static class Render\r
423     {\r
424         public static IRendering Plugin;\r
425     }\r
426 \r
427     public static class RHelp\r
428     {\r
429         static float t1 = 0.075f;\r
430         static float t2 = t1 / 5.7f;\r
431 \r
432         public static Vector3 Smoothed1stOrder(Vector3 curPos, Vector3 targetPos, double lastFrameTime)\r
433         {\r
434             int numIterations = (int)(lastFrameTime * 100);\r
435             do\r
436             {\r
437                 curPos += (targetPos - curPos) * t1;\r
438                 numIterations--;\r
439             }\r
440             while (numIterations > 0);\r
441             if (Vector3.DistanceSquared(curPos, targetPos) < 0.000001)\r
442             {\r
443                 curPos = targetPos;\r
444             }\r
445             return curPos;\r
446         }\r
447 \r
448         public static Vector3 Smoothed2ndOrder(Vector3 curPos, Vector3 targetPos, ref Vector3 accel, double lastFrameTime)\r
449         {\r
450             int numIterations = (int)(lastFrameTime * 100);\r
451             do\r
452             {\r
453                 accel += (targetPos - accel - curPos) * t1;\r
454                 curPos += accel * t2;\r
455                 numIterations--;\r
456             }\r
457             while (numIterations > 0);\r
458             if (Vector3.DistanceSquared(curPos, targetPos) < 0.000001)\r
459             {\r
460                 curPos = targetPos;\r
461             }\r
462             return curPos;\r
463         }\r
464 \r
465         public static OpenTK.Vector2 TKVector3(Vector2 v)\r
466         {\r
467             return new OpenTK.Vector2(v.X, v.Y);\r
468         }\r
469 \r
470         public static OpenTK.Vector3 TKVector3(Vector3 v)\r
471         {\r
472             return new OpenTK.Vector3(v.X, v.Y, v.Z);\r
473         }\r
474 \r
475         public static OpenTK.Vector4 TKVector3(Vector4 v)\r
476         {\r
477             return new OpenTK.Vector4(v.X, v.Y, v.Z, v.W);\r
478         }\r
479     }\r
480 \r
481     /// <summary>\r
482     /// Represents camera object\r
483     /// </summary>\r
484     public class Camera\r
485     {\r
486         Vector3 mPosition;\r
487         Vector3 mFocalPoint;\r
488         bool mModified;\r
489 \r
490         /// <summary>Camera position</summary>\r
491         public Vector3 Position { get { return mPosition; } set { mPosition = value; Modify(); } }\r
492         /// <summary>Camera target</summary>\r
493         public Vector3 FocalPoint { get { return mFocalPoint; } set { mFocalPoint = value; Modify(); } }\r
494         /// <summary>Zoom level</summary>\r
495         public float Zoom;\r
496         /// <summary>Draw distance</summary>\r
497         public float Far;\r
498         /// <summary>Has camera been modified</summary>\r
499         public bool Modified { get { return mModified; } set { mModified = value; } }\r
500 \r
501         public double TimeToTarget = 0d;\r
502 \r
503         public Vector3 RenderPosition;\r
504         public Vector3 RenderFocalPoint;\r
505 \r
506         void Modify()\r
507         {\r
508             mModified = true;\r
509         }\r
510 \r
511         public void Step(double time)\r
512         {\r
513             if (RenderPosition != Position)\r
514             {\r
515                 RenderPosition = RHelp.Smoothed1stOrder(RenderPosition, Position, time);\r
516                 Modified = true;\r
517             }\r
518             if (RenderFocalPoint != FocalPoint)\r
519             {\r
520                 RenderFocalPoint = RHelp.Smoothed1stOrder(RenderFocalPoint, FocalPoint, time);\r
521                 Modified = true;\r
522             }\r
523         }\r
524 \r
525         [Obsolete("Use Step(), left in here for reference")]\r
526         public void Step2(double time)\r
527         {\r
528             TimeToTarget -= time;\r
529             if (TimeToTarget <= time)\r
530             {\r
531                 EndMove();\r
532                 return;\r
533             }\r
534 \r
535             mModified = true;\r
536 \r
537             float pctElapsed = (float)(time / TimeToTarget);\r
538 \r
539             if (RenderPosition != Position)\r
540             {\r
541                 float distance = Vector3.Distance(RenderPosition, Position);\r
542                 RenderPosition = Vector3.Lerp(RenderPosition, Position, (float)(distance * pctElapsed));\r
543             }\r
544 \r
545             if (RenderFocalPoint != FocalPoint)\r
546             {\r
547                 RenderFocalPoint = Interpolate(RenderFocalPoint, FocalPoint, pctElapsed);\r
548             }\r
549         }\r
550 \r
551         Vector3 Interpolate(Vector3 start, Vector3 end, float fraction)\r
552         {\r
553             float distance = Vector3.Distance(start, end);\r
554             Vector3 direction = end - start;\r
555             return start + direction * fraction;\r
556         }\r
557 \r
558         public void EndMove()\r
559         {\r
560             mModified = true;\r
561             TimeToTarget = 0;\r
562             RenderPosition = Position;\r
563             RenderFocalPoint = FocalPoint;\r
564         }\r
565     }\r
566 \r
567     public static class MeshToOBJ\r
568     {\r
569         public static bool MeshesToOBJ(Dictionary<uint, FacetedMesh> meshes, string filename)\r
570         {\r
571             StringBuilder obj = new StringBuilder();\r
572             StringBuilder mtl = new StringBuilder();\r
573 \r
574             FileInfo objFileInfo = new FileInfo(filename);\r
575 \r
576             string mtlFilename = objFileInfo.FullName.Substring(objFileInfo.DirectoryName.Length + 1,\r
577                 objFileInfo.FullName.Length - (objFileInfo.DirectoryName.Length + 1) - 4) + ".mtl";\r
578 \r
579             obj.AppendLine("# Created by libprimrender");\r
580             obj.AppendLine("mtllib ./" + mtlFilename);\r
581             obj.AppendLine();\r
582 \r
583             mtl.AppendLine("# Created by libprimrender");\r
584             mtl.AppendLine();\r
585 \r
586             int primNr = 0;\r
587             foreach (FacetedMesh mesh in meshes.Values)\r
588             {\r
589                 for (int j = 0; j < mesh.Faces.Count; j++)\r
590                 {\r
591                     Face face = mesh.Faces[j];\r
592 \r
593                     if (face.Vertices.Count > 2)\r
594                     {\r
595                         string mtlName = String.Format("material{0}-{1}", primNr, face.ID);\r
596                         Primitive.TextureEntryFace tex = face.TextureFace;\r
597                         string texName = tex.TextureID.ToString() + ".tga";\r
598 \r
599                         // FIXME: Convert the source to TGA (if needed) and copy to the destination\r
600 \r
601                         float shiny = 0.00f;\r
602                         switch (tex.Shiny)\r
603                         {\r
604                             case Shininess.High:\r
605                                 shiny = 1.00f;\r
606                                 break;\r
607                             case Shininess.Medium:\r
608                                 shiny = 0.66f;\r
609                                 break;\r
610                             case Shininess.Low:\r
611                                 shiny = 0.33f;\r
612                                 break;\r
613                         }\r
614 \r
615                         obj.AppendFormat("g face{0}-{1}{2}", primNr, face.ID, Environment.NewLine);\r
616 \r
617                         mtl.AppendLine("newmtl " + mtlName);\r
618                         mtl.AppendFormat("Ka {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);\r
619                         mtl.AppendFormat("Kd {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);\r
620                         //mtl.AppendFormat("Ks {0} {1} {2}{3}");\r
621                         mtl.AppendLine("Tr " + tex.RGBA.A);\r
622                         mtl.AppendLine("Ns " + shiny);\r
623                         mtl.AppendLine("illum 1");\r
624                         if (tex.TextureID != UUID.Zero && tex.TextureID != Primitive.TextureEntry.WHITE_TEXTURE)\r
625                             mtl.AppendLine("map_Kd ./" + texName);\r
626                         mtl.AppendLine();\r
627 \r
628                         // Write the vertices, texture coordinates, and vertex normals for this side\r
629                         for (int k = 0; k < face.Vertices.Count; k++)\r
630                         {\r
631                             Vertex vertex = face.Vertices[k];\r
632 \r
633                             #region Vertex\r
634 \r
635                             Vector3 pos = vertex.Position;\r
636 \r
637                             // Apply scaling\r
638                             pos *= mesh.Prim.Scale;\r
639 \r
640                             // Apply rotation\r
641                             pos *= mesh.Prim.Rotation;\r
642 \r
643                             // The root prim position is sim-relative, while child prim positions are\r
644                             // parent-relative. We want to apply parent-relative translations but not\r
645                             // sim-relative ones\r
646                             if (mesh.Prim.ParentID != 0)\r
647                                 pos += mesh.Prim.Position;\r
648 \r
649                             obj.AppendFormat("v {0} {1} {2}{3}", pos.X, pos.Y, pos.Z, Environment.NewLine);\r
650 \r
651                             #endregion Vertex\r
652 \r
653                             #region Texture Coord\r
654 \r
655                             obj.AppendFormat("vt {0} {1}{2}", vertex.TexCoord.X, vertex.TexCoord.Y,\r
656                                 Environment.NewLine);\r
657 \r
658                             #endregion Texture Coord\r
659 \r
660                             #region Vertex Normal\r
661 \r
662                             // HACK: Sometimes normals are getting set to <NaN,NaN,NaN>\r
663                             if (!Single.IsNaN(vertex.Normal.X) && !Single.IsNaN(vertex.Normal.Y) && !Single.IsNaN(vertex.Normal.Z))\r
664                                 obj.AppendFormat("vn {0} {1} {2}{3}", vertex.Normal.X, vertex.Normal.Y, vertex.Normal.Z,\r
665                                     Environment.NewLine);\r
666                             else\r
667                                 obj.AppendLine("vn 0.0 1.0 0.0");\r
668 \r
669                             #endregion Vertex Normal\r
670                         }\r
671 \r
672                         obj.AppendFormat("# {0} vertices{1}", face.Vertices.Count, Environment.NewLine);\r
673                         obj.AppendLine();\r
674                         obj.AppendLine("usemtl " + mtlName);\r
675 \r
676                         #region Elements\r
677 \r
678                         // Write all of the faces (triangles) for this side\r
679                         for (int k = 0; k < face.Indices.Count / 3; k++)\r
680                         {\r
681                             obj.AppendFormat("f -{0}/-{0}/-{0} -{1}/-{1}/-{1} -{2}/-{2}/-{2}{3}",\r
682                                 face.Vertices.Count - face.Indices[k * 3 + 0],\r
683                                 face.Vertices.Count - face.Indices[k * 3 + 1],\r
684                                 face.Vertices.Count - face.Indices[k * 3 + 2],\r
685                                 Environment.NewLine);\r
686                         }\r
687 \r
688                         obj.AppendFormat("# {0} elements{1}", face.Indices.Count / 3, Environment.NewLine);\r
689                         obj.AppendLine();\r
690 \r
691                         #endregion Elements\r
692                     }\r
693                 }\r
694                 primNr++;\r
695             }\r
696 \r
697             try\r
698             {\r
699                 File.WriteAllText(filename, obj.ToString());\r
700                 File.WriteAllText(mtlFilename, mtl.ToString());\r
701             }\r
702             catch (Exception)\r
703             {\r
704                 return false;\r
705             }\r
706 \r
707             return true;\r
708         }\r
709     }\r
710 \r
711     public static class Math3D\r
712     {\r
713         // Column-major:\r
714         // |  0  4  8 12 |\r
715         // |  1  5  9 13 |\r
716         // |  2  6 10 14 |\r
717         // |  3  7 11 15 |\r
718 \r
719         public static float[] CreateTranslationMatrix(Vector3 v)\r
720         {\r
721             float[] mat = new float[16];\r
722 \r
723             mat[12] = v.X;\r
724             mat[13] = v.Y;\r
725             mat[14] = v.Z;\r
726             mat[0] = mat[5] = mat[10] = mat[15] = 1;\r
727 \r
728             return mat;\r
729         }\r
730 \r
731         public static float[] CreateRotationMatrix(Quaternion q)\r
732         {\r
733             float[] mat = new float[16];\r
734 \r
735             // Transpose the quaternion (don't ask me why)\r
736             q.X = q.X * -1f;\r
737             q.Y = q.Y * -1f;\r
738             q.Z = q.Z * -1f;\r
739 \r
740             float x2 = q.X + q.X;\r
741             float y2 = q.Y + q.Y;\r
742             float z2 = q.Z + q.Z;\r
743             float xx = q.X * x2;\r
744             float xy = q.X * y2;\r
745             float xz = q.X * z2;\r
746             float yy = q.Y * y2;\r
747             float yz = q.Y * z2;\r
748             float zz = q.Z * z2;\r
749             float wx = q.W * x2;\r
750             float wy = q.W * y2;\r
751             float wz = q.W * z2;\r
752 \r
753             mat[0] = 1.0f - (yy + zz);\r
754             mat[1] = xy - wz;\r
755             mat[2] = xz + wy;\r
756             mat[3] = 0.0f;\r
757 \r
758             mat[4] = xy + wz;\r
759             mat[5] = 1.0f - (xx + zz);\r
760             mat[6] = yz - wx;\r
761             mat[7] = 0.0f;\r
762 \r
763             mat[8] = xz - wy;\r
764             mat[9] = yz + wx;\r
765             mat[10] = 1.0f - (xx + yy);\r
766             mat[11] = 0.0f;\r
767 \r
768             mat[12] = 0.0f;\r
769             mat[13] = 0.0f;\r
770             mat[14] = 0.0f;\r
771             mat[15] = 1.0f;\r
772 \r
773             return mat;\r
774         }\r
775 \r
776         public static float[] CreateSRTMatrix(Vector3 scale, Quaternion q, Vector3 pos)\r
777         {\r
778             float[] mat = new float[16];\r
779 \r
780             // Transpose the quaternion (don't ask me why)\r
781             q.X = q.X * -1f;\r
782             q.Y = q.Y * -1f;\r
783             q.Z = q.Z * -1f;\r
784 \r
785             float x2 = q.X + q.X;\r
786             float y2 = q.Y + q.Y;\r
787             float z2 = q.Z + q.Z;\r
788             float xx = q.X * x2;\r
789             float xy = q.X * y2;\r
790             float xz = q.X * z2;\r
791             float yy = q.Y * y2;\r
792             float yz = q.Y * z2;\r
793             float zz = q.Z * z2;\r
794             float wx = q.W * x2;\r
795             float wy = q.W * y2;\r
796             float wz = q.W * z2;\r
797 \r
798             mat[0] = (1.0f - (yy + zz)) * scale.X;\r
799             mat[1] = (xy - wz) * scale.X;\r
800             mat[2] = (xz + wy) * scale.X;\r
801             mat[3] = 0.0f;\r
802 \r
803             mat[4] = (xy + wz) * scale.Y;\r
804             mat[5] = (1.0f - (xx + zz)) * scale.Y;\r
805             mat[6] = (yz - wx) * scale.Y;\r
806             mat[7] = 0.0f;\r
807 \r
808             mat[8] = (xz - wy) * scale.Z;\r
809             mat[9] = (yz + wx) * scale.Z;\r
810             mat[10] = (1.0f - (xx + yy)) * scale.Z;\r
811             mat[11] = 0.0f;\r
812 \r
813             //Positional parts\r
814             mat[12] = pos.X;\r
815             mat[13] = pos.Y;\r
816             mat[14] = pos.Z;\r
817             mat[15] = 1.0f;\r
818 \r
819             return mat;\r
820         }\r
821 \r
822 \r
823         public static float[] CreateScaleMatrix(Vector3 v)\r
824         {\r
825             float[] mat = new float[16];\r
826 \r
827             mat[0] = v.X;\r
828             mat[5] = v.Y;\r
829             mat[10] = v.Z;\r
830             mat[15] = 1;\r
831 \r
832             return mat;\r
833         }\r
834 \r
835         public static float[] Lerp(float[] matrix1, float[] matrix2, float amount)\r
836         {\r
837 \r
838             float[] lerp = new float[16];\r
839             //Probably not doing this as a loop is cheaper(unrolling)\r
840             //also for performance we probably should not create new objects\r
841             // but meh.\r
842             for (int x = 0; x < 16; x++)\r
843             {\r
844                 lerp[x] = matrix1[x] + ((matrix2[x] - matrix1[x]) * amount);\r
845             }\r
846 \r
847             return lerp;\r
848         }\r
849 \r
850 \r
851         public static bool GluProject(OpenTK.Vector3 objPos, OpenTK.Matrix4 modelMatrix, OpenTK.Matrix4 projMatrix, int[] viewport, out OpenTK.Vector3 screenPos)\r
852         {\r
853             OpenTK.Vector4 _in;\r
854             OpenTK.Vector4 _out;\r
855 \r
856             _in.X = objPos.X;\r
857             _in.Y = objPos.Y;\r
858             _in.Z = objPos.Z;\r
859             _in.W = 1.0f;\r
860 \r
861             _out = OpenTK.Vector4.Transform(_in, modelMatrix);\r
862             _in = OpenTK.Vector4.Transform(_out, projMatrix);\r
863 \r
864             if (_in.W <= 0.0)\r
865             {\r
866                 screenPos = OpenTK.Vector3.Zero;\r
867                 return false;\r
868             }\r
869 \r
870             _in.X /= _in.W;\r
871             _in.Y /= _in.W;\r
872             _in.Z /= _in.W;\r
873             /* Map x, y and z to range 0-1 */\r
874             _in.X = _in.X * 0.5f + 0.5f;\r
875             _in.Y = _in.Y * 0.5f + 0.5f;\r
876             _in.Z = _in.Z * 0.5f + 0.5f;\r
877 \r
878             /* Map x,y to viewport */\r
879             _in.X = _in.X * viewport[2] + viewport[0];\r
880             _in.Y = _in.Y * viewport[3] + viewport[1];\r
881 \r
882             screenPos.X = _in.X;\r
883             screenPos.Y = _in.Y;\r
884             screenPos.Z = _in.Z;\r
885 \r
886             return true;\r
887         }\r
888     }\r
889 \r
890     public class attachment_point\r
891     {\r
892         public string name;\r
893         public string joint;\r
894         public Vector3 position;\r
895         public Quaternion rotation;\r
896         public int id;\r
897         public int group;\r
898 \r
899         public GLMesh jointmesh;\r
900         public int jointmeshindex;\r
901 \r
902         public attachment_point(XmlNode node)\r
903         {\r
904             name = node.Attributes.GetNamedItem("name").Value;\r
905             joint = node.Attributes.GetNamedItem("joint").Value;\r
906             position = VisualParamEx.XmlParseVector(node.Attributes.GetNamedItem("position").Value);\r
907             rotation = VisualParamEx.XmlParseRotation(node.Attributes.GetNamedItem("rotation").Value);\r
908             id = Int32.Parse(node.Attributes.GetNamedItem("id").Value);\r
909             group = Int32.Parse(node.Attributes.GetNamedItem("group").Value);\r
910         }\r
911 \r
912     }\r
913 \r
914     /// <summary>\r
915     /// Subclass of LindenMesh that adds vertex, index, and texture coordinate\r
916     /// arrays suitable for pushing direct to OpenGL\r
917     /// </summary>\r
918     public class GLMesh : LindenMesh\r
919     {\r
920         /// <summary>\r
921         /// Subclass of LODMesh that adds an index array suitable for pushing\r
922         /// direct to OpenGL\r
923         /// </summary>\r
924         /// \r
925 \r
926         public int teFaceID;\r
927         public Dictionary<int, VisualParamEx> _evp = new Dictionary<int, VisualParamEx>();\r
928 \r
929         new public class LODMesh : LindenMesh.LODMesh\r
930         {\r
931             public ushort[] Indices;\r
932 \r
933             public override void LoadMesh(string filename)\r
934             {\r
935                 base.LoadMesh(filename);\r
936 \r
937                 // Generate the index array\r
938                 Indices = new ushort[_numFaces * 3];\r
939                 int current = 0;\r
940                 for (int i = 0; i < _numFaces; i++)\r
941                 {\r
942                     Indices[current++] = (ushort)_faces[i].Indices[0];\r
943                     Indices[current++] = (ushort)_faces[i].Indices[1];\r
944                     Indices[current++] = (ushort)_faces[i].Indices[2];\r
945                 }\r
946             }\r
947         }\r
948 \r
949         /// <summary>\r
950         /// \r
951         /// </summary>\r
952         public struct GLData\r
953         {\r
954             public float[] Vertices;\r
955             public float[] Normals;\r
956             public ushort[] Indices;\r
957             public float[] TexCoords;\r
958             public Vector3 Center;\r
959             public float[] weights; //strictly these are constant and don't need instancing with the GLMesh\r
960             public string[] skinJoints;  //strictly these are constant and don't need instancing with the GLMesh\r
961         }\r
962 \r
963         public static GLData baseRenderData;\r
964         public GLData RenderData;\r
965         public GLData OrigRenderData;\r
966         public GLData MorphRenderData;\r
967 \r
968         public GLAvatar av;\r
969 \r
970         public GLMesh(string name)\r
971             : base(name)\r
972         {\r
973         }\r
974 \r
975         public GLMesh(GLMesh source, GLAvatar av)\r
976             : base(source.Name)\r
977         {\r
978             this.av = av;\r
979             // Make a new GLMesh copy from the supplied source\r
980 \r
981             RenderData.Vertices = new float[source.RenderData.Vertices.Length];\r
982             RenderData.Normals = new float[source.RenderData.Normals.Length];\r
983             RenderData.TexCoords = new float[source.RenderData.TexCoords.Length];\r
984             RenderData.Indices = new ushort[source.RenderData.Indices.Length];\r
985 \r
986             RenderData.weights = new float[source.RenderData.weights.Length];\r
987             RenderData.skinJoints = new string[source.RenderData.skinJoints.Length];\r
988 \r
989             Array.Copy(source.RenderData.Vertices, RenderData.Vertices, source.RenderData.Vertices.Length);\r
990             Array.Copy(source.RenderData.Normals, RenderData.Normals, source.RenderData.Normals.Length);\r
991 \r
992             Array.Copy(source.RenderData.TexCoords, RenderData.TexCoords, source.RenderData.TexCoords.Length);\r
993             Array.Copy(source.RenderData.Indices, RenderData.Indices, source.RenderData.Indices.Length);\r
994             Array.Copy(source.RenderData.weights, RenderData.weights, source.RenderData.weights.Length);\r
995             Array.Copy(source.RenderData.skinJoints, RenderData.skinJoints, source.RenderData.skinJoints.Length);\r
996 \r
997 \r
998             RenderData.Center = new Vector3(source.RenderData.Center);\r
999 \r
1000             teFaceID = source.teFaceID;\r
1001 \r
1002             _rotationAngles = new Vector3(source.RotationAngles);\r
1003             _scale = new Vector3(source.Scale);\r
1004             _position = new Vector3(source.Position);\r
1005 \r
1006             // We should not need to instance these the reference from the top should be constant\r
1007             _evp = source._evp;\r
1008             _morphs = source._morphs;\r
1009 \r
1010             OrigRenderData.Indices = new ushort[source.RenderData.Indices.Length];\r
1011             OrigRenderData.TexCoords = new float[source.RenderData.TexCoords.Length];\r
1012             OrigRenderData.Vertices = new float[source.RenderData.Vertices.Length];\r
1013 \r
1014             MorphRenderData.Vertices = new float[source.RenderData.Vertices.Length];\r
1015 \r
1016             Array.Copy(source.RenderData.Vertices, OrigRenderData.Vertices, source.RenderData.Vertices.Length);\r
1017             Array.Copy(source.RenderData.Vertices, MorphRenderData.Vertices, source.RenderData.Vertices.Length);\r
1018 \r
1019             Array.Copy(source.RenderData.TexCoords, OrigRenderData.TexCoords, source.RenderData.TexCoords.Length);\r
1020             Array.Copy(source.RenderData.Indices, OrigRenderData.Indices, source.RenderData.Indices.Length);\r
1021 \r
1022 \r
1023 \r
1024         }\r
1025 \r
1026         public void setMeshPos(Vector3 pos)\r
1027         {\r
1028             _position = pos;\r
1029         }\r
1030 \r
1031         public void setMeshRot(Vector3 rot)\r
1032         {\r
1033             _rotationAngles = rot;\r
1034         }\r
1035 \r
1036         public override void LoadMesh(string filename)\r
1037         {\r
1038             base.LoadMesh(filename);\r
1039 \r
1040             float minX, minY, minZ;\r
1041             minX = minY = minZ = Single.MaxValue;\r
1042             float maxX, maxY, maxZ;\r
1043             maxX = maxY = maxZ = Single.MinValue;\r
1044 \r
1045             // Generate the vertex array\r
1046             RenderData.Vertices = new float[_numVertices * 3];\r
1047             RenderData.Normals = new float[_numVertices * 3];\r
1048 \r
1049             Quaternion quat = Quaternion.CreateFromEulers(0, 0, (float)(Math.PI / 4.0));\r
1050 \r
1051             int current = 0;\r
1052             for (int i = 0; i < _numVertices; i++)\r
1053             {\r
1054 \r
1055                 RenderData.Normals[current] = _vertices[i].Normal.X;\r
1056                 RenderData.Vertices[current++] = _vertices[i].Coord.X;\r
1057                 RenderData.Normals[current] = _vertices[i].Normal.Y;\r
1058                 RenderData.Vertices[current++] = _vertices[i].Coord.Y;\r
1059                 RenderData.Normals[current] = _vertices[i].Normal.Z;\r
1060                 RenderData.Vertices[current++] = _vertices[i].Coord.Z;\r
1061 \r
1062                 if (_vertices[i].Coord.X < minX)\r
1063                     minX = _vertices[i].Coord.X;\r
1064                 else if (_vertices[i].Coord.X > maxX)\r
1065                     maxX = _vertices[i].Coord.X;\r
1066 \r
1067                 if (_vertices[i].Coord.Y < minY)\r
1068                     minY = _vertices[i].Coord.Y;\r
1069                 else if (_vertices[i].Coord.Y > maxY)\r
1070                     maxY = _vertices[i].Coord.Y;\r
1071 \r
1072                 if (_vertices[i].Coord.Z < minZ)\r
1073                     minZ = _vertices[i].Coord.Z;\r
1074                 else if (_vertices[i].Coord.Z > maxZ)\r
1075                     maxZ = _vertices[i].Coord.Z;\r
1076             }\r
1077 \r
1078             // Calculate the center-point from the bounding box edges\r
1079             RenderData.Center = new Vector3((minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2);\r
1080 \r
1081             // Generate the index array\r
1082             RenderData.Indices = new ushort[_numFaces * 3];\r
1083             current = 0;\r
1084             for (int i = 0; i < _numFaces; i++)\r
1085             {\r
1086                 RenderData.Indices[current++] = (ushort)_faces[i].Indices[0];\r
1087                 RenderData.Indices[current++] = (ushort)_faces[i].Indices[1];\r
1088                 RenderData.Indices[current++] = (ushort)_faces[i].Indices[2];\r
1089             }\r
1090 \r
1091             // Generate the texcoord array\r
1092             RenderData.TexCoords = new float[_numVertices * 2];\r
1093             current = 0;\r
1094             for (int i = 0; i < _numVertices; i++)\r
1095             {\r
1096                 RenderData.TexCoords[current++] = _vertices[i].TexCoord.X;\r
1097                 RenderData.TexCoords[current++] = _vertices[i].TexCoord.Y;\r
1098             }\r
1099 \r
1100             RenderData.weights = new float[_numVertices];\r
1101             for (int i = 0; i < _numVertices; i++)\r
1102             {\r
1103                 RenderData.weights[i] = _vertices[i].Weight;\r
1104             }\r
1105 \r
1106             RenderData.skinJoints = new string[_skinJoints.Length + 3];\r
1107             for (int i = 1; i < _skinJoints.Length; i++)\r
1108             {\r
1109                 RenderData.skinJoints[i] = _skinJoints[i];\r
1110             }\r
1111 \r
1112 \r
1113         }\r
1114 \r
1115         public override void LoadLODMesh(int level, string filename)\r
1116         {\r
1117             LODMesh lod = new LODMesh();\r
1118             lod.LoadMesh(filename);\r
1119             _lodMeshes[level] = lod;\r
1120         }\r
1121 \r
1122         public void applyjointweights()\r
1123         {\r
1124 \r
1125             /*Each weight actually contains two pieces of information. \r
1126              * The number to the left of the decimal point is the index of the joint and also \r
1127              * implicitly indexes to the following joint. The actual weight is to the right of \r
1128              * the decimal point and interpolates between these two joints. The index is into an \r
1129              * "expanded" list of joints, not just a linear array of the joints as defined in the \r
1130              * skeleton file. In particular, any joint that has more than one child will be repeated \r
1131              * in the list for each of its children.\r
1132              */\r
1133 \r
1134             float weight = -9999;\r
1135             int jointindex = 0;\r
1136             float factor;\r
1137 \r
1138             Bone ba = null;\r
1139             Bone bb = null;\r
1140 \r
1141             for (int v = 0, x = 0; v < RenderData.Vertices.Length; v = v + 3, x++)\r
1142             {\r
1143                 if (weight != RenderData.weights[x])\r
1144                 {\r
1145 \r
1146                     jointindex = (int)Math.Floor(weight = RenderData.weights[x]);\r
1147                     factor = RenderData.weights[x] - jointindex;\r
1148                     weight = weight - jointindex;\r
1149 \r
1150                     string jointname = "", jointname2 = "";\r
1151 \r
1152                     if (this.Name == "upperBodyMesh")\r
1153                     {\r
1154                         jointname = skeleton.mUpperMeshMapping[jointindex];\r
1155                         jointindex++;\r
1156                         jointname2 = skeleton.mUpperMeshMapping[jointindex];\r
1157                     }\r
1158                     else if (Name == "lowerBodyMesh")\r
1159                     {\r
1160                         jointname = skeleton.mLowerMeshMapping[jointindex];\r
1161                         jointindex++;\r
1162                         jointname2 = skeleton.mLowerMeshMapping[jointindex];\r
1163                     }\r
1164                     else if (Name == "headMesh")\r
1165                     {\r
1166                         jointname = skeleton.mHeadMeshMapping[jointindex];\r
1167                         jointindex++;\r
1168                         jointname2 = skeleton.mHeadMeshMapping[jointindex];\r
1169                     }\r
1170                     else\r
1171                     {\r
1172                         return; // not interested in this mesh\r
1173                     }\r
1174 \r
1175 \r
1176                     if (jointname == "")\r
1177                     {\r
1178                         //Don't yet handle this, its a split joint to two children\r
1179                         continue;\r
1180                     }\r
1181                     else\r
1182                     {\r
1183                         ba = av.skel.mBones[jointname];\r
1184                     }\r
1185 \r
1186                     if (jointname2 == "")\r
1187                     {\r
1188                         bb = null;\r
1189                     }\r
1190                     else\r
1191                     {\r
1192                         bb = av.skel.mBones[jointname2];\r
1193                     }\r
1194                 }\r
1195 \r
1196                 //Special cases 0 is not used\r
1197                 // ON upper torso 5 and 10 are not used\r
1198                 // 4 is neck and 6 and 11 are the left and right collar bones\r
1199 \r
1200                 Vector3 lerp;\r
1201 \r
1202                 Vector3 offset;\r
1203                 Quaternion rot = ba.getRotation();\r
1204 \r
1205                 if (bb != null)\r
1206                 {\r
1207                     Vector3 oa = ba.getOffset() - ba.getOrigOffset();\r
1208                     Vector3 ob = bb.getOffset() - bb.getOrigOffset();\r
1209                     lerp = Vector3.Lerp(oa, ob, weight);\r
1210                     offset = Vector3.Lerp(ba.getOffset(), bb.getOffset(), weight);\r
1211                 }\r
1212                 else\r
1213                 {\r
1214                     lerp = ba.getOffset() - ba.getOrigOffset();\r
1215                     offset = ba.getOffset();\r
1216                     rot = ba.getRotation();\r
1217                 }\r
1218 \r
1219                 Vector3 pos = new Vector3(MorphRenderData.Vertices[v], MorphRenderData.Vertices[v + 1], MorphRenderData.Vertices[v + 2]);\r
1220                 pos = pos + lerp;\r
1221                 pos = pos - offset;\r
1222                 pos = pos * rot;\r
1223                 pos = pos + offset;\r
1224 \r
1225                 RenderData.Vertices[v] = pos.X;\r
1226                 RenderData.Vertices[v + 1] = pos.Y;\r
1227                 RenderData.Vertices[v + 2] = pos.Z;\r
1228             }\r
1229         }\r
1230 \r
1231         public void morphmesh(Morph morph, float weight)\r
1232         {\r
1233             for (int v = 0; v < morph.NumVertices; v++)\r
1234             {\r
1235                 MorphVertex mvx = morph.Vertices[v];\r
1236 \r
1237                 uint i = mvx.VertexIndex;\r
1238 \r
1239                 MorphRenderData.Vertices[i * 3] = OrigRenderData.Vertices[i * 3] + mvx.Coord.X * weight;\r
1240                 MorphRenderData.Vertices[(i * 3) + 1] = OrigRenderData.Vertices[i * 3 + 1] + mvx.Coord.Y * weight;\r
1241                 MorphRenderData.Vertices[(i * 3) + 2] = OrigRenderData.Vertices[i * 3 + 2] + mvx.Coord.Z * weight;\r
1242 \r
1243                 RenderData.TexCoords[i * 2] = OrigRenderData.TexCoords[i * 2] + mvx.TexCoord.X * weight;\r
1244                 RenderData.TexCoords[(i * 2) + 1] = OrigRenderData.TexCoords[i * 2 + 1] + mvx.TexCoord.Y * weight;\r
1245 \r
1246             }\r
1247         }\r
1248     }\r
1249 \r
1250     public class GLAvatar\r
1251     {\r
1252         private static Dictionary<string, GLMesh> _defaultmeshes = new Dictionary<string, GLMesh>();\r
1253         public Dictionary<string, GLMesh> _meshes = new Dictionary<string, GLMesh>();\r
1254 \r
1255         public skeleton skel = new skeleton();\r
1256         public static Dictionary<int, attachment_point> attachment_points = new Dictionary<int, attachment_point>();\r
1257 \r
1258         public bool _wireframe = true;\r
1259         public bool _showSkirt = false;\r
1260 \r
1261         public VisualParamEx.EparamSex msex;\r
1262 \r
1263         public byte[] VisualAppearanceParameters = new byte[1024];\r
1264         bool vpsent = false;\r
1265         static bool lindenMeshesLoaded = false;\r
1266 \r
1267         public GLAvatar()\r
1268         {\r
1269             foreach (KeyValuePair<string, GLMesh> kvp in _defaultmeshes)\r
1270             {\r
1271                 GLMesh mesh = new GLMesh(kvp.Value, this); // Instance our meshes\r
1272                 _meshes.Add(kvp.Key, mesh);\r
1273 \r
1274             }\r
1275         }\r
1276 \r
1277         public static void dumptweaks()\r
1278         {\r
1279 \r
1280             for (int x = 0; x < VisualParamEx.tweakable_params.Count; x++)\r
1281             {\r
1282                 VisualParamEx vpe = (VisualParamEx)VisualParamEx.tweakable_params.GetByIndex(x);\r
1283                 Console.WriteLine(string.Format("{0} is {1}", x, vpe.Name));\r
1284             }\r
1285 \r
1286 \r
1287         }\r
1288 \r
1289         public static void loadlindenmeshes2(string LODfilename)\r
1290         {\r
1291             // Already have mashes loaded?\r
1292             if (lindenMeshesLoaded) return;\r
1293 \r
1294             attachment_points.Clear();\r
1295 \r
1296 \r
1297             string basedir = Directory.GetCurrentDirectory() + System.IO.Path.DirectorySeparatorChar + "character" + System.IO.Path.DirectorySeparatorChar;\r
1298 \r
1299             XmlDocument lad = new XmlDocument();\r
1300             lad.Load(basedir + LODfilename);\r
1301 \r
1302             //Firstly read the skeleton section this contains attachment point info and the bone deform info for visual params\r
1303             // And load the skeleton file in to the bones class\r
1304 \r
1305             XmlNodeList skeleton = lad.GetElementsByTagName("skeleton");\r
1306             string skeletonfilename = skeleton[0].Attributes.GetNamedItem("file_name").Value;\r
1307             Bone.loadbones(skeletonfilename);\r
1308 \r
1309             // Next read all the skeleton child nodes, we have attachment points and bone deform params\r
1310             // attachment points are an offset and rotation from a bone location\r
1311             // the name of the bone they reference is the joint paramater\r
1312             // params in the skeleton nodes are bone deforms, eg leg length changes the scale of the leg bones\r
1313 \r
1314             foreach (XmlNode skeletonnode in skeleton[0].ChildNodes)\r
1315             {\r
1316                 if (skeletonnode.Name == "attachment_point")\r
1317                 {\r
1318                     attachment_point point = new attachment_point(skeletonnode);\r
1319                     attachment_points.Add(point.id, point);\r
1320                 }\r
1321 \r
1322                 if (skeletonnode.Name == "param")\r
1323                 {\r
1324                     //Bone deform param\r
1325                     VisualParamEx vp = new VisualParamEx(skeletonnode, VisualParamEx.ParamType.TYPE_BONEDEFORM);\r
1326                 }\r
1327             }\r
1328 \r
1329             //Now we parse the mesh nodes, mesh nodes reference a particular LLM file with a LOD\r
1330             //and also list VisualParams for the various mesh morphs that can be applied\r
1331 \r
1332             XmlNodeList meshes = lad.GetElementsByTagName("mesh");\r
1333             foreach (XmlNode meshNode in meshes)\r
1334             {\r
1335                 string type = meshNode.Attributes.GetNamedItem("type").Value;\r
1336                 int lod = Int32.Parse(meshNode.Attributes.GetNamedItem("lod").Value);\r
1337                 string fileName = meshNode.Attributes.GetNamedItem("file_name").Value;\r
1338 \r
1339                 GLMesh mesh = (_defaultmeshes.ContainsKey(type) ? _defaultmeshes[type] : new GLMesh(type));\r
1340 \r
1341                 if (meshNode.HasChildNodes)\r
1342                 {\r
1343                     foreach (XmlNode paramnode in meshNode.ChildNodes)\r
1344                     {\r
1345                         if (paramnode.Name == "param")\r
1346                         {\r
1347                             VisualParamEx vp = new VisualParamEx(paramnode, VisualParamEx.ParamType.TYPE_MORPH);\r
1348 \r
1349                             mesh._evp.Add(vp.ParamID, vp); //Not sure we really need this may optimise out later\r
1350                             vp.morphmesh = mesh.Name;\r
1351                         }\r
1352                     }\r
1353                 }\r
1354 \r
1355                 // Set up the texture elemenets for each mesh\r
1356                 // And hack the eyeball position\r
1357                 switch (mesh.Name)\r
1358                 {\r
1359                     case "lowerBodyMesh":\r
1360                         mesh.teFaceID = (int)AvatarTextureIndex.LowerBaked;\r
1361                         break;\r
1362 \r
1363                     case "upperBodyMesh":\r
1364                         mesh.teFaceID = (int)AvatarTextureIndex.UpperBaked;\r
1365                         break;\r
1366 \r
1367                     case "headMesh":\r
1368                         mesh.teFaceID = (int)AvatarTextureIndex.HeadBaked;\r
1369                         break;\r
1370 \r
1371                     case "hairMesh":\r
1372                         mesh.teFaceID = (int)AvatarTextureIndex.HairBaked;\r
1373                         break;\r
1374 \r
1375                     case "eyelashMesh":\r
1376                         mesh.teFaceID = (int)AvatarTextureIndex.HeadBaked;\r
1377                         break;\r
1378 \r
1379                     case "eyeBallRightMesh":\r
1380                         mesh.setMeshPos(Bone.mBones["mEyeLeft"].getOffset());\r
1381                         //mesh.setMeshRot(Bone.getRotation("mEyeLeft"));\r
1382                         mesh.teFaceID = (int)AvatarTextureIndex.EyesBaked;\r
1383                         break;\r
1384 \r
1385                     case "eyeBallLeftMesh":\r
1386                         mesh.setMeshPos(Bone.mBones["mEyeRight"].getOffset());\r
1387                         //mesh.setMeshRot(Bone.getRotation("mEyeRight"));\r
1388                         mesh.teFaceID = (int)AvatarTextureIndex.EyesBaked;\r
1389                         break;\r
1390 \r
1391                     case "skirtMesh":\r
1392                         mesh.teFaceID = (int)AvatarTextureIndex.SkirtBaked;\r
1393                         break;\r
1394 \r
1395                     default:\r
1396                         mesh.teFaceID = 0;\r
1397                         break;\r
1398                 }\r
1399 \r
1400                 if (lod == 0)\r
1401                     mesh.LoadMesh(basedir + fileName);\r
1402                 else\r
1403                     mesh.LoadLODMesh(lod, basedir + fileName);\r
1404 \r
1405                 _defaultmeshes[type] = mesh;\r
1406 \r
1407             }\r
1408 \r
1409             // Next are the textureing params, skipping for the moment\r
1410 \r
1411             XmlNodeList colors = lad.GetElementsByTagName("global_color");\r
1412             {\r
1413                 foreach (XmlNode globalcolornode in colors)\r
1414                 {\r
1415                     foreach (XmlNode node in globalcolornode.ChildNodes)\r
1416                     {\r
1417                         if (node.Name == "param")\r
1418                         {\r
1419                             VisualParamEx vp = new VisualParamEx(node, VisualParamEx.ParamType.TYPE_COLOR);\r
1420                         }\r
1421                     }\r
1422                 }\r
1423             }\r
1424 \r
1425             // Get layer paramaters, a bit of a verbose way to do it but we probably want to get access\r
1426             // to some of the other data not just the <param> tag\r
1427 \r
1428             XmlNodeList layer_sets = lad.GetElementsByTagName("layer_set");\r
1429             {\r
1430                 foreach (XmlNode layer_set in layer_sets)\r
1431                 {\r
1432                     foreach (XmlNode layer in layer_set.ChildNodes)\r
1433                     {\r
1434                         foreach (XmlNode layernode in layer.ChildNodes)\r
1435                         {\r
1436                             if (layernode.Name == "param")\r
1437                             {\r
1438                                 VisualParamEx vp = new VisualParamEx(layernode, VisualParamEx.ParamType.TYPE_COLOR);\r
1439                             }\r
1440                         }\r
1441                     }\r
1442                 }\r
1443             }\r
1444 \r
1445             // Next are the driver parameters, these are parameters that change multiple real parameters\r
1446 \r
1447             XmlNodeList drivers = lad.GetElementsByTagName("driver_parameters");\r
1448 \r
1449             foreach (XmlNode node in drivers[0].ChildNodes) //lazy \r
1450             {\r
1451                 if (node.Name == "param")\r
1452                 {\r
1453                     VisualParamEx vp = new VisualParamEx(node, VisualParamEx.ParamType.TYPE_DRIVER);\r
1454                 }\r
1455             }\r
1456 \r
1457             lindenMeshesLoaded = true;\r
1458         }\r
1459 \r
1460         public void morphtest(Avatar av, int param, float weight)\r
1461         {\r
1462             VisualParamEx vpx;\r
1463             if (VisualParamEx.allParams.TryGetValue(param, out vpx))\r
1464             {\r
1465 \r
1466                 Logger.Log(string.Format("Applying visual parameter {0} id {1} value {2}", vpx.Name, vpx.ParamID, weight), Helpers.LogLevel.Info);\r
1467 \r
1468                 //weight = weight * 2.0f;\r
1469                 //weight=weight-1.0f;\r
1470 \r
1471                 float value = vpx.MinValue + ((vpx.MaxValue - vpx.MinValue) * weight);\r
1472 \r
1473 \r
1474                 if (vpx.pType == VisualParamEx.ParamType.TYPE_MORPH)\r
1475                 {\r
1476                     // Its a morph\r
1477                     GLMesh mesh;\r
1478                     if (_meshes.TryGetValue(vpx.morphmesh, out mesh))\r
1479                     {\r
1480                         foreach (LindenMesh.Morph morph in mesh.Morphs) //optimise me to a dictionary\r
1481                         {\r
1482                             if (morph.Name == vpx.Name)\r
1483                             {\r
1484                                 mesh.morphmesh(morph, value);\r
1485                             }\r
1486                         }\r
1487                     }\r
1488                     else\r
1489                     {\r
1490                         // Not a mesh morph \r
1491 \r
1492                         // Its a volume deform, these appear to be related to collision volumes\r
1493                         /*\r
1494                         if (vpx.VolumeDeforms == null)\r
1495                         {\r
1496                             Logger.Log(String.Format("paramater {0} has invalid mesh {1}", param, vpx.morphmesh), Helpers.LogLevel.Warning);\r
1497                         }\r
1498                         else\r
1499                         {\r
1500                             foreach (KeyValuePair<string, VisualParamEx.VolumeDeform> kvp in vpx.VolumeDeforms)\r
1501                             {\r
1502                                 skel.deformbone(kvp.Key, kvp.Value.pos, kvp.Value.scale);\r
1503                             }\r
1504                         }\r
1505                          * */\r
1506 \r
1507                     }\r
1508 \r
1509                 }\r
1510                 else\r
1511                 {\r
1512                     // Its not a morph, it might be a driver though\r
1513                     if (vpx.pType == VisualParamEx.ParamType.TYPE_DRIVER)\r
1514                     {\r
1515                         foreach (VisualParamEx.driven child in vpx.childparams)\r
1516                         {\r
1517                             morphtest(av, child.id, weight); //TO DO use minmax if they are present\r
1518                         }\r
1519                         return;\r
1520                     }\r
1521 \r
1522                     //Is this a bone deform?\r
1523                     if (vpx.pType == VisualParamEx.ParamType.TYPE_BONEDEFORM)\r
1524                     {\r
1525                         foreach (KeyValuePair<string, Vector3> kvp in vpx.BoneDeforms)\r
1526                         {\r
1527                             skel.deformbone(kvp.Key, new Vector3(0, 0, 0), kvp.Value * value, Quaternion.Identity);\r
1528                         }\r
1529                     }\r
1530                     else\r
1531                     {\r
1532                         Logger.Log(String.Format("paramater {0} is not a morph and not a driver", param), Helpers.LogLevel.Warning);\r
1533                     }\r
1534                 }\r
1535 \r
1536             }\r
1537             else\r
1538             {\r
1539                 Logger.Log("Invalid paramater " + param.ToString(), Helpers.LogLevel.Warning);\r
1540             }\r
1541 \r
1542             /*\r
1543             foreach (GLMesh mesh in _meshes.Values)\r
1544             {\r
1545                 VisualParamEx evp;\r
1546                 if (mesh._evp.TryGetValue(param, out evp))\r
1547                 {\r
1548                     foreach (LindenMesh.Morph morph in mesh.Morphs)\r
1549                     {\r
1550                         if (morph.Name == evp.Name)\r
1551                         {\r
1552                             mesh.morphmesh(morph, weight);\r
1553                         }\r
1554                     }\r
1555                 }\r
1556                 else\r
1557                 {\r
1558                     Console.WriteLine("No such visual param in morphs");\r
1559                 }\r
1560             \r
1561             }\r
1562              */\r
1563         }\r
1564 \r
1565         public void morph(Avatar av)\r
1566         {\r
1567 \r
1568             if (av.VisualParameters == null)\r
1569                 return;\r
1570 \r
1571 \r
1572             ThreadPool.QueueUserWorkItem(sync =>\r
1573             {\r
1574                 int x = 0;\r
1575 \r
1576                 if (av.VisualParameters.Length > 123)\r
1577                 {\r
1578                     if (av.VisualParameters[31] > 0.5)\r
1579                     {\r
1580                         msex = VisualParamEx.EparamSex.SEX_MALE;\r
1581                     }\r
1582                     else\r
1583                     {\r
1584                         msex = VisualParamEx.EparamSex.SEX_FEMALE;\r
1585                     }\r
1586                 }\r
1587 \r
1588 \r
1589                 foreach (byte vpvalue in av.VisualParameters)\r
1590                 {\r
1591                     if (vpsent == true && VisualAppearanceParameters[x] == vpvalue)\r
1592                     {\r
1593                         x++;\r
1594                         continue;\r
1595                     }\r
1596 \r
1597                     VisualAppearanceParameters[x] = vpvalue;\r
1598 \r
1599                     if (x >= VisualParamEx.tweakable_params.Count)\r
1600                     {\r
1601                         Logger.Log("Two many visual paramaters in Avatar appearance", Helpers.LogLevel.Warning);\r
1602                         break;\r
1603                     }\r
1604 \r
1605                     VisualParamEx vpe = (VisualParamEx)VisualParamEx.tweakable_params.GetByIndex(x);\r
1606 \r
1607                     if (vpe.sex != VisualParamEx.EparamSex.SEX_BOTH && vpe.sex != msex)\r
1608                     {\r
1609                         x++;\r
1610                         continue;\r
1611                     }\r
1612 \r
1613                     float value = (vpvalue / 255.0f);\r
1614                     this.morphtest(av, vpe.ParamID, value);\r
1615 \r
1616                     x++;\r
1617                 }\r
1618 \r
1619                 vpsent = true;\r
1620 \r
1621                 foreach (GLMesh mesh in _meshes.Values)\r
1622                 {\r
1623                     mesh.applyjointweights();\r
1624                 }\r
1625             });\r
1626         }\r
1627     }\r
1628 \r
1629     public class RenderAvatar : SceneObject\r
1630     {\r
1631         public RenderAvatar()\r
1632         {\r
1633             Type = SceneObjectType.Avatar;\r
1634         }\r
1635 \r
1636         public override Primitive BasePrim\r
1637         {\r
1638             get { return avatar; }\r
1639             set { if (value is Avatar) avatar = (Avatar)value; }\r
1640         }\r
1641 \r
1642         public override void Step(double time)\r
1643         {\r
1644             if (RenderPosition != SimPosition)\r
1645             {\r
1646                 RenderPosition = RHelp.Smoothed1stOrder(RenderPosition, SimPosition, time);\r
1647             }\r
1648             RenderRotation = SimRotation;\r
1649             base.Step(time);\r
1650         }\r
1651 \r
1652         public GLAvatar glavatar = new GLAvatar();\r
1653         public Avatar avatar;\r
1654         public FaceData[] data = new FaceData[32];\r
1655         public Dictionary<UUID, Animation> animlist = new Dictionary<UUID, Animation>();\r
1656         public Dictionary<WearableType, AppearanceManager.WearableData> Wearables = new Dictionary<WearableType, AppearanceManager.WearableData>();\r
1657 \r
1658     }\r
1659 \r
1660     public class skeleton\r
1661     {\r
1662         public Dictionary<string, Bone> mBones;\r
1663         public static Dictionary<int, string> mUpperMeshMapping = new Dictionary<int, string>();\r
1664         public static Dictionary<int, string> mLowerMeshMapping = new Dictionary<int, string>();\r
1665         public static Dictionary<int, string> mHeadMeshMapping = new Dictionary<int, string>();\r
1666 \r
1667         public static Dictionary<UUID, BinBVHAnimationReader> mKnownAnimations = new Dictionary<UUID, BinBVHAnimationReader>();\r
1668         public Dictionary<UUID, Animation> animlist = new Dictionary<UUID, Animation>();\r
1669 \r
1670         public bool mNeedsUpdate = false;\r
1671         public bool mNeedsMeshRebuild = false;\r
1672 \r
1673 \r
1674         public skeleton()\r
1675         {\r
1676             mBones = new Dictionary<string, Bone>(Bone.mBones); //copy from the static defines\r
1677 \r
1678             //FUDGE\r
1679             if (mUpperMeshMapping.Count == 0)\r
1680             {\r
1681                 mUpperMeshMapping.Add(1, "mPelvis");\r
1682                 mUpperMeshMapping.Add(2, "mTorso");\r
1683                 mUpperMeshMapping.Add(3, "mChest");\r
1684                 mUpperMeshMapping.Add(4, "mNeck");\r
1685                 mUpperMeshMapping.Add(5, "");\r
1686                 mUpperMeshMapping.Add(6, "mCollarLeft");\r
1687                 mUpperMeshMapping.Add(7, "mShoulderLeft");\r
1688                 mUpperMeshMapping.Add(8, "mElbowLeft");\r
1689                 mUpperMeshMapping.Add(9, "mWristLeft");\r
1690                 mUpperMeshMapping.Add(10, "");\r
1691                 mUpperMeshMapping.Add(11, "mCollarRight");\r
1692                 mUpperMeshMapping.Add(12, "mShoulderRight");\r
1693                 mUpperMeshMapping.Add(13, "mElbowRight");\r
1694                 mUpperMeshMapping.Add(14, "mWristRight");\r
1695                 mUpperMeshMapping.Add(15, "");\r
1696 \r
1697                 mLowerMeshMapping.Add(1, "mPelvis");\r
1698                 mLowerMeshMapping.Add(2, "mHipRight");\r
1699                 mLowerMeshMapping.Add(3, "mKneeRight");\r
1700                 mLowerMeshMapping.Add(4, "mAnkleRight");\r
1701                 mLowerMeshMapping.Add(5, "");\r
1702                 mLowerMeshMapping.Add(6, "mHipLeft");\r
1703                 mLowerMeshMapping.Add(7, "mKneeLeft");\r
1704                 mLowerMeshMapping.Add(8, "mAnkleLeft");\r
1705                 mLowerMeshMapping.Add(9, "");\r
1706 \r
1707                 mHeadMeshMapping.Add(1, "mNeck");\r
1708                 mHeadMeshMapping.Add(2, "mHead");\r
1709                 mHeadMeshMapping.Add(3, "");\r
1710 \r
1711             }\r
1712         }\r
1713 \r
1714         public void deformbone(string name, Vector3 pos, Vector3 scale, Quaternion rotation)\r
1715         {\r
1716             Bone bone;\r
1717             if (mBones.TryGetValue(name, out bone))\r
1718             {\r
1719                 bone.deformbone(pos, scale, rotation);\r
1720             }\r
1721         }\r
1722 \r
1723         //TODO check offset and rot calcuations should each offset be multiplied by its parent rotation in\r
1724         // a standard child/parent rot/offset way?\r
1725         public Vector3 getOffset(string bonename)\r
1726         {\r
1727             Bone b;\r
1728             if (mBones.TryGetValue(bonename, out b))\r
1729             {\r
1730                 return (b.getOffset());\r
1731             }\r
1732             else\r
1733             {\r
1734                 return Vector3.Zero;\r
1735             }\r
1736         }\r
1737 \r
1738         public Quaternion getRotation(string bonename)\r
1739         {\r
1740             Bone b;\r
1741             if (mBones.TryGetValue(bonename, out b))\r
1742             {\r
1743                 return (b.getRotation());\r
1744             }\r
1745             else\r
1746             {\r
1747                 return Quaternion.Identity;\r
1748             }\r
1749         }\r
1750 \r
1751         // Add animations to the global decoded list\r
1752         // TODO garbage collect unused animations somehow\r
1753         public static void addanimation(OpenMetaverse.Assets.Asset asset)\r
1754         {\r
1755             BinBVHAnimationReader b = new BinBVHAnimationReader(asset.AssetData);\r
1756             if (!mKnownAnimations.ContainsKey(asset.AssetID))\r
1757             {\r
1758                 Logger.Log("Adding new decoded animaton known animations " + asset.AssetID.ToString(), Helpers.LogLevel.Info);\r
1759                 mKnownAnimations.Add(asset.AssetID, b);\r
1760             }\r
1761         }\r
1762 \r
1763         public bool addplayinganimation(Animation anim)\r
1764         {\r
1765 \r
1766             if (!animlist.ContainsKey(anim.AnimationID))\r
1767             {\r
1768                 Logger.Log("Adding new playing animaton " + anim.AnimationID.ToString(), Helpers.LogLevel.Info);\r
1769                 animlist.Add(anim.AnimationID, anim);\r
1770             }\r
1771 \r
1772             mNeedsUpdate = true;\r
1773 \r
1774             if (skeleton.mKnownAnimations.ContainsKey(anim.AnimationID))\r
1775             {\r
1776                 return false;\r
1777             }\r
1778 \r
1779             return true;\r
1780 \r
1781         }\r
1782 \r
1783         public void firstkeyframe()\r
1784         {\r
1785             if (mNeedsUpdate == false)\r
1786                 return;\r
1787 \r
1788             foreach (Animation anim in this.animlist.Values) // TODO priority sort\r
1789             {\r
1790                 BinBVHAnimationReader b;\r
1791                 if (skeleton.mKnownAnimations.TryGetValue(anim.AnimationID, out b))\r
1792                 {\r
1793                     foreach (binBVHJoint joint in b.joints)\r
1794                     {\r
1795                         //actually get the last for a pose the last frame is probably the static one once\r
1796                         //we have reached position\r
1797                         binBVHJointKey rot = joint.rotationkeys[joint.rotationkeys.Length-1];\r
1798 \r
1799                         Vector3 pos = new Vector3(0, 0, 0);\r
1800                         //binBVHJointKey pos = joint.positionkeys[0];\r
1801 \r
1802                         deformbone(joint.Name, new Vector3(0, 0, 0), pos, new Quaternion(rot.key_element.X, rot.key_element.Y, rot.key_element.Z));\r
1803 \r
1804                     }\r
1805 \r
1806                     mNeedsMeshRebuild = true;\r
1807                 }\r
1808             }\r
1809 \r
1810             mNeedsUpdate = false;\r
1811         }\r
1812     }\r
1813 \r
1814     public class Bone\r
1815     {\r
1816         public string name;\r
1817         public Vector3 pos;\r
1818         public Quaternion rot;\r
1819         public Vector3 scale;\r
1820         public Vector3 piviot;\r
1821 \r
1822         public Vector3 orig_pos;\r
1823         public Quaternion orig_rot;\r
1824         public Vector3 orig_scale;\r
1825         public Vector3 orig_piviot;\r
1826 \r
1827         Matrix4 mDeformMatrix = Matrix4.Identity;\r
1828 \r
1829         public Bone parent;\r
1830 \r
1831         public List<Bone> children = new List<Bone>();\r
1832 \r
1833         public static Dictionary<string, Bone> mBones = new Dictionary<string, Bone>();\r
1834         public static Dictionary<int, Bone> mIndexedBones = new Dictionary<int, Bone>();\r
1835         static int boneaddindex = 0;\r
1836 \r
1837         public Bone()\r
1838         {\r
1839         }\r
1840 \r
1841         public Bone(Bone source)\r
1842         {\r
1843             name = String.Copy(source.name);\r
1844             pos = new Vector3(source.pos);\r
1845             rot = new Quaternion(source.rot);\r
1846             scale = new Vector3(source.scale);\r
1847             piviot = new Vector3(source.piviot);\r
1848 \r
1849             orig_piviot = source.orig_piviot;\r
1850             orig_pos = source.orig_pos;\r
1851             orig_rot = source.orig_rot;\r
1852             orig_scale = source.orig_scale;\r
1853 \r
1854             mDeformMatrix = new Matrix4(source.mDeformMatrix);\r
1855         }\r
1856 \r
1857         public static void loadbones(string skeletonfilename)\r
1858         {\r
1859             mBones.Clear();\r
1860             string basedir = Directory.GetCurrentDirectory() + System.IO.Path.DirectorySeparatorChar + "character" + System.IO.Path.DirectorySeparatorChar;\r
1861             XmlDocument skeleton = new XmlDocument();\r
1862             skeleton.Load(basedir + skeletonfilename);\r
1863             XmlNode boneslist = skeleton.GetElementsByTagName("linden_skeleton")[0];\r
1864             addbone(boneslist.ChildNodes[0], null);\r
1865         }\r
1866 \r
1867         public static void addbone(XmlNode bone, Bone parent)\r
1868         {\r
1869 \r
1870             if (bone.Name != "bone")\r
1871                 return;\r
1872 \r
1873             Bone b = new Bone();\r
1874             b.name = bone.Attributes.GetNamedItem("name").Value;\r
1875 \r
1876             string pos = bone.Attributes.GetNamedItem("pos").Value;\r
1877             string[] posparts = pos.Split(' ');\r
1878             b.pos = new Vector3(float.Parse(posparts[0]), float.Parse(posparts[1]), float.Parse(posparts[2]));\r
1879             b.orig_pos = new Vector3(b.pos);\r
1880 \r
1881             string rot = bone.Attributes.GetNamedItem("rot").Value;\r
1882             string[] rotparts = rot.Split(' ');\r
1883             b.rot = 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));\r
1884             b.orig_rot = new Quaternion(b.rot);\r
1885 \r
1886             string scale = bone.Attributes.GetNamedItem("scale").Value;\r
1887             string[] scaleparts = scale.Split(' ');\r
1888             b.scale = new Vector3(float.Parse(scaleparts[0]), float.Parse(scaleparts[1]), float.Parse(scaleparts[2]));\r
1889             b.orig_scale = new Vector3(b.scale);\r
1890 \r
1891 \r
1892             float[] deform = Math3D.CreateSRTMatrix(new Vector3(1, 1, 1), b.rot, b.orig_pos);\r
1893             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]);\r
1894 \r
1895             //TODO piviot\r
1896 \r
1897             b.parent = parent;\r
1898 \r
1899             if (parent != null)\r
1900                 parent.children.Add(b);\r
1901 \r
1902             mBones.Add(b.name, b);\r
1903             mIndexedBones.Add(boneaddindex++, b);\r
1904 \r
1905             Logger.Log("Found bone " + b.name, Helpers.LogLevel.Info);\r
1906 \r
1907             foreach (XmlNode childbone in bone.ChildNodes)\r
1908             {\r
1909                 addbone(childbone, b);\r
1910             }\r
1911 \r
1912         }\r
1913 \r
1914         public void deformbone(Vector3 pos, Vector3 scale, Quaternion rot)\r
1915         {\r
1916             float[] deform = Math3D.CreateSRTMatrix(scale, rot, this.orig_pos);\r
1917             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]);\r
1918             this.pos = Bone.mBones[name].orig_pos + pos;\r
1919             this.scale = Bone.mBones[name].orig_scale + scale;\r
1920             this.rot = Bone.mBones[name].orig_rot * rot;\r
1921         }\r
1922 \r
1923         public Matrix4 getdeform()\r
1924         {\r
1925             if (this.parent != null)\r
1926             {\r
1927                 return mDeformMatrix * parent.getdeform();\r
1928             }\r
1929             else\r
1930             {\r
1931                 return mDeformMatrix;\r
1932             }\r
1933         }\r
1934 \r
1935         public Vector3 getOffset()\r
1936         {\r
1937             if (parent != null)\r
1938             {\r
1939                 Quaternion totalrot = getParentRot(); // we don't want this joints rotation included\r
1940                 Vector3 parento = parent.getOffset();\r
1941                 Vector3 mepre = pos * scale;\r
1942                 mepre = mepre * totalrot;\r
1943                 return parento + mepre;\r
1944             }\r
1945             else\r
1946             {\r
1947                 return (pos * scale) * getRotation();\r
1948             }\r
1949         }\r
1950 \r
1951         public Vector3 getMyOffset()\r
1952         {\r
1953             return pos * scale;\r
1954         }\r
1955 \r
1956         public Vector3 getOrigOffset()\r
1957         {\r
1958             if (parent != null)\r
1959             {\r
1960                 return ((parent.getOrigOffset()) + orig_pos);\r
1961             }\r
1962             else\r
1963             {\r
1964                 return orig_pos;\r
1965             }\r
1966         }\r
1967 \r
1968         public static Quaternion getRotation(string bonename)\r
1969         {\r
1970             Bone b;\r
1971             if (mBones.TryGetValue(bonename, out b))\r
1972             {\r
1973                 return (b.getRotation());\r
1974             }\r
1975             else\r
1976             {\r
1977                 return Quaternion.Identity;\r
1978             }\r
1979         }\r
1980 \r
1981 \r
1982         public Quaternion getParentRot()\r
1983         {\r
1984             Quaternion totalrot = Quaternion.Identity;\r
1985 \r
1986             if (parent != null)\r
1987             {\r
1988                 totalrot = parent.getRotation();\r
1989             }\r
1990 \r
1991             return totalrot;\r
1992 \r
1993         }\r
1994 \r
1995         public Quaternion getRotation()\r
1996         {\r
1997             Quaternion totalrot = rot;\r
1998 \r
1999             if (parent != null)\r
2000             {\r
2001                 totalrot = rot * parent.getRotation();\r
2002             }\r
2003 \r
2004             return totalrot;\r
2005         }\r
2006 \r
2007     }\r
2008 \r
2009     public class VisualParamEx\r
2010     {\r
2011 \r
2012         static public Dictionary<int, VisualParamEx> allParams = new Dictionary<int, VisualParamEx>();\r
2013         static public Dictionary<int, VisualParamEx> deformParams = new Dictionary<int, VisualParamEx>();\r
2014         static public Dictionary<int, VisualParamEx> morphParams = new Dictionary<int, VisualParamEx>();\r
2015         static public Dictionary<int, VisualParamEx> drivenParams = new Dictionary<int, VisualParamEx>();\r
2016         static public SortedList tweakable_params = new SortedList();\r
2017 \r
2018         public Dictionary<string, Vector3> BoneDeforms = null;\r
2019         public Dictionary<string, VolumeDeform> VolumeDeforms = null;\r
2020         public List<driven> childparams = null;\r
2021 \r
2022         public string morphmesh = null;\r
2023 \r
2024         enum GroupType\r
2025         {\r
2026             VISUAL_PARAM_GROUP_TWEAKABLE = 0,\r
2027             VISUAL_PARAM_GROUP_ANIMATABLE,\r
2028             VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT,\r
2029         }\r
2030 \r
2031         public struct VolumeDeform\r
2032         {\r
2033             public string name;\r
2034             public Vector3 scale;\r
2035             public Vector3 pos;\r
2036         }\r
2037 \r
2038         public enum EparamSex\r
2039         {\r
2040             SEX_BOTH = 0,\r
2041             SEX_FEMALE = 1,\r
2042             SEX_MALE = 2\r
2043         }\r
2044 \r
2045         public enum ParamType\r
2046         {\r
2047             TYPE_BONEDEFORM,\r
2048             TYPE_MORPH,\r
2049             TYPE_DRIVER,\r
2050             TYPE_COLOR,\r
2051             TYPE_LAYER\r
2052         }\r
2053 \r
2054         public struct driven\r
2055         {\r
2056             public int id;\r
2057             public float max1;\r
2058             public float max2;\r
2059             public float min1;\r
2060             public float min2;\r
2061             public bool hasMinMax;\r
2062         }\r
2063 \r
2064         public string meshname;\r
2065 \r
2066         /// <summary>Index of this visual param</summary>\r
2067         public int ParamID;\r
2068         /// <summary>Internal name</summary>\r
2069         public string Name;\r
2070         /// <summary>Group ID this parameter belongs to</summary>\r
2071         public int Group;\r
2072         /// <summary>Name of the wearable this parameter belongs to</summary>\r
2073         public string Wearable;\r
2074         /// <summary>Displayable label of this characteristic</summary>\r
2075         public string Label;\r
2076         /// <summary>Displayable label for the minimum value of this characteristic</summary>\r
2077         public string LabelMin;\r
2078         /// <summary>Displayable label for the maximum value of this characteristic</summary>\r
2079         public string LabelMax;\r
2080         /// <summary>Default value</summary>\r
2081         public float DefaultValue;\r
2082         /// <summary>Minimum value</summary>\r
2083         public float MinValue;\r
2084         /// <summary>Maximum value</summary>\r
2085         public float MaxValue;\r
2086         /// <summary>Is this param used for creation of bump layer?</summary>\r
2087         public bool IsBumpAttribute;\r
2088         /// <summary>Alpha blending/bump info</summary>\r
2089         public VisualAlphaParam? AlphaParams;\r
2090         /// <summary>Color information</summary>\r
2091         public VisualColorParam? ColorParams;\r
2092         /// <summary>Array of param IDs that are drivers for this parameter</summary>\r
2093         public int[] Drivers;\r
2094         /// <summary>The Avatar Sex that this parameter applies to</summary>\r
2095         public EparamSex sex;\r
2096 \r
2097         public ParamType pType;\r
2098 \r
2099         public static int count = 0;\r
2100 \r
2101         /// <summary>\r
2102         /// Set all the values through the constructor\r
2103         /// </summary>\r
2104         /// <param name="paramID">Index of this visual param</param>\r
2105         /// <param name="name">Internal name</param>\r
2106         /// <param name="group"></param>\r
2107         /// <param name="wearable"></param>\r
2108         /// <param name="label">Displayable label of this characteristic</param>\r
2109         /// <param name="labelMin">Displayable label for the minimum value of this characteristic</param>\r
2110         /// <param name="labelMax">Displayable label for the maximum value of this characteristic</param>\r
2111         /// <param name="def">Default value</param>\r
2112         /// <param name="min">Minimum value</param>\r
2113         /// <param name="max">Maximum value</param>\r
2114         /// <param name="isBumpAttribute">Is this param used for creation of bump layer?</param>\r
2115         /// <param name="drivers">Array of param IDs that are drivers for this parameter</param>\r
2116         /// <param name="alpha">Alpha blending/bump info</param>\r
2117         /// <param name="colorParams">Color information</param>\r
2118         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)\r
2119         {\r
2120             ParamID = paramID;\r
2121             Name = name;\r
2122             Group = group;\r
2123             Wearable = wearable;\r
2124             Label = label;\r
2125             LabelMin = labelMin;\r
2126             LabelMax = labelMax;\r
2127             DefaultValue = def;\r
2128             MaxValue = max;\r
2129             MinValue = min;\r
2130             IsBumpAttribute = isBumpAttribute;\r
2131             Drivers = drivers;\r
2132             AlphaParams = alpha;\r
2133             ColorParams = colorParams;\r
2134             sex = EparamSex.SEX_BOTH;\r
2135         }\r
2136 \r
2137         public VisualParamEx(XmlNode node, ParamType pt)\r
2138         {\r
2139             pType = pt;\r
2140 \r
2141             ParamID = Int32.Parse(node.Attributes.GetNamedItem("id").Value);\r
2142             Name = node.Attributes.GetNamedItem("name").Value;\r
2143             Group = Int32.Parse(node.Attributes.GetNamedItem("group").Value);\r
2144 \r
2145             //These dont exist for facal expresion morphs\r
2146             if (node.Attributes.GetNamedItem("wearable") != null)\r
2147                 Wearable = node.Attributes.GetNamedItem("wearable").Value;\r
2148 \r
2149             MinValue = float.Parse(node.Attributes.GetNamedItem("value_min").Value);\r
2150             MaxValue = float.Parse(node.Attributes.GetNamedItem("value_max").Value);\r
2151 \r
2152             // These do not exists for driven parameters\r
2153             if (node.Attributes.GetNamedItem("label_min") != null)\r
2154             {\r
2155                 LabelMin = node.Attributes.GetNamedItem("label_min").Value;\r
2156             }\r
2157 \r
2158             if (node.Attributes.GetNamedItem("label_max") != null)\r
2159             {\r
2160                 LabelMax = node.Attributes.GetNamedItem("label_max").Value;\r
2161             }\r
2162 \r
2163             XmlNode sexnode = node.Attributes.GetNamedItem("sex");\r
2164 \r
2165             if (sexnode != null)\r
2166             {\r
2167                 if (sexnode.Value == "male")\r
2168                 {\r
2169                     sex = EparamSex.SEX_MALE;\r
2170                 }\r
2171                 else\r
2172                 {\r
2173                     sex = EparamSex.SEX_FEMALE;\r
2174                 }\r
2175 \r
2176             }\r
2177 \r
2178             Group = int.Parse(node.Attributes.GetNamedItem("group").Value);\r
2179 \r
2180             if (Group == (int)GroupType.VISUAL_PARAM_GROUP_TWEAKABLE)\r
2181             {\r
2182                 if (!tweakable_params.ContainsKey(ParamID)) //stupid duplicate shared params\r
2183                 {\r
2184                     tweakable_params.Add(this.ParamID, this);\r
2185                 }\r
2186                 Logger.Log(String.Format("Adding tweakable paramater ID {0} {1}", count, this.Name), Helpers.LogLevel.Info);\r
2187                 count++;\r
2188             }\r
2189 \r
2190             //TODO other paramaters but these arew concerned with editing the GUI display so not too fussed at the moment\r
2191 \r
2192             try\r
2193             {\r
2194                 allParams.Add(ParamID, this);\r
2195             }\r
2196             catch (Exception e)\r
2197             {\r
2198                 Logger.Log("Duplicate VisualParam in allParams id " + ParamID.ToString(), Helpers.LogLevel.Info);\r
2199             }\r
2200 \r
2201             if (pt == ParamType.TYPE_BONEDEFORM)\r
2202             {\r
2203                 // If we are in the skeleton section then we also have bone deforms to parse\r
2204                 BoneDeforms = new Dictionary<string, Vector3>();\r
2205                 if (node.HasChildNodes && node.ChildNodes[0].HasChildNodes)\r
2206                 {\r
2207                     ParseBoneDeforms(node.ChildNodes[0].ChildNodes);\r
2208                 }\r
2209                 deformParams.Add(ParamID, this);\r
2210             }\r
2211 \r
2212             if (pt == ParamType.TYPE_MORPH)\r
2213             {\r
2214                 VolumeDeforms = new Dictionary<string, VolumeDeform>();\r
2215                 if (node.HasChildNodes && node.ChildNodes[0].HasChildNodes)\r
2216                 {\r
2217                     ParseVolumeDeforms(node.ChildNodes[0].ChildNodes);\r
2218                 }\r
2219 \r
2220                 try\r
2221                 {\r
2222                     morphParams.Add(ParamID, this);\r
2223                 }\r
2224                 catch (Exception e)\r
2225                 {\r
2226                     Logger.Log("Duplicate VisualParam in morphParams id " + ParamID.ToString(), Helpers.LogLevel.Info);\r
2227                 }\r
2228 \r
2229             }\r
2230 \r
2231             if (pt == ParamType.TYPE_DRIVER)\r
2232             {\r
2233                 childparams = new List<driven>();\r
2234                 if (node.HasChildNodes && node.ChildNodes[0].HasChildNodes) //LAZY\r
2235                 {\r
2236                     ParseDrivers(node.ChildNodes[0].ChildNodes);\r
2237                 }\r
2238 \r
2239                 drivenParams.Add(ParamID, this);\r
2240 \r
2241             }\r
2242 \r
2243             if (pt == ParamType.TYPE_COLOR)\r
2244             {\r
2245                 if (node.HasChildNodes)\r
2246                 {\r
2247                     foreach (XmlNode colorchild in node.ChildNodes)\r
2248                     {\r
2249                         if (colorchild.Name == "param_color")\r
2250                         {\r
2251                             //TODO extract <value color="50, 25, 5, 255" />\r
2252                         }\r
2253                     }\r
2254 \r
2255                 }\r
2256             }\r
2257 \r
2258         }\r
2259 \r
2260         void ParseBoneDeforms(XmlNodeList deforms)\r
2261         {\r
2262             foreach (XmlNode node in deforms)\r
2263             {\r
2264                 if (node.Name == "bone")\r
2265                 {\r
2266                     string name = node.Attributes.GetNamedItem("name").Value;\r
2267                     Vector3 scale = XmlParseVector(node.Attributes.GetNamedItem("scale").Value);\r
2268                     BoneDeforms.Add(name, scale);\r
2269                 }\r
2270             }\r
2271         }\r
2272 \r
2273         void ParseVolumeDeforms(XmlNodeList deforms)\r
2274         {\r
2275             foreach (XmlNode node in deforms)\r
2276             {\r
2277                 if (node.Name == "volume_morph")\r
2278                 {\r
2279                     VolumeDeform vd = new VolumeDeform();\r
2280                     vd.name = node.Attributes.GetNamedItem("name").Value;\r
2281                     vd.name = vd.name.ToLower();\r
2282 \r
2283                     if (node.Attributes.GetNamedItem("scale") != null)\r
2284                     {\r
2285                         vd.scale = XmlParseVector(node.Attributes.GetNamedItem("scale").Value);\r
2286                     }\r
2287                     else\r
2288                     {\r
2289                         vd.scale = new Vector3(0, 0, 0);\r
2290                     }\r
2291 \r
2292                     if (node.Attributes.GetNamedItem("pos") != null)\r
2293                     {\r
2294                         vd.pos = XmlParseVector(node.Attributes.GetNamedItem("pos").Value);\r
2295                     }\r
2296                     else\r
2297                     {\r
2298                         vd.pos = new Vector3(0f, 0f, 0f);\r
2299                     }\r
2300 \r
2301                     VolumeDeforms.Add(vd.name, vd);\r
2302                 }\r
2303             }\r
2304         }\r
2305 \r
2306         void ParseDrivers(XmlNodeList drivennodes)\r
2307         {\r
2308             foreach (XmlNode node in drivennodes)\r
2309             {\r
2310                 if (node.Name == "driven")\r
2311                 {\r
2312                     driven d = new driven();\r
2313 \r
2314                     d.id = Int32.Parse(node.Attributes.GetNamedItem("id").Value);\r
2315                     XmlNode param = node.Attributes.GetNamedItem("max1");\r
2316                     if (param != null)\r
2317                     {\r
2318                         d.max1 = float.Parse(param.Value);\r
2319                         d.max2 = float.Parse(node.Attributes.GetNamedItem("max2").Value);\r
2320                         d.min1 = float.Parse(node.Attributes.GetNamedItem("min1").Value);\r
2321                         d.max2 = float.Parse(node.Attributes.GetNamedItem("min2").Value);\r
2322                         d.hasMinMax = true;\r
2323                     }\r
2324                     else\r
2325                     {\r
2326                         d.hasMinMax = false;\r
2327                     }\r
2328 \r
2329                     childparams.Add(d);\r
2330 \r
2331                 }\r
2332             }\r
2333         }\r
2334 \r
2335         public static Vector3 XmlParseVector(string data)\r
2336         {\r
2337             string[] posparts = data.Split(' ');\r
2338             return new Vector3(float.Parse(posparts[0]), float.Parse(posparts[1]), float.Parse(posparts[2]));\r
2339         }\r
2340 \r
2341         public static Quaternion XmlParseRotation(string data)\r
2342         {\r
2343             string[] rotparts = data.Split(' ');\r
2344             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));\r
2345         }\r
2346     }\r
2347 }\r