OSDN Git Service

Second attempt at invisiprims
[radegast/radegast.git] / Radegast / GUI / Rendering / RenderingHelpers.cs
1 // \r
2 // Radegast Metaverse Client\r
3 // Copyright (c) 2009-2011, Radegast Development Team\r
4 // All rights reserved.\r
5 // \r
6 // Redistribution and use in source and binary forms, with or without\r
7 // modification, are permitted provided that the following conditions are met:\r
8 // \r
9 //     * Redistributions of source code must retain the above copyright notice,\r
10 //       this list of conditions and the following disclaimer.\r
11 //     * Redistributions in binary form must reproduce the above copyright\r
12 //       notice, this list of conditions and the following disclaimer in the\r
13 //       documentation and/or other materials provided with the distribution.\r
14 //     * Neither the name of the application "Radegast", nor the names of its\r
15 //       contributors may be used to endorse or promote products derived from\r
16 //       this software without specific prior CreateReflectionTexture permission.\r
17 // \r
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r
21 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\r
22 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
23 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\r
24 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\r
25 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\r
26 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
28 //\r
29 // $Id$\r
30 //\r
31 \r
32 using System;\r
33 using System.Collections.Generic;\r
34 using System.Collections;\r
35 using System.Linq;\r
36 using System.Text;\r
37 using System.IO;\r
38 using System.IO.Compression;\r
39 using System.Xml;\r
40 using System.Threading;\r
41 using OpenTK.Graphics.OpenGL;\r
42 using System.Runtime.InteropServices;\r
43 using System.Drawing;\r
44 using System.Drawing.Imaging;\r
45 using OpenMetaverse;\r
46 using OpenMetaverse.Rendering;\r
47 \r
48 namespace Radegast.Rendering\r
49 {\r
50     [StructLayout(LayoutKind.Sequential)]\r
51     public struct Color4b\r
52     {\r
53         public byte R;\r
54         public byte G;\r
55         public byte B;\r
56         public byte A;\r
57     }\r
58 \r
59     [StructLayout(LayoutKind.Explicit)]\r
60     public struct ColorVertex\r
61     {\r
62         [FieldOffset(0)]\r
63         public Vertex Vertex;\r
64         [FieldOffset(32)]\r
65         public Color4b Color;\r
66         public static int Size = 36;\r
67     }\r
68 \r
69     public class TextureInfo\r
70     {\r
71         public System.Drawing.Image Texture;\r
72         public int TexturePointer;\r
73         public bool HasAlpha;\r
74         public bool FullAlpha;\r
75         public bool IsMask;\r
76         public bool IsInvisible;\r
77         public UUID TextureID;\r
78         public bool FetchFailed;\r
79     }\r
80 \r
81     public class TextureLoadItem\r
82     {\r
83         public FaceData Data;\r
84         public Primitive Prim;\r
85         public Primitive.TextureEntryFace TeFace;\r
86         public byte[] TextureData = null;\r
87         public byte[] TGAData = null;\r
88         public bool LoadAssetFromCache = false;\r
89     }\r
90 \r
91     public enum RenderPass\r
92     {\r
93         Picking,\r
94         Simple,\r
95         Alpha,\r
96         Invisible\r
97     }\r
98 \r
99     public enum SceneObjectType\r
100     {\r
101         None,\r
102         Primitive,\r
103         Avatar,\r
104     }\r
105 \r
106     /// <summary>\r
107     /// Base class for all scene objects\r
108     /// </summary>\r
109     public abstract class SceneObject : IComparable, IDisposable\r
110     {\r
111         #region Public fields\r
112         /// <summary>Interpolated local position of the object</summary>\r
113         public Vector3 InterpolatedPosition;\r
114         /// <summary>Interpolated local rotation of the object/summary>\r
115         public Quaternion InterpolatedRotation;\r
116         /// <summary>Rendered position of the object in the region</summary>\r
117         public Vector3 RenderPosition;\r
118         /// <summary>Rendered rotationm of the object in the region</summary>\r
119         public Quaternion RenderRotation;\r
120         /// <summary>Per frame calculated square of the distance from camera</summary>\r
121         public float DistanceSquared;\r
122         /// <summary>Bounding volume of the object</summary>\r
123         public BoundingVolume BoundingVolume;\r
124         /// <summary>Was the sim position and distance from camera calculated during this frame</summary>\r
125         public bool PositionCalculated;\r
126         /// <summary>Scene object type</summary>\r
127         public SceneObjectType Type = SceneObjectType.None;\r
128         /// <summary>Libomv primitive</summary>\r
129         public virtual Primitive BasePrim { get; set; }\r
130         /// <summary>Were initial initialization tasks done</summary>\r
131         public bool Initialized;\r
132         /// <summary>Is this object disposed</summary>\r
133         public bool IsDisposed = false;\r
134         public int AlphaQueryID = -1;\r
135         public int SimpleQueryID = -1;\r
136         public bool HasAlphaFaces;\r
137         public bool HasSimpleFaces;\r
138         public bool HasInvisibleFaces;\r
139 \r
140         #endregion Public fields\r
141 \r
142         uint previousParent = uint.MaxValue;\r
143 \r
144         /// <summary>\r
145         /// Cleanup resources used\r
146         /// </summary>\r
147         public virtual void Dispose()\r
148         {\r
149             IsDisposed = true;\r
150         }\r
151 \r
152         /// <summary>\r
153         /// Task performed the fist time object is set for rendering\r
154         /// </summary>\r
155         public virtual void Initialize()\r
156         {\r
157             RenderPosition = InterpolatedPosition = BasePrim.Position;\r
158             RenderRotation = InterpolatedRotation = BasePrim.Rotation;\r
159             Initialized = true;\r
160         }\r
161 \r
162         /// <summary>\r
163         /// Perform per frame tasks\r
164         /// </summary>\r
165         /// <param name="time">Time since the last call (last frame time in seconds)</param>\r
166         public virtual void Step(float time)\r
167         {\r
168             // Don't interpolate when parent changes (sit/stand link/unlink)\r
169             if (previousParent != BasePrim.ParentID)\r
170             {\r
171                 previousParent = BasePrim.ParentID;\r
172                 InterpolatedPosition = BasePrim.Position;\r
173                 InterpolatedRotation = BasePrim.Rotation;\r
174                 return;\r
175             }\r
176 \r
177             // Linear velocity and acceleration\r
178             if (BasePrim.Velocity != Vector3.Zero)\r
179             {\r
180                 BasePrim.Position = InterpolatedPosition = BasePrim.Position + BasePrim.Velocity * time\r
181                     * 0.98f * RadegastInstance.GlobalInstance.Client.Network.CurrentSim.Stats.Dilation;\r
182                 BasePrim.Velocity += BasePrim.Acceleration * time;\r
183             }\r
184             else if (InterpolatedPosition != BasePrim.Position)\r
185             {\r
186                 InterpolatedPosition = RHelp.Smoothed1stOrder(InterpolatedPosition, BasePrim.Position, time);\r
187             }\r
188 \r
189             // Angular velocity (target omega)\r
190             if (BasePrim.AngularVelocity != Vector3.Zero)\r
191             {\r
192                 Vector3 angVel = BasePrim.AngularVelocity;\r
193                 float angle = time * angVel.Length();\r
194                 Quaternion dQ = Quaternion.CreateFromAxisAngle(angVel, angle);\r
195                 InterpolatedRotation = dQ * InterpolatedRotation;\r
196             }\r
197             else if (InterpolatedRotation != BasePrim.Rotation && !(this is RenderAvatar))\r
198             {\r
199                 InterpolatedRotation = Quaternion.Slerp(InterpolatedRotation, BasePrim.Rotation, time * 10f);\r
200                 if (1f - Math.Abs(Quaternion.Dot(InterpolatedRotation, BasePrim.Rotation)) < 0.0001)\r
201                     InterpolatedRotation = BasePrim.Rotation;\r
202             }\r
203             else\r
204             {\r
205                 InterpolatedRotation = BasePrim.Rotation;\r
206             }\r
207         }\r
208 \r
209         /// <summary>\r
210         /// Render scene object\r
211         /// </summary>\r
212         /// <param name="pass">Which pass are we currently in</param>\r
213         /// <param name="pickingID">ID used to identify which object was picked</param>\r
214         /// <param name="scene">Main scene renderer</param>\r
215         /// <param name="time">Time it took to render the last frame</param>\r
216         public virtual void Render(RenderPass pass, int pickingID, SceneWindow scene, float time)\r
217         {\r
218         }\r
219 \r
220         /// <summary>\r
221         /// Implementation of the IComparable interface\r
222         /// used for sorting by distance\r
223         /// </summary>\r
224         /// <param name="other">Object we are comparing to</param>\r
225         /// <returns>Result of the comparison</returns>\r
226         public virtual int CompareTo(object other)\r
227         {\r
228             SceneObject o = (SceneObject)other;\r
229             if (this.DistanceSquared < o.DistanceSquared)\r
230                 return -1;\r
231             else if (this.DistanceSquared > o.DistanceSquared)\r
232                 return 1;\r
233             else\r
234                 return 0;\r
235         }\r
236 \r
237         #region Occlusion queries\r
238         public void StartQuery(RenderPass pass)\r
239         {\r
240             if (!RenderSettings.OcclusionCullingEnabled) return;\r
241 \r
242             if (pass == RenderPass.Simple)\r
243             {\r
244                 StartSimpleQuery();\r
245             }\r
246             else if (pass == RenderPass.Alpha)\r
247             {\r
248                 StartAlphaQuery();\r
249             }\r
250         }\r
251 \r
252         public void EndQuery(RenderPass pass)\r
253         {\r
254             if (!RenderSettings.OcclusionCullingEnabled) return;\r
255 \r
256             if (pass == RenderPass.Simple)\r
257             {\r
258                 EndSimpleQuery();\r
259             }\r
260             else if (pass == RenderPass.Alpha)\r
261             {\r
262                 EndAlphaQuery();\r
263             }\r
264         }\r
265 \r
266         public void StartAlphaQuery()\r
267         {\r
268             if (!RenderSettings.OcclusionCullingEnabled) return;\r
269 \r
270             if (AlphaQueryID == -1)\r
271             {\r
272                 Compat.GenQueries(out AlphaQueryID);\r
273             }\r
274             if (AlphaQueryID > 0)\r
275             {\r
276                 Compat.BeginQuery(QueryTarget.SamplesPassed, AlphaQueryID);\r
277             }\r
278         }\r
279 \r
280         public void EndAlphaQuery()\r
281         {\r
282             if (!RenderSettings.OcclusionCullingEnabled) return;\r
283 \r
284             if (AlphaQueryID > 0)\r
285             {\r
286                 Compat.EndQuery(QueryTarget.SamplesPassed);\r
287             }\r
288         }\r
289 \r
290         public void StartSimpleQuery()\r
291         {\r
292             if (!RenderSettings.OcclusionCullingEnabled) return;\r
293 \r
294             if (SimpleQueryID == -1)\r
295             {\r
296                 Compat.GenQueries(out SimpleQueryID);\r
297             }\r
298             if (SimpleQueryID > 0)\r
299             {\r
300                 Compat.BeginQuery(QueryTarget.SamplesPassed, SimpleQueryID);\r
301             }\r
302         }\r
303 \r
304         public void EndSimpleQuery()\r
305         {\r
306             if (!RenderSettings.OcclusionCullingEnabled) return;\r
307 \r
308             if (SimpleQueryID > 0)\r
309             {\r
310                 Compat.EndQuery(QueryTarget.SamplesPassed);\r
311             }\r
312         }\r
313 \r
314         public bool Occluded()\r
315         {\r
316             if (!RenderSettings.OcclusionCullingEnabled) return false;\r
317 \r
318             if (HasInvisibleFaces) return false;\r
319 \r
320             if ((SimpleQueryID == -1 && AlphaQueryID == -1))\r
321             {\r
322                 return false;\r
323             }\r
324 \r
325             if ((!HasAlphaFaces && !HasSimpleFaces)) return true;\r
326 \r
327             int samples = 1;\r
328             if (HasSimpleFaces && SimpleQueryID > 0)\r
329             {\r
330                 Compat.GetQueryObject(SimpleQueryID, GetQueryObjectParam.QueryResult, out samples);\r
331             }\r
332             if (HasSimpleFaces && samples > 0)\r
333             {\r
334                 return false;\r
335             }\r
336 \r
337             samples = 1;\r
338             if (HasAlphaFaces && AlphaQueryID > 0)\r
339             {\r
340                 Compat.GetQueryObject(AlphaQueryID, GetQueryObjectParam.QueryResult, out samples);\r
341             }\r
342             if (HasAlphaFaces && samples > 0)\r
343             {\r
344                 return false;\r
345             }\r
346 \r
347             return true;\r
348         }\r
349         #endregion Occlusion queries\r
350     }\r
351 \r
352     public static class RHelp\r
353     {\r
354         public static readonly Vector3 InvalidPosition = new Vector3(99999f, 99999f, 99999f);\r
355         static float t1 = 0.075f;\r
356         static float t2 = t1 / 5.7f;\r
357 \r
358         public static Vector3 Smoothed1stOrder(Vector3 curPos, Vector3 targetPos, float lastFrameTime)\r
359         {\r
360             int numIterations = (int)(lastFrameTime * 100);\r
361             do\r
362             {\r
363                 curPos += (targetPos - curPos) * t1;\r
364                 numIterations--;\r
365             }\r
366             while (numIterations > 0);\r
367             if (Vector3.DistanceSquared(curPos, targetPos) < 0.00001f)\r
368             {\r
369                 curPos = targetPos;\r
370             }\r
371             return curPos;\r
372         }\r
373 \r
374         public static Vector3 Smoothed2ndOrder(Vector3 curPos, Vector3 targetPos, ref Vector3 accel, float lastFrameTime)\r
375         {\r
376             int numIterations = (int)(lastFrameTime * 100);\r
377             do\r
378             {\r
379                 accel += (targetPos - accel - curPos) * t1;\r
380                 curPos += accel * t2;\r
381                 numIterations--;\r
382             }\r
383             while (numIterations > 0);\r
384             if (Vector3.DistanceSquared(curPos, targetPos) < 0.00001f)\r
385             {\r
386                 curPos = targetPos;\r
387             }\r
388             return curPos;\r
389         }\r
390 \r
391         public static OpenTK.Vector2 TKVector3(Vector2 v)\r
392         {\r
393             return new OpenTK.Vector2(v.X, v.Y);\r
394         }\r
395 \r
396         public static OpenTK.Vector3 TKVector3(Vector3 v)\r
397         {\r
398             return new OpenTK.Vector3(v.X, v.Y, v.Z);\r
399         }\r
400 \r
401         public static OpenTK.Vector4 TKVector3(Vector4 v)\r
402         {\r
403             return new OpenTK.Vector4(v.X, v.Y, v.Z, v.W);\r
404         }\r
405 \r
406         public static Vector2 OMVVector2(OpenTK.Vector2 v)\r
407         {\r
408             return new Vector2(v.X, v.Y);\r
409         }\r
410 \r
411         public static Vector3 OMVVector3(OpenTK.Vector3 v)\r
412         {\r
413             return new Vector3(v.X, v.Y, v.Z);\r
414         }\r
415 \r
416         public static Vector4 OMVVector4(OpenTK.Vector4 v)\r
417         {\r
418             return new Vector4(v.X, v.Y, v.Z, v.W);\r
419         }\r
420 \r
421         public static Color WinColor(OpenTK.Graphics.Color4 color)\r
422         {\r
423             return Color.FromArgb((int)(color.A * 255), (int)(color.R * 255), (int)(color.G * 255), (int)(color.B * 255));\r
424         }\r
425 \r
426         public static Color WinColor(Color4 color)\r
427         {\r
428             return Color.FromArgb((int)(color.A * 255), (int)(color.R * 255), (int)(color.G * 255), (int)(color.B * 255));\r
429         }\r
430 \r
431         public static int NextPow2(int start)\r
432         {\r
433             int pow = 1;\r
434             while (pow < start) pow *= 2;\r
435             return pow;\r
436         }\r
437 \r
438         #region Cached image save and load\r
439         public static readonly string RAD_IMG_MAGIC = "radegast_img";\r
440 \r
441         public static bool LoadCachedImage(UUID textureID, out byte[] tgaData, out bool hasAlpha, out bool fullAlpha, out bool isMask)\r
442         {\r
443             tgaData = null;\r
444             hasAlpha = fullAlpha = isMask = false;\r
445 \r
446             try\r
447             {\r
448                 string fname = System.IO.Path.Combine(RadegastInstance.GlobalInstance.Client.Settings.ASSET_CACHE_DIR, string.Format("{0}.rzi", textureID));\r
449                 //string fname = System.IO.Path.Combine(".", string.Format("{0}.rzi", textureID));\r
450 \r
451                 using (var f = File.Open(fname, FileMode.Open, FileAccess.Read, FileShare.Read))\r
452                 {\r
453                     byte[] header = new byte[36];\r
454                     int i = 0;\r
455                     f.Read(header, 0, header.Length);\r
456 \r
457                     // check if the file is starting with magic string\r
458                     if (RAD_IMG_MAGIC != Utils.BytesToString(header, 0, RAD_IMG_MAGIC.Length))\r
459                         return false;\r
460                     i += RAD_IMG_MAGIC.Length;\r
461 \r
462                     if (header[i++] != 1) // check version\r
463                         return false;\r
464 \r
465                     hasAlpha = header[i++] == 1;\r
466                     fullAlpha = header[i++] == 1;\r
467                     isMask = header[i++] == 1;\r
468 \r
469                     int uncompressedSize = Utils.BytesToInt(header, i);\r
470                     i += 4;\r
471 \r
472                     textureID = new UUID(header, i);\r
473                     i += 16;\r
474 \r
475                     tgaData = new byte[uncompressedSize];\r
476                     using (var compressed = new DeflateStream(f, CompressionMode.Decompress))\r
477                     {\r
478                         int read = 0;\r
479                         while ((read = compressed.Read(tgaData, read, uncompressedSize - read)) > 0) ;\r
480                     }\r
481                 }\r
482 \r
483                 return true;\r
484             }\r
485             catch (FileNotFoundException) { }\r
486             catch (Exception ex)\r
487             {\r
488                 Logger.DebugLog(string.Format("Failed to load radegast cache file {0}: {1}", textureID, ex.Message));\r
489             }\r
490             return false;\r
491         }\r
492 \r
493         public static bool SaveCachedImage(byte[] tgaData, UUID textureID, bool hasAlpha, bool fullAlpha, bool isMask)\r
494         {\r
495             try\r
496             {\r
497                 string fname = System.IO.Path.Combine(RadegastInstance.GlobalInstance.Client.Settings.ASSET_CACHE_DIR, string.Format("{0}.rzi", textureID));\r
498                 //string fname = System.IO.Path.Combine(".", string.Format("{0}.rzi", textureID));\r
499 \r
500                 using (var f = File.Open(fname, FileMode.Create, FileAccess.Write, FileShare.None))\r
501                 {\r
502                     int i = 0;\r
503                     // magic header\r
504                     f.Write(Utils.StringToBytes(RAD_IMG_MAGIC), 0, RAD_IMG_MAGIC.Length);\r
505                     i += RAD_IMG_MAGIC.Length;\r
506 \r
507                     // version\r
508                     f.WriteByte((byte)1);\r
509                     i++;\r
510 \r
511                     // texture info\r
512                     f.WriteByte(hasAlpha ? (byte)1 : (byte)0);\r
513                     f.WriteByte(fullAlpha ? (byte)1 : (byte)0);\r
514                     f.WriteByte(isMask ? (byte)1 : (byte)0);\r
515                     i += 3;\r
516 \r
517                     // texture size\r
518                     byte[] uncompressedSize = Utils.IntToBytes(tgaData.Length);\r
519                     f.Write(uncompressedSize, 0, uncompressedSize.Length);\r
520                     i += uncompressedSize.Length;\r
521 \r
522                     // texture id\r
523                     byte[] id = new byte[16];\r
524                     textureID.ToBytes(id, 0);\r
525                     f.Write(id, 0, 16);\r
526                     i += 16;\r
527 \r
528                     // compressed texture data\r
529                     using (var compressed = new DeflateStream(f, CompressionMode.Compress))\r
530                     {\r
531                         compressed.Write(tgaData, 0, tgaData.Length);\r
532                     }\r
533                 }\r
534                 return true;\r
535             }\r
536             catch (Exception ex)\r
537             {\r
538                 Logger.DebugLog(string.Format("Failed to save radegast cache file {0}: {1}", textureID, ex.Message));\r
539                 return false;\r
540             }\r
541         }\r
542         #endregion Cached image save and load\r
543 \r
544         #region Static vertices and indices for a cube (used for bounding box drawing)\r
545         /**********************************************\r
546           5 --- 4\r
547          /|    /|\r
548         1 --- 0 |\r
549         | 6 --| 7\r
550         |/    |/\r
551         2 --- 3\r
552         ***********************************************/\r
553         public static readonly float[] CubeVertices = new float[]\r
554         {\r
555              0.5f,  0.5f,  0.5f, // 0\r
556                 -0.5f,  0.5f,  0.5f, // 1\r
557                 -0.5f, -0.5f,  0.5f, // 2\r
558                  0.5f, -0.5f,  0.5f, // 3\r
559                  0.5f,  0.5f, -0.5f, // 4\r
560                 -0.5f,  0.5f, -0.5f, // 5\r
561                 -0.5f, -0.5f, -0.5f, // 6\r
562                  0.5f, -0.5f, -0.5f  // 7\r
563         };\r
564 \r
565         public static readonly ushort[] CubeIndices = new ushort[]\r
566         {\r
567             0, 1, 2, 3,     // Front Face\r
568                 4, 5, 6, 7,     // Back Face\r
569                 1, 2, 6, 5,     // Left Face\r
570                 0, 3, 7, 4,     // Right Face\r
571                 0, 1, 5, 4,     // Top Face\r
572                 2, 3, 7, 6      // Bottom Face\r
573         };\r
574         #endregion Static vertices and indices for a cube (used for bounding box drawing)\r
575 \r
576         public static int GLLoadImage(Bitmap bitmap, bool hasAlpha)\r
577         {\r
578             return GLLoadImage(bitmap, hasAlpha, true);\r
579         }\r
580 \r
581         public static int GLLoadImage(Bitmap bitmap, bool hasAlpha, bool useMipmap)\r
582         {\r
583             useMipmap = useMipmap && RenderSettings.HasMipmap;\r
584             int ret = -1;\r
585             GL.GenTextures(1, out ret);\r
586             GL.BindTexture(TextureTarget.Texture2D, ret);\r
587 \r
588             Rectangle rectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height);\r
589 \r
590             BitmapData bitmapData =\r
591                 bitmap.LockBits(\r
592                 rectangle,\r
593                 ImageLockMode.ReadOnly,\r
594                 hasAlpha ? System.Drawing.Imaging.PixelFormat.Format32bppArgb : System.Drawing.Imaging.PixelFormat.Format24bppRgb);\r
595 \r
596             GL.TexImage2D(\r
597                 TextureTarget.Texture2D,\r
598                 0,\r
599                 hasAlpha ? PixelInternalFormat.Rgba : PixelInternalFormat.Rgb8,\r
600                 bitmap.Width,\r
601                 bitmap.Height,\r
602                 0,\r
603                 hasAlpha ? OpenTK.Graphics.OpenGL.PixelFormat.Bgra : OpenTK.Graphics.OpenGL.PixelFormat.Bgr,\r
604                 PixelType.UnsignedByte,\r
605                 bitmapData.Scan0);\r
606 \r
607             GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);\r
608             GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);\r
609             GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);\r
610             if (useMipmap)\r
611             {\r
612                 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.LinearMipmapLinear);\r
613                 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.GenerateMipmap, 1);\r
614                 GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);\r
615             }\r
616             else\r
617             {\r
618                 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);\r
619             }\r
620 \r
621             bitmap.UnlockBits(bitmapData);\r
622             return ret;\r
623         }\r
624 \r
625         public static void Draw2DBox(float x, float y, float width, float height, float depth)\r
626         {\r
627             GL.Begin(BeginMode.Quads);\r
628             {\r
629                 GL.TexCoord2(0, 1);\r
630                 GL.Vertex3(x, y, depth);\r
631                 GL.TexCoord2(1, 1);\r
632                 GL.Vertex3(x + width, y, depth);\r
633                 GL.TexCoord2(1, 0);\r
634                 GL.Vertex3(x + width, y + height, depth);\r
635                 GL.TexCoord2(0, 0);\r
636                 GL.Vertex3(x, y + height, depth);\r
637             }\r
638             GL.End();\r
639         }\r
640 \r
641         public static void ResetMaterial()\r
642         {\r
643             GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Ambient, new float[] { 0.2f, 0.2f, 0.2f, 1.0f });\r
644             GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Diffuse, new float[] { 0.8f, 0.8f, 0.8f, 1.0f });\r
645             GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Specular, new float[] { 0f, 0f, 0f, 1.0f });\r
646             GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Emission, new float[] { 0f, 0f, 0f, 1.0f });\r
647             GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Shininess, 0f);\r
648             ShaderProgram.Stop();\r
649         }\r
650     }\r
651 \r
652     /// <summary>\r
653     /// Represents camera object\r
654     /// </summary>\r
655     public class Camera\r
656     {\r
657         /// <summary>\r
658         /// Indicates that there was manual camera movement, stop tracking objects\r
659         /// </summary>\r
660         public bool Manual;\r
661         Vector3 mPosition;\r
662         Vector3 mFocalPoint;\r
663         bool mModified;\r
664 \r
665         /// <summary>Camera position</summary>\r
666         public Vector3 Position { get { return mPosition; } set { mPosition = value; Modify(); } }\r
667         /// <summary>Camera target</summary>\r
668         public Vector3 FocalPoint { get { return mFocalPoint; } set { mFocalPoint = value; Modify(); } }\r
669         /// <summary>Zoom level</summary>\r
670         public float Zoom;\r
671         /// <summary>Draw distance</summary>\r
672         public float Far;\r
673         /// <summary>Has camera been modified</summary>\r
674         public bool Modified { get { return mModified; } set { mModified = value; } }\r
675 \r
676         public float TimeToTarget = 0f;\r
677 \r
678         public Vector3 RenderPosition;\r
679         public Vector3 RenderFocalPoint;\r
680 \r
681         void Modify()\r
682         {\r
683             mModified = true;\r
684         }\r
685 \r
686         public void Step(float time)\r
687         {\r
688             if (RenderPosition != Position)\r
689             {\r
690                 RenderPosition = RHelp.Smoothed1stOrder(RenderPosition, Position, time);\r
691                 Modified = true;\r
692             }\r
693             if (RenderFocalPoint != FocalPoint)\r
694             {\r
695                 RenderFocalPoint = RHelp.Smoothed1stOrder(RenderFocalPoint, FocalPoint, time);\r
696                 Modified = true;\r
697             }\r
698         }\r
699 \r
700         [Obsolete("Use Step(), left in here for reference")]\r
701         public void Step2(float time)\r
702         {\r
703             TimeToTarget -= time;\r
704             if (TimeToTarget <= time)\r
705             {\r
706                 EndMove();\r
707                 return;\r
708             }\r
709 \r
710             mModified = true;\r
711 \r
712             float pctElapsed = time / TimeToTarget;\r
713 \r
714             if (RenderPosition != Position)\r
715             {\r
716                 float distance = Vector3.Distance(RenderPosition, Position);\r
717                 RenderPosition = Vector3.Lerp(RenderPosition, Position, distance * pctElapsed);\r
718             }\r
719 \r
720             if (RenderFocalPoint != FocalPoint)\r
721             {\r
722                 RenderFocalPoint = Interpolate(RenderFocalPoint, FocalPoint, pctElapsed);\r
723             }\r
724         }\r
725 \r
726         Vector3 Interpolate(Vector3 start, Vector3 end, float fraction)\r
727         {\r
728             float distance = Vector3.Distance(start, end);\r
729             Vector3 direction = end - start;\r
730             return start + direction * fraction;\r
731         }\r
732 \r
733         public void EndMove()\r
734         {\r
735             mModified = true;\r
736             TimeToTarget = 0;\r
737             RenderPosition = Position;\r
738             RenderFocalPoint = FocalPoint;\r
739         }\r
740 \r
741         public void Pan(float deltaX, float deltaY)\r
742         {\r
743             Manual = true;\r
744             Vector3 direction = Position - FocalPoint;\r
745             direction.Normalize();\r
746             Vector3 vy = direction % Vector3.UnitZ;\r
747             Vector3 vx = vy % direction;\r
748             Vector3 vxy = vx * deltaY + vy * deltaX;\r
749             Position += vxy;\r
750             FocalPoint += vxy;\r
751         }\r
752 \r
753         public void Rotate(float delta, bool horizontal)\r
754         {\r
755             Manual = true;\r
756             Vector3 direction = Position - FocalPoint;\r
757             if (horizontal)\r
758             {\r
759                 Position = FocalPoint + direction * new Quaternion(0f, 0f, (float)Math.Sin(delta), (float)Math.Cos(delta));\r
760             }\r
761             else\r
762             {\r
763                 Position = FocalPoint + direction * Quaternion.CreateFromAxisAngle(direction % Vector3.UnitZ, delta);\r
764             }\r
765         }\r
766 \r
767         public void MoveToTarget(float delta)\r
768         {\r
769             Manual = true;\r
770             Position += (Position - FocalPoint) * delta;\r
771         }\r
772 \r
773         /// <summary>\r
774         /// Sets the world in perspective of the camera\r
775         /// </summary>\r
776         public void LookAt()\r
777         {\r
778             OpenTK.Matrix4 lookAt = OpenTK.Matrix4.LookAt(\r
779                 RenderPosition.X, RenderPosition.Y, RenderPosition.Z,\r
780                 RenderFocalPoint.X, RenderFocalPoint.Y, RenderFocalPoint.Z,\r
781                 0f, 0f, 1f);\r
782             GL.MultMatrix(ref lookAt);\r
783         }\r
784     }\r
785 \r
786     public static class MeshToOBJ\r
787     {\r
788         public static bool MeshesToOBJ(Dictionary<uint, FacetedMesh> meshes, string filename)\r
789         {\r
790             StringBuilder obj = new StringBuilder();\r
791             StringBuilder mtl = new StringBuilder();\r
792 \r
793             FileInfo objFileInfo = new FileInfo(filename);\r
794 \r
795             string mtlFilename = objFileInfo.FullName.Substring(objFileInfo.DirectoryName.Length + 1,\r
796                 objFileInfo.FullName.Length - (objFileInfo.DirectoryName.Length + 1) - 4) + ".mtl";\r
797 \r
798             obj.AppendLine("# Created by libprimrender");\r
799             obj.AppendLine("mtllib ./" + mtlFilename);\r
800             obj.AppendLine();\r
801 \r
802             mtl.AppendLine("# Created by libprimrender");\r
803             mtl.AppendLine();\r
804 \r
805             int primNr = 0;\r
806             foreach (FacetedMesh mesh in meshes.Values)\r
807             {\r
808                 for (int j = 0; j < mesh.Faces.Count; j++)\r
809                 {\r
810                     Face face = mesh.Faces[j];\r
811 \r
812                     if (face.Vertices.Count > 2)\r
813                     {\r
814                         string mtlName = String.Format("material{0}-{1}", primNr, face.ID);\r
815                         Primitive.TextureEntryFace tex = face.TextureFace;\r
816                         string texName = tex.TextureID.ToString() + ".tga";\r
817 \r
818                         // FIXME: Convert the source to TGA (if needed) and copy to the destination\r
819 \r
820                         float shiny = 0.00f;\r
821                         switch (tex.Shiny)\r
822                         {\r
823                             case Shininess.High:\r
824                                 shiny = 1.00f;\r
825                                 break;\r
826                             case Shininess.Medium:\r
827                                 shiny = 0.66f;\r
828                                 break;\r
829                             case Shininess.Low:\r
830                                 shiny = 0.33f;\r
831                                 break;\r
832                         }\r
833 \r
834                         obj.AppendFormat("g face{0}-{1}{2}", primNr, face.ID, Environment.NewLine);\r
835 \r
836                         mtl.AppendLine("newmtl " + mtlName);\r
837                         mtl.AppendFormat("Ka {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);\r
838                         mtl.AppendFormat("Kd {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);\r
839                         //mtl.AppendFormat("Ks {0} {1} {2}{3}");\r
840                         mtl.AppendLine("Tr " + tex.RGBA.A);\r
841                         mtl.AppendLine("Ns " + shiny);\r
842                         mtl.AppendLine("illum 1");\r
843                         if (tex.TextureID != UUID.Zero && tex.TextureID != Primitive.TextureEntry.WHITE_TEXTURE)\r
844                             mtl.AppendLine("map_Kd ./" + texName);\r
845                         mtl.AppendLine();\r
846 \r
847                         // Write the vertices, texture coordinates, and vertex normals for this side\r
848                         for (int k = 0; k < face.Vertices.Count; k++)\r
849                         {\r
850                             Vertex vertex = face.Vertices[k];\r
851 \r
852                             #region Vertex\r
853 \r
854                             Vector3 pos = vertex.Position;\r
855 \r
856                             // Apply scaling\r
857                             pos *= mesh.Prim.Scale;\r
858 \r
859                             // Apply rotation\r
860                             pos *= mesh.Prim.Rotation;\r
861 \r
862                             // The root prim position is sim-relative, while child prim positions are\r
863                             // parent-relative. We want to apply parent-relative translations but not\r
864                             // sim-relative ones\r
865                             if (mesh.Prim.ParentID != 0)\r
866                                 pos += mesh.Prim.Position;\r
867 \r
868                             obj.AppendFormat("v {0} {1} {2}{3}", pos.X, pos.Y, pos.Z, Environment.NewLine);\r
869 \r
870                             #endregion Vertex\r
871 \r
872                             #region Texture Coord\r
873 \r
874                             obj.AppendFormat("vt {0} {1}{2}", vertex.TexCoord.X, vertex.TexCoord.Y,\r
875                                 Environment.NewLine);\r
876 \r
877                             #endregion Texture Coord\r
878 \r
879                             #region Vertex Normal\r
880 \r
881                             // HACK: Sometimes normals are getting set to <NaN,NaN,NaN>\r
882                             if (!Single.IsNaN(vertex.Normal.X) && !Single.IsNaN(vertex.Normal.Y) && !Single.IsNaN(vertex.Normal.Z))\r
883                                 obj.AppendFormat("vn {0} {1} {2}{3}", vertex.Normal.X, vertex.Normal.Y, vertex.Normal.Z,\r
884                                     Environment.NewLine);\r
885                             else\r
886                                 obj.AppendLine("vn 0.0 1.0 0.0");\r
887 \r
888                             #endregion Vertex Normal\r
889                         }\r
890 \r
891                         obj.AppendFormat("# {0} vertices{1}", face.Vertices.Count, Environment.NewLine);\r
892                         obj.AppendLine();\r
893                         obj.AppendLine("usemtl " + mtlName);\r
894 \r
895                         #region Elements\r
896 \r
897                         // Write all of the faces (triangles) for this side\r
898                         for (int k = 0; k < face.Indices.Count / 3; k++)\r
899                         {\r
900                             obj.AppendFormat("f -{0}/-{0}/-{0} -{1}/-{1}/-{1} -{2}/-{2}/-{2}{3}",\r
901                                 face.Vertices.Count - face.Indices[k * 3 + 0],\r
902                                 face.Vertices.Count - face.Indices[k * 3 + 1],\r
903                                 face.Vertices.Count - face.Indices[k * 3 + 2],\r
904                                 Environment.NewLine);\r
905                         }\r
906 \r
907                         obj.AppendFormat("# {0} elements{1}", face.Indices.Count / 3, Environment.NewLine);\r
908                         obj.AppendLine();\r
909 \r
910                         #endregion Elements\r
911                     }\r
912                 }\r
913                 primNr++;\r
914             }\r
915 \r
916             try\r
917             {\r
918                 File.WriteAllText(filename, obj.ToString());\r
919                 File.WriteAllText(mtlFilename, mtl.ToString());\r
920             }\r
921             catch (Exception)\r
922             {\r
923                 return false;\r
924             }\r
925 \r
926             return true;\r
927         }\r
928     }\r
929 \r
930     public static class Math3D\r
931     {\r
932         // Column-major:\r
933         // |  0  4  8 12 |\r
934         // |  1  5  9 13 |\r
935         // |  2  6 10 14 |\r
936         // |  3  7 11 15 |\r
937 \r
938         public static float[] CreateTranslationMatrix(Vector3 v)\r
939         {\r
940             float[] mat = new float[16];\r
941 \r
942             mat[12] = v.X;\r
943             mat[13] = v.Y;\r
944             mat[14] = v.Z;\r
945             mat[0] = mat[5] = mat[10] = mat[15] = 1;\r
946 \r
947             return mat;\r
948         }\r
949 \r
950         public static float[] CreateRotationMatrix(Quaternion q)\r
951         {\r
952             float[] mat = new float[16];\r
953 \r
954             // Transpose the quaternion (don't ask me why)\r
955             q.X = q.X * -1f;\r
956             q.Y = q.Y * -1f;\r
957             q.Z = q.Z * -1f;\r
958 \r
959             float x2 = q.X + q.X;\r
960             float y2 = q.Y + q.Y;\r
961             float z2 = q.Z + q.Z;\r
962             float xx = q.X * x2;\r
963             float xy = q.X * y2;\r
964             float xz = q.X * z2;\r
965             float yy = q.Y * y2;\r
966             float yz = q.Y * z2;\r
967             float zz = q.Z * z2;\r
968             float wx = q.W * x2;\r
969             float wy = q.W * y2;\r
970             float wz = q.W * z2;\r
971 \r
972             mat[0] = 1.0f - (yy + zz);\r
973             mat[1] = xy - wz;\r
974             mat[2] = xz + wy;\r
975             mat[3] = 0.0f;\r
976 \r
977             mat[4] = xy + wz;\r
978             mat[5] = 1.0f - (xx + zz);\r
979             mat[6] = yz - wx;\r
980             mat[7] = 0.0f;\r
981 \r
982             mat[8] = xz - wy;\r
983             mat[9] = yz + wx;\r
984             mat[10] = 1.0f - (xx + yy);\r
985             mat[11] = 0.0f;\r
986 \r
987             mat[12] = 0.0f;\r
988             mat[13] = 0.0f;\r
989             mat[14] = 0.0f;\r
990             mat[15] = 1.0f;\r
991 \r
992             return mat;\r
993         }\r
994 \r
995         public static float[] CreateSRTMatrix(Vector3 scale, Quaternion q, Vector3 pos)\r
996         {\r
997             float[] mat = new float[16];\r
998 \r
999             // Transpose the quaternion (don't ask me why)\r
1000             q.X = q.X * -1f;\r
1001             q.Y = q.Y * -1f;\r
1002             q.Z = q.Z * -1f;\r
1003 \r
1004             float x2 = q.X + q.X;\r
1005             float y2 = q.Y + q.Y;\r
1006             float z2 = q.Z + q.Z;\r
1007             float xx = q.X * x2;\r
1008             float xy = q.X * y2;\r
1009             float xz = q.X * z2;\r
1010             float yy = q.Y * y2;\r
1011             float yz = q.Y * z2;\r
1012             float zz = q.Z * z2;\r
1013             float wx = q.W * x2;\r
1014             float wy = q.W * y2;\r
1015             float wz = q.W * z2;\r
1016 \r
1017             mat[0] = (1.0f - (yy + zz)) * scale.X;\r
1018             mat[1] = (xy - wz) * scale.X;\r
1019             mat[2] = (xz + wy) * scale.X;\r
1020             mat[3] = 0.0f;\r
1021 \r
1022             mat[4] = (xy + wz) * scale.Y;\r
1023             mat[5] = (1.0f - (xx + zz)) * scale.Y;\r
1024             mat[6] = (yz - wx) * scale.Y;\r
1025             mat[7] = 0.0f;\r
1026 \r
1027             mat[8] = (xz - wy) * scale.Z;\r
1028             mat[9] = (yz + wx) * scale.Z;\r
1029             mat[10] = (1.0f - (xx + yy)) * scale.Z;\r
1030             mat[11] = 0.0f;\r
1031 \r
1032             //Positional parts\r
1033             mat[12] = pos.X;\r
1034             mat[13] = pos.Y;\r
1035             mat[14] = pos.Z;\r
1036             mat[15] = 1.0f;\r
1037 \r
1038             return mat;\r
1039         }\r
1040 \r
1041 \r
1042         public static float[] CreateScaleMatrix(Vector3 v)\r
1043         {\r
1044             float[] mat = new float[16];\r
1045 \r
1046             mat[0] = v.X;\r
1047             mat[5] = v.Y;\r
1048             mat[10] = v.Z;\r
1049             mat[15] = 1;\r
1050 \r
1051             return mat;\r
1052         }\r
1053 \r
1054         public static float[] Lerp(float[] matrix1, float[] matrix2, float amount)\r
1055         {\r
1056 \r
1057             float[] lerp = new float[16];\r
1058             //Probably not doing this as a loop is cheaper(unrolling)\r
1059             //also for performance we probably should not create new objects\r
1060             // but meh.\r
1061             for (int x = 0; x < 16; x++)\r
1062             {\r
1063                 lerp[x] = matrix1[x] + ((matrix2[x] - matrix1[x]) * amount);\r
1064             }\r
1065 \r
1066             return lerp;\r
1067         }\r
1068 \r
1069 \r
1070         public static bool GluProject(OpenTK.Vector3 objPos, OpenTK.Matrix4 modelMatrix, OpenTK.Matrix4 projMatrix, int[] viewport, out OpenTK.Vector3 screenPos)\r
1071         {\r
1072             OpenTK.Vector4 _in;\r
1073             OpenTK.Vector4 _out;\r
1074 \r
1075             _in.X = objPos.X;\r
1076             _in.Y = objPos.Y;\r
1077             _in.Z = objPos.Z;\r
1078             _in.W = 1.0f;\r
1079 \r
1080             _out = OpenTK.Vector4.Transform(_in, modelMatrix);\r
1081             _in = OpenTK.Vector4.Transform(_out, projMatrix);\r
1082 \r
1083             if (_in.W <= 0.0)\r
1084             {\r
1085                 screenPos = OpenTK.Vector3.Zero;\r
1086                 return false;\r
1087             }\r
1088 \r
1089             _in.X /= _in.W;\r
1090             _in.Y /= _in.W;\r
1091             _in.Z /= _in.W;\r
1092             /* Map x, y and z to range 0-1 */\r
1093             _in.X = _in.X * 0.5f + 0.5f;\r
1094             _in.Y = _in.Y * 0.5f + 0.5f;\r
1095             _in.Z = _in.Z * 0.5f + 0.5f;\r
1096 \r
1097             /* Map x,y to viewport */\r
1098             _in.X = _in.X * viewport[2] + viewport[0];\r
1099             _in.Y = _in.Y * viewport[3] + viewport[1];\r
1100 \r
1101             screenPos.X = _in.X;\r
1102             screenPos.Y = _in.Y;\r
1103             screenPos.Z = _in.Z;\r
1104 \r
1105             return true;\r
1106         }\r
1107 \r
1108         public static bool GluUnProject(float winx, float winy, float winz, OpenTK.Matrix4 modelMatrix, OpenTK.Matrix4 projMatrix, int[] viewport, out OpenTK.Vector3 pos)\r
1109         {\r
1110             OpenTK.Matrix4 finalMatrix;\r
1111             OpenTK.Vector4 _in;\r
1112             OpenTK.Vector4 _out;\r
1113 \r
1114             finalMatrix = OpenTK.Matrix4.Mult(modelMatrix, projMatrix);\r
1115 \r
1116             finalMatrix.Invert();\r
1117 \r
1118             _in.X = winx;\r
1119             _in.Y = winy;\r
1120             _in.Z = winz;\r
1121             _in.W = 1.0f;\r
1122 \r
1123             /* Map x and y from window coordinates */\r
1124             _in.X = (_in.X - viewport[0]) / viewport[2];\r
1125             _in.Y = (_in.Y - viewport[1]) / viewport[3];\r
1126 \r
1127             pos = OpenTK.Vector3.Zero;\r
1128 \r
1129             /* Map to range -1 to 1 */\r
1130             _in.X = _in.X * 2 - 1;\r
1131             _in.Y = _in.Y * 2 - 1;\r
1132             _in.Z = _in.Z * 2 - 1;\r
1133 \r
1134             //__gluMultMatrixVecd(finalMatrix, _in, _out);\r
1135             // check if this works:\r
1136             _out = OpenTK.Vector4.Transform(_in, finalMatrix);\r
1137 \r
1138             if (_out.W == 0.0f)\r
1139                 return false;\r
1140             _out.X /= _out.W;\r
1141             _out.Y /= _out.W;\r
1142             _out.Z /= _out.W;\r
1143             pos.X = _out.X;\r
1144             pos.Y = _out.Y;\r
1145             pos.Z = _out.Z;\r
1146             return true;\r
1147         }\r
1148 \r
1149         public static double[] AbovePlane(double height)\r
1150         {\r
1151             return new double[] { 0, 0, 1, -height };\r
1152         }\r
1153 \r
1154         public static double[] BelowPlane(double height)\r
1155         {\r
1156             return new double[] { 0, 0, -1, height };\r
1157         }\r
1158     }\r
1159 \r
1160     /*\r
1161      *  Helper classs for reading the static VFS file, call \r
1162      *  staticVFS.readVFSheaders() with the path to the static_data.db2 and static_index.db2 files\r
1163      *  and it will pass and dump in to openmetaverse_data for you\r
1164      *  This should only be needed to be used if LL update the static VFS in order to refresh our data\r
1165      */\r
1166 \r
1167     class VFSblock\r
1168     {\r
1169         public int mLocation;\r
1170         public int mLength;\r
1171         public int mAccessTime;\r
1172         public UUID mFileID;\r
1173         public int mSize;\r
1174         public AssetType mAssetType;\r
1175 \r
1176         public int readblock(byte[] blockdata, int offset)\r
1177         {\r
1178 \r
1179             BitPack input = new BitPack(blockdata, offset);\r
1180             mLocation = input.UnpackInt();\r
1181             mLength = input.UnpackInt();\r
1182             mAccessTime = input.UnpackInt();\r
1183             mFileID = input.UnpackUUID();\r
1184             int filetype = input.UnpackShort();\r
1185             mAssetType = (AssetType)filetype;\r
1186             mSize = input.UnpackInt();\r
1187             offset += 34;\r
1188 \r
1189             Logger.Log(String.Format("Found header for {0} type {1} length {2} at {3}", mFileID, mAssetType, mSize, mLocation), Helpers.LogLevel.Info);\r
1190 \r
1191             return offset;\r
1192         }\r
1193 \r
1194     }\r
1195 \r
1196     public class staticVFS\r
1197     {\r
1198         public static void readVFSheaders(string datafile, string indexfile)\r
1199         {\r
1200             FileStream datastream;\r
1201             FileStream indexstream;\r
1202 \r
1203             datastream = File.Open(datafile, FileMode.Open);\r
1204             indexstream = File.Open(indexfile, FileMode.Open);\r
1205 \r
1206             int offset = 0;\r
1207 \r
1208             byte[] blockdata = new byte[indexstream.Length];\r
1209             indexstream.Read(blockdata, 0, (int)indexstream.Length);\r
1210 \r
1211             while (offset < indexstream.Length)\r
1212             {\r
1213                 VFSblock block = new VFSblock();\r
1214                 offset = block.readblock(blockdata, offset);\r
1215 \r
1216                 FileStream writer = File.Open(OpenMetaverse.Settings.RESOURCE_DIR + System.IO.Path.DirectorySeparatorChar + block.mFileID.ToString(), FileMode.Create);\r
1217                 byte[] data = new byte[block.mSize];\r
1218                 datastream.Seek(block.mLocation, SeekOrigin.Begin);\r
1219                 datastream.Read(data, 0, block.mSize);\r
1220                 writer.Write(data, 0, block.mSize);\r
1221                 writer.Close();\r
1222             }\r
1223 \r
1224         }\r
1225     }\r
1226 }