OSDN Git Service

36fb96c01eba58945f694606b81688976061eddb
[radegast/radegast.git] / Radegast / GUI / Rendering / PrimWorkshop.cs
1 // 
2 // Radegast Metaverse Client
3 // Copyright (c) 2009-2011, Radegast Development Team
4 // All rights reserved.
5 // 
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
8 // 
9 //     * Redistributions of source code must retain the above copyright notice,
10 //       this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above copyright
12 //       notice, this list of conditions and the following disclaimer in the
13 //       documentation and/or other materials provided with the distribution.
14 //     * Neither the name of the application "Radegast", nor the names of its
15 //       contributors may be used to endorse or promote products derived from
16 //       this software without specific prior written permission.
17 // 
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 //
29 // $Id$
30 //
31
32 #region Usings
33 using System;
34 using System.Collections.Generic;
35 using System.Drawing;
36 using System.Drawing.Imaging;
37 using System.IO;
38 using System.Windows.Forms;
39 using System.Text;
40 using System.Threading;
41 using System.Linq;
42 using OpenTK.Graphics.OpenGL;
43 using OpenMetaverse;
44 using OpenMetaverse.Rendering;
45 using OpenMetaverse.Assets;
46 using OpenMetaverse.Imaging;
47 using OpenMetaverse.StructuredData;
48 #endregion Usings
49
50 namespace Radegast.Rendering
51 {
52
53     public partial class frmPrimWorkshop : RadegastForm
54     {
55         #region Public fields
56         /// <summary>
57         /// The OpenGL surface
58         /// </summary>
59         public OpenTK.GLControl glControl = null;
60
61         /// <summary>
62         /// Use multi sampling (anti aliasing)
63         /// </summary>
64         public bool UseMultiSampling = true;
65
66         /// <summary>
67         /// Is rendering engine ready and enabled
68         /// </summary>
69         public bool RenderingEnabled = false;
70
71         /// <summary>
72         /// Rednder in wireframe mode
73         /// </summary>
74         public bool Wireframe = false;
75
76         /// <summary>
77         /// List of prims in the scene
78         /// </summary>
79         Dictionary<uint, FacetedMesh> Prims = new Dictionary<uint, FacetedMesh>();
80
81         /// <summary>
82         /// Local ID of the root prim
83         /// </summary>
84         public uint RootPrimLocalID = 0;
85
86         /// <summary>
87         /// Camera center
88         /// </summary>
89         public Vector3 Center = Vector3.Zero;
90         #endregion Public fields
91
92         #region Private fields
93
94         Dictionary<UUID, TextureInfo> TexturesPtrMap = new Dictionary<UUID, TextureInfo>();
95         RadegastInstance instance;
96         MeshmerizerR renderer;
97         OpenTK.Graphics.GraphicsMode GLMode = null;
98         AutoResetEvent TextureThreadContextReady = new AutoResetEvent(false);
99         BlockingQueue<TextureLoadItem> PendingTextures = new BlockingQueue<TextureLoadItem>();
100         float[] lightPos = new float[] { 0f, 0f, 1f, 0f };
101         bool hasMipmap;
102
103         #endregion Private fields
104
105         #region Construction and disposal
106         public frmPrimWorkshop(RadegastInstance instance, uint rootLocalID)
107             : base(instance)
108         {
109             this.RootPrimLocalID = rootLocalID;
110
111             InitializeComponent();
112             Disposed += new EventHandler(frmPrimWorkshop_Disposed);
113             AutoSavePosition = true;
114             UseMultiSampling = cbAA.Checked = instance.GlobalSettings["use_multi_sampling"];
115             cbAA.CheckedChanged += cbAA_CheckedChanged;
116
117             this.instance = instance;
118
119             renderer = new MeshmerizerR();
120
121             Client.Objects.TerseObjectUpdate += new EventHandler<TerseObjectUpdateEventArgs>(Objects_TerseObjectUpdate);
122             Client.Objects.ObjectUpdate += new EventHandler<PrimEventArgs>(Objects_ObjectUpdate);
123             Client.Objects.ObjectDataBlockUpdate += new EventHandler<ObjectDataBlockUpdateEventArgs>(Objects_ObjectDataBlockUpdate);
124         }
125
126         void frmPrimWorkshop_Disposed(object sender, EventArgs e)
127         {
128             if (glControl != null)
129             {
130                 glControl.Dispose();
131             }
132             glControl = null;
133             Client.Objects.TerseObjectUpdate -= new EventHandler<TerseObjectUpdateEventArgs>(Objects_TerseObjectUpdate);
134             Client.Objects.ObjectUpdate -= new EventHandler<PrimEventArgs>(Objects_ObjectUpdate);
135             Client.Objects.ObjectDataBlockUpdate -= new EventHandler<ObjectDataBlockUpdateEventArgs>(Objects_ObjectDataBlockUpdate);
136         }
137         #endregion Construction and disposal
138
139         #region Network messaage handlers
140         void Objects_TerseObjectUpdate(object sender, TerseObjectUpdateEventArgs e)
141         {
142             if (Prims.ContainsKey(e.Prim.LocalID))
143             {
144                 UpdatePrimBlocking(e.Prim);
145             }
146         }
147
148         void Objects_ObjectUpdate(object sender, PrimEventArgs e)
149         {
150             if (Prims.ContainsKey(e.Prim.LocalID) || Prims.ContainsKey(e.Prim.ParentID))
151             {
152                 UpdatePrimBlocking(e.Prim);
153             }
154         }
155
156         void Objects_ObjectDataBlockUpdate(object sender, ObjectDataBlockUpdateEventArgs e)
157         {
158             if (Prims.ContainsKey(e.Prim.LocalID))
159             {
160                 UpdatePrimBlocking(e.Prim);
161             }
162         }
163         #endregion Network messaage handlers
164
165         #region glControl setup and disposal
166         public void SetupGLControl()
167         {
168             RenderingEnabled = false;
169
170             if (glControl != null)
171                 glControl.Dispose();
172             glControl = null;
173
174             GLMode = null;
175
176             try
177             {
178                 if (!UseMultiSampling)
179                 {
180                     GLMode = new OpenTK.Graphics.GraphicsMode(OpenTK.DisplayDevice.Default.BitsPerPixel, 24, 8, 0);
181                 }
182                 else
183                 {
184                     for (int aa = 0; aa <= 4; aa += 2)
185                     {
186                         var testMode = new OpenTK.Graphics.GraphicsMode(OpenTK.DisplayDevice.Default.BitsPerPixel, 24, 8, aa);
187                         if (testMode.Samples == aa)
188                         {
189                             GLMode = testMode;
190                         }
191                     }
192                 }
193             }
194             catch
195             {
196                 GLMode = null;
197             }
198
199
200             try
201             {
202                 if (GLMode == null)
203                 {
204                     // Try default mode
205                     glControl = new OpenTK.GLControl();
206                 }
207                 else
208                 {
209                     glControl = new OpenTK.GLControl(GLMode);
210                 }
211             }
212             catch (Exception ex)
213             {
214                 Logger.Log(ex.Message, Helpers.LogLevel.Warning, Client);
215                 glControl = null;
216             }
217
218             if (glControl == null)
219             {
220                 Logger.Log("Failed to initialize OpenGL control, cannot continue", Helpers.LogLevel.Error, Client);
221                 return;
222             }
223
224             Logger.Log("Initializing OpenGL mode: " + GLMode.ToString(), Helpers.LogLevel.Info);
225
226             glControl.Paint += glControl_Paint;
227             glControl.Resize += glControl_Resize;
228             glControl.MouseDown += glControl_MouseDown;
229             glControl.MouseUp += glControl_MouseUp;
230             glControl.MouseMove += glControl_MouseMove;
231             glControl.MouseWheel += glControl_MouseWheel;
232             glControl.Load += new EventHandler(glControl_Load);
233             glControl.Disposed += new EventHandler(glControl_Disposed);
234             glControl.Dock = DockStyle.Fill;
235             Controls.Add(glControl);
236             glControl.BringToFront();
237         }
238
239         void glControl_Disposed(object sender, EventArgs e)
240         {
241             TextureThreadRunning = false;
242             PendingTextures.Close();
243         }
244
245         void glControl_Load(object sender, EventArgs e)
246         {
247             try
248             {
249                 GL.ShadeModel(ShadingModel.Smooth);
250                 GL.ClearColor(0f, 0f, 0f, 0f);
251
252                 //GL.LightModel(LightModelParameter.LightModelAmbient, new float[] { 0.5f, 0.5f, 0.5f, 1.0f });
253
254                 GL.Enable(EnableCap.Lighting);
255                 GL.Enable(EnableCap.Light0);
256                 GL.Light(LightName.Light0, LightParameter.Ambient, new float[] { 0.5f, 0.5f, 0.5f, 1f });
257                 GL.Light(LightName.Light0, LightParameter.Diffuse, new float[] { 0.3f, 0.3f, 0.3f, 1f });
258                 GL.Light(LightName.Light0, LightParameter.Specular, new float[] { 0.8f, 0.8f, 0.8f, 1.0f });
259                 GL.Light(LightName.Light0, LightParameter.Position, lightPos);
260
261                 GL.ClearDepth(1.0d);
262                 GL.Enable(EnableCap.DepthTest);
263                 GL.Enable(EnableCap.ColorMaterial);
264                 GL.Enable(EnableCap.CullFace);
265                 GL.ColorMaterial(MaterialFace.Front, ColorMaterialParameter.AmbientAndDiffuse);
266                 GL.ColorMaterial(MaterialFace.Front, ColorMaterialParameter.Specular);
267
268                 GL.DepthMask(true);
269                 GL.DepthFunc(DepthFunction.Lequal);
270                 GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);
271                 GL.MatrixMode(MatrixMode.Projection);
272
273                 GL.Enable(EnableCap.Blend);
274                 GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
275                 hasMipmap = GL.GetString(StringName.Extensions).Contains("GL_SGIS_generate_mipmap");
276
277                 RenderingEnabled = true;
278                 // Call the resizing function which sets up the GL drawing window
279                 // and will also invalidate the GL control
280                 glControl_Resize(null, null);
281
282                 glControl.Context.MakeCurrent(null);
283                 TextureThreadContextReady.Reset();
284                 var textureThread = new Thread(() => TextureThread())
285                 {
286                     IsBackground = true,
287                     Name = "TextureLoadingThread"
288                 };
289                 textureThread.Start();
290                 TextureThreadContextReady.WaitOne(1000, false);
291                 glControl.MakeCurrent();
292             }
293             catch (Exception ex)
294             {
295                 RenderingEnabled = false;
296                 Logger.Log("Failed to initialize OpenGL control", Helpers.LogLevel.Warning, Client, ex);
297             }
298         }
299         #endregion glControl setup and disposal
300
301         #region glControl paint and resize events
302         private void glControl_Paint(object sender, PaintEventArgs e)
303         {
304             if (!RenderingEnabled) return;
305
306             Render(false);
307
308             glControl.SwapBuffers();
309         }
310
311         private void glControl_Resize(object sender, EventArgs e)
312         {
313             if (!RenderingEnabled) return;
314             glControl.MakeCurrent();
315
316             GL.ClearColor(0.39f, 0.58f, 0.93f, 1.0f);
317
318             GL.Viewport(0, 0, glControl.Width, glControl.Height);
319
320             GL.PushMatrix();
321             GL.MatrixMode(MatrixMode.Projection);
322             GL.LoadIdentity();
323
324             float dAspRat = (float)glControl.Width / (float)glControl.Height;
325             GluPerspective(50f, dAspRat, 0.1f, 100.0f);
326
327             GL.MatrixMode(MatrixMode.Modelview);
328             GL.PopMatrix();
329         }
330         #endregion glControl paint and resize events
331
332         #region Mouse handling
333         bool dragging = false;
334         int dragX, dragY, downX, downY;
335
336         private void glControl_MouseWheel(object sender, MouseEventArgs e)
337         {
338             int newVal = Utils.Clamp(scrollZoom.Value + e.Delta / 10, scrollZoom.Minimum, scrollZoom.Maximum);
339
340             if (scrollZoom.Value != newVal)
341             {
342                 scrollZoom.Value = newVal;
343                 glControl_Resize(null, null);
344                 SafeInvalidate();
345             }
346         }
347
348         FacetedMesh RightclickedPrim;
349         int RightclickedFaceID;
350
351         private void glControl_MouseDown(object sender, MouseEventArgs e)
352         {
353             if (e.Button == MouseButtons.Left || e.Button == MouseButtons.Middle)
354             {
355                 dragging = true;
356                 downX = dragX = e.X;
357                 downY = dragY = e.Y;
358             }
359             else if (e.Button == MouseButtons.Right)
360             {
361                 if (TryPick(e.X, e.Y, out RightclickedPrim, out RightclickedFaceID))
362                 {
363                     ctxObjects.Show(glControl, e.X, e.Y);
364                 }
365             }
366
367         }
368
369         private void glControl_MouseMove(object sender, MouseEventArgs e)
370         {
371             if (dragging)
372             {
373                 int deltaX = e.X - dragX;
374                 int deltaY = e.Y - dragY;
375
376                 if (e.Button == MouseButtons.Left)
377                 {
378                     if (ModifierKeys == Keys.Control || ModifierKeys == (Keys.Alt | Keys.Control | Keys.Shift))
379                     {
380                         Center.X -= deltaX / 100f;
381                         Center.Z += deltaY / 100f;
382                     }
383
384                     if (ModifierKeys == Keys.Alt)
385                     {
386                         Center.Y -= deltaY / 25f;
387
388                         int newYaw = scrollYaw.Value + deltaX;
389                         if (newYaw < 0) newYaw += 360;
390                         if (newYaw > 360) newYaw -= 360;
391
392                         scrollYaw.Value = newYaw;
393
394                     }
395
396                     if (ModifierKeys == Keys.None || ModifierKeys == (Keys.Alt | Keys.Control))
397                     {
398                         int newRoll = scrollRoll.Value + deltaY;
399                         if (newRoll < 0) newRoll += 360;
400                         if (newRoll > 360) newRoll -= 360;
401
402                         scrollRoll.Value = newRoll;
403
404
405                         int newYaw = scrollYaw.Value + deltaX;
406                         if (newYaw < 0) newYaw += 360;
407                         if (newYaw > 360) newYaw -= 360;
408
409                         scrollYaw.Value = newYaw;
410
411                     }
412                 }
413                 else if (e.Button == MouseButtons.Middle)
414                 {
415                     Center.X -= deltaX / 100f;
416                     Center.Z += deltaY / 100f;
417
418                 }
419
420                 dragX = e.X;
421                 dragY = e.Y;
422
423                 SafeInvalidate();
424             }
425         }
426
427         private void glControl_MouseUp(object sender, MouseEventArgs e)
428         {
429             if (e.Button == MouseButtons.Left)
430             {
431                 dragging = false;
432
433                 if (e.X == downX && e.Y == downY) // click
434                 {
435                     FacetedMesh picked;
436                     int faceID;
437                     if (TryPick(e.X, e.Y, out picked, out faceID))
438                     {
439                         Client.Self.Grab(picked.Prim.LocalID, Vector3.Zero, Vector3.Zero, Vector3.Zero, faceID, Vector3.Zero, Vector3.Zero, Vector3.Zero);
440                         Client.Self.DeGrab(picked.Prim.LocalID);
441                     }
442                 }
443                 SafeInvalidate();
444             }
445         }
446         #endregion Mouse handling
447
448         #region Texture thread
449         bool TextureThreadRunning = true;
450
451         void TextureThread()
452         {
453             OpenTK.INativeWindow window = new OpenTK.NativeWindow();
454             OpenTK.Graphics.IGraphicsContext context = new OpenTK.Graphics.GraphicsContext(GLMode, window.WindowInfo);
455             context.MakeCurrent(window.WindowInfo);
456             TextureThreadContextReady.Set();
457             PendingTextures.Open();
458             Logger.DebugLog("Started Texture Thread");
459
460             while (window.Exists && TextureThreadRunning)
461             {
462                 window.ProcessEvents();
463
464                 TextureLoadItem item = null;
465
466                 if (!PendingTextures.Dequeue(Timeout.Infinite, ref item)) continue;
467
468                 if (TexturesPtrMap.ContainsKey(item.TeFace.TextureID))
469                 {
470                     item.Data.TextureInfo = TexturesPtrMap[item.TeFace.TextureID];
471                     GL.BindTexture(TextureTarget.Texture2D, item.Data.TextureInfo.TexturePointer);
472                     continue;
473                 }
474
475                 if (LoadTexture(item.TeFace.TextureID, ref item.Data.TextureInfo.Texture, false))
476                 {
477                     GL.GenTextures(1, out item.Data.TextureInfo.TexturePointer);
478                     GL.BindTexture(TextureTarget.Texture2D, item.Data.TextureInfo.TexturePointer);
479
480                     Bitmap bitmap = (Bitmap)item.Data.TextureInfo.Texture;
481
482                     bool hasAlpha;
483                     if (item.Data.TextureInfo.Texture.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb)
484                     {
485                         hasAlpha = true;
486                     }
487                     else
488                     {
489                         hasAlpha = false;
490                     }
491                     item.Data.TextureInfo.HasAlpha = hasAlpha;
492
493                     bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
494                     Rectangle rectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
495
496                     BitmapData bitmapData =
497                         bitmap.LockBits(
498                         rectangle,
499                         ImageLockMode.ReadOnly,
500                         hasAlpha ? System.Drawing.Imaging.PixelFormat.Format32bppArgb : System.Drawing.Imaging.PixelFormat.Format24bppRgb);
501
502                     GL.TexImage2D(
503                         TextureTarget.Texture2D,
504                         0,
505                         hasAlpha ? PixelInternalFormat.Rgba : PixelInternalFormat.Rgb8,
506                         bitmap.Width,
507                         bitmap.Height,
508                         0,
509                         hasAlpha ? OpenTK.Graphics.OpenGL.PixelFormat.Bgra : OpenTK.Graphics.OpenGL.PixelFormat.Bgr,
510                         PixelType.UnsignedByte,
511                         bitmapData.Scan0);
512
513                     GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
514                     GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
515                     GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
516                     if (hasMipmap)
517                     {
518                         GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.LinearMipmapLinear);
519                         GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.GenerateMipmap, 1);
520                         GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
521                     }
522                     else
523                     {
524                         GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
525                     }
526
527                     TexturesPtrMap[item.TeFace.TextureID] = item.Data.TextureInfo;
528
529                     bitmap.UnlockBits(bitmapData);
530                     bitmap.Dispose();
531                     item.Data.TextureInfo.Texture = null;
532
533                     GL.Flush();
534                     SafeInvalidate();
535                 }
536             }
537             Logger.DebugLog("Texture thread exited");
538         }
539         #endregion Texture thread
540
541         private void frmPrimWorkshop_Shown(object sender, EventArgs e)
542         {
543             SetupGLControl();
544
545             ThreadPool.QueueUserWorkItem(sync =>
546                 {
547                     if (Client.Network.CurrentSim.ObjectsPrimitives.ContainsKey(RootPrimLocalID))
548                     {
549                         UpdatePrimBlocking(Client.Network.CurrentSim.ObjectsPrimitives[RootPrimLocalID]);
550                         var children = Client.Network.CurrentSim.ObjectsPrimitives.FindAll((Primitive p) => { return p.ParentID == RootPrimLocalID; });
551                         children.ForEach(p => UpdatePrimBlocking(p));
552                     }
553                 }
554             );
555
556         }
557
558         #region Public methods
559         public void SetView(Vector3 center, int roll, int pitch, int yaw, int zoom)
560         {
561             this.Center = center;
562             scrollRoll.Value = roll;
563             scrollPitch.Value = pitch;
564             scrollYaw.Value = yaw;
565             scrollZoom.Value = zoom;
566         }
567         #endregion Public methods
568
569         #region Private methods (the meat)
570         private OpenTK.Vector3 WorldToScreen(OpenTK.Vector3 world)
571         {
572             OpenTK.Vector3 screen;
573             double[] ModelViewMatrix = new double[16];
574             double[] ProjectionMatrix = new double[16];
575             int[] Viewport = new int[4];
576
577             GL.GetInteger(GetPName.Viewport, Viewport);
578             GL.GetDouble(GetPName.ModelviewMatrix, ModelViewMatrix);
579             GL.GetDouble(GetPName.ProjectionMatrix, ProjectionMatrix);
580
581 #pragma warning disable 0618
582             OpenTK.Graphics.Glu.Project(world,
583                 ModelViewMatrix,
584                 ProjectionMatrix,
585                 Viewport,
586                 out screen);
587 #pragma warning restore 0618
588
589             screen.Y = glControl.Height - screen.Y;
590             return screen;
591         }
592
593 #pragma warning disable 0612
594         OpenTK.Graphics.TextPrinter Printer = new OpenTK.Graphics.TextPrinter(OpenTK.Graphics.TextQuality.High);
595 #pragma warning restore 0612
596         private void RenderText()
597         {
598             lock (Prims)
599             {
600                 int primNr = 0;
601                 foreach (FacetedMesh mesh in Prims.Values)
602                 {
603                     primNr++;
604                     Primitive prim = mesh.Prim;
605                     if (!string.IsNullOrEmpty(prim.Text))
606                     {
607                         string text = System.Text.RegularExpressions.Regex.Replace(prim.Text, "(\r?\n)+", "\n");
608                         OpenTK.Vector3 screenPos = OpenTK.Vector3.Zero;
609                         OpenTK.Vector3 primPos = OpenTK.Vector3.Zero;
610
611                         // Is it child prim
612                         FacetedMesh parent = null;
613                         if (Prims.TryGetValue(prim.ParentID, out parent))
614                         {
615                             var newPrimPos = prim.Position * Matrix4.CreateFromQuaternion(parent.Prim.Rotation);
616                             primPos = new OpenTK.Vector3(newPrimPos.X, newPrimPos.Y, newPrimPos.Z);
617                         }
618
619                         primPos.Z += prim.Scale.Z * 0.7f;
620                         screenPos = WorldToScreen(primPos);
621                         Printer.Begin();
622
623                         Color color = Color.FromArgb((int)(prim.TextColor.A * 255), (int)(prim.TextColor.R * 255), (int)(prim.TextColor.G * 255), (int)(prim.TextColor.B * 255));
624
625                         using (Font f = new Font(FontFamily.GenericSansSerif, 10f, FontStyle.Bold))
626                         {
627                             var size = Printer.Measure(text, f);
628                             screenPos.X -= size.BoundingBox.Width / 2;
629                             screenPos.Y -= size.BoundingBox.Height;
630
631                             // Shadow
632                             if (color != Color.Black)
633                             {
634                                 Printer.Print(text, f, Color.Black, new RectangleF(screenPos.X + 1, screenPos.Y + 1, size.BoundingBox.Width, size.BoundingBox.Height), OpenTK.Graphics.TextPrinterOptions.Default, OpenTK.Graphics.TextAlignment.Center);
635                             }
636                             Printer.Print(text, f, color, new RectangleF(screenPos.X, screenPos.Y, size.BoundingBox.Width, size.BoundingBox.Height), OpenTK.Graphics.TextPrinterOptions.Default, OpenTK.Graphics.TextAlignment.Center);
637                         }
638                         Printer.End();
639                     }
640                 }
641             }
642         }
643
644         private void RenderObjects(RenderPass pass)
645         {
646             lock (Prims)
647             {
648                 int primNr = 0;
649                 foreach (FacetedMesh mesh in Prims.Values)
650                 {
651                     primNr++;
652                     Primitive prim = mesh.Prim;
653                     // Individual prim matrix
654                     GL.PushMatrix();
655
656                     if (prim.ParentID == RootPrimLocalID)
657                     {
658                         FacetedMesh parent = null;
659                         if (Prims.TryGetValue(prim.ParentID, out parent))
660                         {
661                             // Apply prim translation and rotation relative to the root prim
662                             GL.MultMatrix(Math3D.CreateRotationMatrix(parent.Prim.Rotation));
663                             //GL.MultMatrixf(Math3D.CreateTranslationMatrix(parent.Prim.Position));
664                         }
665
666                         // Prim roation relative to root
667                         GL.MultMatrix(Math3D.CreateTranslationMatrix(prim.Position));
668                     }
669
670                     // Prim roation
671                     GL.MultMatrix(Math3D.CreateRotationMatrix(prim.Rotation));
672
673                     // Prim scaling
674                     GL.Scale(prim.Scale.X, prim.Scale.Y, prim.Scale.Z);
675
676                     // Draw the prim faces
677                     for (int j = 0; j < mesh.Faces.Count; j++)
678                     {
679                         Primitive.TextureEntryFace teFace = mesh.Prim.Textures.FaceTextures[j];
680                         Face face = mesh.Faces[j];
681                         FaceData data = (FaceData)face.UserData;
682
683                         if (teFace == null)
684                             teFace = mesh.Prim.Textures.DefaultTexture;
685
686                         if (pass != RenderPass.Picking)
687                         {
688                             bool belongToAlphaPass = (teFace.RGBA.A < 0.99) || data.TextureInfo.HasAlpha;
689
690                             if (belongToAlphaPass && pass != RenderPass.Alpha) continue;
691                             if (!belongToAlphaPass && pass == RenderPass.Alpha) continue;
692
693                             // Don't render transparent faces
694                             if (teFace.RGBA.A <= 0.01f) continue;
695
696                             switch (teFace.Shiny)
697                             {
698                                 case Shininess.High:
699                                     GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 94f);
700                                     break;
701
702                                 case Shininess.Medium:
703                                     GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 64f);
704                                     break;
705
706                                 case Shininess.Low:
707                                     GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 24f);
708                                     break;
709
710
711                                 case Shininess.None:
712                                 default:
713                                     GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 0f);
714                                     break;
715                             }
716
717                             var faceColor = new float[] { teFace.RGBA.R, teFace.RGBA.G, teFace.RGBA.B, teFace.RGBA.A };
718
719                             GL.Color4(faceColor);
720                             GL.Material(MaterialFace.Front, MaterialParameter.AmbientAndDiffuse, faceColor);
721                             GL.Material(MaterialFace.Front, MaterialParameter.Specular, faceColor);
722
723                             if (data.TextureInfo.TexturePointer != 0)
724                             {
725                                 GL.Enable(EnableCap.Texture2D);
726                             }
727                             else
728                             {
729                                 GL.Disable(EnableCap.Texture2D);
730                             }
731
732                             // Bind the texture
733                             GL.BindTexture(TextureTarget.Texture2D, data.TextureInfo.TexturePointer);
734                         }
735                         else
736                         {
737                             data.PickingID = primNr;
738                             var primNrBytes = Utils.Int16ToBytes((short)primNr);
739                             var faceColor = new byte[] { primNrBytes[0], primNrBytes[1], (byte)j, 255 };
740
741                             GL.Color4(faceColor);
742                         }
743
744                         GL.TexCoordPointer(2, TexCoordPointerType.Float, 0, data.TexCoords);
745                         GL.VertexPointer(3, VertexPointerType.Float, 0, data.Vertices);
746                         GL.NormalPointer(NormalPointerType.Float, 0, data.Normals);
747                         GL.DrawElements(BeginMode.Triangles, data.Indices.Length, DrawElementsType.UnsignedShort, data.Indices);
748
749                     }
750
751                     // Pop the prim matrix
752                     GL.PopMatrix();
753                 }
754             }
755         }
756
757         private void Render(bool picking)
758         {
759             glControl.MakeCurrent();
760             if (picking)
761             {
762                 GL.ClearColor(1f, 1f, 1f, 1f);
763             }
764             else
765             {
766                 GL.ClearColor(0.39f, 0.58f, 0.93f, 1.0f);
767             }
768
769             GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
770             GL.LoadIdentity();
771
772             // Setup wireframe or solid fill drawing mode
773             if (Wireframe && !picking)
774             {
775                 GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);
776             }
777             else
778             {
779                 GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);
780             }
781
782             var mLookAt = OpenTK.Matrix4d.LookAt(
783                     Center.X, (double)scrollZoom.Value * 0.1d + Center.Y, Center.Z,
784                     Center.X, Center.Y, Center.Z,
785                     0d, 0d, 1d);
786             GL.MultMatrix(ref mLookAt);
787
788             //OpenTK.Graphics.Glu.LookAt(
789             //        Center.X, (double)scrollZoom.Value * 0.1d + Center.Y, Center.Z,
790             //        Center.X, Center.Y, Center.Z,
791             //        0d, 0d, 1d);
792
793             //GL.Light(LightName.Light0, LightParameter.Position, lightPos);
794
795             // Push the world matrix
796             GL.PushMatrix();
797
798             GL.EnableClientState(ArrayCap.VertexArray);
799             GL.EnableClientState(ArrayCap.TextureCoordArray);
800             GL.EnableClientState(ArrayCap.NormalArray);
801
802             // World rotations
803             GL.Rotate((float)scrollRoll.Value, 1f, 0f, 0f);
804             GL.Rotate((float)scrollPitch.Value, 0f, 1f, 0f);
805             GL.Rotate((float)scrollYaw.Value, 0f, 0f, 1f);
806
807             if (picking)
808             {
809                 RenderObjects(RenderPass.Picking);
810             }
811             else
812             {
813                 RenderObjects(RenderPass.Simple);
814                 RenderObjects(RenderPass.Alpha);
815                 RenderText();
816             }
817
818             // Pop the world matrix
819             GL.PopMatrix();
820
821             GL.DisableClientState(ArrayCap.TextureCoordArray);
822             GL.DisableClientState(ArrayCap.VertexArray);
823             GL.DisableClientState(ArrayCap.NormalArray);
824
825             GL.Flush();
826         }
827
828         private void GluPerspective(float fovy, float aspect, float zNear, float zFar)
829         {
830             float fH = (float)Math.Tan(fovy / 360 * (float)Math.PI) * zNear;
831             float fW = fH * aspect;
832             GL.Frustum(-fW, fW, -fH, fH, zNear, zFar);
833         }
834
835         private bool TryPick(int x, int y, out FacetedMesh picked, out int faceID)
836         {
837             // Save old attributes
838             GL.PushAttrib(AttribMask.AllAttribBits);
839
840             // Disable some attributes to make the objects flat / solid color when they are drawn
841             GL.Disable(EnableCap.Fog);
842             GL.Disable(EnableCap.Texture2D);
843             GL.Disable(EnableCap.Dither);
844             GL.Disable(EnableCap.Lighting);
845             GL.Disable(EnableCap.LineStipple);
846             GL.Disable(EnableCap.PolygonStipple);
847             GL.Disable(EnableCap.CullFace);
848             GL.Disable(EnableCap.Blend);
849             GL.Disable(EnableCap.AlphaTest);
850
851             Render(true);
852
853             byte[] color = new byte[4];
854             GL.ReadPixels(x, glControl.Height - y, 1, 1, OpenTK.Graphics.OpenGL.PixelFormat.Rgba, PixelType.UnsignedByte, color);
855
856             GL.PopAttrib();
857
858             int primID = Utils.BytesToUInt16(color, 0);
859             faceID = color[2];
860
861             picked = null;
862
863             lock (Prims)
864             {
865                 foreach (var mesh in Prims.Values)
866                 {
867                     foreach (var face in mesh.Faces)
868                     {
869                         if (((FaceData)face.UserData).PickingID == primID)
870                         {
871                             picked = mesh;
872                             break;
873                         }
874                     }
875
876                     if (picked != null) break;
877                 }
878             }
879
880             return picked != null;
881         }
882
883
884         private void UpdatePrimBlocking(Primitive prim)
885         {
886
887             FacetedMesh mesh = null;
888             FacetedMesh existingMesh = null;
889
890             lock (Prims)
891             {
892                 if (Prims.ContainsKey(prim.LocalID))
893                 {
894                     existingMesh = Prims[prim.LocalID];
895                 }
896             }
897
898             if (prim.Textures == null)
899                 return;
900
901             try
902             {
903                 if (prim.Sculpt != null && prim.Sculpt.SculptTexture != UUID.Zero)
904                 {
905                     if (prim.Sculpt.Type != SculptType.Mesh)
906                     { // Regular sculptie
907                         Image img = null;
908                         if (!LoadTexture(prim.Sculpt.SculptTexture, ref img, true))
909                             return;
910                         mesh = renderer.GenerateFacetedSculptMesh(prim, (Bitmap)img, DetailLevel.Highest);
911                     }
912                     else
913                     { // Mesh
914                         AutoResetEvent gotMesh = new AutoResetEvent(false);
915                         bool meshSuccess = false;
916
917                         Client.Assets.RequestMesh(prim.Sculpt.SculptTexture, (success, meshAsset) =>
918                             {
919                                 if (!success || !FacetedMesh.TryDecodeFromAsset(prim, meshAsset, DetailLevel.Highest, out mesh))
920                                 {
921                                     Logger.Log("Failed to fetch or decode the mesh asset", Helpers.LogLevel.Warning, Client);
922                                 }
923                                 else
924                                 {
925                                     meshSuccess = true;
926                                 }
927                                 gotMesh.Set();
928                             });
929
930                         if (!gotMesh.WaitOne(20 * 1000, false)) return;
931                         if (!meshSuccess) return;
932                     }
933                 }
934                 else
935                 {
936                     mesh = renderer.GenerateFacetedMesh(prim, DetailLevel.Highest);
937                 }
938             }
939             catch
940             {
941                 return;
942             }
943
944             // Create a FaceData struct for each face that stores the 3D data
945             // in a OpenGL friendly format
946             for (int j = 0; j < mesh.Faces.Count; j++)
947             {
948                 Face face = mesh.Faces[j];
949                 FaceData data = new FaceData();
950
951                 // Vertices for this face
952                 data.Vertices = new float[face.Vertices.Count * 3];
953                 data.Normals = new float[face.Vertices.Count * 3];
954                 for (int k = 0; k < face.Vertices.Count; k++)
955                 {
956                     data.Vertices[k * 3 + 0] = face.Vertices[k].Position.X;
957                     data.Vertices[k * 3 + 1] = face.Vertices[k].Position.Y;
958                     data.Vertices[k * 3 + 2] = face.Vertices[k].Position.Z;
959
960                     data.Normals[k * 3 + 0] = face.Vertices[k].Normal.X;
961                     data.Normals[k * 3 + 1] = face.Vertices[k].Normal.Y;
962                     data.Normals[k * 3 + 2] = face.Vertices[k].Normal.Z;
963                 }
964
965                 // Indices for this face
966                 data.Indices = face.Indices.ToArray();
967
968                 // Texture transform for this face
969                 Primitive.TextureEntryFace teFace = prim.Textures.GetFace((uint)j);
970                 renderer.TransformTexCoords(face.Vertices, face.Center, teFace);
971
972                 // Texcoords for this face
973                 data.TexCoords = new float[face.Vertices.Count * 2];
974                 for (int k = 0; k < face.Vertices.Count; k++)
975                 {
976                     data.TexCoords[k * 2 + 0] = face.Vertices[k].TexCoord.X;
977                     data.TexCoords[k * 2 + 1] = face.Vertices[k].TexCoord.Y;
978                 }
979
980                 // Set the UserData for this face to our FaceData struct
981                 face.UserData = data;
982                 mesh.Faces[j] = face;
983
984
985                 if (existingMesh != null &&
986                     j < existingMesh.Faces.Count &&
987                     existingMesh.Faces[j].TextureFace.TextureID == teFace.TextureID &&
988                     ((FaceData)existingMesh.Faces[j].UserData).TextureInfo.TexturePointer != 0
989                     )
990                 {
991                     FaceData existingData = (FaceData)existingMesh.Faces[j].UserData;
992                     data.TextureInfo.TexturePointer = existingData.TextureInfo.TexturePointer;
993                 }
994                 else
995                 {
996
997                     var textureItem = new TextureLoadItem()
998                     {
999                         Data = data,
1000                         Prim = prim,
1001                         TeFace = teFace
1002                     };
1003
1004                     PendingTextures.Enqueue(textureItem);
1005                 }
1006
1007             }
1008
1009             lock (Prims)
1010             {
1011                 Prims[prim.LocalID] = mesh;
1012             }
1013             SafeInvalidate();
1014         }
1015
1016         private bool LoadTexture(UUID textureID, ref Image texture, bool removeAlpha)
1017         {
1018             ManualResetEvent gotImage = new ManualResetEvent(false);
1019             Image img = null;
1020
1021             try
1022             {
1023                 gotImage.Reset();
1024                 instance.Client.Assets.RequestImage(textureID, (TextureRequestState state, AssetTexture assetTexture) =>
1025                     {
1026                         if (state == TextureRequestState.Finished)
1027                         {
1028                             ManagedImage mi;
1029                             OpenJPEG.DecodeToImage(assetTexture.AssetData, out mi);
1030
1031                             if (removeAlpha)
1032                             {
1033                                 if ((mi.Channels & ManagedImage.ImageChannels.Alpha) != 0)
1034                                 {
1035                                     mi.ConvertChannels(mi.Channels & ~ManagedImage.ImageChannels.Alpha);
1036                                 }
1037                             }
1038
1039                             img = LoadTGAClass.LoadTGA(new MemoryStream(mi.ExportTGA()));
1040                         }
1041                         gotImage.Set();
1042                     }
1043                 );
1044                 gotImage.WaitOne(30 * 1000, false);
1045                 if (img != null)
1046                 {
1047                     texture = img;
1048                     return true;
1049                 }
1050                 return false;
1051             }
1052             catch (Exception e)
1053             {
1054                 Logger.Log(e.Message, Helpers.LogLevel.Error, instance.Client, e);
1055                 return false;
1056             }
1057         }
1058
1059         private void SafeInvalidate()
1060         {
1061             if (glControl == null || !RenderingEnabled) return;
1062
1063             if (InvokeRequired)
1064             {
1065                 if (!instance.MonoRuntime || IsHandleCreated)
1066                 {
1067                     BeginInvoke(new MethodInvoker(() => SafeInvalidate()));
1068                 }
1069                 return;
1070             }
1071
1072             glControl.Invalidate();
1073         }
1074         #endregion Private methods (the meat)
1075
1076         #region Form controls handlers
1077         private void scroll_ValueChanged(object sender, EventArgs e)
1078         {
1079             SafeInvalidate();
1080         }
1081
1082         private void scrollZoom_ValueChanged(object sender, EventArgs e)
1083         {
1084             glControl_Resize(null, null);
1085             SafeInvalidate();
1086         }
1087
1088         private void chkWireFrame_CheckedChanged(object sender, EventArgs e)
1089         {
1090             Wireframe = chkWireFrame.Checked;
1091             SafeInvalidate();
1092         }
1093
1094         private void btnReset_Click(object sender, EventArgs e)
1095         {
1096             scrollYaw.Value = 90;
1097             scrollPitch.Value = 0;
1098             scrollRoll.Value = 0;
1099             scrollZoom.Value = -30;
1100             Center = Vector3.Zero;
1101
1102             SafeInvalidate();
1103         }
1104
1105         private void oBJToolStripMenuItem_Click(object sender, EventArgs e)
1106         {
1107             SaveFileDialog dialog = new SaveFileDialog();
1108             dialog.Filter = "OBJ files (*.obj)|*.obj";
1109
1110             if (dialog.ShowDialog() == DialogResult.OK)
1111             {
1112                 if (!MeshToOBJ.MeshesToOBJ(Prims, dialog.FileName))
1113                 {
1114                     MessageBox.Show("Failed to save file " + dialog.FileName +
1115                         ". Ensure that you have permission to write to that file and it is currently not in use");
1116                 }
1117             }
1118         }
1119
1120         private void cbAA_CheckedChanged(object sender, EventArgs e)
1121         {
1122             instance.GlobalSettings["use_multi_sampling"] = UseMultiSampling = cbAA.Checked;
1123             SetupGLControl();
1124         }
1125
1126         #endregion Form controls handlers
1127
1128         #region Context menu
1129         private void ctxObjects_Opening(object sender, System.ComponentModel.CancelEventArgs e)
1130         {
1131             if (instance.State.IsSitting)
1132             {
1133                 sitToolStripMenuItem.Text = "Stand up";
1134             }
1135             else if (RightclickedPrim.Prim.Properties != null
1136                 && !string.IsNullOrEmpty(RightclickedPrim.Prim.Properties.SitName))
1137             {
1138                 sitToolStripMenuItem.Text = RightclickedPrim.Prim.Properties.SitName;
1139             }
1140             else
1141             {
1142                 sitToolStripMenuItem.Text = "Sit";
1143             }
1144
1145             if (RightclickedPrim.Prim.Properties != null
1146                 && !string.IsNullOrEmpty(RightclickedPrim.Prim.Properties.TouchName))
1147             {
1148                 touchToolStripMenuItem.Text = RightclickedPrim.Prim.Properties.TouchName;
1149             }
1150             else
1151             {
1152                 touchToolStripMenuItem.Text = "Touch";
1153             }
1154         }
1155
1156         private void touchToolStripMenuItem_Click(object sender, EventArgs e)
1157         {
1158
1159             Client.Self.Grab(RightclickedPrim.Prim.LocalID, Vector3.Zero, Vector3.Zero, Vector3.Zero, RightclickedFaceID, Vector3.Zero, Vector3.Zero, Vector3.Zero);
1160             Thread.Sleep(100);
1161             Client.Self.DeGrab(RightclickedPrim.Prim.LocalID);
1162         }
1163
1164         private void sitToolStripMenuItem_Click(object sender, EventArgs e)
1165         {
1166             if (!instance.State.IsSitting)
1167             {
1168                 instance.State.SetSitting(true, RightclickedPrim.Prim.ID);
1169             }
1170             else
1171             {
1172                 instance.State.SetSitting(false, UUID.Zero);
1173             }
1174         }
1175
1176         private void takeToolStripMenuItem_Click(object sender, EventArgs e)
1177         {
1178             instance.MediaManager.PlayUISound(UISounds.ObjectDelete);
1179             Client.Inventory.RequestDeRezToInventory(RightclickedPrim.Prim.LocalID);
1180             Close();
1181         }
1182
1183         private void returnToolStripMenuItem_Click(object sender, EventArgs e)
1184         {
1185             instance.MediaManager.PlayUISound(UISounds.ObjectDelete);
1186             Client.Inventory.RequestDeRezToInventory(RightclickedPrim.Prim.LocalID, DeRezDestination.ReturnToOwner, UUID.Zero, UUID.Random());
1187             Close();
1188         }
1189
1190         private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
1191         {
1192             if (RightclickedPrim.Prim.Properties != null && RightclickedPrim.Prim.Properties.OwnerID != Client.Self.AgentID)
1193                 returnToolStripMenuItem_Click(sender, e);
1194             else
1195             {
1196                 instance.MediaManager.PlayUISound(UISounds.ObjectDelete);
1197                 Client.Inventory.RequestDeRezToInventory(RightclickedPrim.Prim.LocalID, DeRezDestination.AgentInventoryTake, Client.Inventory.FindFolderForType(AssetType.TrashFolder), UUID.Random());
1198             }
1199             Close();
1200         }
1201         #endregion Context menu
1202
1203
1204
1205     }
1206 }