OSDN Git Service

Fix animation looping and implement at partial stacking of priorities, TODO prioritie...
[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.IO.Compression;\r
8 using System.Xml;\r
9 using System.Threading;\r
10 using OpenTK.Graphics.OpenGL;\r
11 using System.Runtime.InteropServices;\r
12 using OpenMetaverse;\r
13 using OpenMetaverse.Rendering;\r
14 \r
15 namespace Radegast.Rendering\r
16 {\r
17     public class FaceData\r
18     {\r
19         public float[] Vertices;\r
20         public ushort[] Indices;\r
21         public float[] TexCoords;\r
22         public float[] Normals;\r
23         public int PickingID = -1;\r
24         public int VertexVBO = -1;\r
25         public int IndexVBO = -1;\r
26         public TextureInfo TextureInfo = new TextureInfo();\r
27         public BoundingVolume BoundingVolume = new BoundingVolume();\r
28         public static int VertexSize = 32; // sizeof (vertex), 2  x vector3 + 1 x vector2 = 8 floats x 4 bytes = 32 bytes \r
29         public TextureAnimationInfo AnimInfo;\r
30         public int QueryID = 0;\r
31 \r
32         public void CheckVBO(Face face)\r
33         {\r
34             if (VertexVBO == -1)\r
35             {\r
36                 Vertex[] vArray = face.Vertices.ToArray();\r
37                 GL.GenBuffers(1, out VertexVBO);\r
38                 GL.BindBuffer(BufferTarget.ArrayBuffer, VertexVBO);\r
39                 GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vArray.Length * VertexSize), vArray, BufferUsageHint.StaticDraw);\r
40             }\r
41 \r
42             if (IndexVBO == -1)\r
43             {\r
44                 ushort[] iArray = face.Indices.ToArray();\r
45                 GL.GenBuffers(1, out IndexVBO);\r
46                 GL.BindBuffer(BufferTarget.ElementArrayBuffer, IndexVBO);\r
47                 GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(iArray.Length * sizeof(ushort)), iArray, BufferUsageHint.StaticDraw);\r
48             }\r
49         }\r
50     }\r
51 \r
52     public class TextureAnimationInfo\r
53     {\r
54         public Primitive.TextureAnimation PrimAnimInfo;\r
55         public float CurrentFrame;\r
56         public float CurrentTime;\r
57         public bool PingPong;\r
58         float LastTime = 0f;\r
59         float TotalTime = 0f;\r
60 \r
61         public void Step(float lastFrameTime)\r
62         {\r
63             float numFrames = 1f;\r
64             float fullLength = 1f;\r
65 \r
66             if (PrimAnimInfo.Length > 0)\r
67             {\r
68                 numFrames = PrimAnimInfo.Length;\r
69             }\r
70             else\r
71             {\r
72                 numFrames = Math.Max(1f, (float)(PrimAnimInfo.SizeX * PrimAnimInfo.SizeY));\r
73             }\r
74 \r
75             if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.PING_PONG) != 0)\r
76             {\r
77                 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) != 0)\r
78                 {\r
79                     fullLength = 2f * numFrames;\r
80                 }\r
81                 else if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.LOOP) != 0)\r
82                 {\r
83                     fullLength = 2f * numFrames - 2f;\r
84                     fullLength = Math.Max(1f, fullLength);\r
85                 }\r
86                 else\r
87                 {\r
88                     fullLength = 2f * numFrames - 1f;\r
89                     fullLength = Math.Max(1f, fullLength);\r
90                 }\r
91             }\r
92             else\r
93             {\r
94                 fullLength = numFrames;\r
95             }\r
96 \r
97             float frameCounter;\r
98             if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) != 0)\r
99             {\r
100                 frameCounter = lastFrameTime * PrimAnimInfo.Rate + LastTime;\r
101             }\r
102             else\r
103             {\r
104                 TotalTime += lastFrameTime;\r
105                 frameCounter = TotalTime * PrimAnimInfo.Rate;\r
106             }\r
107             LastTime = frameCounter;\r
108 \r
109             if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.LOOP) != 0)\r
110             {\r
111                 frameCounter %= fullLength;\r
112             }\r
113             else\r
114             {\r
115                 frameCounter = Math.Min(fullLength - 1f, frameCounter);\r
116             }\r
117 \r
118             if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) == 0)\r
119             {\r
120                 frameCounter = (float)Math.Floor(frameCounter + 0.01f);\r
121             }\r
122 \r
123             if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.PING_PONG) != 0)\r
124             {\r
125                 if (frameCounter > numFrames)\r
126                 {\r
127                     if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) != 0)\r
128                     {\r
129                         frameCounter = numFrames - (frameCounter - numFrames);\r
130                     }\r
131                     else\r
132                     {\r
133                         frameCounter = (numFrames - 1.99f) - (frameCounter - numFrames);\r
134                     }\r
135                 }\r
136             }\r
137 \r
138             if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.REVERSE) != 0)\r
139             {\r
140                 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) != 0)\r
141                 {\r
142                     frameCounter = numFrames - frameCounter;\r
143                 }\r
144                 else\r
145                 {\r
146                     frameCounter = (numFrames - 0.99f) - frameCounter;\r
147                 }\r
148             }\r
149 \r
150             frameCounter += PrimAnimInfo.Start;\r
151 \r
152             if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) == 0)\r
153             {\r
154                 frameCounter = (float)Math.Round(frameCounter);\r
155             }\r
156 \r
157 \r
158             GL.MatrixMode(MatrixMode.Texture);\r
159             GL.LoadIdentity();\r
160 \r
161             if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.ROTATE) != 0)\r
162             {\r
163                 GL.Translate(0.5f, 0.5f, 0f);\r
164                 GL.Rotate(Utils.RAD_TO_DEG * frameCounter, OpenTK.Vector3d.UnitZ);\r
165                 GL.Translate(-0.5f, -0.5f, 0f);\r
166             }\r
167             else if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SCALE) != 0)\r
168             {\r
169                 GL.Scale(frameCounter, frameCounter, 0);\r
170             }\r
171             else // Translate\r
172             {\r
173                 float sizeX = Math.Max(1f, (float)PrimAnimInfo.SizeX);\r
174                 float sizeY = Math.Max(1f, (float)PrimAnimInfo.SizeY);\r
175 \r
176                 GL.Scale(1f / sizeX, 1f / sizeY, 0);\r
177                 GL.Translate(frameCounter % sizeX, Math.Floor(frameCounter / sizeY), 0);\r
178             }\r
179 \r
180             GL.MatrixMode(MatrixMode.Modelview);\r
181         }\r
182 \r
183         [Obsolete("Use Step() instead")]\r
184         public void ExperimentalStep(float time)\r
185         {\r
186             int reverseFactor = 1;\r
187             float rate = PrimAnimInfo.Rate;\r
188 \r
189             if (rate < 0)\r
190             {\r
191                 rate = -rate;\r
192                 reverseFactor = -reverseFactor;\r
193             }\r
194 \r
195             if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.REVERSE) != 0)\r
196             {\r
197                 reverseFactor = -reverseFactor;\r
198             }\r
199 \r
200             CurrentTime += time;\r
201             float totalTime = 1 / rate;\r
202 \r
203             uint x = Math.Max(1, PrimAnimInfo.SizeX);\r
204             uint y = Math.Max(1, PrimAnimInfo.SizeY);\r
205             uint nrFrames = x * y;\r
206 \r
207             if (PrimAnimInfo.Length > 0 && PrimAnimInfo.Length < nrFrames)\r
208             {\r
209                 nrFrames = (uint)PrimAnimInfo.Length;\r
210             }\r
211 \r
212             GL.MatrixMode(MatrixMode.Texture);\r
213             GL.LoadIdentity();\r
214 \r
215             if (CurrentTime >= totalTime)\r
216             {\r
217                 CurrentTime = 0;\r
218                 CurrentFrame++;\r
219                 if (CurrentFrame > nrFrames) CurrentFrame = (uint)PrimAnimInfo.Start;\r
220                 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.PING_PONG) != 0)\r
221                 {\r
222                     PingPong = !PingPong;\r
223                 }\r
224             }\r
225 \r
226             float smoothOffset = 0f;\r
227 \r
228             if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) != 0)\r
229             {\r
230                 smoothOffset = (CurrentTime / totalTime) * reverseFactor;\r
231             }\r
232 \r
233             float f = CurrentFrame;\r
234             if (reverseFactor < 0)\r
235             {\r
236                 f = nrFrames - CurrentFrame;\r
237             }\r
238 \r
239             if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.ROTATE) == 0) // not rotating\r
240             {\r
241                 GL.Scale(1f / x, 1f / y, 0f);\r
242                 GL.Translate((f % x) + smoothOffset, f / y, 0);\r
243             }\r
244             else\r
245             {\r
246                 smoothOffset = (CurrentTime * PrimAnimInfo.Rate);\r
247                 float startAngle = PrimAnimInfo.Start;\r
248                 float endAngle = PrimAnimInfo.Length;\r
249                 float angle = startAngle + (endAngle - startAngle) * smoothOffset;\r
250                 GL.Translate(0.5f, 0.5f, 0f);\r
251                 GL.Rotate(Utils.RAD_TO_DEG * angle, OpenTK.Vector3d.UnitZ);\r
252                 GL.Translate(-0.5f, -0.5f, 0f);\r
253             }\r
254 \r
255             GL.MatrixMode(MatrixMode.Modelview);\r
256         }\r
257 \r
258     }\r
259 \r
260 \r
261     public class TextureInfo\r
262     {\r
263         public System.Drawing.Image Texture;\r
264         public int TexturePointer;\r
265         public bool HasAlpha;\r
266         public bool FullAlpha;\r
267         public bool IsMask;\r
268         public UUID TextureID;\r
269         public bool FetchFailed;\r
270     }\r
271 \r
272     public class TextureLoadItem\r
273     {\r
274         public FaceData Data;\r
275         public Primitive Prim;\r
276         public Primitive.TextureEntryFace TeFace;\r
277         public byte[] TextureData = null;\r
278         public byte[] TGAData = null;\r
279         public bool LoadAssetFromCache = false;\r
280     }\r
281 \r
282     public enum RenderPass\r
283     {\r
284         Picking,\r
285         Simple,\r
286         Alpha\r
287     }\r
288 \r
289     public enum SceneObjectType\r
290     {\r
291         None,\r
292         Primitive,\r
293         Avatar,\r
294     }\r
295 \r
296     /// <summary>\r
297     /// Base class for all scene objects\r
298     /// </summary>\r
299     public abstract class SceneObject : IComparable, IDisposable\r
300     {\r
301         #region Public fields\r
302         /// <summary>Interpolated local position of the object</summary>\r
303         public Vector3 InterpolatedPosition;\r
304         /// <summary>Interpolated local rotation of the object/summary>\r
305         public Quaternion InterpolatedRotation;\r
306         /// <summary>Rendered position of the object in the region</summary>\r
307         public Vector3 RenderPosition;\r
308         /// <summary>Rendered rotationm of the object in the region</summary>\r
309         public Quaternion RenderRotation;\r
310         /// <summary>Per frame calculated square of the distance from camera</summary>\r
311         public float DistanceSquared;\r
312         /// <summary>Bounding volume of the object</summary>\r
313         public BoundingVolume BoundingVolume;\r
314         /// <summary>Was the sim position and distance from camera calculated during this frame</summary>\r
315         public bool PositionCalculated;\r
316         /// <summary>Scene object type</summary>\r
317         public SceneObjectType Type = SceneObjectType.None;\r
318         /// <summary>Libomv primitive</summary>\r
319         public virtual Primitive BasePrim { get; set; }\r
320         /// <summary>Were initial initialization tasks done</summary>\r
321         public bool Initialized;\r
322         public int AlphaQueryID = -1;\r
323         public int SimpleQueryID = -1;\r
324         public bool HasAlphaFaces;\r
325         public bool HasSimpleFaces;\r
326 \r
327         #endregion Public fields\r
328 \r
329         /// <summary>\r
330         /// Cleanup resources used\r
331         /// </summary>\r
332         public virtual void Dispose()\r
333         {\r
334         }\r
335 \r
336         /// <summary>\r
337         /// Task performed the fist time object is set for rendering\r
338         /// </summary>\r
339         public virtual void Initialize()\r
340         {\r
341             RenderPosition = InterpolatedPosition = BasePrim.Position;\r
342             RenderRotation = InterpolatedRotation = BasePrim.Rotation;\r
343             Initialized = true;\r
344         }\r
345 \r
346         /// <summary>\r
347         /// Perform per frame tasks\r
348         /// </summary>\r
349         /// <param name="time">Time since the last call (last frame time in seconds)</param>\r
350         public virtual void Step(float time)\r
351         {\r
352             // Linear velocity and acceleration\r
353             if (BasePrim.Velocity != Vector3.Zero)\r
354             {\r
355                 BasePrim.Position = InterpolatedPosition = BasePrim.Position + BasePrim.Velocity * time\r
356                     * 0.98f * RadegastInstance.GlobalInstance.Client.Network.CurrentSim.Stats.Dilation;\r
357                 BasePrim.Velocity += BasePrim.Acceleration * time;\r
358             }\r
359             else if (InterpolatedPosition != BasePrim.Position)\r
360             {\r
361                 InterpolatedPosition = RHelp.Smoothed1stOrder(InterpolatedPosition, BasePrim.Position, time);\r
362             }\r
363 \r
364             // Angular velocity (target omega)\r
365             if (BasePrim.AngularVelocity != Vector3.Zero)\r
366             {\r
367                 Vector3 angVel = BasePrim.AngularVelocity;\r
368                 float angle = time * angVel.Length();\r
369                 Quaternion dQ = Quaternion.CreateFromAxisAngle(angVel, angle);\r
370                 InterpolatedRotation = dQ * InterpolatedRotation;\r
371             }\r
372             else if (InterpolatedRotation != BasePrim.Rotation)\r
373             {\r
374                 InterpolatedRotation = Quaternion.Slerp(InterpolatedRotation, BasePrim.Rotation, time * 10f);\r
375                 if (Math.Abs(1f - Quaternion.Dot(InterpolatedRotation, BasePrim.Rotation)) < 0.0001)\r
376                     InterpolatedRotation = BasePrim.Rotation;\r
377             }\r
378         }\r
379 \r
380         /// <summary>\r
381         /// Implementation of the IComparable interface\r
382         /// used for sorting by distance\r
383         /// </summary>\r
384         /// <param name="other">Object we are comparing to</param>\r
385         /// <returns>Result of the comparison</returns>\r
386         public virtual int CompareTo(object other)\r
387         {\r
388             SceneObject o = (SceneObject)other;\r
389             if (this.DistanceSquared < o.DistanceSquared)\r
390                 return -1;\r
391             else if (this.DistanceSquared > o.DistanceSquared)\r
392                 return 1;\r
393             else\r
394                 return 0;\r
395         }\r
396 \r
397         public void StartQuery(RenderPass pass)\r
398         {\r
399             if (pass == RenderPass.Simple)\r
400             {\r
401                 StartSimpleQuery();\r
402             }\r
403             else if (pass == RenderPass.Alpha)\r
404             {\r
405                 StartAlphaQuery();\r
406             }\r
407         }\r
408 \r
409         public void EndQuery(RenderPass pass)\r
410         {\r
411             if (pass == RenderPass.Simple)\r
412             {\r
413                 EndSimpleQuery();\r
414             }\r
415             else if (pass == RenderPass.Alpha)\r
416             {\r
417                 EndAlphaQuery();\r
418             }\r
419         }\r
420 \r
421         public void StartAlphaQuery()\r
422         {\r
423             if (AlphaQueryID == -1)\r
424             {\r
425                 GL.GenQueries(1, out AlphaQueryID);\r
426             }\r
427             if (AlphaQueryID > 0)\r
428             {\r
429                 GL.BeginQuery(QueryTarget.SamplesPassed, AlphaQueryID);\r
430             }\r
431         }\r
432 \r
433         public void EndAlphaQuery()\r
434         {\r
435             if (AlphaQueryID > 0)\r
436             {\r
437                 GL.EndQuery(QueryTarget.SamplesPassed);\r
438             }\r
439         }\r
440 \r
441         public void StartSimpleQuery()\r
442         {\r
443             if (SimpleQueryID == -1)\r
444             {\r
445                 GL.GenQueries(1, out SimpleQueryID);\r
446             }\r
447             if (SimpleQueryID > 0)\r
448             {\r
449                 GL.BeginQuery(QueryTarget.SamplesPassed, SimpleQueryID);\r
450             }\r
451         }\r
452 \r
453         public void EndSimpleQuery()\r
454         {\r
455             if (SimpleQueryID > 0)\r
456             {\r
457                 GL.EndQuery(QueryTarget.SamplesPassed);\r
458             }\r
459         }\r
460 \r
461         public bool Occluded()\r
462         {\r
463             if ((SimpleQueryID == -1 && AlphaQueryID == -1))\r
464             {\r
465                 return false;\r
466             }\r
467 \r
468             if ((!HasAlphaFaces && !HasSimpleFaces)) return true;\r
469 \r
470             int samples = 1;\r
471             if (HasSimpleFaces && SimpleQueryID > 0)\r
472             {\r
473                 GL.GetQueryObject(SimpleQueryID, GetQueryObjectParam.QueryResult, out samples);\r
474             }\r
475             if (HasSimpleFaces && samples > 0)\r
476             {\r
477                 return false;\r
478             }\r
479 \r
480             samples = 1;\r
481             if (HasAlphaFaces && AlphaQueryID > 0)\r
482             {\r
483                 GL.GetQueryObject(AlphaQueryID, GetQueryObjectParam.QueryResult, out samples);\r
484             }\r
485             if (HasAlphaFaces && samples > 0)\r
486             {\r
487                 return false;\r
488             }\r
489 \r
490             return true;\r
491         }\r
492     }\r
493 \r
494     public class RenderPrimitive : SceneObject\r
495     {\r
496         public Primitive Prim;\r
497         public List<Face> Faces;\r
498         /// <summary>Is this object attached to an avatar</summary>\r
499         public bool Attached;\r
500         /// <summary>Do we know if object is attached</summary>\r
501         public bool AttachedStateKnown;\r
502         /// <summary>Are meshes constructed and ready for this prim</summary>\r
503         public bool Meshed;\r
504         /// <summary>Process of creating a mesh is underway</summary>\r
505         public bool Meshing;\r
506         /// <summary>Hash code for mesh to detect when mesh is regenerated</summary>\r
507         public int LastMeshHash;\r
508 \r
509         public RenderPrimitive()\r
510         {\r
511             Type = SceneObjectType.Primitive;\r
512         }\r
513 \r
514         public int GetMeshHash()\r
515         {\r
516             if (Prim.Type == PrimType.Sculpt || Prim.Type == PrimType.Mesh)\r
517             {\r
518                 return Prim.Sculpt.GetHashCode();\r
519             }\r
520             else\r
521             {\r
522                 return Prim.PrimData.GetHashCode();\r
523             }\r
524         }\r
525 \r
526         public override Primitive BasePrim\r
527         {\r
528             get { return Prim; }\r
529             set { Prim = value; }\r
530         }\r
531 \r
532         public override void Initialize()\r
533         {\r
534             AttachedStateKnown = false;\r
535             base.Initialize();\r
536         }\r
537 \r
538         public override string ToString()\r
539         {\r
540             uint id = Prim == null ? 0 : Prim.LocalID;\r
541             float distance = (float)Math.Sqrt(DistanceSquared);\r
542             return string.Format("LocalID: {0}, distance {0.00}", id, distance);\r
543         }\r
544     }\r
545 \r
546     public static class Render\r
547     {\r
548         public static IRendering Plugin;\r
549     }\r
550 \r
551     public static class RHelp\r
552     {\r
553         public static readonly Vector3 InvalidPosition = new Vector3(99999f, 99999f, 99999f);\r
554         static float t1 = 0.075f;\r
555         static float t2 = t1 / 5.7f;\r
556 \r
557         public static Vector3 Smoothed1stOrder(Vector3 curPos, Vector3 targetPos, float lastFrameTime)\r
558         {\r
559             int numIterations = (int)(lastFrameTime * 100);\r
560             do\r
561             {\r
562                 curPos += (targetPos - curPos) * t1;\r
563                 numIterations--;\r
564             }\r
565             while (numIterations > 0);\r
566             if (Vector3.DistanceSquared(curPos, targetPos) < 0.00001f)\r
567             {\r
568                 curPos = targetPos;\r
569             }\r
570             return curPos;\r
571         }\r
572 \r
573         public static Vector3 Smoothed2ndOrder(Vector3 curPos, Vector3 targetPos, ref Vector3 accel, float lastFrameTime)\r
574         {\r
575             int numIterations = (int)(lastFrameTime * 100);\r
576             do\r
577             {\r
578                 accel += (targetPos - accel - curPos) * t1;\r
579                 curPos += accel * t2;\r
580                 numIterations--;\r
581             }\r
582             while (numIterations > 0);\r
583             if (Vector3.DistanceSquared(curPos, targetPos) < 0.00001f)\r
584             {\r
585                 curPos = targetPos;\r
586             }\r
587             return curPos;\r
588         }\r
589 \r
590         public static OpenTK.Vector2 TKVector3(Vector2 v)\r
591         {\r
592             return new OpenTK.Vector2(v.X, v.Y);\r
593         }\r
594 \r
595         public static OpenTK.Vector3 TKVector3(Vector3 v)\r
596         {\r
597             return new OpenTK.Vector3(v.X, v.Y, v.Z);\r
598         }\r
599 \r
600         public static OpenTK.Vector4 TKVector3(Vector4 v)\r
601         {\r
602             return new OpenTK.Vector4(v.X, v.Y, v.Z, v.W);\r
603         }\r
604 \r
605         #region Cached image save and load\r
606         public static readonly string RAD_IMG_MAGIC = "radegast_img";\r
607 \r
608         public static bool LoadCachedImage(UUID textureID, out byte[] tgaData, out bool hasAlpha, out bool fullAlpha, out bool isMask)\r
609         {\r
610             tgaData = null;\r
611             hasAlpha = fullAlpha = isMask = false;\r
612 \r
613             try\r
614             {\r
615                 string fname = System.IO.Path.Combine(RadegastInstance.GlobalInstance.Client.Settings.ASSET_CACHE_DIR, string.Format("{0}.rzi", textureID));\r
616                 //string fname = System.IO.Path.Combine(".", string.Format("{0}.rzi", textureID));\r
617 \r
618                 using (var f = File.Open(fname, FileMode.Open, FileAccess.Read, FileShare.Read))\r
619                 {\r
620                     byte[] header = new byte[36];\r
621                     int i = 0;\r
622                     f.Read(header, 0, header.Length);\r
623 \r
624                     // check if the file is starting with magic string\r
625                     if (RAD_IMG_MAGIC != Utils.BytesToString(header, 0, RAD_IMG_MAGIC.Length))\r
626                         return false;\r
627                     i += RAD_IMG_MAGIC.Length;\r
628 \r
629                     if (header[i++] != 1) // check version\r
630                         return false;\r
631 \r
632                     hasAlpha = header[i++] == 1;\r
633                     fullAlpha = header[i++] == 1;\r
634                     isMask = header[i++] == 1;\r
635 \r
636                     int uncompressedSize = Utils.BytesToInt(header, i);\r
637                     i += 4;\r
638 \r
639                     textureID = new UUID(header, i);\r
640                     i += 16;\r
641 \r
642                     tgaData = new byte[uncompressedSize];\r
643                     using (var compressed = new DeflateStream(f, CompressionMode.Decompress))\r
644                     {\r
645                         int read = 0;\r
646                         while ((read = compressed.Read(tgaData, read, uncompressedSize - read)) > 0) ;\r
647                     }\r
648                 }\r
649 \r
650                 return true;\r
651             }\r
652             catch (FileNotFoundException) { }\r
653             catch (Exception ex)\r
654             {\r
655                 Logger.DebugLog(string.Format("Failed to load radegast cache file {0}: {1}", textureID, ex.Message));\r
656             }\r
657             return false;\r
658         }\r
659 \r
660         public static bool SaveCachedImage(byte[] tgaData, UUID textureID, bool hasAlpha, bool fullAlpha, bool isMask)\r
661         {\r
662             try\r
663             {\r
664                 string fname = System.IO.Path.Combine(RadegastInstance.GlobalInstance.Client.Settings.ASSET_CACHE_DIR, string.Format("{0}.rzi", textureID));\r
665                 //string fname = System.IO.Path.Combine(".", string.Format("{0}.rzi", textureID));\r
666 \r
667                 using (var f = File.Open(fname, FileMode.Create, FileAccess.Write, FileShare.None))\r
668                 {\r
669                     int i = 0;\r
670                     // magic header\r
671                     f.Write(Utils.StringToBytes(RAD_IMG_MAGIC), 0, RAD_IMG_MAGIC.Length);\r
672                     i += RAD_IMG_MAGIC.Length;\r
673 \r
674                     // version\r
675                     f.WriteByte((byte)1);\r
676                     i++;\r
677 \r
678                     // texture info\r
679                     f.WriteByte(hasAlpha ? (byte)1 : (byte)0);\r
680                     f.WriteByte(fullAlpha ? (byte)1 : (byte)0);\r
681                     f.WriteByte(isMask ? (byte)1 : (byte)0);\r
682                     i += 3;\r
683 \r
684                     // texture size\r
685                     byte[] uncompressedSize = Utils.IntToBytes(tgaData.Length);\r
686                     f.Write(uncompressedSize, 0, uncompressedSize.Length);\r
687                     i += uncompressedSize.Length;\r
688 \r
689                     // texture id\r
690                     byte[] id = new byte[16];\r
691                     textureID.ToBytes(id, 0);\r
692                     f.Write(id, 0, 16);\r
693                     i += 16;\r
694 \r
695                     // compressed texture data\r
696                     using (var compressed = new DeflateStream(f, CompressionMode.Compress))\r
697                     {\r
698                         compressed.Write(tgaData, 0, tgaData.Length);\r
699                     }\r
700                 }\r
701                 return true;\r
702             }\r
703             catch (Exception ex)\r
704             {\r
705                 Logger.DebugLog(string.Format("Failed to save radegast cache file {0}: {1}", textureID, ex.Message));\r
706                 return false;\r
707             }\r
708         }\r
709         #endregion Cached image save and load\r
710 \r
711         #region Static vertices and indices for a cube (used for bounding box drawing)\r
712         /**********************************************\r
713           5 --- 4\r
714          /|    /|\r
715         1 --- 0 |\r
716         | 6 --| 7\r
717         |/    |/\r
718         2 --- 3\r
719         ***********************************************/\r
720         public static readonly float[] CubeVertices = new float[]\r
721         {\r
722              0.5f,  0.5f,  0.5f, // 0\r
723                 -0.5f,  0.5f,  0.5f, // 1\r
724                 -0.5f, -0.5f,  0.5f, // 2\r
725                  0.5f, -0.5f,  0.5f, // 3\r
726                  0.5f,  0.5f, -0.5f, // 4\r
727                 -0.5f,  0.5f, -0.5f, // 5\r
728                 -0.5f, -0.5f, -0.5f, // 6\r
729                  0.5f, -0.5f, -0.5f  // 7\r
730         };\r
731 \r
732         public static readonly ushort[] CubeIndices = new ushort[]\r
733         {\r
734             0, 1, 2, 3,     // Front Face\r
735                 4, 5, 6, 7,     // Back Face\r
736                 1, 2, 6, 5,     // Left Face\r
737                 0, 3, 7, 4,     // Right Face\r
738                 0, 1, 5, 4,     // Top Face\r
739                 2, 3, 7, 6      // Bottom Face\r
740         };\r
741         #endregion Static vertices and indices for a cube (used for bounding box drawing)\r
742     }\r
743 \r
744     /// <summary>\r
745     /// Represents camera object\r
746     /// </summary>\r
747     public class Camera\r
748     {\r
749         Vector3 mPosition;\r
750         Vector3 mFocalPoint;\r
751         bool mModified;\r
752 \r
753         /// <summary>Camera position</summary>\r
754         public Vector3 Position { get { return mPosition; } set { mPosition = value; Modify(); } }\r
755         /// <summary>Camera target</summary>\r
756         public Vector3 FocalPoint { get { return mFocalPoint; } set { mFocalPoint = value; Modify(); } }\r
757         /// <summary>Zoom level</summary>\r
758         public float Zoom;\r
759         /// <summary>Draw distance</summary>\r
760         public float Far;\r
761         /// <summary>Has camera been modified</summary>\r
762         public bool Modified { get { return mModified; } set { mModified = value; } }\r
763 \r
764         public float TimeToTarget = 0f;\r
765 \r
766         public Vector3 RenderPosition;\r
767         public Vector3 RenderFocalPoint;\r
768 \r
769         void Modify()\r
770         {\r
771             mModified = true;\r
772         }\r
773 \r
774         public void Step(float time)\r
775         {\r
776             if (RenderPosition != Position)\r
777             {\r
778                 RenderPosition = RHelp.Smoothed1stOrder(RenderPosition, Position, time);\r
779                 Modified = true;\r
780             }\r
781             if (RenderFocalPoint != FocalPoint)\r
782             {\r
783                 RenderFocalPoint = RHelp.Smoothed1stOrder(RenderFocalPoint, FocalPoint, time);\r
784                 Modified = true;\r
785             }\r
786         }\r
787 \r
788         [Obsolete("Use Step(), left in here for reference")]\r
789         public void Step2(float time)\r
790         {\r
791             TimeToTarget -= time;\r
792             if (TimeToTarget <= time)\r
793             {\r
794                 EndMove();\r
795                 return;\r
796             }\r
797 \r
798             mModified = true;\r
799 \r
800             float pctElapsed = time / TimeToTarget;\r
801 \r
802             if (RenderPosition != Position)\r
803             {\r
804                 float distance = Vector3.Distance(RenderPosition, Position);\r
805                 RenderPosition = Vector3.Lerp(RenderPosition, Position, distance * pctElapsed);\r
806             }\r
807 \r
808             if (RenderFocalPoint != FocalPoint)\r
809             {\r
810                 RenderFocalPoint = Interpolate(RenderFocalPoint, FocalPoint, pctElapsed);\r
811             }\r
812         }\r
813 \r
814         Vector3 Interpolate(Vector3 start, Vector3 end, float fraction)\r
815         {\r
816             float distance = Vector3.Distance(start, end);\r
817             Vector3 direction = end - start;\r
818             return start + direction * fraction;\r
819         }\r
820 \r
821         public void EndMove()\r
822         {\r
823             mModified = true;\r
824             TimeToTarget = 0;\r
825             RenderPosition = Position;\r
826             RenderFocalPoint = FocalPoint;\r
827         }\r
828     }\r
829 \r
830     public static class MeshToOBJ\r
831     {\r
832         public static bool MeshesToOBJ(Dictionary<uint, FacetedMesh> meshes, string filename)\r
833         {\r
834             StringBuilder obj = new StringBuilder();\r
835             StringBuilder mtl = new StringBuilder();\r
836 \r
837             FileInfo objFileInfo = new FileInfo(filename);\r
838 \r
839             string mtlFilename = objFileInfo.FullName.Substring(objFileInfo.DirectoryName.Length + 1,\r
840                 objFileInfo.FullName.Length - (objFileInfo.DirectoryName.Length + 1) - 4) + ".mtl";\r
841 \r
842             obj.AppendLine("# Created by libprimrender");\r
843             obj.AppendLine("mtllib ./" + mtlFilename);\r
844             obj.AppendLine();\r
845 \r
846             mtl.AppendLine("# Created by libprimrender");\r
847             mtl.AppendLine();\r
848 \r
849             int primNr = 0;\r
850             foreach (FacetedMesh mesh in meshes.Values)\r
851             {\r
852                 for (int j = 0; j < mesh.Faces.Count; j++)\r
853                 {\r
854                     Face face = mesh.Faces[j];\r
855 \r
856                     if (face.Vertices.Count > 2)\r
857                     {\r
858                         string mtlName = String.Format("material{0}-{1}", primNr, face.ID);\r
859                         Primitive.TextureEntryFace tex = face.TextureFace;\r
860                         string texName = tex.TextureID.ToString() + ".tga";\r
861 \r
862                         // FIXME: Convert the source to TGA (if needed) and copy to the destination\r
863 \r
864                         float shiny = 0.00f;\r
865                         switch (tex.Shiny)\r
866                         {\r
867                             case Shininess.High:\r
868                                 shiny = 1.00f;\r
869                                 break;\r
870                             case Shininess.Medium:\r
871                                 shiny = 0.66f;\r
872                                 break;\r
873                             case Shininess.Low:\r
874                                 shiny = 0.33f;\r
875                                 break;\r
876                         }\r
877 \r
878                         obj.AppendFormat("g face{0}-{1}{2}", primNr, face.ID, Environment.NewLine);\r
879 \r
880                         mtl.AppendLine("newmtl " + mtlName);\r
881                         mtl.AppendFormat("Ka {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);\r
882                         mtl.AppendFormat("Kd {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);\r
883                         //mtl.AppendFormat("Ks {0} {1} {2}{3}");\r
884                         mtl.AppendLine("Tr " + tex.RGBA.A);\r
885                         mtl.AppendLine("Ns " + shiny);\r
886                         mtl.AppendLine("illum 1");\r
887                         if (tex.TextureID != UUID.Zero && tex.TextureID != Primitive.TextureEntry.WHITE_TEXTURE)\r
888                             mtl.AppendLine("map_Kd ./" + texName);\r
889                         mtl.AppendLine();\r
890 \r
891                         // Write the vertices, texture coordinates, and vertex normals for this side\r
892                         for (int k = 0; k < face.Vertices.Count; k++)\r
893                         {\r
894                             Vertex vertex = face.Vertices[k];\r
895 \r
896                             #region Vertex\r
897 \r
898                             Vector3 pos = vertex.Position;\r
899 \r
900                             // Apply scaling\r
901                             pos *= mesh.Prim.Scale;\r
902 \r
903                             // Apply rotation\r
904                             pos *= mesh.Prim.Rotation;\r
905 \r
906                             // The root prim position is sim-relative, while child prim positions are\r
907                             // parent-relative. We want to apply parent-relative translations but not\r
908                             // sim-relative ones\r
909                             if (mesh.Prim.ParentID != 0)\r
910                                 pos += mesh.Prim.Position;\r
911 \r
912                             obj.AppendFormat("v {0} {1} {2}{3}", pos.X, pos.Y, pos.Z, Environment.NewLine);\r
913 \r
914                             #endregion Vertex\r
915 \r
916                             #region Texture Coord\r
917 \r
918                             obj.AppendFormat("vt {0} {1}{2}", vertex.TexCoord.X, vertex.TexCoord.Y,\r
919                                 Environment.NewLine);\r
920 \r
921                             #endregion Texture Coord\r
922 \r
923                             #region Vertex Normal\r
924 \r
925                             // HACK: Sometimes normals are getting set to <NaN,NaN,NaN>\r
926                             if (!Single.IsNaN(vertex.Normal.X) && !Single.IsNaN(vertex.Normal.Y) && !Single.IsNaN(vertex.Normal.Z))\r
927                                 obj.AppendFormat("vn {0} {1} {2}{3}", vertex.Normal.X, vertex.Normal.Y, vertex.Normal.Z,\r
928                                     Environment.NewLine);\r
929                             else\r
930                                 obj.AppendLine("vn 0.0 1.0 0.0");\r
931 \r
932                             #endregion Vertex Normal\r
933                         }\r
934 \r
935                         obj.AppendFormat("# {0} vertices{1}", face.Vertices.Count, Environment.NewLine);\r
936                         obj.AppendLine();\r
937                         obj.AppendLine("usemtl " + mtlName);\r
938 \r
939                         #region Elements\r
940 \r
941                         // Write all of the faces (triangles) for this side\r
942                         for (int k = 0; k < face.Indices.Count / 3; k++)\r
943                         {\r
944                             obj.AppendFormat("f -{0}/-{0}/-{0} -{1}/-{1}/-{1} -{2}/-{2}/-{2}{3}",\r
945                                 face.Vertices.Count - face.Indices[k * 3 + 0],\r
946                                 face.Vertices.Count - face.Indices[k * 3 + 1],\r
947                                 face.Vertices.Count - face.Indices[k * 3 + 2],\r
948                                 Environment.NewLine);\r
949                         }\r
950 \r
951                         obj.AppendFormat("# {0} elements{1}", face.Indices.Count / 3, Environment.NewLine);\r
952                         obj.AppendLine();\r
953 \r
954                         #endregion Elements\r
955                     }\r
956                 }\r
957                 primNr++;\r
958             }\r
959 \r
960             try\r
961             {\r
962                 File.WriteAllText(filename, obj.ToString());\r
963                 File.WriteAllText(mtlFilename, mtl.ToString());\r
964             }\r
965             catch (Exception)\r
966             {\r
967                 return false;\r
968             }\r
969 \r
970             return true;\r
971         }\r
972     }\r
973 \r
974     public static class Math3D\r
975     {\r
976         // Column-major:\r
977         // |  0  4  8 12 |\r
978         // |  1  5  9 13 |\r
979         // |  2  6 10 14 |\r
980         // |  3  7 11 15 |\r
981 \r
982         public static float[] CreateTranslationMatrix(Vector3 v)\r
983         {\r
984             float[] mat = new float[16];\r
985 \r
986             mat[12] = v.X;\r
987             mat[13] = v.Y;\r
988             mat[14] = v.Z;\r
989             mat[0] = mat[5] = mat[10] = mat[15] = 1;\r
990 \r
991             return mat;\r
992         }\r
993 \r
994         public static float[] CreateRotationMatrix(Quaternion q)\r
995         {\r
996             float[] mat = new float[16];\r
997 \r
998             // Transpose the quaternion (don't ask me why)\r
999             q.X = q.X * -1f;\r
1000             q.Y = q.Y * -1f;\r
1001             q.Z = q.Z * -1f;\r
1002 \r
1003             float x2 = q.X + q.X;\r
1004             float y2 = q.Y + q.Y;\r
1005             float z2 = q.Z + q.Z;\r
1006             float xx = q.X * x2;\r
1007             float xy = q.X * y2;\r
1008             float xz = q.X * z2;\r
1009             float yy = q.Y * y2;\r
1010             float yz = q.Y * z2;\r
1011             float zz = q.Z * z2;\r
1012             float wx = q.W * x2;\r
1013             float wy = q.W * y2;\r
1014             float wz = q.W * z2;\r
1015 \r
1016             mat[0] = 1.0f - (yy + zz);\r
1017             mat[1] = xy - wz;\r
1018             mat[2] = xz + wy;\r
1019             mat[3] = 0.0f;\r
1020 \r
1021             mat[4] = xy + wz;\r
1022             mat[5] = 1.0f - (xx + zz);\r
1023             mat[6] = yz - wx;\r
1024             mat[7] = 0.0f;\r
1025 \r
1026             mat[8] = xz - wy;\r
1027             mat[9] = yz + wx;\r
1028             mat[10] = 1.0f - (xx + yy);\r
1029             mat[11] = 0.0f;\r
1030 \r
1031             mat[12] = 0.0f;\r
1032             mat[13] = 0.0f;\r
1033             mat[14] = 0.0f;\r
1034             mat[15] = 1.0f;\r
1035 \r
1036             return mat;\r
1037         }\r
1038 \r
1039         public static float[] CreateSRTMatrix(Vector3 scale, Quaternion q, Vector3 pos)\r
1040         {\r
1041             float[] mat = new float[16];\r
1042 \r
1043             // Transpose the quaternion (don't ask me why)\r
1044             q.X = q.X * -1f;\r
1045             q.Y = q.Y * -1f;\r
1046             q.Z = q.Z * -1f;\r
1047 \r
1048             float x2 = q.X + q.X;\r
1049             float y2 = q.Y + q.Y;\r
1050             float z2 = q.Z + q.Z;\r
1051             float xx = q.X * x2;\r
1052             float xy = q.X * y2;\r
1053             float xz = q.X * z2;\r
1054             float yy = q.Y * y2;\r
1055             float yz = q.Y * z2;\r
1056             float zz = q.Z * z2;\r
1057             float wx = q.W * x2;\r
1058             float wy = q.W * y2;\r
1059             float wz = q.W * z2;\r
1060 \r
1061             mat[0] = (1.0f - (yy + zz)) * scale.X;\r
1062             mat[1] = (xy - wz) * scale.X;\r
1063             mat[2] = (xz + wy) * scale.X;\r
1064             mat[3] = 0.0f;\r
1065 \r
1066             mat[4] = (xy + wz) * scale.Y;\r
1067             mat[5] = (1.0f - (xx + zz)) * scale.Y;\r
1068             mat[6] = (yz - wx) * scale.Y;\r
1069             mat[7] = 0.0f;\r
1070 \r
1071             mat[8] = (xz - wy) * scale.Z;\r
1072             mat[9] = (yz + wx) * scale.Z;\r
1073             mat[10] = (1.0f - (xx + yy)) * scale.Z;\r
1074             mat[11] = 0.0f;\r
1075 \r
1076             //Positional parts\r
1077             mat[12] = pos.X;\r
1078             mat[13] = pos.Y;\r
1079             mat[14] = pos.Z;\r
1080             mat[15] = 1.0f;\r
1081 \r
1082             return mat;\r
1083         }\r
1084 \r
1085 \r
1086         public static float[] CreateScaleMatrix(Vector3 v)\r
1087         {\r
1088             float[] mat = new float[16];\r
1089 \r
1090             mat[0] = v.X;\r
1091             mat[5] = v.Y;\r
1092             mat[10] = v.Z;\r
1093             mat[15] = 1;\r
1094 \r
1095             return mat;\r
1096         }\r
1097 \r
1098         public static float[] Lerp(float[] matrix1, float[] matrix2, float amount)\r
1099         {\r
1100 \r
1101             float[] lerp = new float[16];\r
1102             //Probably not doing this as a loop is cheaper(unrolling)\r
1103             //also for performance we probably should not create new objects\r
1104             // but meh.\r
1105             for (int x = 0; x < 16; x++)\r
1106             {\r
1107                 lerp[x] = matrix1[x] + ((matrix2[x] - matrix1[x]) * amount);\r
1108             }\r
1109 \r
1110             return lerp;\r
1111         }\r
1112 \r
1113 \r
1114         public static bool GluProject(OpenTK.Vector3 objPos, OpenTK.Matrix4 modelMatrix, OpenTK.Matrix4 projMatrix, int[] viewport, out OpenTK.Vector3 screenPos)\r
1115         {\r
1116             OpenTK.Vector4 _in;\r
1117             OpenTK.Vector4 _out;\r
1118 \r
1119             _in.X = objPos.X;\r
1120             _in.Y = objPos.Y;\r
1121             _in.Z = objPos.Z;\r
1122             _in.W = 1.0f;\r
1123 \r
1124             _out = OpenTK.Vector4.Transform(_in, modelMatrix);\r
1125             _in = OpenTK.Vector4.Transform(_out, projMatrix);\r
1126 \r
1127             if (_in.W <= 0.0)\r
1128             {\r
1129                 screenPos = OpenTK.Vector3.Zero;\r
1130                 return false;\r
1131             }\r
1132 \r
1133             _in.X /= _in.W;\r
1134             _in.Y /= _in.W;\r
1135             _in.Z /= _in.W;\r
1136             /* Map x, y and z to range 0-1 */\r
1137             _in.X = _in.X * 0.5f + 0.5f;\r
1138             _in.Y = _in.Y * 0.5f + 0.5f;\r
1139             _in.Z = _in.Z * 0.5f + 0.5f;\r
1140 \r
1141             /* Map x,y to viewport */\r
1142             _in.X = _in.X * viewport[2] + viewport[0];\r
1143             _in.Y = _in.Y * viewport[3] + viewport[1];\r
1144 \r
1145             screenPos.X = _in.X;\r
1146             screenPos.Y = _in.Y;\r
1147             screenPos.Z = _in.Z;\r
1148 \r
1149             return true;\r
1150         }\r
1151     }\r
1152 \r
1153     public class attachment_point\r
1154     {\r
1155         public string name;\r
1156         public string joint;\r
1157         public Vector3 position;\r
1158         public Quaternion rotation;\r
1159         public int id;\r
1160         public int group;\r
1161 \r
1162         public GLMesh jointmesh;\r
1163         public int jointmeshindex;\r
1164 \r
1165         public attachment_point(XmlNode node)\r
1166         {\r
1167             name = node.Attributes.GetNamedItem("name").Value;\r
1168             joint = node.Attributes.GetNamedItem("joint").Value;\r
1169             position = VisualParamEx.XmlParseVector(node.Attributes.GetNamedItem("position").Value);\r
1170             rotation = VisualParamEx.XmlParseRotation(node.Attributes.GetNamedItem("rotation").Value);\r
1171             id = Int32.Parse(node.Attributes.GetNamedItem("id").Value);\r
1172             group = Int32.Parse(node.Attributes.GetNamedItem("group").Value);\r
1173         }\r
1174 \r
1175     }\r
1176 \r
1177     /// <summary>\r
1178     /// Subclass of LindenMesh that adds vertex, index, and texture coordinate\r
1179     /// arrays suitable for pushing direct to OpenGL\r
1180     /// </summary>\r
1181     public class GLMesh : LindenMesh\r
1182     {\r
1183         /// <summary>\r
1184         /// Subclass of LODMesh that adds an index array suitable for pushing\r
1185         /// direct to OpenGL\r
1186         /// </summary>\r
1187         /// \r
1188 \r
1189         public int teFaceID;\r
1190         public Dictionary<int, VisualParamEx> _evp = new Dictionary<int, VisualParamEx>();\r
1191 \r
1192         new public class LODMesh : LindenMesh.LODMesh\r
1193         {\r
1194             public ushort[] Indices;\r
1195 \r
1196             public override void LoadMesh(string filename)\r
1197             {\r
1198                 base.LoadMesh(filename);\r
1199 \r
1200                 // Generate the index array\r
1201                 Indices = new ushort[_numFaces * 3];\r
1202                 int current = 0;\r
1203                 for (int i = 0; i < _numFaces; i++)\r
1204                 {\r
1205                     Indices[current++] = (ushort)_faces[i].Indices[0];\r
1206                     Indices[current++] = (ushort)_faces[i].Indices[1];\r
1207                     Indices[current++] = (ushort)_faces[i].Indices[2];\r
1208                 }\r
1209             }\r
1210         }\r
1211 \r
1212         /// <summary>\r
1213         /// \r
1214         /// </summary>\r
1215         public struct GLData\r
1216         {\r
1217             public float[] Vertices;\r
1218             public float[] Normals;\r
1219             public ushort[] Indices;\r
1220             public float[] TexCoords;\r
1221             public Vector3 Center;\r
1222             public float[] weights; //strictly these are constant and don't need instancing with the GLMesh\r
1223             public string[] skinJoints;  //strictly these are constant and don't need instancing with the GLMesh\r
1224         }\r
1225 \r
1226         public static GLData baseRenderData;\r
1227         public GLData RenderData;\r
1228         public GLData OrigRenderData;\r
1229         public GLData MorphRenderData;\r
1230 \r
1231         public GLAvatar av;\r
1232 \r
1233         public GLMesh(string name)\r
1234             : base(name)\r
1235         {\r
1236         }\r
1237 \r
1238         public GLMesh(GLMesh source, GLAvatar av)\r
1239             : base(source.Name)\r
1240         {\r
1241             this.av = av;\r
1242             // Make a new GLMesh copy from the supplied source\r
1243 \r
1244             RenderData.Vertices = new float[source.RenderData.Vertices.Length];\r
1245             RenderData.Normals = new float[source.RenderData.Normals.Length];\r
1246             RenderData.TexCoords = new float[source.RenderData.TexCoords.Length];\r
1247             RenderData.Indices = new ushort[source.RenderData.Indices.Length];\r
1248 \r
1249             RenderData.weights = new float[source.RenderData.weights.Length];\r
1250             RenderData.skinJoints = new string[source.RenderData.skinJoints.Length];\r
1251 \r
1252             Array.Copy(source.RenderData.Vertices, RenderData.Vertices, source.RenderData.Vertices.Length);\r
1253             Array.Copy(source.RenderData.Normals, RenderData.Normals, source.RenderData.Normals.Length);\r
1254 \r
1255             Array.Copy(source.RenderData.TexCoords, RenderData.TexCoords, source.RenderData.TexCoords.Length);\r
1256             Array.Copy(source.RenderData.Indices, RenderData.Indices, source.RenderData.Indices.Length);\r
1257             Array.Copy(source.RenderData.weights, RenderData.weights, source.RenderData.weights.Length);\r
1258             Array.Copy(source.RenderData.skinJoints, RenderData.skinJoints, source.RenderData.skinJoints.Length);\r
1259 \r
1260             RenderData.Center = new Vector3(source.RenderData.Center);\r
1261 \r
1262             teFaceID = source.teFaceID;\r
1263 \r
1264             _rotationAngles = new Vector3(source.RotationAngles);\r
1265             _scale = new Vector3(source.Scale);\r
1266             _position = new Vector3(source.Position);\r
1267 \r
1268             // We should not need to instance these the reference from the top should be constant\r
1269             _evp = source._evp;\r
1270             _morphs = source._morphs;\r
1271 \r
1272             OrigRenderData.Indices = new ushort[source.RenderData.Indices.Length];\r
1273             OrigRenderData.TexCoords = new float[source.RenderData.TexCoords.Length];\r
1274             OrigRenderData.Vertices = new float[source.RenderData.Vertices.Length];\r
1275 \r
1276             MorphRenderData.Vertices = new float[source.RenderData.Vertices.Length];\r
1277 \r
1278             Array.Copy(source.RenderData.Vertices, OrigRenderData.Vertices, source.RenderData.Vertices.Length);\r
1279             Array.Copy(source.RenderData.Vertices, MorphRenderData.Vertices, source.RenderData.Vertices.Length);\r
1280 \r
1281             Array.Copy(source.RenderData.TexCoords, OrigRenderData.TexCoords, source.RenderData.TexCoords.Length);\r
1282             Array.Copy(source.RenderData.Indices, OrigRenderData.Indices, source.RenderData.Indices.Length);\r
1283 \r
1284 \r
1285 \r
1286         }\r
1287 \r
1288         public void setMeshPos(Vector3 pos)\r
1289         {\r
1290             _position = pos;\r
1291         }\r
1292 \r
1293         public void setMeshRot(Vector3 rot)\r
1294         {\r
1295             _rotationAngles = rot;\r
1296         }\r
1297 \r
1298         public override void LoadMesh(string filename)\r
1299         {\r
1300             base.LoadMesh(filename);\r
1301 \r
1302             float minX, minY, minZ;\r
1303             minX = minY = minZ = Single.MaxValue;\r
1304             float maxX, maxY, maxZ;\r
1305             maxX = maxY = maxZ = Single.MinValue;\r
1306 \r
1307             // Generate the vertex array\r
1308             RenderData.Vertices = new float[_numVertices * 3];\r
1309             RenderData.Normals = new float[_numVertices * 3];\r
1310 \r
1311             Quaternion quat = Quaternion.CreateFromEulers(0, 0, (float)(Math.PI / 4.0));\r
1312 \r
1313             int current = 0;\r
1314             for (int i = 0; i < _numVertices; i++)\r
1315             {\r
1316 \r
1317                 RenderData.Normals[current] = _vertices[i].Normal.X;\r
1318                 RenderData.Vertices[current++] = _vertices[i].Coord.X;\r
1319                 RenderData.Normals[current] = _vertices[i].Normal.Y;\r
1320                 RenderData.Vertices[current++] = _vertices[i].Coord.Y;\r
1321                 RenderData.Normals[current] = _vertices[i].Normal.Z;\r
1322                 RenderData.Vertices[current++] = _vertices[i].Coord.Z;\r
1323 \r
1324                 if (_vertices[i].Coord.X < minX)\r
1325                     minX = _vertices[i].Coord.X;\r
1326                 else if (_vertices[i].Coord.X > maxX)\r
1327                     maxX = _vertices[i].Coord.X;\r
1328 \r
1329                 if (_vertices[i].Coord.Y < minY)\r
1330                     minY = _vertices[i].Coord.Y;\r
1331                 else if (_vertices[i].Coord.Y > maxY)\r
1332                     maxY = _vertices[i].Coord.Y;\r
1333 \r
1334                 if (_vertices[i].Coord.Z < minZ)\r
1335                     minZ = _vertices[i].Coord.Z;\r
1336                 else if (_vertices[i].Coord.Z > maxZ)\r
1337                     maxZ = _vertices[i].Coord.Z;\r
1338             }\r
1339 \r
1340             // Calculate the center-point from the bounding box edges\r
1341             RenderData.Center = new Vector3((minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2);\r
1342 \r
1343             // Generate the index array\r
1344             RenderData.Indices = new ushort[_numFaces * 3];\r
1345             current = 0;\r
1346             for (int i = 0; i < _numFaces; i++)\r
1347             {\r
1348                 RenderData.Indices[current++] = (ushort)_faces[i].Indices[0];\r
1349                 RenderData.Indices[current++] = (ushort)_faces[i].Indices[1];\r
1350                 RenderData.Indices[current++] = (ushort)_faces[i].Indices[2];\r
1351             }\r
1352 \r
1353             // Generate the texcoord array\r
1354             RenderData.TexCoords = new float[_numVertices * 2];\r
1355             current = 0;\r
1356             for (int i = 0; i < _numVertices; i++)\r
1357             {\r
1358                 RenderData.TexCoords[current++] = _vertices[i].TexCoord.X;\r
1359                 RenderData.TexCoords[current++] = _vertices[i].TexCoord.Y;\r
1360             }\r
1361 \r
1362             RenderData.weights = new float[_numVertices];\r
1363             for (int i = 0; i < _numVertices; i++)\r
1364             {\r
1365                 RenderData.weights[i] = _vertices[i].Weight;\r
1366             }\r
1367 \r
1368             RenderData.skinJoints = new string[_skinJoints.Length + 3];\r
1369             for (int i = 1; i < _skinJoints.Length; i++)\r
1370             {\r
1371                 RenderData.skinJoints[i] = _skinJoints[i];\r
1372             }\r
1373 \r
1374 \r
1375         }\r
1376 \r
1377         public override void LoadLODMesh(int level, string filename)\r
1378         {\r
1379             LODMesh lod = new LODMesh();\r
1380             lod.LoadMesh(filename);\r
1381             _lodMeshes[level] = lod;\r
1382         }\r
1383 \r
1384         public void applyjointweights()\r
1385         {\r
1386 \r
1387             /*Each weight actually contains two pieces of information. \r
1388              * The number to the left of the decimal point is the index of the joint and also \r
1389              * implicitly indexes to the following joint. The actual weight is to the right of \r
1390              * the decimal point and interpolates between these two joints. The index is into an \r
1391              * "expanded" list of joints, not just a linear array of the joints as defined in the \r
1392              * skeleton file. In particular, any joint that has more than one child will be repeated \r
1393              * in the list for each of its children.\r
1394              */\r
1395 \r
1396             float weight = -9999;\r
1397             int jointindex = 0;\r
1398             float factor;\r
1399 \r
1400             Bone ba = null;\r
1401             Bone bb = null;\r
1402 \r
1403             for (int v = 0, x = 0; v < RenderData.Vertices.Length; v = v + 3, x++)\r
1404             {\r
1405                 if (weight != RenderData.weights[x])\r
1406                 {\r
1407 \r
1408                     jointindex = (int)Math.Floor(weight = RenderData.weights[x]);\r
1409                     factor = RenderData.weights[x] - jointindex;\r
1410                     weight = weight - jointindex;\r
1411 \r
1412                     string jointname = "", jointname2 = "";\r
1413 \r
1414                     if (this.Name == "upperBodyMesh")\r
1415                     {\r
1416                         jointname = skeleton.mUpperMeshMapping[jointindex];\r
1417                         jointindex++;\r
1418                         jointname2 = skeleton.mUpperMeshMapping[jointindex];\r
1419                     }\r
1420                     else if (Name == "lowerBodyMesh")\r
1421                     {\r
1422                         jointname = skeleton.mLowerMeshMapping[jointindex];\r
1423                         jointindex++;\r
1424                         jointname2 = skeleton.mLowerMeshMapping[jointindex];\r
1425                     }\r
1426                     else if (Name == "headMesh")\r
1427                     {\r
1428                         jointname = skeleton.mHeadMeshMapping[jointindex];\r
1429                         jointindex++;\r
1430                         jointname2 = skeleton.mHeadMeshMapping[jointindex];\r
1431                     }\r
1432                     else\r
1433                     {\r
1434                         return; // not interested in this mesh\r
1435                     }\r
1436 \r
1437 \r
1438                     if (jointname == "")\r
1439                     {\r
1440                         //Don't yet handle this, its a split joint to two children\r
1441                         ba = av.skel.mBones[jointname2];\r
1442                         bb = null;\r
1443 \r
1444                         //continue;\r
1445                     }\r
1446                     else\r
1447                     {\r
1448 \r
1449                         ba = av.skel.mBones[jointname];\r
1450                     }\r
1451 \r
1452                     if (jointname2 == "")\r
1453                     {\r
1454                         bb = null;\r
1455                     }\r
1456                     else\r
1457                     {\r
1458                         bb = av.skel.mBones[jointname2];\r
1459                     }\r
1460                 }\r
1461 \r
1462                 //Special cases 0 is not used\r
1463                 // ON upper torso 5 and 10 are not used\r
1464                 // 4 is neck and 6 and 11 are the left and right collar bones\r
1465 \r
1466                 Vector3 lerp;\r
1467                 Vector3 offset;\r
1468                 Quaternion rot = ba.getTotalRotation();\r
1469 \r
1470                 if (bb != null)\r
1471                 {\r
1472                     lerp = Vector3.Lerp(ba.getDeltaOffset(), bb.getDeltaOffset(), weight);\r
1473                     offset = Vector3.Lerp(ba.getTotalOffset(), bb.getTotalOffset(), weight);\r
1474                 }\r
1475                 else\r
1476                 {\r
1477                     lerp = ba.getDeltaOffset();\r
1478                     offset = ba.getTotalOffset();\r
1479                     rot = ba.getTotalRotation();\r
1480                 }\r
1481 \r
1482                 Vector3 pos = new Vector3(MorphRenderData.Vertices[v], MorphRenderData.Vertices[v + 1], MorphRenderData.Vertices[v + 2]);\r
1483 \r
1484                 //move back to mesh local coords\r
1485                 pos = pos - offset;\r
1486                 // apply LERPd offset\r
1487                 pos = pos + lerp;\r
1488                 // rotate our offset by total rotation\r
1489                 pos = pos * rot;\r
1490                 //move back to avatar local coords\r
1491                 pos = pos + offset;\r
1492 \r
1493                 RenderData.Vertices[v] = pos.X;\r
1494                 RenderData.Vertices[v + 1] = pos.Y;\r
1495                 RenderData.Vertices[v + 2] = pos.Z;\r
1496             }\r
1497         }\r
1498 \r
1499         public void resetallmorphs()\r
1500         {\r
1501             for (int i = 0; i < OrigRenderData.Vertices.Length / 3; i++)\r
1502             {\r
1503 \r
1504                 MorphRenderData.Vertices[i * 3] = OrigRenderData.Vertices[i * 3];\r
1505                 MorphRenderData.Vertices[(i * 3) + 1] = OrigRenderData.Vertices[i * 3 + 1];\r
1506                 MorphRenderData.Vertices[(i * 3) + 2] = OrigRenderData.Vertices[i * 3 + 2];\r
1507 \r
1508                 //MorphRenderData.Normals[i * 3] = OrigRenderData.Normals[i * 3];\r
1509                 //MorphRenderData.Normals[(i * 3) + 1] = OrigRenderData.Normals[i * 3 + 1];\r
1510                 //MorphRenderData.Normals[(i * 3) + 2] = OrigRenderData.Normals[i * 3 + 2];\r
1511 \r
1512                 RenderData.TexCoords[i * 2] = OrigRenderData.TexCoords[i * 2];\r
1513                 RenderData.TexCoords[(i * 2) + 1] = OrigRenderData.TexCoords[i * 2 + 1];\r
1514 \r
1515             }\r
1516 \r
1517         }\r
1518 \r
1519         public void morphmesh(Morph morph, float weight)\r
1520         {\r
1521             for (int v = 0; v < morph.NumVertices; v++)\r
1522             {\r
1523                 MorphVertex mvx = morph.Vertices[v];\r
1524 \r
1525                 uint i = mvx.VertexIndex;\r
1526 \r
1527                 MorphRenderData.Vertices[i * 3] = MorphRenderData.Vertices[i * 3] + mvx.Coord.X * weight;\r
1528                 MorphRenderData.Vertices[(i * 3) + 1] = MorphRenderData.Vertices[i * 3 + 1] + mvx.Coord.Y * weight;\r
1529                 MorphRenderData.Vertices[(i * 3) + 2] = MorphRenderData.Vertices[i * 3 + 2] + mvx.Coord.Z * weight;\r
1530 \r
1531                 //MorphRenderData.Normals[i * 3] = MorphRenderData.Normals[i * 3] + mvx.Normal.X * weight;\r
1532                 //MorphRenderData.Normals[(i * 3)+1] = MorphRenderData.Normals[(i * 3)+1] + mvx.Normal.Y * weight;\r
1533                 //MorphRenderData.Normals[(i * 3)+2] = MorphRenderData.Normals[(i * 3)+2] + mvx.Normal.Z * weight;\r
1534 \r
1535                 RenderData.TexCoords[i * 2] = OrigRenderData.TexCoords[i * 2] + mvx.TexCoord.X * weight;\r
1536                 RenderData.TexCoords[(i * 2) + 1] = OrigRenderData.TexCoords[i * 2 + 1] + mvx.TexCoord.Y * weight;\r
1537 \r
1538             }\r
1539         }\r
1540     }\r
1541 \r
1542     public class GLAvatar\r
1543     {\r
1544         private static Dictionary<string, GLMesh> _defaultmeshes = new Dictionary<string, GLMesh>();\r
1545         public Dictionary<string, GLMesh> _meshes = new Dictionary<string, GLMesh>();\r
1546 \r
1547         public skeleton skel = new skeleton();\r
1548         public static Dictionary<int, attachment_point> attachment_points = new Dictionary<int, attachment_point>();\r
1549 \r
1550         public bool _wireframe = true;\r
1551         public bool _showSkirt = false;\r
1552 \r
1553         public VisualParamEx.EparamSex msex;\r
1554 \r
1555         public byte[] VisualAppearanceParameters = new byte[1024];\r
1556         bool vpsent = false;\r
1557         static bool lindenMeshesLoaded = false;\r
1558 \r
1559         public GLAvatar()\r
1560         {\r
1561             foreach (KeyValuePair<string, GLMesh> kvp in _defaultmeshes)\r
1562             {\r
1563                 GLMesh mesh = new GLMesh(kvp.Value, this); // Instance our meshes\r
1564                 _meshes.Add(kvp.Key, mesh);\r
1565 \r
1566             }\r
1567         }\r
1568 \r
1569         public static void dumptweaks()\r
1570         {\r
1571 \r
1572             for (int x = 0; x < VisualParamEx.tweakable_params.Count; x++)\r
1573             {\r
1574                 VisualParamEx vpe = (VisualParamEx)VisualParamEx.tweakable_params.GetByIndex(x);\r
1575                 Console.WriteLine(string.Format("{0} is {1}", x, vpe.Name));\r
1576             }\r
1577 \r
1578 \r
1579         }\r
1580 \r
1581         public static void loadlindenmeshes2(string LODfilename)\r
1582         {\r
1583             // Already have mashes loaded?\r
1584             if (lindenMeshesLoaded) return;\r
1585 \r
1586             attachment_points.Clear();\r
1587 \r
1588 \r
1589             string basedir = Directory.GetCurrentDirectory() + System.IO.Path.DirectorySeparatorChar + "character" + System.IO.Path.DirectorySeparatorChar;\r
1590 \r
1591             XmlDocument lad = new XmlDocument();\r
1592             lad.Load(basedir + LODfilename);\r
1593 \r
1594             //Firstly read the skeleton section this contains attachment point info and the bone deform info for visual params\r
1595             // And load the skeleton file in to the bones class\r
1596 \r
1597             XmlNodeList skeleton = lad.GetElementsByTagName("skeleton");\r
1598             string skeletonfilename = skeleton[0].Attributes.GetNamedItem("file_name").Value;\r
1599             Bone.loadbones(skeletonfilename);\r
1600 \r
1601             // Next read all the skeleton child nodes, we have attachment points and bone deform params\r
1602             // attachment points are an offset and rotation from a bone location\r
1603             // the name of the bone they reference is the joint paramater\r
1604             // params in the skeleton nodes are bone deforms, eg leg length changes the scale of the leg bones\r
1605 \r
1606             foreach (XmlNode skeletonnode in skeleton[0].ChildNodes)\r
1607             {\r
1608                 if (skeletonnode.Name == "attachment_point")\r
1609                 {\r
1610                     attachment_point point = new attachment_point(skeletonnode);\r
1611                     attachment_points.Add(point.id, point);\r
1612                 }\r
1613 \r
1614                 if (skeletonnode.Name == "param")\r
1615                 {\r
1616                     //Bone deform param\r
1617                     VisualParamEx vp = new VisualParamEx(skeletonnode, VisualParamEx.ParamType.TYPE_BONEDEFORM);\r
1618                 }\r
1619             }\r
1620 \r
1621             //Now we parse the mesh nodes, mesh nodes reference a particular LLM file with a LOD\r
1622             //and also list VisualParams for the various mesh morphs that can be applied\r
1623 \r
1624             XmlNodeList meshes = lad.GetElementsByTagName("mesh");\r
1625             foreach (XmlNode meshNode in meshes)\r
1626             {\r
1627                 string type = meshNode.Attributes.GetNamedItem("type").Value;\r
1628                 int lod = Int32.Parse(meshNode.Attributes.GetNamedItem("lod").Value);\r
1629                 string fileName = meshNode.Attributes.GetNamedItem("file_name").Value;\r
1630 \r
1631                 GLMesh mesh = (_defaultmeshes.ContainsKey(type) ? _defaultmeshes[type] : new GLMesh(type));\r
1632 \r
1633                 if (meshNode.HasChildNodes)\r
1634                 {\r
1635                     foreach (XmlNode paramnode in meshNode.ChildNodes)\r
1636                     {\r
1637                         if (paramnode.Name == "param")\r
1638                         {\r
1639                             VisualParamEx vp = new VisualParamEx(paramnode, VisualParamEx.ParamType.TYPE_MORPH);\r
1640 \r
1641                             mesh._evp.Add(vp.ParamID, vp); //Not sure we really need this may optimise out later\r
1642                             vp.morphmesh = mesh.Name;\r
1643                         }\r
1644                     }\r
1645                 }\r
1646 \r
1647                 // Set up the texture elemenets for each mesh\r
1648                 // And hack the eyeball position\r
1649                 switch (mesh.Name)\r
1650                 {\r
1651                     case "lowerBodyMesh":\r
1652                         mesh.teFaceID = (int)AvatarTextureIndex.LowerBaked;\r
1653                         break;\r
1654 \r
1655                     case "upperBodyMesh":\r
1656                         mesh.teFaceID = (int)AvatarTextureIndex.UpperBaked;\r
1657                         break;\r
1658 \r
1659                     case "headMesh":\r
1660                         mesh.teFaceID = (int)AvatarTextureIndex.HeadBaked;\r
1661                         break;\r
1662 \r
1663                     case "hairMesh":\r
1664                         mesh.teFaceID = (int)AvatarTextureIndex.HairBaked;\r
1665                         break;\r
1666 \r
1667                     case "eyelashMesh":\r
1668                         mesh.teFaceID = (int)AvatarTextureIndex.HeadBaked;\r
1669                         break;\r
1670 \r
1671                     case "eyeBallRightMesh":\r
1672                         mesh.setMeshPos(Bone.mBones["mEyeLeft"].getTotalOffset());\r
1673                         //mesh.setMeshRot(Bone.getRotation("mEyeLeft"));\r
1674                         mesh.teFaceID = (int)AvatarTextureIndex.EyesBaked;\r
1675                         break;\r
1676 \r
1677                     case "eyeBallLeftMesh":\r
1678                         mesh.setMeshPos(Bone.mBones["mEyeRight"].getTotalOffset());\r
1679                         //mesh.setMeshRot(Bone.getRotation("mEyeRight"));\r
1680                         mesh.teFaceID = (int)AvatarTextureIndex.EyesBaked;\r
1681                         break;\r
1682 \r
1683                     case "skirtMesh":\r
1684                         mesh.teFaceID = (int)AvatarTextureIndex.SkirtBaked;\r
1685                         break;\r
1686 \r
1687                     default:\r
1688                         mesh.teFaceID = 0;\r
1689                         break;\r
1690                 }\r
1691 \r
1692                 if (lod == 0)\r
1693                     mesh.LoadMesh(basedir + fileName);\r
1694                 else\r
1695                     mesh.LoadLODMesh(lod, basedir + fileName);\r
1696 \r
1697                 _defaultmeshes[type] = mesh;\r
1698 \r
1699             }\r
1700 \r
1701             // Next are the textureing params, skipping for the moment\r
1702 \r
1703             XmlNodeList colors = lad.GetElementsByTagName("global_color");\r
1704             {\r
1705                 foreach (XmlNode globalcolornode in colors)\r
1706                 {\r
1707                     foreach (XmlNode node in globalcolornode.ChildNodes)\r
1708                     {\r
1709                         if (node.Name == "param")\r
1710                         {\r
1711                             VisualParamEx vp = new VisualParamEx(node, VisualParamEx.ParamType.TYPE_COLOR);\r
1712                         }\r
1713                     }\r
1714                 }\r
1715             }\r
1716 \r
1717             // Get layer paramaters, a bit of a verbose way to do it but we probably want to get access\r
1718             // to some of the other data not just the <param> tag\r
1719 \r
1720             XmlNodeList layer_sets = lad.GetElementsByTagName("layer_set");\r
1721             {\r
1722                 foreach (XmlNode layer_set in layer_sets)\r
1723                 {\r
1724                     foreach (XmlNode layer in layer_set.ChildNodes)\r
1725                     {\r
1726                         foreach (XmlNode layernode in layer.ChildNodes)\r
1727                         {\r
1728                             if (layernode.Name == "param")\r
1729                             {\r
1730                                 VisualParamEx vp = new VisualParamEx(layernode, VisualParamEx.ParamType.TYPE_COLOR);\r
1731                             }\r
1732                         }\r
1733                     }\r
1734                 }\r
1735             }\r
1736 \r
1737             // Next are the driver parameters, these are parameters that change multiple real parameters\r
1738 \r
1739             XmlNodeList drivers = lad.GetElementsByTagName("driver_parameters");\r
1740 \r
1741             foreach (XmlNode node in drivers[0].ChildNodes) //lazy \r
1742             {\r
1743                 if (node.Name == "param")\r
1744                 {\r
1745                     VisualParamEx vp = new VisualParamEx(node, VisualParamEx.ParamType.TYPE_DRIVER);\r
1746                 }\r
1747             }\r
1748 \r
1749             lindenMeshesLoaded = true;\r
1750         }\r
1751 \r
1752         public void morphtest(Avatar av, int param, float weight)\r
1753         {\r
1754             VisualParamEx vpx;\r
1755             if (VisualParamEx.allParams.TryGetValue(param, out vpx))\r
1756             {\r
1757 \r
1758                 //Logger.Log(string.Format("Applying visual parameter {0} id {1} value {2}", vpx.Name, vpx.ParamID, weight), Helpers.LogLevel.Info);\r
1759 \r
1760                 //weight = weight * 2.0f;\r
1761                 //weight=weight-1.0f;\r
1762 \r
1763                 if (weight < 0)\r
1764                     weight = 0;\r
1765 \r
1766                 if (weight > 1.0)\r
1767                     weight = 1;\r
1768 \r
1769                 float value = vpx.MinValue + ((vpx.MaxValue - vpx.MinValue) * weight);\r
1770 \r
1771                 if (vpx.pType == VisualParamEx.ParamType.TYPE_MORPH)\r
1772                 {\r
1773                     // Its a morph\r
1774                     GLMesh mesh;\r
1775                     if (_meshes.TryGetValue(vpx.morphmesh, out mesh))\r
1776                     {\r
1777                         foreach (LindenMesh.Morph morph in mesh.Morphs) //optimise me to a dictionary\r
1778                         {\r
1779                             if (morph.Name == vpx.Name)\r
1780                             {\r
1781                                 if (mesh.Name == "skirtMesh" && _showSkirt == false)\r
1782                                     return;\r
1783 \r
1784                                 mesh.morphmesh(morph, value);\r
1785 \r
1786                                 return;\r
1787                             }\r
1788                         }\r
1789                     }\r
1790                     else\r
1791                     {\r
1792                         // Not a mesh morph \r
1793 \r
1794                         // Its a volume deform, these appear to be related to collision volumes\r
1795                         /*\r
1796                         if (vpx.VolumeDeforms == null)\r
1797                         {\r
1798                             Logger.Log(String.Format("paramater {0} has invalid mesh {1}", param, vpx.morphmesh), Helpers.LogLevel.Warning);\r
1799                         }\r
1800                         else\r
1801                         {\r
1802                             foreach (KeyValuePair<string, VisualParamEx.VolumeDeform> kvp in vpx.VolumeDeforms)\r
1803                             {\r
1804                                 skel.deformbone(kvp.Key, kvp.Value.pos, kvp.Value.scale);\r
1805                             }\r
1806                         }\r
1807                          * */\r
1808 \r
1809                     }\r
1810 \r
1811                 }\r
1812                 else\r
1813                 {\r
1814                     // Its not a morph, it might be a driver though\r
1815                     if (vpx.pType == VisualParamEx.ParamType.TYPE_DRIVER)\r
1816                     {\r
1817                         foreach (VisualParamEx.driven child in vpx.childparams)\r
1818                         {\r
1819                             morphtest(av, child.id, weight); //TO DO use minmax if they are present\r
1820                         }\r
1821                         return;\r
1822                     }\r
1823 \r
1824                     //Is this a bone deform?\r
1825                     if (vpx.pType == VisualParamEx.ParamType.TYPE_BONEDEFORM)\r
1826                     {\r
1827                         foreach (KeyValuePair<string, Vector3> kvp in vpx.BoneDeforms)\r
1828                         {\r
1829                             skel.deformbone(kvp.Key, new Vector3(0, 0, 0), kvp.Value * value, Quaternion.Identity);\r
1830                         }\r
1831                         return;\r
1832                     }\r
1833                     else\r
1834                     {\r
1835                         //Logger.Log(String.Format("paramater {0} is not a morph and not a driver", param), Helpers.LogLevel.Warning);\r
1836                     }\r
1837                 }\r
1838 \r
1839             }\r
1840             else\r
1841             {\r
1842                 Logger.Log("Invalid paramater " + param.ToString(), Helpers.LogLevel.Warning);\r
1843             }\r
1844         }\r
1845 \r
1846         public void morph(Avatar av)\r
1847         {\r
1848 \r
1849             if (av.VisualParameters == null)\r
1850                 return;\r
1851 \r
1852             ThreadPool.QueueUserWorkItem(sync =>\r
1853             {\r
1854                 int x = 0;\r
1855 \r
1856                 if (av.VisualParameters.Length > 123)\r
1857                 {\r
1858                     if (av.VisualParameters[31] > 127)\r
1859                     {\r
1860                         msex = VisualParamEx.EparamSex.SEX_MALE;\r
1861                     }\r
1862                     else\r
1863                     {\r
1864                         msex = VisualParamEx.EparamSex.SEX_FEMALE;\r
1865                     }\r
1866                 }\r
1867 \r
1868                 foreach (GLMesh mesh in _meshes.Values)\r
1869                 {\r
1870                     mesh.resetallmorphs();\r
1871                 }\r
1872 \r
1873                 foreach (byte vpvalue in av.VisualParameters)\r
1874                 {\r
1875                     /*\r
1876                     if (vpsent == true && VisualAppearanceParameters[x] == vpvalue)\r
1877                     {\r
1878                      \r
1879                        x++;\r
1880                        continue;\r
1881                     }\r
1882                     */\r
1883 \r
1884                     VisualAppearanceParameters[x] = vpvalue;\r
1885 \r
1886                     if (x >= VisualParamEx.tweakable_params.Count)\r
1887                     {\r
1888                         //Logger.Log("Two many visual paramaters in Avatar appearance", Helpers.LogLevel.Warning);\r
1889                         break;\r
1890                     }\r
1891 \r
1892                     VisualParamEx vpe = (VisualParamEx)VisualParamEx.tweakable_params.GetByIndex(x);\r
1893 \r
1894                     if (vpe.sex != VisualParamEx.EparamSex.SEX_BOTH && vpe.sex != msex)\r
1895                     {\r
1896                         x++;\r
1897                         continue;\r
1898                     }\r
1899 \r
1900                     float value = (vpvalue / 255.0f);\r
1901                     this.morphtest(av, vpe.ParamID, value);\r
1902 \r
1903                     x++;\r
1904                     //  if (x > 100)\r
1905                     //    break;\r
1906                 }\r
1907 \r
1908                 vpsent = true;\r
1909 \r
1910                 foreach (GLMesh mesh in _meshes.Values)\r
1911                 {\r
1912                     mesh.applyjointweights();\r
1913                 }\r
1914             });\r
1915         }\r
1916     }\r
1917 \r
1918     public class RenderAvatar : SceneObject\r
1919     {\r
1920         public static readonly BoundingVolume AvatarBoundingVolume;\r
1921 \r
1922         // Static constructor\r
1923         static RenderAvatar()\r
1924         {\r
1925             AvatarBoundingVolume = new BoundingVolume();\r
1926             // Bounding sphere for avatar is 1m in diametar\r
1927             // Bounding box 1m cube\r
1928             // These values get scaled with Avatar.Scale by the time we perform culling\r
1929             AvatarBoundingVolume.R = 1f;\r
1930             AvatarBoundingVolume.Min = new Vector3(-0.5f, -0.5f, -0.5f);\r
1931             AvatarBoundingVolume.Max = new Vector3(0.5f, 0.5f, 0.5f);\r
1932         }\r
1933 \r
1934         // Default constructor\r
1935         public RenderAvatar()\r
1936         {\r
1937             BoundingVolume = AvatarBoundingVolume;\r
1938             Type = SceneObjectType.Avatar;\r
1939         }\r
1940 \r
1941         public override Primitive BasePrim\r
1942         {\r
1943             get { return avatar; }\r
1944             set { if (value is Avatar) avatar = (Avatar)value; }\r
1945         }\r
1946 \r
1947         public override void Step(float time)\r
1948         {\r
1949             base.Step(time);\r
1950         }\r
1951 \r
1952         public GLAvatar glavatar = new GLAvatar();\r
1953         public Avatar avatar;\r
1954         public FaceData[] data = new FaceData[32];\r
1955         public Dictionary<UUID, Animation> animlist = new Dictionary<UUID, Animation>();\r
1956         public Dictionary<WearableType, AppearanceManager.WearableData> Wearables = new Dictionary<WearableType, AppearanceManager.WearableData>();\r
1957 \r
1958     }\r
1959 \r
1960     public class skeleton\r
1961     {\r
1962         public Dictionary<string, Bone> mBones;\r
1963         public Dictionary<string, int> mPriority = new Dictionary<string, int>();\r
1964         public static Dictionary<int, string> mUpperMeshMapping = new Dictionary<int, string>();\r
1965         public static Dictionary<int, string> mLowerMeshMapping = new Dictionary<int, string>();\r
1966         public static Dictionary<int, string> mHeadMeshMapping = new Dictionary<int, string>();\r
1967 \r
1968         public List<BinBVHAnimationReader> mAnimations = new List<BinBVHAnimationReader>();\r
1969 \r
1970         public static Dictionary<UUID, RenderAvatar> mAnimationTransactions = new Dictionary<UUID, RenderAvatar>();\r
1971 \r
1972         public static Dictionary<UUID, BinBVHAnimationReader> mAnimationCache = new Dictionary<UUID, BinBVHAnimationReader>();\r
1973 \r
1974         public bool mNeedsUpdate = false;\r
1975         public bool mNeedsMeshRebuild = false;\r
1976 \r
1977         public Bone mLeftEye = null;\r
1978         public Bone mRightEye = null;\r
1979 \r
1980         public struct binBVHJointState\r
1981         {\r
1982             public float currenttime_rot;\r
1983             public int lastkeyframe_rot;\r
1984             public int nextkeyframe_rot;\r
1985 \r
1986             public float currenttime_pos;\r
1987             public int lastkeyframe_pos;\r
1988             public int nextkeyframe_pos;\r
1989 \r
1990             public int loopinframe;\r
1991             public int loopoutframe;\r
1992         }\r
1993 \r
1994 \r
1995         public skeleton()\r
1996         {\r
1997 \r
1998             \r
1999 \r
2000             mBones = new Dictionary<string, Bone>();\r
2001 \r
2002             foreach (Bone src in Bone.mBones.Values)\r
2003             {\r
2004                 Bone newbone = new Bone(src);\r
2005                 mBones.Add(newbone.name, newbone);\r
2006             }\r
2007 \r
2008             //rebuild the skeleton structure on the new copy\r
2009             foreach (Bone src in mBones.Values)\r
2010             {\r
2011                 if (src.mParentBone != null)\r
2012                 {\r
2013                     src.parent = mBones[src.mParentBone];\r
2014                     src.parent.children.Add(src);\r
2015                 }\r
2016             }\r
2017 \r
2018             //FUDGE\r
2019             if (mUpperMeshMapping.Count == 0)\r
2020             {\r
2021                 mUpperMeshMapping.Add(1, "mPelvis");\r
2022                 mUpperMeshMapping.Add(2, "mTorso");\r
2023                 mUpperMeshMapping.Add(3, "mChest");\r
2024                 mUpperMeshMapping.Add(4, "mNeck");\r
2025                 mUpperMeshMapping.Add(5, "");\r
2026                 mUpperMeshMapping.Add(6, "mCollarLeft");\r
2027                 mUpperMeshMapping.Add(7, "mShoulderLeft");\r
2028                 mUpperMeshMapping.Add(8, "mElbowLeft");\r
2029                 mUpperMeshMapping.Add(9, "mWristLeft");\r
2030                 mUpperMeshMapping.Add(10, "");\r
2031                 mUpperMeshMapping.Add(11, "mCollarRight");\r
2032                 mUpperMeshMapping.Add(12, "mShoulderRight");\r
2033                 mUpperMeshMapping.Add(13, "mElbowRight");\r
2034                 mUpperMeshMapping.Add(14, "mWristRight");\r
2035                 mUpperMeshMapping.Add(15, "");\r
2036 \r
2037                 mLowerMeshMapping.Add(1, "mPelvis");\r
2038                 mLowerMeshMapping.Add(2, "mHipRight");\r
2039                 mLowerMeshMapping.Add(3, "mKneeRight");\r
2040                 mLowerMeshMapping.Add(4, "mAnkleRight");\r
2041                 mLowerMeshMapping.Add(5, "");\r
2042                 mLowerMeshMapping.Add(6, "mHipLeft");\r
2043                 mLowerMeshMapping.Add(7, "mKneeLeft");\r
2044                 mLowerMeshMapping.Add(8, "mAnkleLeft");\r
2045                 mLowerMeshMapping.Add(9, "");\r
2046 \r
2047                 mHeadMeshMapping.Add(1, "mNeck");\r
2048                 mHeadMeshMapping.Add(2, "mHead");\r
2049                 mHeadMeshMapping.Add(3, "");\r
2050 \r
2051             }\r
2052 \r
2053             mLeftEye = mBones["mEyeLeft"];\r
2054             mRightEye = mBones["mEyeRight"];\r
2055 \r
2056         }\r
2057 \r
2058         public void deformbone(string name, Vector3 pos, Vector3 scale, Quaternion rotation)\r
2059         {\r
2060             Bone bone;\r
2061             if (mBones.TryGetValue(name, out bone))\r
2062             {\r
2063                 bone.deformbone(pos, scale, rotation);\r
2064             }\r
2065         }\r
2066 \r
2067         //TODO check offset and rot calcuations should each offset be multiplied by its parent rotation in\r
2068         // a standard child/parent rot/offset way?\r
2069         public Vector3 getOffset(string bonename)\r
2070         {\r
2071             Bone b;\r
2072             if (mBones.TryGetValue(bonename, out b))\r
2073             {\r
2074                 return (b.getTotalOffset());\r
2075             }\r
2076             else\r
2077             {\r
2078                 return Vector3.Zero;\r
2079             }\r
2080         }\r
2081 \r
2082         public Quaternion getRotation(string bonename)\r
2083         {\r
2084             Bone b;\r
2085             if (mBones.TryGetValue(bonename, out b))\r
2086             {\r
2087                 return (b.getTotalRotation());\r
2088             }\r
2089             else\r
2090             {\r
2091                 return Quaternion.Identity;\r
2092             }\r
2093         }\r
2094 \r
2095 \r
2096         public void flushanimations()\r
2097         {\r
2098             lock (mAnimations)\r
2099             {\r
2100                 mAnimations.Clear();\r
2101             }\r
2102         }\r
2103 \r
2104         // Add animations to the global decoded list\r
2105         // TODO garbage collect unused animations somehow\r
2106         public static void addanimation(OpenMetaverse.Assets.Asset asset,UUID tid, BinBVHAnimationReader b)\r
2107         {\r
2108             RenderAvatar av;\r
2109             mAnimationTransactions.TryGetValue(tid, out av);\r
2110             if (av == null)\r
2111                 return;\r
2112 \r
2113             mAnimationTransactions.Remove(tid);\r
2114 \r
2115             if (asset != null)\r
2116             {\r
2117                 b = new BinBVHAnimationReader(asset.AssetData);\r
2118                 mAnimationCache[asset.AssetID] = b;\r
2119                 Logger.Log("Adding new decoded animaton known animations " + asset.AssetID.ToString(), Helpers.LogLevel.Info);\r
2120             }\r
2121             \r
2122             int pos=0;\r
2123             foreach (binBVHJoint joint in b.joints)\r
2124             {\r
2125                 binBVHJointState state;\r
2126 \r
2127                 state.lastkeyframe_rot = 0;\r
2128                 state.nextkeyframe_rot = 1;\r
2129 \r
2130                 state.lastkeyframe_pos = 0;\r
2131                 state.nextkeyframe_pos = 1;\r
2132 \r
2133                 state.currenttime_rot = 0;\r
2134                 state.currenttime_pos = 0;\r
2135 \r
2136                 state.loopinframe = 0;\r
2137                 state.loopoutframe = joint.rotationkeys.Length - 1;\r
2138 \r
2139                 if (b.Loop == true)\r
2140                 {\r
2141                     int frame=0;\r
2142                     foreach( binBVHJointKey key in joint.rotationkeys)\r
2143                     {\r
2144                         if (key.time == b.InPoint)\r
2145                         {\r
2146                             state.loopinframe = frame;\r
2147                         }\r
2148 \r
2149                         if (key.time == b.OutPoint)\r
2150                         {\r
2151                             state.loopoutframe = frame;\r
2152                         }\r
2153 \r
2154                         frame++;\r
2155 \r
2156                     }\r
2157 \r
2158                 }\r
2159 \r
2160                 b.joints[pos].Tag = state;\r
2161                 pos++;\r
2162             }\r
2163 \r
2164             lock (av.glavatar.skel.mAnimations)\r
2165             {\r
2166                 av.glavatar.skel.mAnimations.Add(b);\r
2167             }\r
2168         }\r
2169 \r
2170         public void animate(float lastframetime)\r
2171         {\r
2172             mPriority.Clear();\r
2173 \r
2174             lock(mAnimations)\r
2175             foreach (BinBVHAnimationReader b in mAnimations) \r
2176                 {\r
2177                     if (b == null)\r
2178                         continue;\r
2179 \r
2180                         int jpos = 0;\r
2181                         foreach (binBVHJoint joint in b.joints)\r
2182                         {\r
2183                             int prio=0;\r
2184       \r
2185                             //Quick hack to stack animations in the correct order\r
2186                             //TODO we need to do this per joint as they all have their own priorities as well ;-(\r
2187                             if (mPriority.TryGetValue(joint.Name, out prio))\r
2188                             {\r
2189                                 if (prio > b.Priority)\r
2190                                     continue;\r
2191                             }\r
2192 \r
2193                             mPriority[joint.Name] = b.Priority;\r
2194                 \r
2195                             binBVHJointState state = (binBVHJointState) b.joints[jpos].Tag;\r
2196 \r
2197                             state.currenttime_rot += lastframetime;\r
2198                             state.currenttime_pos += lastframetime;\r
2199 \r
2200                             //fudge\r
2201                             if (b.joints[jpos].rotationkeys.Length == 1)\r
2202                             {\r
2203                                 state.nextkeyframe_rot = 0;\r
2204                             }\r
2205 \r
2206                             Vector3 poslerp = Vector3.Zero;\r
2207 \r
2208                             if (b.joints[jpos].positionkeys.Length > 2)\r
2209                             {\r
2210                                 binBVHJointKey pos2 = b.joints[jpos].positionkeys[state.nextkeyframe_pos];\r
2211 \r
2212 \r
2213                                 if (state.currenttime_pos > pos2.time)\r
2214                                 {\r
2215                                     state.lastkeyframe_pos++;\r
2216                                     state.nextkeyframe_pos++;\r
2217 \r
2218                                     if (state.nextkeyframe_pos >= b.joints[jpos].positionkeys.Length || (state.nextkeyframe_pos >=state.loopoutframe && b.Loop==true))\r
2219                                     {\r
2220                                         if (b.Loop == true)\r
2221                                         {\r
2222                                             state.nextkeyframe_pos = state.loopinframe;\r
2223                                             state.currenttime_pos = b.InPoint;\r
2224 \r
2225                                             if (state.lastkeyframe_pos >= b.joints[jpos].positionkeys.Length)\r
2226                                             {\r
2227                                                 state.lastkeyframe_pos = state.loopinframe;\r
2228                                             }\r
2229                                         }\r
2230                                         else\r
2231                                         {\r
2232                                             state.nextkeyframe_pos = joint.positionkeys.Length - 1;\r
2233                                         }\r
2234                                     }\r
2235 \r
2236                                    \r
2237 \r
2238                                     if (state.lastkeyframe_pos >= b.joints[jpos].positionkeys.Length)\r
2239                                     {\r
2240                                         if (b.Loop == true)\r
2241                                         {\r
2242                                             state.lastkeyframe_pos = 0;\r
2243                                             state.currenttime_pos = 0;\r
2244 \r
2245                                         }\r
2246                                         else\r
2247                                         {\r
2248                                             state.lastkeyframe_pos = joint.positionkeys.Length - 1;\r
2249                                             if (state.lastkeyframe_pos < 0)//eeww\r
2250                                                 state.lastkeyframe_pos = 0; \r
2251                                         }\r
2252                                     }\r
2253                                 }\r
2254 \r
2255                                 binBVHJointKey pos = b.joints[jpos].positionkeys[state.lastkeyframe_pos];\r
2256 \r
2257 \r
2258                                 float delta = (pos2.time - pos.time) / ((state.currenttime_pos) - (pos.time - b.joints[jpos].positionkeys[0].time));\r
2259 \r
2260                                 if (delta < 0)\r
2261                                     delta = 0;\r
2262 \r
2263                                 if (delta > 1)\r
2264                                     delta = 1;\r
2265 \r
2266                                 poslerp = Vector3.Lerp(pos.key_element, pos2.key_element, delta) *-1;\r
2267 \r
2268                             }\r
2269 \r
2270 \r
2271                             Vector3 rotlerp = Vector3.Zero;\r
2272                             if (b.joints[jpos].rotationkeys.Length > 0)\r
2273                             {\r
2274                                 binBVHJointKey rot2 = b.joints[jpos].rotationkeys[state.nextkeyframe_rot];\r
2275 \r
2276                                 if (state.currenttime_rot > rot2.time)\r
2277                                 {\r
2278                                     state.lastkeyframe_rot++;\r
2279                                     state.nextkeyframe_rot++;\r
2280 \r
2281                                     if (state.nextkeyframe_rot >= b.joints[jpos].rotationkeys.Length || (state.nextkeyframe_rot >= state.loopoutframe && b.Loop == true))\r
2282                                     {\r
2283                                         if (b.Loop == true)\r
2284                                         {\r
2285                                             state.nextkeyframe_rot = state.loopinframe;\r
2286                                             state.currenttime_rot = b.InPoint;\r
2287 \r
2288                                             if (state.lastkeyframe_rot >= b.joints[jpos].rotationkeys.Length)\r
2289                                             {\r
2290                                                 state.lastkeyframe_rot = state.loopinframe;\r
2291                                                \r
2292                  \r
2293                                             }\r
2294 \r
2295                                         }\r
2296                                         else\r
2297                                         {\r
2298                                             state.nextkeyframe_rot = joint.rotationkeys.Length - 1;\r
2299                                         }\r
2300                                     }\r
2301 \r
2302                                     if (state.lastkeyframe_rot >= b.joints[jpos].rotationkeys.Length)\r
2303                                     {\r
2304                                         if (b.Loop == true)\r
2305                                         {\r
2306                                             state.lastkeyframe_rot = 0;\r
2307                                             state.currenttime_rot = 0;\r
2308 \r
2309                                         }\r
2310                                         else\r
2311                                         {\r
2312                                             state.lastkeyframe_rot = joint.rotationkeys.Length - 1;\r
2313                                         }\r
2314                                     }\r
2315                                 }\r
2316                               \r
2317                                 binBVHJointKey rot = b.joints[jpos].rotationkeys[state.lastkeyframe_rot];\r
2318                                 rot2 = b.joints[jpos].rotationkeys[state.nextkeyframe_rot];\r
2319 \r
2320                                 float deltarot = (rot2.time - rot.time) / ((state.currenttime_rot) - (rot.time - b.joints[jpos].rotationkeys[0].time));\r
2321 \r
2322                                \r
2323                                 if (deltarot < 0)\r
2324                                     deltarot = 0;\r
2325 \r
2326                                 if (deltarot > 1)\r
2327                                     deltarot = 1;\r
2328 \r
2329                                 rotlerp = Vector3.Lerp(rot.key_element, rot2.key_element, deltarot);\r
2330 \r
2331                             }\r
2332 \r
2333                             b.joints[jpos].Tag = (object)state;\r
2334                         \r
2335                             deformbone(joint.Name, poslerp, new Vector3(0, 0, 0), new Quaternion(rotlerp.X, rotlerp.Y, rotlerp.Z));\r
2336 \r
2337                             jpos++;\r
2338                         }\r
2339 \r
2340                         mNeedsMeshRebuild = true;\r
2341                     }\r
2342 \r
2343             mNeedsUpdate = false;\r
2344         }\r
2345     }\r
2346 \r
2347     public class Bone\r
2348     {\r
2349         public string name;\r
2350         public Vector3 pos;\r
2351         public Quaternion rot;\r
2352         public Vector3 scale;\r
2353         public Vector3 piviot;\r
2354 \r
2355         public Vector3 orig_pos;\r
2356         public Quaternion orig_rot;\r
2357         public Vector3 orig_scale;\r
2358         public Vector3 orig_piviot;\r
2359 \r
2360         Matrix4 mDeformMatrix = Matrix4.Identity;\r
2361 \r
2362         public Bone parent;\r
2363 \r
2364         public List<Bone> children = new List<Bone>();\r
2365 \r
2366         public static Dictionary<string, Bone> mBones = new Dictionary<string, Bone>();\r
2367         public static Dictionary<int, Bone> mIndexedBones = new Dictionary<int, Bone>();\r
2368         static int boneaddindex = 0;\r
2369 \r
2370         private bool rotdirty = true;\r
2371         private bool posdirty = true;\r
2372 \r
2373         private Vector3 mTotalPos;\r
2374         private Quaternion mTotalRot;\r
2375 \r
2376         private Vector3 mDeltaPos;\r
2377         private Quaternion mDeltaRot;\r
2378 \r
2379         public string mParentBone = null;\r
2380 \r
2381         public Bone()\r
2382         {\r
2383         }\r
2384 \r
2385         public Bone(Bone source)\r
2386         {\r
2387             name = String.Copy(source.name);\r
2388             pos = new Vector3(source.pos);\r
2389             rot = new Quaternion(source.rot);\r
2390             scale = new Vector3(source.scale);\r
2391             piviot = new Vector3(source.piviot);\r
2392 \r
2393             orig_piviot = source.orig_piviot;\r
2394             orig_pos = source.orig_pos;\r
2395             orig_rot = source.orig_rot;\r
2396             orig_scale = source.orig_scale;\r
2397 \r
2398             mParentBone = source.mParentBone;\r
2399 \r
2400             mDeformMatrix = new Matrix4(source.mDeformMatrix);\r
2401         }\r
2402 \r
2403         public static void loadbones(string skeletonfilename)\r
2404         {\r
2405             mBones.Clear();\r
2406             string basedir = Directory.GetCurrentDirectory() + System.IO.Path.DirectorySeparatorChar + "character" + System.IO.Path.DirectorySeparatorChar;\r
2407             XmlDocument skeleton = new XmlDocument();\r
2408             skeleton.Load(basedir + skeletonfilename);\r
2409             XmlNode boneslist = skeleton.GetElementsByTagName("linden_skeleton")[0];\r
2410             addbone(boneslist.ChildNodes[0], null);\r
2411         }\r
2412 \r
2413         public static void addbone(XmlNode bone, Bone parent)\r
2414         {\r
2415 \r
2416             if (bone.Name != "bone")\r
2417                 return;\r
2418 \r
2419             Bone b = new Bone();\r
2420             b.name = bone.Attributes.GetNamedItem("name").Value;\r
2421 \r
2422             string pos = bone.Attributes.GetNamedItem("pos").Value;\r
2423             string[] posparts = pos.Split(' ');\r
2424             b.pos = new Vector3(float.Parse(posparts[0]), float.Parse(posparts[1]), float.Parse(posparts[2]));\r
2425             b.orig_pos = new Vector3(b.pos);\r
2426 \r
2427             string rot = bone.Attributes.GetNamedItem("rot").Value;\r
2428             string[] rotparts = rot.Split(' ');\r
2429             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
2430             b.orig_rot = new Quaternion(b.rot);\r
2431 \r
2432             string scale = bone.Attributes.GetNamedItem("scale").Value;\r
2433             string[] scaleparts = scale.Split(' ');\r
2434             b.scale = new Vector3(float.Parse(scaleparts[0]), float.Parse(scaleparts[1]), float.Parse(scaleparts[2]));\r
2435             b.orig_scale = new Vector3(b.scale);\r
2436 \r
2437             float[] deform = Math3D.CreateSRTMatrix(new Vector3(1, 1, 1), b.rot, b.orig_pos);\r
2438             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
2439 \r
2440             //TODO piviot\r
2441 \r
2442             b.parent = parent;\r
2443             if (parent != null)\r
2444             {\r
2445                 b.mParentBone = parent.name;\r
2446                 parent.children.Add(b);\r
2447             }\r
2448 \r
2449             mBones.Add(b.name, b);\r
2450             mIndexedBones.Add(boneaddindex++, b);\r
2451 \r
2452             Logger.Log("Found bone " + b.name, Helpers.LogLevel.Info);\r
2453 \r
2454             foreach (XmlNode childbone in bone.ChildNodes)\r
2455             {\r
2456                 addbone(childbone, b);\r
2457             }\r
2458 \r
2459         }\r
2460 \r
2461         public void deformbone(Vector3 pos, Vector3 scale, Quaternion rot)\r
2462         {\r
2463             //float[] deform = Math3D.CreateSRTMatrix(scale, rot, this.orig_pos);\r
2464             //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
2465             this.pos = Bone.mBones[name].orig_pos + pos;\r
2466             this.scale = Bone.mBones[name].orig_scale + scale;\r
2467             this.rot = Bone.mBones[name].orig_rot * rot;\r
2468 \r
2469             markdirty();\r
2470         }\r
2471 \r
2472         // If we deform a bone mark this bone and all its children as dirty.  \r
2473         public void markdirty()\r
2474         {\r
2475             rotdirty = true;\r
2476             posdirty = true;\r
2477             foreach (Bone childbone in children)\r
2478             {\r
2479                 childbone.markdirty();\r
2480             }\r
2481         }\r
2482 \r
2483         public Matrix4 getdeform()\r
2484         {\r
2485             if (this.parent != null)\r
2486             {\r
2487                 return mDeformMatrix * parent.getdeform();\r
2488             }\r
2489             else\r
2490             {\r
2491                 return mDeformMatrix;\r
2492             }\r
2493         }\r
2494 \r
2495         private Vector3 getOffset()\r
2496         {\r
2497             if (parent != null)\r
2498             {\r
2499                 Quaternion totalrot = getParentRot(); // we don't want this joints rotation included\r
2500                 Vector3 parento = parent.getOffset();\r
2501                 Vector3 mepre = pos * scale;\r
2502                 mepre = mepre * totalrot;\r
2503                 mTotalPos = parento + mepre;\r
2504               \r
2505                 Vector3 orig = getOrigOffset();\r
2506                 mDeltaPos = mTotalPos - orig;\r
2507 \r
2508                 posdirty = false;\r
2509 \r
2510                 return mTotalPos;\r
2511             }\r
2512             else\r
2513             {\r
2514                 Vector3 orig = getOrigOffset();\r
2515                 mTotalPos = (pos * scale);\r
2516                 mDeltaPos = mTotalPos - orig;\r
2517                 posdirty = false;\r
2518                 return mTotalPos;\r
2519               \r
2520             }\r
2521         }\r
2522 \r
2523         public Vector3 getMyOffset()\r
2524         {\r
2525             return pos * scale;\r
2526         }\r
2527 \r
2528         // Try to save some cycles by not recalculating positions and rotations every time\r
2529         public Vector3 getTotalOffset()\r
2530         {\r
2531             if (posdirty == false)\r
2532             {\r
2533                 return mTotalPos;\r
2534             }\r
2535             else\r
2536             {\r
2537                 return getOffset();\r
2538             }\r
2539         }\r
2540 \r
2541         public Vector3 getDeltaOffset()\r
2542         {\r
2543             if (posdirty == false)\r
2544             {\r
2545                 return mDeltaPos;\r
2546             }\r
2547             else\r
2548             {\r
2549                 getOffset();\r
2550                 return mDeltaPos;\r
2551             }\r
2552         }\r
2553 \r
2554         private Vector3 getOrigOffset()\r
2555         {\r
2556             if (parent != null)\r
2557             {\r
2558                 return (parent.getOrigOffset() + orig_pos);\r
2559             }\r
2560             else\r
2561             {\r
2562                 return orig_pos;\r
2563             }\r
2564         }\r
2565 \r
2566         private static Quaternion getRotation(string bonename)\r
2567         {\r
2568             Bone b;\r
2569             if (mBones.TryGetValue(bonename, out b))\r
2570             {\r
2571                 return (b.getRotation());\r
2572             }\r
2573             else\r
2574             {\r
2575                 return Quaternion.Identity;\r
2576             }\r
2577         }\r
2578 \r
2579 \r
2580         private Quaternion getParentRot()\r
2581         {\r
2582             Quaternion totalrot = Quaternion.Identity;\r
2583 \r
2584             if (parent != null)\r
2585             {\r
2586                 totalrot = parent.getRotation();\r
2587             }\r
2588 \r
2589             return totalrot;\r
2590 \r
2591         }\r
2592 \r
2593         private Quaternion getRotation()\r
2594         {\r
2595             Quaternion totalrot = rot;\r
2596 \r
2597             if (parent != null)\r
2598             {\r
2599                 totalrot = rot * parent.getRotation();\r
2600             }\r
2601 \r
2602             mTotalRot = totalrot;\r
2603             rotdirty = false;\r
2604 \r
2605             return totalrot;\r
2606         }\r
2607 \r
2608         public Quaternion getTotalRotation()\r
2609         {\r
2610             if (rotdirty == false)\r
2611             {\r
2612                 return mTotalRot;\r
2613             }\r
2614             else\r
2615             {\r
2616                 return getRotation();\r
2617             }\r
2618         }\r
2619     }\r
2620 \r
2621     public class VisualParamEx\r
2622     {\r
2623 \r
2624         static public Dictionary<int, VisualParamEx> allParams = new Dictionary<int, VisualParamEx>();\r
2625         static public Dictionary<int, VisualParamEx> deformParams = new Dictionary<int, VisualParamEx>();\r
2626         static public Dictionary<int, VisualParamEx> morphParams = new Dictionary<int, VisualParamEx>();\r
2627         static public Dictionary<int, VisualParamEx> drivenParams = new Dictionary<int, VisualParamEx>();\r
2628         static public SortedList tweakable_params = new SortedList();\r
2629 \r
2630         public Dictionary<string, Vector3> BoneDeforms = null;\r
2631         public Dictionary<string, VolumeDeform> VolumeDeforms = null;\r
2632         public List<driven> childparams = null;\r
2633 \r
2634         public string morphmesh = null;\r
2635 \r
2636         enum GroupType\r
2637         {\r
2638             VISUAL_PARAM_GROUP_TWEAKABLE = 0,\r
2639             VISUAL_PARAM_GROUP_ANIMATABLE,\r
2640             VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT,\r
2641         }\r
2642 \r
2643         public struct VolumeDeform\r
2644         {\r
2645             public string name;\r
2646             public Vector3 scale;\r
2647             public Vector3 pos;\r
2648         }\r
2649 \r
2650         public enum EparamSex\r
2651         {\r
2652             SEX_BOTH = 0,\r
2653             SEX_FEMALE = 1,\r
2654             SEX_MALE = 2\r
2655         }\r
2656 \r
2657         public enum ParamType\r
2658         {\r
2659             TYPE_BONEDEFORM,\r
2660             TYPE_MORPH,\r
2661             TYPE_DRIVER,\r
2662             TYPE_COLOR,\r
2663             TYPE_LAYER\r
2664         }\r
2665 \r
2666         public struct driven\r
2667         {\r
2668             public int id;\r
2669             public float max1;\r
2670             public float max2;\r
2671             public float min1;\r
2672             public float min2;\r
2673             public bool hasMinMax;\r
2674         }\r
2675 \r
2676         public string meshname;\r
2677 \r
2678         /// <summary>Index of this visual param</summary>\r
2679         public int ParamID;\r
2680         /// <summary>Internal name</summary>\r
2681         public string Name;\r
2682         /// <summary>Group ID this parameter belongs to</summary>\r
2683         public int Group;\r
2684         /// <summary>Name of the wearable this parameter belongs to</summary>\r
2685         public string Wearable;\r
2686         /// <summary>Displayable label of this characteristic</summary>\r
2687         public string Label;\r
2688         /// <summary>Displayable label for the minimum value of this characteristic</summary>\r
2689         public string LabelMin;\r
2690         /// <summary>Displayable label for the maximum value of this characteristic</summary>\r
2691         public string LabelMax;\r
2692         /// <summary>Default value</summary>\r
2693         public float DefaultValue;\r
2694         /// <summary>Minimum value</summary>\r
2695         public float MinValue;\r
2696         /// <summary>Maximum value</summary>\r
2697         public float MaxValue;\r
2698         /// <summary>Is this param used for creation of bump layer?</summary>\r
2699         public bool IsBumpAttribute;\r
2700         /// <summary>Alpha blending/bump info</summary>\r
2701         public VisualAlphaParam? AlphaParams;\r
2702         /// <summary>Color information</summary>\r
2703         public VisualColorParam? ColorParams;\r
2704         /// <summary>Array of param IDs that are drivers for this parameter</summary>\r
2705         public int[] Drivers;\r
2706         /// <summary>The Avatar Sex that this parameter applies to</summary>\r
2707         public EparamSex sex;\r
2708 \r
2709         public ParamType pType;\r
2710 \r
2711         public static int count = 0;\r
2712 \r
2713         /// <summary>\r
2714         /// Set all the values through the constructor\r
2715         /// </summary>\r
2716         /// <param name="paramID">Index of this visual param</param>\r
2717         /// <param name="name">Internal name</param>\r
2718         /// <param name="group"></param>\r
2719         /// <param name="wearable"></param>\r
2720         /// <param name="label">Displayable label of this characteristic</param>\r
2721         /// <param name="labelMin">Displayable label for the minimum value of this characteristic</param>\r
2722         /// <param name="labelMax">Displayable label for the maximum value of this characteristic</param>\r
2723         /// <param name="def">Default value</param>\r
2724         /// <param name="min">Minimum value</param>\r
2725         /// <param name="max">Maximum value</param>\r
2726         /// <param name="isBumpAttribute">Is this param used for creation of bump layer?</param>\r
2727         /// <param name="drivers">Array of param IDs that are drivers for this parameter</param>\r
2728         /// <param name="alpha">Alpha blending/bump info</param>\r
2729         /// <param name="colorParams">Color information</param>\r
2730         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
2731         {\r
2732             ParamID = paramID;\r
2733             Name = name;\r
2734             Group = group;\r
2735             Wearable = wearable;\r
2736             Label = label;\r
2737             LabelMin = labelMin;\r
2738             LabelMax = labelMax;\r
2739             DefaultValue = def;\r
2740             MaxValue = max;\r
2741             MinValue = min;\r
2742             IsBumpAttribute = isBumpAttribute;\r
2743             Drivers = drivers;\r
2744             AlphaParams = alpha;\r
2745             ColorParams = colorParams;\r
2746             sex = EparamSex.SEX_BOTH;\r
2747         }\r
2748 \r
2749         public VisualParamEx(XmlNode node, ParamType pt)\r
2750         {\r
2751             pType = pt;\r
2752 \r
2753             ParamID = Int32.Parse(node.Attributes.GetNamedItem("id").Value);\r
2754             Name = node.Attributes.GetNamedItem("name").Value;\r
2755             Group = Int32.Parse(node.Attributes.GetNamedItem("group").Value);\r
2756 \r
2757             //These dont exist for facal expresion morphs\r
2758             if (node.Attributes.GetNamedItem("wearable") != null)\r
2759                 Wearable = node.Attributes.GetNamedItem("wearable").Value;\r
2760 \r
2761             MinValue = float.Parse(node.Attributes.GetNamedItem("value_min").Value);\r
2762             MaxValue = float.Parse(node.Attributes.GetNamedItem("value_max").Value);\r
2763 \r
2764             // These do not exists for driven parameters\r
2765             if (node.Attributes.GetNamedItem("label_min") != null)\r
2766             {\r
2767                 LabelMin = node.Attributes.GetNamedItem("label_min").Value;\r
2768             }\r
2769 \r
2770             if (node.Attributes.GetNamedItem("label_max") != null)\r
2771             {\r
2772                 LabelMax = node.Attributes.GetNamedItem("label_max").Value;\r
2773             }\r
2774 \r
2775             XmlNode sexnode = node.Attributes.GetNamedItem("sex");\r
2776 \r
2777             if (sexnode != null)\r
2778             {\r
2779                 if (sexnode.Value == "male")\r
2780                 {\r
2781                     sex = EparamSex.SEX_MALE;\r
2782                 }\r
2783                 else\r
2784                 {\r
2785                     sex = EparamSex.SEX_FEMALE;\r
2786                 }\r
2787 \r
2788             }\r
2789 \r
2790             Group = int.Parse(node.Attributes.GetNamedItem("group").Value);\r
2791 \r
2792             if (Group == (int)GroupType.VISUAL_PARAM_GROUP_TWEAKABLE)\r
2793             {\r
2794                 if (!tweakable_params.ContainsKey(ParamID)) //stupid duplicate shared params\r
2795                 {\r
2796                     tweakable_params.Add(this.ParamID, this);\r
2797                 }\r
2798                 //Logger.Log(String.Format("Adding tweakable paramater ID {0} {1}", count, this.Name), Helpers.LogLevel.Info);\r
2799                 count++;\r
2800             }\r
2801 \r
2802             //TODO other paramaters but these arew concerned with editing the GUI display so not too fussed at the moment\r
2803 \r
2804             try\r
2805             {\r
2806                 allParams.Add(ParamID, this);\r
2807             }\r
2808             catch\r
2809             {\r
2810                 Logger.Log("Duplicate VisualParam in allParams id " + ParamID.ToString(), Helpers.LogLevel.Info);\r
2811             }\r
2812 \r
2813             if (pt == ParamType.TYPE_BONEDEFORM)\r
2814             {\r
2815                 // If we are in the skeleton section then we also have bone deforms to parse\r
2816                 BoneDeforms = new Dictionary<string, Vector3>();\r
2817                 if (node.HasChildNodes && node.ChildNodes[0].HasChildNodes)\r
2818                 {\r
2819                     ParseBoneDeforms(node.ChildNodes[0].ChildNodes);\r
2820                 }\r
2821                 deformParams.Add(ParamID, this);\r
2822             }\r
2823 \r
2824             if (pt == ParamType.TYPE_MORPH)\r
2825             {\r
2826                 VolumeDeforms = new Dictionary<string, VolumeDeform>();\r
2827                 if (node.HasChildNodes && node.ChildNodes[0].HasChildNodes)\r
2828                 {\r
2829                     ParseVolumeDeforms(node.ChildNodes[0].ChildNodes);\r
2830                 }\r
2831 \r
2832                 try\r
2833                 {\r
2834                     morphParams.Add(ParamID, this);\r
2835                 }\r
2836                 catch\r
2837                 {\r
2838                     Logger.Log("Duplicate VisualParam in morphParams id " + ParamID.ToString(), Helpers.LogLevel.Info);\r
2839                 }\r
2840 \r
2841             }\r
2842 \r
2843             if (pt == ParamType.TYPE_DRIVER)\r
2844             {\r
2845                 childparams = new List<driven>();\r
2846                 if (node.HasChildNodes && node.ChildNodes[0].HasChildNodes) //LAZY\r
2847                 {\r
2848                     ParseDrivers(node.ChildNodes[0].ChildNodes);\r
2849                 }\r
2850 \r
2851                 drivenParams.Add(ParamID, this);\r
2852 \r
2853             }\r
2854 \r
2855             if (pt == ParamType.TYPE_COLOR)\r
2856             {\r
2857                 if (node.HasChildNodes)\r
2858                 {\r
2859                     foreach (XmlNode colorchild in node.ChildNodes)\r
2860                     {\r
2861                         if (colorchild.Name == "param_color")\r
2862                         {\r
2863                             //TODO extract <value color="50, 25, 5, 255" />\r
2864                         }\r
2865                     }\r
2866 \r
2867                 }\r
2868             }\r
2869 \r
2870         }\r
2871 \r
2872         void ParseBoneDeforms(XmlNodeList deforms)\r
2873         {\r
2874             foreach (XmlNode node in deforms)\r
2875             {\r
2876                 if (node.Name == "bone")\r
2877                 {\r
2878                     string name = node.Attributes.GetNamedItem("name").Value;\r
2879                     Vector3 scale = XmlParseVector(node.Attributes.GetNamedItem("scale").Value);\r
2880                     BoneDeforms.Add(name, scale);\r
2881                 }\r
2882             }\r
2883         }\r
2884 \r
2885         void ParseVolumeDeforms(XmlNodeList deforms)\r
2886         {\r
2887             foreach (XmlNode node in deforms)\r
2888             {\r
2889                 if (node.Name == "volume_morph")\r
2890                 {\r
2891                     VolumeDeform vd = new VolumeDeform();\r
2892                     vd.name = node.Attributes.GetNamedItem("name").Value;\r
2893                     vd.name = vd.name.ToLower();\r
2894 \r
2895                     if (node.Attributes.GetNamedItem("scale") != null)\r
2896                     {\r
2897                         vd.scale = XmlParseVector(node.Attributes.GetNamedItem("scale").Value);\r
2898                     }\r
2899                     else\r
2900                     {\r
2901                         vd.scale = new Vector3(0, 0, 0);\r
2902                     }\r
2903 \r
2904                     if (node.Attributes.GetNamedItem("pos") != null)\r
2905                     {\r
2906                         vd.pos = XmlParseVector(node.Attributes.GetNamedItem("pos").Value);\r
2907                     }\r
2908                     else\r
2909                     {\r
2910                         vd.pos = new Vector3(0f, 0f, 0f);\r
2911                     }\r
2912 \r
2913                     VolumeDeforms.Add(vd.name, vd);\r
2914                 }\r
2915             }\r
2916         }\r
2917 \r
2918         void ParseDrivers(XmlNodeList drivennodes)\r
2919         {\r
2920             foreach (XmlNode node in drivennodes)\r
2921             {\r
2922                 if (node.Name == "driven")\r
2923                 {\r
2924                     driven d = new driven();\r
2925 \r
2926                     d.id = Int32.Parse(node.Attributes.GetNamedItem("id").Value);\r
2927                     XmlNode param = node.Attributes.GetNamedItem("max1");\r
2928                     if (param != null)\r
2929                     {\r
2930                         d.max1 = float.Parse(param.Value);\r
2931                         d.max2 = float.Parse(node.Attributes.GetNamedItem("max2").Value);\r
2932                         d.min1 = float.Parse(node.Attributes.GetNamedItem("min1").Value);\r
2933                         d.max2 = float.Parse(node.Attributes.GetNamedItem("min2").Value);\r
2934                         d.hasMinMax = true;\r
2935                     }\r
2936                     else\r
2937                     {\r
2938                         d.hasMinMax = false;\r
2939                     }\r
2940 \r
2941                     childparams.Add(d);\r
2942 \r
2943                 }\r
2944             }\r
2945         }\r
2946 \r
2947         public static Vector3 XmlParseVector(string data)\r
2948         {\r
2949             string[] posparts = data.Split(' ');\r
2950             return new Vector3(float.Parse(posparts[0]), float.Parse(posparts[1]), float.Parse(posparts[2]));\r
2951         }\r
2952 \r
2953         public static Quaternion XmlParseRotation(string data)\r
2954         {\r
2955             string[] rotparts = data.Split(' ');\r
2956             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
2957         }\r
2958     }\r
2959 \r
2960     /*\r
2961      *  Helper classs for reading the static VFS file, call \r
2962      *  staticVFS.readVFSheaders() with the path to the static_data.db2 and static_index.db2 files\r
2963      *  and it will pass and dump in to openmetaverse_data for you\r
2964      *  This should only be needed to be used if LL update the static VFS in order to refresh our data\r
2965      */\r
2966 \r
2967     class VFSblock\r
2968     {\r
2969         public int mLocation;\r
2970         public int mLength;\r
2971         public int mAccessTime;\r
2972         public UUID mFileID;\r
2973         public int mSize;\r
2974         public AssetType mAssetType;\r
2975 \r
2976         public int readblock(byte[] blockdata, int offset)\r
2977         {\r
2978              \r
2979              BitPack input = new BitPack(blockdata, offset);\r
2980              mLocation = input.UnpackInt();\r
2981              mLength = input.UnpackInt();\r
2982              mAccessTime = input.UnpackInt();\r
2983              mFileID = input.UnpackUUID();\r
2984              int filetype = input.UnpackShort();\r
2985              mAssetType = (AssetType)filetype;\r
2986              mSize = input.UnpackInt();\r
2987              offset += 34;\r
2988 \r
2989              Logger.Log(String.Format("Found header for {0} type {1} length {2} at {3}", mFileID, mAssetType, mSize, mLocation),Helpers.LogLevel.Info);\r
2990            \r
2991              return offset;\r
2992         }\r
2993 \r
2994     }\r
2995 \r
2996     public class staticVFS\r
2997     {\r
2998         public static void readVFSheaders(string datafile, string indexfile)\r
2999         {\r
3000             FileStream datastream;\r
3001             FileStream indexstream;\r
3002 \r
3003             datastream = File.Open(datafile, FileMode.Open);\r
3004             indexstream = File.Open(indexfile, FileMode.Open);\r
3005 \r
3006             int offset=0;\r
3007 \r
3008             byte[] blockdata = new byte[indexstream.Length];\r
3009             indexstream.Read(blockdata, 0, (int)indexstream.Length);\r
3010 \r
3011             while (offset < indexstream.Length)\r
3012             {\r
3013                 VFSblock block = new VFSblock();\r
3014                 offset = block.readblock(blockdata, offset);\r
3015 \r
3016                 FileStream writer = File.Open(OpenMetaverse.Settings.RESOURCE_DIR+System.IO.Path.DirectorySeparatorChar+block.mFileID.ToString(),FileMode.Create);\r
3017                 byte[] data = new byte[block.mSize];\r
3018                 datastream.Seek(block.mLocation, SeekOrigin.Begin);\r
3019                 datastream.Read(data, 0, block.mSize);\r
3020                 writer.Write(data, 0, block.mSize);\r
3021                 writer.Close();\r
3022             }\r
3023            \r
3024         }\r
3025     }\r
3026 }