OSDN Git Service

Simple interpolation of object position.
[radegast/radegast.git] / Radegast / GUI / Rendering / Rendering.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 written 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 #region Usings\r
33 using System;\r
34 using System.Collections.Generic;\r
35 using System.Drawing;\r
36 using System.Drawing.Imaging;\r
37 using System.IO;\r
38 using System.Windows.Forms;\r
39 using System.Text;\r
40 using System.Threading;\r
41 using System.Linq;\r
42 using OpenTK.Graphics.OpenGL;\r
43 using OpenMetaverse;\r
44 using OpenMetaverse.Rendering;\r
45 using OpenMetaverse.Assets;\r
46 using OpenMetaverse.Imaging;\r
47 using OpenMetaverse.StructuredData;\r
48 #endregion Usings\r
49 \r
50 namespace Radegast.Rendering\r
51 {\r
52 \r
53     public partial class SceneWindow : RadegastForm\r
54     {\r
55         #region Public fields\r
56         /// <summary>\r
57         /// The OpenGL surface\r
58         /// </summary>\r
59         public OpenTK.GLControl glControl = null;\r
60 \r
61         /// <summary>\r
62         /// Use multi sampling (anti aliasing)\r
63         /// </summary>\r
64         public bool UseMultiSampling = true;\r
65 \r
66         /// <summary>\r
67         /// Is rendering engine ready and enabled\r
68         /// </summary>\r
69         public bool RenderingEnabled = false;\r
70 \r
71         /// <summary>\r
72         /// Rednder in wireframe mode\r
73         /// </summary>\r
74         public bool Wireframe = false;\r
75 \r
76         /// <summary>\r
77         /// Object from up to this distance from us will be rendered\r
78         /// </summary>\r
79         public float DrawDistance\r
80         {\r
81             get { return drawDistance; }\r
82             set\r
83             {\r
84                 drawDistance = value;\r
85                 drawDistanceSquared = value * value;\r
86                 if (Camera != null)\r
87                     Camera.Far = value;\r
88             }\r
89         }\r
90 \r
91         /// <summary>\r
92         /// List of prims in the scene\r
93         /// </summary>\r
94         Dictionary<uint, RenderPrimitive> Prims = new Dictionary<uint, RenderPrimitive>();\r
95         List<SceneObject> SortedObjects;\r
96         Dictionary<uint, RenderAvatar> Avatars = new Dictionary<uint, RenderAvatar>();\r
97 \r
98         /// <summary>\r
99         /// Render prims\r
100         /// </summary>\r
101         public bool PrimitiveRenderingEnabled = true;\r
102 \r
103         /// <summary>\r
104         /// Render avatars\r
105         /// </summary>\r
106         public bool AvatarRenderingEnabled = true;\r
107 \r
108         /// <summary>\r
109         /// Show avatar skeloton\r
110         /// </summary>\r
111         public bool RenderAvatarSkeleton = false;\r
112 \r
113         #endregion Public fields\r
114 \r
115         #region Private fields\r
116 \r
117         Camera Camera;\r
118         Dictionary<UUID, TextureInfo> TexturesPtrMap = new Dictionary<UUID, TextureInfo>();\r
119         RadegastInstance instance;\r
120         MeshmerizerR renderer;\r
121         OpenTK.Graphics.GraphicsMode GLMode = null;\r
122         AutoResetEvent TextureThreadContextReady = new AutoResetEvent(false);\r
123         BlockingQueue<TextureLoadItem> PendingTextures = new BlockingQueue<TextureLoadItem>();\r
124 \r
125         bool hasMipmap;\r
126         Font HoverTextFont = new Font(FontFamily.GenericSansSerif, 9f, FontStyle.Regular);\r
127         Font AvatarTagFont = new Font(FontFamily.GenericSansSerif, 10f, FontStyle.Bold);\r
128         Dictionary<UUID, Bitmap> sculptCache = new Dictionary<UUID, Bitmap>();\r
129         OpenTK.Matrix4 ModelMatrix;\r
130         OpenTK.Matrix4 ProjectionMatrix;\r
131         int[] Viewport = new int[4];\r
132         bool useVBO = true;\r
133         System.Diagnostics.Stopwatch renderTimer;\r
134         double lastFrameTime = 0d;\r
135         double advTimerTick = 0d;\r
136         float minLODFactor = 0.0001f;\r
137 \r
138         float[] lightPos = new float[] { 128f, 128f, 5000f, 0f };\r
139         float ambient = 0.26f;\r
140         float difuse = 0.27f;\r
141         float specular = 0.20f;\r
142         OpenTK.Vector4 ambientColor;\r
143         OpenTK.Vector4 difuseColor;\r
144         OpenTK.Vector4 specularColor;\r
145         float drawDistance = 48f;\r
146         float drawDistanceSquared = 48f * 48f;\r
147 \r
148         #endregion Private fields\r
149 \r
150         #region Construction and disposal\r
151         public SceneWindow(RadegastInstance instance)\r
152             : base(instance)\r
153         {\r
154             InitializeComponent();\r
155             Disposed += new EventHandler(frmPrimWorkshop_Disposed);\r
156             AutoSavePosition = true;\r
157             UseMultiSampling = cbAA.Checked = instance.GlobalSettings["use_multi_sampling"];\r
158             cbAA.CheckedChanged += cbAA_CheckedChanged;\r
159 \r
160             this.instance = instance;\r
161 \r
162             renderer = new MeshmerizerR();\r
163             renderTimer = new System.Diagnostics.Stopwatch();\r
164             renderTimer.Start();\r
165 \r
166             // Camera initial setting\r
167             Camera = new Camera();\r
168             InitCamera();\r
169 \r
170             GLAvatar.loadlindenmeshes2("avatar_lad.xml");\r
171 \r
172             foreach (VisualParamEx vpe in VisualParamEx.morphParams.Values)\r
173             {\r
174                 comboBox_morph.Items.Add(vpe.Name);\r
175             }\r
176 \r
177             foreach (VisualParamEx vpe in VisualParamEx.drivenParams.Values)\r
178             {\r
179                 comboBox_driver.Items.Add(vpe.Name);\r
180             }\r
181 \r
182             tbDrawDistance.Value = (int)DrawDistance;\r
183             lblDrawDistance.Text = string.Format("Draw distance: {0}", tbDrawDistance.Value);\r
184 \r
185             Client.Objects.TerseObjectUpdate += new EventHandler<TerseObjectUpdateEventArgs>(Objects_TerseObjectUpdate);\r
186             Client.Objects.ObjectUpdate += new EventHandler<PrimEventArgs>(Objects_ObjectUpdate);\r
187             Client.Objects.ObjectDataBlockUpdate += new EventHandler<ObjectDataBlockUpdateEventArgs>(Objects_ObjectDataBlockUpdate);\r
188             Client.Objects.KillObject += new EventHandler<KillObjectEventArgs>(Objects_KillObject);\r
189             Client.Network.SimChanged += new EventHandler<SimChangedEventArgs>(Network_SimChanged);\r
190             Client.Self.TeleportProgress += new EventHandler<TeleportEventArgs>(Self_TeleportProgress);\r
191             Client.Terrain.LandPatchReceived += new EventHandler<LandPatchReceivedEventArgs>(Terrain_LandPatchReceived);\r
192             //Client.Avatars.AvatarAnimation += new EventHandler<AvatarAnimationEventArgs>(AvatarAnimationChanged);\r
193             Client.Avatars.AvatarAppearance += new EventHandler<AvatarAppearanceEventArgs>(Avatars_AvatarAppearance);\r
194             Instance.Netcom.ClientDisconnected += new EventHandler<DisconnectedEventArgs>(Netcom_ClientDisconnected);\r
195             Application.Idle += new EventHandler(Application_Idle);\r
196         }\r
197 \r
198 \r
199         void frmPrimWorkshop_Disposed(object sender, EventArgs e)\r
200         {\r
201             RenderingEnabled = false;\r
202             Application.Idle -= new EventHandler(Application_Idle);\r
203 \r
204             PendingTextures.Close();\r
205 \r
206             Client.Objects.TerseObjectUpdate -= new EventHandler<TerseObjectUpdateEventArgs>(Objects_TerseObjectUpdate);\r
207             Client.Objects.ObjectUpdate -= new EventHandler<PrimEventArgs>(Objects_ObjectUpdate);\r
208             Client.Objects.ObjectDataBlockUpdate -= new EventHandler<ObjectDataBlockUpdateEventArgs>(Objects_ObjectDataBlockUpdate);\r
209             Client.Objects.KillObject -= new EventHandler<KillObjectEventArgs>(Objects_KillObject);\r
210             Client.Network.SimChanged -= new EventHandler<SimChangedEventArgs>(Network_SimChanged);\r
211             Client.Self.TeleportProgress -= new EventHandler<TeleportEventArgs>(Self_TeleportProgress);\r
212             Client.Terrain.LandPatchReceived -= new EventHandler<LandPatchReceivedEventArgs>(Terrain_LandPatchReceived);\r
213             //Client.Avatars.AvatarAnimation -= new EventHandler<AvatarAnimationEventArgs>(AvatarAnimationChanged);\r
214             Client.Avatars.AvatarAppearance -= new EventHandler<AvatarAppearanceEventArgs>(Avatars_AvatarAppearance);\r
215 \r
216             if (instance.Netcom != null)\r
217             {\r
218                 Instance.Netcom.ClientDisconnected -= new EventHandler<DisconnectedEventArgs>(Netcom_ClientDisconnected);\r
219             }\r
220 \r
221             if (glControl != null)\r
222             {\r
223                 glControl.Dispose();\r
224             }\r
225             glControl = null;\r
226 \r
227             lock (sculptCache)\r
228             {\r
229                 foreach (var img in sculptCache.Values)\r
230                     img.Dispose();\r
231                 sculptCache.Clear();\r
232             }\r
233 \r
234             lock (Prims) Prims.Clear();\r
235             lock (Avatars) Avatars.Clear();\r
236 \r
237             TexturesPtrMap.Clear();\r
238             GC.Collect();\r
239         }\r
240 \r
241         void Application_Idle(object sender, EventArgs e)\r
242         {\r
243             if (glControl != null && !glControl.IsDisposed && RenderingEnabled)\r
244             {\r
245                 try\r
246                 {\r
247                     while (glControl != null && glControl.IsIdle)\r
248                     {\r
249                         MainRenderLoop();\r
250                         if (instance.MonoRuntime)\r
251                         {\r
252                             Application.DoEvents();\r
253                         }\r
254                     }\r
255                 }\r
256                 catch (ObjectDisposedException)\r
257                 { }\r
258             }\r
259         }\r
260         #endregion Construction and disposal\r
261 \r
262         #region Network messaage handlers\r
263         void Terrain_LandPatchReceived(object sender, LandPatchReceivedEventArgs e)\r
264         {\r
265             if (e.Simulator.Handle == Client.Network.CurrentSim.Handle)\r
266             {\r
267                 TerrainModified = true;\r
268             }\r
269         }\r
270 \r
271         void Netcom_ClientDisconnected(object sender, DisconnectedEventArgs e)\r
272         {\r
273             if (InvokeRequired)\r
274             {\r
275                 if (IsHandleCreated || !instance.MonoRuntime)\r
276                 {\r
277                     BeginInvoke(new MethodInvoker(() => Netcom_ClientDisconnected(sender, e)));\r
278                 }\r
279                 return;\r
280             }\r
281 \r
282             Dispose();\r
283         }\r
284 \r
285         void Self_TeleportProgress(object sender, TeleportEventArgs e)\r
286         {\r
287             switch (e.Status)\r
288             {\r
289                 case TeleportStatus.Progress:\r
290                 case TeleportStatus.Start:\r
291                     RenderingEnabled = false;\r
292                     break;\r
293 \r
294                 case TeleportStatus.Cancelled:\r
295                 case TeleportStatus.Failed:\r
296                     RenderingEnabled = true;\r
297                     break;\r
298 \r
299                 case TeleportStatus.Finished:\r
300                     ThreadPool.QueueUserWorkItem(sync =>\r
301                     {\r
302                         Thread.Sleep(3000);\r
303                         InitCamera();\r
304                         LoadCurrentPrims();\r
305                         RenderingEnabled = true;\r
306                     });\r
307                     break;\r
308             }\r
309         }\r
310 \r
311         void Network_SimChanged(object sender, SimChangedEventArgs e)\r
312         {\r
313             ResetTerrain();\r
314             lock (sculptCache)\r
315             {\r
316                 foreach (var img in sculptCache.Values)\r
317                     img.Dispose();\r
318                 sculptCache.Clear();\r
319             }\r
320             lock (Prims) Prims.Clear();\r
321         }\r
322 \r
323         void Objects_KillObject(object sender, KillObjectEventArgs e)\r
324         {\r
325             if (e.Simulator.Handle != Client.Network.CurrentSim.Handle) return;\r
326             // TODO: there should be really cleanup of resources when removing prims and avatars\r
327             lock (Prims) Prims.Remove(e.ObjectLocalID);\r
328             lock (Avatars) Avatars.Remove(e.ObjectLocalID);\r
329         }\r
330 \r
331         void Objects_TerseObjectUpdate(object sender, TerseObjectUpdateEventArgs e)\r
332         {\r
333             if (e.Simulator.Handle != Client.Network.CurrentSim.Handle) return;\r
334             UpdatePrimBlocking(e.Prim);\r
335         }\r
336 \r
337         void Objects_ObjectUpdate(object sender, PrimEventArgs e)\r
338         {\r
339             if (e.Simulator.Handle != Client.Network.CurrentSim.Handle) return;\r
340             UpdatePrimBlocking(e.Prim);\r
341         }\r
342 \r
343         void Objects_ObjectDataBlockUpdate(object sender, ObjectDataBlockUpdateEventArgs e)\r
344         {\r
345             if (e.Simulator.Handle != Client.Network.CurrentSim.Handle) return;\r
346             UpdatePrimBlocking(e.Prim);\r
347         }\r
348 \r
349         void AvatarAnimationChanged(object sender, AvatarAnimationEventArgs e)\r
350         {\r
351 \r
352             // We don't currently have UUID -> RenderAvatar mapping so we need to walk the list\r
353             foreach (RenderAvatar av in Avatars.Values)\r
354             {\r
355                 if (av.avatar.ID == e.AvatarID)\r
356                 {\r
357                     foreach (Animation anim in e.Animations)\r
358                     {\r
359                         Client.Assets.RequestAsset(anim.AnimationID, AssetType.Animation, false, animRecievedCallback);\r
360                         av.animlist.Add(anim.AnimationID, anim);\r
361                     }\r
362                     break;\r
363                 }\r
364             }\r
365         }\r
366 \r
367         void animRecievedCallback(AssetDownload transfer, Asset asset)\r
368         {\r
369             if (transfer.Success)\r
370             {\r
371                 BinBVHAnimationReader b = new BinBVHAnimationReader(asset.AssetData);\r
372 \r
373             }\r
374         }\r
375 \r
376         void Avatars_AvatarAppearance(object sender, AvatarAppearanceEventArgs e)\r
377         {\r
378             // We don't currently have UUID -> RenderAvatar mapping so we need to walk the list\r
379             foreach (RenderAvatar av in Avatars.Values)\r
380             {\r
381                 if (av.avatar.ID == e.AvatarID)\r
382                 {\r
383                     av.glavatar.morph(av.avatar);\r
384                 }\r
385             }\r
386         }\r
387 \r
388 \r
389         #endregion Network messaage handlers\r
390 \r
391         #region glControl setup and disposal\r
392         public void SetupGLControl()\r
393         {\r
394             RenderingEnabled = false;\r
395 \r
396             if (glControl != null)\r
397                 glControl.Dispose();\r
398             glControl = null;\r
399 \r
400             GLMode = null;\r
401 \r
402             try\r
403             {\r
404                 if (!UseMultiSampling)\r
405                 {\r
406                     GLMode = new OpenTK.Graphics.GraphicsMode(OpenTK.DisplayDevice.Default.BitsPerPixel, 24, 8, 0);\r
407                 }\r
408                 else\r
409                 {\r
410                     for (int aa = 0; aa <= 4; aa += 2)\r
411                     {\r
412                         var testMode = new OpenTK.Graphics.GraphicsMode(OpenTK.DisplayDevice.Default.BitsPerPixel, 24, 8, aa);\r
413                         if (testMode.Samples == aa)\r
414                         {\r
415                             GLMode = testMode;\r
416                         }\r
417                     }\r
418                 }\r
419             }\r
420             catch\r
421             {\r
422                 GLMode = null;\r
423             }\r
424 \r
425 \r
426             try\r
427             {\r
428                 if (GLMode == null)\r
429                 {\r
430                     // Try default mode\r
431                     glControl = new OpenTK.GLControl();\r
432                 }\r
433                 else\r
434                 {\r
435                     glControl = new OpenTK.GLControl(GLMode);\r
436                 }\r
437             }\r
438             catch (Exception ex)\r
439             {\r
440                 Logger.Log(ex.Message, Helpers.LogLevel.Warning, Client);\r
441                 glControl = null;\r
442             }\r
443 \r
444             if (glControl == null)\r
445             {\r
446                 Logger.Log("Failed to initialize OpenGL control, cannot continue", Helpers.LogLevel.Error, Client);\r
447                 return;\r
448             }\r
449 \r
450             Logger.Log("Initializing OpenGL mode: " + GLMode.ToString(), Helpers.LogLevel.Info);\r
451 \r
452             glControl.Paint += glControl_Paint;\r
453             glControl.Resize += glControl_Resize;\r
454             glControl.MouseDown += glControl_MouseDown;\r
455             glControl.MouseUp += glControl_MouseUp;\r
456             glControl.MouseMove += glControl_MouseMove;\r
457             glControl.MouseWheel += glControl_MouseWheel;\r
458             glControl.Load += new EventHandler(glControl_Load);\r
459             glControl.Disposed += new EventHandler(glControl_Disposed);\r
460             glControl.Dock = DockStyle.Fill;\r
461             glControl.VSync = false;\r
462             Controls.Add(glControl);\r
463             glControl.BringToFront();\r
464         }\r
465 \r
466         void glControl_Disposed(object sender, EventArgs e)\r
467         {\r
468             TextureThreadRunning = false;\r
469             PendingTextures.Close();\r
470             glControl.Paint -= glControl_Paint;\r
471             glControl.Resize -= glControl_Resize;\r
472             glControl.MouseDown -= glControl_MouseDown;\r
473             glControl.MouseUp -= glControl_MouseUp;\r
474             glControl.MouseMove -= glControl_MouseMove;\r
475             glControl.MouseWheel -= glControl_MouseWheel;\r
476             glControl.Load -= new EventHandler(glControl_Load);\r
477             glControl.Disposed -= glControl_Disposed;\r
478         }\r
479 \r
480         void SetSun()\r
481         {\r
482             ambientColor = new OpenTK.Vector4(ambient, ambient, ambient, difuse);\r
483             difuseColor = new OpenTK.Vector4(difuse, difuse, difuse, difuse);\r
484             specularColor = new OpenTK.Vector4(specular, specular, specular, specular);\r
485             GL.Light(LightName.Light0, LightParameter.Ambient, ambientColor);\r
486             GL.Light(LightName.Light0, LightParameter.Diffuse, difuseColor);\r
487             GL.Light(LightName.Light0, LightParameter.Specular, specularColor);\r
488             GL.Light(LightName.Light0, LightParameter.Position, lightPos);\r
489         }\r
490 \r
491         void glControl_Load(object sender, EventArgs e)\r
492         {\r
493             try\r
494             {\r
495                 GL.ShadeModel(ShadingModel.Smooth);\r
496 \r
497                 GL.Enable(EnableCap.Lighting);\r
498                 GL.Enable(EnableCap.Light0);\r
499                 SetSun();\r
500 \r
501                 GL.ClearDepth(1.0d);\r
502                 GL.Enable(EnableCap.DepthTest);\r
503                 GL.Enable(EnableCap.CullFace);\r
504                 GL.CullFace(CullFaceMode.Back);\r
505 \r
506                 // GL.Color() tracks objects ambient and diffuse color\r
507                 GL.Enable(EnableCap.ColorMaterial);\r
508                 GL.ColorMaterial(MaterialFace.Front, ColorMaterialParameter.AmbientAndDiffuse);\r
509 \r
510                 GL.DepthMask(true);\r
511                 GL.DepthFunc(DepthFunction.Lequal);\r
512                 GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);\r
513                 GL.MatrixMode(MatrixMode.Projection);\r
514 \r
515                 GL.Enable(EnableCap.Blend);\r
516                 GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);\r
517                 string glExtensions = GL.GetString(StringName.Extensions);\r
518                 hasMipmap = glExtensions.Contains("GL_SGIS_generate_mipmap");\r
519                 useVBO = glExtensions.Contains("ARB_vertex_buffer_object");\r
520 \r
521                 // Double check if we have mipmap ability\r
522                 if (hasMipmap)\r
523                 {\r
524                     try\r
525                     {\r
526                         int testID = -1;\r
527                         Bitmap testPic = new Bitmap(1, 1);\r
528                         BitmapData testData = testPic.LockBits(new Rectangle(0, 0, 1, 1), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);\r
529                         GL.GenTextures(1, out testID);\r
530                         GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgb8, 1, 1, 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgr, PixelType.UnsignedByte, testData.Scan0);\r
531                         GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.LinearMipmapLinear);\r
532                         GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.GenerateMipmap, 1);\r
533                         GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);\r
534                         testPic.UnlockBits(testData);\r
535                         testPic.Dispose();\r
536                         GL.DeleteTexture(testID);\r
537                     }\r
538                     catch\r
539                     {\r
540                         Logger.DebugLog("Don't have glGenerateMipmap() after all");\r
541                         hasMipmap = false;\r
542                     }\r
543                 }\r
544 \r
545                 RenderingEnabled = true;\r
546                 // Call the resizing function which sets up the GL drawing window\r
547                 // and will also invalidate the GL control\r
548                 glControl_Resize(null, null);\r
549 \r
550                 glControl.Context.MakeCurrent(null);\r
551                 TextureThreadContextReady.Reset();\r
552                 var textureThread = new Thread(() => TextureThread())\r
553                 {\r
554                     IsBackground = true,\r
555                     Name = "TextureLoadingThread"\r
556                 };\r
557                 textureThread.Start();\r
558                 TextureThreadContextReady.WaitOne(1000, false);\r
559                 glControl.MakeCurrent();\r
560             }\r
561             catch (Exception ex)\r
562             {\r
563                 RenderingEnabled = false;\r
564                 Logger.Log("Failed to initialize OpenGL control", Helpers.LogLevel.Warning, Client, ex);\r
565             }\r
566         }\r
567         #endregion glControl setup and disposal\r
568 \r
569         #region glControl paint and resize events\r
570         private void MainRenderLoop()\r
571         {\r
572             if (!RenderingEnabled) return;\r
573             lastFrameTime = renderTimer.Elapsed.TotalSeconds;\r
574 \r
575             // Something went horribly wrong\r
576             if (lastFrameTime < 0) return;\r
577 \r
578             // Stopwatch loses resolution if it runs for a long time, reset it\r
579             renderTimer.Reset();\r
580             renderTimer.Start();\r
581 \r
582             Render(false);\r
583 \r
584             glControl.SwapBuffers();\r
585         }\r
586 \r
587         void glControl_Paint(object sender, EventArgs e)\r
588         {\r
589             MainRenderLoop();\r
590         }\r
591 \r
592         private void glControl_Resize(object sender, EventArgs e)\r
593         {\r
594             if (!RenderingEnabled) return;\r
595             glControl.MakeCurrent();\r
596 \r
597             GL.ClearColor(0.39f, 0.58f, 0.93f, 1.0f);\r
598 \r
599             GL.Viewport(0, 0, glControl.Width, glControl.Height);\r
600 \r
601             GL.PushMatrix();\r
602             GL.MatrixMode(MatrixMode.Projection);\r
603             GL.LoadIdentity();\r
604 \r
605             SetPerspective();\r
606 \r
607             GL.MatrixMode(MatrixMode.Modelview);\r
608             GL.PopMatrix();\r
609         }\r
610         #endregion glControl paint and resize events\r
611 \r
612         #region Mouse handling\r
613         bool dragging = false;\r
614         int dragX, dragY, downX, downY;\r
615 \r
616         private void glControl_MouseWheel(object sender, MouseEventArgs e)\r
617         {\r
618         }\r
619 \r
620         RenderPrimitive RightclickedPrim;\r
621         int RightclickedFaceID;\r
622 \r
623         private void glControl_MouseDown(object sender, MouseEventArgs e)\r
624         {\r
625             if (e.Button == MouseButtons.Left)\r
626             {\r
627                 dragging = true;\r
628                 downX = dragX = e.X;\r
629                 downY = dragY = e.Y;\r
630             }\r
631             else if (e.Button == MouseButtons.Right)\r
632             {\r
633                 object picked;\r
634                 if (TryPick(e.X, e.Y, out picked, out RightclickedFaceID))\r
635                 {\r
636                     if (picked is RenderPrimitive)\r
637                     {\r
638                         RightclickedPrim = (RenderPrimitive)picked;\r
639                         ctxObjects.Show(glControl, e.X, e.Y);\r
640                     }\r
641                     else if (picked is RenderAvatar)\r
642                     {\r
643                         // TODO: add context menu when clicked on an avatar\r
644                     }\r
645                 }\r
646             }\r
647 \r
648         }\r
649 \r
650         private void glControl_MouseMove(object sender, MouseEventArgs e)\r
651         {\r
652             if (dragging)\r
653             {\r
654                 int deltaX = e.X - dragX;\r
655                 int deltaY = e.Y - dragY;\r
656                 float pixelToM = 1f / 75f;\r
657 \r
658                 if (e.Button == MouseButtons.Left)\r
659                 {\r
660                     // Pan\r
661                     if (ModifierKeys == Keys.Control || ModifierKeys == (Keys.Alt | Keys.Control | Keys.Shift))\r
662                     {\r
663                         Vector3 direction = Camera.Position - Camera.FocalPoint;\r
664                         direction.Normalize();\r
665                         Vector3 vy = direction % new Vector3(0f, 0f, 1f);\r
666                         Vector3 vx = vy % direction;\r
667                         Vector3 vxy = vx * deltaY * pixelToM * 2 + vy * deltaX * pixelToM * 2;\r
668                         Camera.Position += vxy;\r
669                         Camera.FocalPoint += vxy;\r
670                     }\r
671 \r
672                     // Alt-zoom (up down move camera closer to target, left right rotate around target)\r
673                     if (ModifierKeys == Keys.Alt)\r
674                     {\r
675                         Camera.Position += (Camera.Position - Camera.FocalPoint) * deltaY * pixelToM;\r
676                         var dx = -(deltaX * pixelToM);\r
677                         Camera.Position = Camera.FocalPoint + (Camera.Position - Camera.FocalPoint) * new Quaternion(0f, 0f, (float)Math.Sin(dx), (float)Math.Cos(dx));\r
678                     }\r
679 \r
680                     // Rotate camera in a vertical circle around target on up down mouse movement\r
681                     if (ModifierKeys == (Keys.Alt | Keys.Control))\r
682                     {\r
683                         Camera.Position = Camera.FocalPoint +\r
684                             (Camera.Position - Camera.FocalPoint)\r
685                             * Quaternion.CreateFromAxisAngle((Camera.Position - Camera.FocalPoint) % new Vector3(0f, 0f, 1f), deltaY * pixelToM);\r
686                         var dx = -(deltaX * pixelToM);\r
687                         Camera.Position = Camera.FocalPoint + (Camera.Position - Camera.FocalPoint) * new Quaternion(0f, 0f, (float)Math.Sin(dx), (float)Math.Cos(dx));\r
688                     }\r
689 \r
690                 }\r
691 \r
692                 dragX = e.X;\r
693                 dragY = e.Y;\r
694             }\r
695         }\r
696 \r
697         private void glControl_MouseUp(object sender, MouseEventArgs e)\r
698         {\r
699             if (e.Button == MouseButtons.Left)\r
700             {\r
701                 dragging = false;\r
702 \r
703                 if (e.X == downX && e.Y == downY) // click\r
704                 {\r
705                     object clicked;\r
706                     int faceID;\r
707                     if (TryPick(e.X, e.Y, out clicked, out faceID))\r
708                     {\r
709                         if (clicked is RenderPrimitive)\r
710                         {\r
711                             RenderPrimitive picked = (RenderPrimitive)clicked;\r
712 \r
713                             if (ModifierKeys == Keys.None)\r
714                             {\r
715                                 Client.Self.Grab(picked.Prim.LocalID, Vector3.Zero, Vector3.Zero, Vector3.Zero, faceID, Vector3.Zero, Vector3.Zero, Vector3.Zero);\r
716                                 Client.Self.GrabUpdate(picked.Prim.ID, Vector3.Zero, Vector3.Zero, Vector3.Zero, Vector3.Zero, faceID, Vector3.Zero, Vector3.Zero, Vector3.Zero);\r
717                                 Client.Self.DeGrab(picked.Prim.LocalID);\r
718                             }\r
719                             else if (ModifierKeys == Keys.Alt)\r
720                             {\r
721                                 Camera.FocalPoint = picked.SimPosition;\r
722                                 Cursor.Position = glControl.PointToScreen(new Point(glControl.Width / 2, glControl.Height / 2));\r
723                             }\r
724                         }\r
725                         else if (clicked is RenderAvatar)\r
726                         {\r
727                             RenderAvatar av = (RenderAvatar)clicked;\r
728                             if (ModifierKeys == Keys.Alt)\r
729                             {\r
730                                 Vector3 pos = av.SimPosition;\r
731                                 pos.Z += 1.5f; // focus roughly on the chest area\r
732                                 Camera.FocalPoint = pos;\r
733                                 Cursor.Position = glControl.PointToScreen(new Point(glControl.Width / 2, glControl.Height / 2));\r
734                             }\r
735                         }\r
736                     }\r
737                 }\r
738             }\r
739         }\r
740         #endregion Mouse handling\r
741 \r
742         // Switch to ortho display mode for drawing hud\r
743         public void GLHUDBegin()\r
744         {\r
745             GL.Disable(EnableCap.DepthTest);\r
746             GL.Disable(EnableCap.Lighting);\r
747             GL.Disable(EnableCap.Light0);\r
748             GL.MatrixMode(MatrixMode.Projection);\r
749             GL.PushMatrix();\r
750             GL.LoadIdentity();\r
751             GL.Ortho(0, glControl.Width, 0, glControl.Height, -5, 1);\r
752             GL.MatrixMode(MatrixMode.Modelview);\r
753             GL.LoadIdentity();\r
754         }\r
755 \r
756         // Switch back to frustrum display mode\r
757         public void GLHUDEnd()\r
758         {\r
759             GL.Enable(EnableCap.DepthTest);\r
760             GL.Enable(EnableCap.Lighting);\r
761             GL.Enable(EnableCap.Light0);\r
762             GL.MatrixMode(MatrixMode.Projection);\r
763             GL.PopMatrix();\r
764             GL.MatrixMode(MatrixMode.Modelview);\r
765         }\r
766 \r
767         public int GLLoadImage(Bitmap bitmap, bool hasAlpha)\r
768         {\r
769             int ret = -1;\r
770             GL.GenTextures(1, out ret);\r
771             GL.BindTexture(TextureTarget.Texture2D, ret);\r
772 \r
773             Rectangle rectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height);\r
774 \r
775             BitmapData bitmapData =\r
776                 bitmap.LockBits(\r
777                 rectangle,\r
778                 ImageLockMode.ReadOnly,\r
779                 hasAlpha ? System.Drawing.Imaging.PixelFormat.Format32bppArgb : System.Drawing.Imaging.PixelFormat.Format24bppRgb);\r
780 \r
781             GL.TexImage2D(\r
782                 TextureTarget.Texture2D,\r
783                 0,\r
784                 hasAlpha ? PixelInternalFormat.Rgba : PixelInternalFormat.Rgb8,\r
785                 bitmap.Width,\r
786                 bitmap.Height,\r
787                 0,\r
788                 hasAlpha ? OpenTK.Graphics.OpenGL.PixelFormat.Bgra : OpenTK.Graphics.OpenGL.PixelFormat.Bgr,\r
789                 PixelType.UnsignedByte,\r
790                 bitmapData.Scan0);\r
791 \r
792             GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);\r
793             GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);\r
794             GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);\r
795             if (hasMipmap)\r
796             {\r
797                 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.LinearMipmapLinear);\r
798                 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.GenerateMipmap, 1);\r
799                 GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);\r
800             }\r
801             else\r
802             {\r
803                 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);\r
804             }\r
805 \r
806             bitmap.UnlockBits(bitmapData);\r
807             return ret;\r
808         }\r
809 \r
810         #region Texture thread\r
811         bool TextureThreadRunning = true;\r
812 \r
813         void TextureThread()\r
814         {\r
815             OpenTK.INativeWindow window = new OpenTK.NativeWindow();\r
816             OpenTK.Graphics.IGraphicsContext context = new OpenTK.Graphics.GraphicsContext(GLMode, window.WindowInfo);\r
817             context.MakeCurrent(window.WindowInfo);\r
818             TextureThreadContextReady.Set();\r
819             PendingTextures.Open();\r
820             Logger.DebugLog("Started Texture Thread");\r
821 \r
822             while (window.Exists && TextureThreadRunning)\r
823             {\r
824                 window.ProcessEvents();\r
825 \r
826                 TextureLoadItem item = null;\r
827 \r
828                 if (!PendingTextures.Dequeue(Timeout.Infinite, ref item)) continue;\r
829 \r
830                 // Already have this one loaded\r
831                 if (item.Data.TextureInfo.TexturePointer != 0) continue;\r
832 \r
833                 if (item.TextureData != null)\r
834                 {\r
835                     ManagedImage mi;\r
836                     Image img;\r
837                     if (!OpenJPEG.DecodeToImage(item.TextureData, out mi)) continue;\r
838 \r
839                     bool hasAlpha = false;\r
840                     bool fullAlpha = false;\r
841                     bool isMask = false;\r
842                     if ((mi.Channels & ManagedImage.ImageChannels.Alpha) != 0)\r
843                     {\r
844                         fullAlpha = true;\r
845                         isMask = true;\r
846 \r
847                         // Do we really have alpha, is it all full alpha, or is it a mask\r
848                         for (int i = 0; i < mi.Alpha.Length; i++)\r
849                         {\r
850                             if (mi.Alpha[i] < 255)\r
851                             {\r
852                                 hasAlpha = true;\r
853                             }\r
854                             if (mi.Alpha[i] != 0)\r
855                             {\r
856                                 fullAlpha = false;\r
857                             }\r
858                             if (mi.Alpha[i] != 0 && mi.Alpha[i] != 255)\r
859                             {\r
860                                 isMask = false;\r
861                             }\r
862                         }\r
863 \r
864                         if (!hasAlpha)\r
865                         {\r
866                             mi.ConvertChannels(mi.Channels & ~ManagedImage.ImageChannels.Alpha);\r
867                         }\r
868                     }\r
869 \r
870                     using (MemoryStream byteData = new MemoryStream(mi.ExportTGA()))\r
871                     {\r
872                         img = OpenMetaverse.Imaging.LoadTGAClass.LoadTGA(byteData);\r
873                     }\r
874 \r
875                     Bitmap bitmap = (Bitmap)img;\r
876 \r
877                     item.Data.TextureInfo.HasAlpha = hasAlpha;\r
878                     item.Data.TextureInfo.FullAlpha = fullAlpha;\r
879                     item.Data.TextureInfo.IsMask = isMask;\r
880                     bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);\r
881                     item.Data.TextureInfo.TexturePointer = GLLoadImage(bitmap, hasAlpha);\r
882                     bitmap.Dispose();\r
883                     item.TextureData = null;\r
884                 }\r
885             }\r
886             Logger.DebugLog("Texture thread exited");\r
887         }\r
888         #endregion Texture thread\r
889 \r
890         void LoadCurrentPrims()\r
891         {\r
892             if (!Client.Network.Connected) return;\r
893 \r
894             ThreadPool.QueueUserWorkItem(sync =>\r
895             {\r
896                 if (PrimitiveRenderingEnabled)\r
897                 {\r
898                     List<Primitive> mainPrims = Client.Network.CurrentSim.ObjectsPrimitives.FindAll((Primitive root) => root.ParentID == 0);\r
899                     foreach (Primitive mainPrim in mainPrims)\r
900                     {\r
901                         UpdatePrimBlocking(mainPrim);\r
902                         Client.Network.CurrentSim.ObjectsPrimitives\r
903                             .FindAll((Primitive child) => child.ParentID == mainPrim.LocalID)\r
904                             .ForEach((Primitive subPrim) => UpdatePrimBlocking(subPrim));\r
905                     }\r
906                 }\r
907 \r
908                 if (AvatarRenderingEnabled)\r
909                 {\r
910                     List<Avatar> avis = Client.Network.CurrentSim.ObjectsAvatars.FindAll((Avatar a) => true);\r
911                     foreach (Avatar avatar in avis)\r
912                     {\r
913                         UpdatePrimBlocking(avatar);\r
914                         Client.Network.CurrentSim.ObjectsPrimitives\r
915                             .FindAll((Primitive child) => child.ParentID == avatar.LocalID)\r
916                             .ForEach((Primitive attachedPrim) =>\r
917                             {\r
918                                 UpdatePrimBlocking(attachedPrim);\r
919                                 Client.Network.CurrentSim.ObjectsPrimitives\r
920                                     .FindAll((Primitive child) => child.ParentID == attachedPrim.LocalID)\r
921                                     .ForEach((Primitive attachedPrimChild) =>\r
922                                     {\r
923                                         UpdatePrimBlocking(attachedPrimChild);\r
924                                     });\r
925                             });\r
926                     }\r
927                 }\r
928             });\r
929         }\r
930 \r
931         private void frmPrimWorkshop_Shown(object sender, EventArgs e)\r
932         {\r
933             SetupGLControl();\r
934             LoadCurrentPrims();\r
935         }\r
936 \r
937         #region Private methods (the meat)\r
938         private void UpdateCamera()\r
939         {\r
940             if (Client != null)\r
941             {\r
942                 Client.Self.Movement.Camera.LookAt(Camera.Position, Camera.FocalPoint);\r
943                 Client.Self.Movement.Camera.Far = Camera.Far = DrawDistance;\r
944             }\r
945         }\r
946 \r
947         void InitCamera()\r
948         {\r
949             Vector3 camPos = Client.Self.SimPosition + new Vector3(-2, 0, 0) * Client.Self.Movement.BodyRotation;\r
950             camPos.Z += 2f;\r
951             Camera.Position = camPos;\r
952             Camera.FocalPoint = Client.Self.SimPosition + new Vector3(5, 0, 0) * Client.Self.Movement.BodyRotation;\r
953             Camera.Zoom = 1.0f;\r
954             Camera.Far = DrawDistance;\r
955             Camera.EndMove();\r
956         }\r
957 \r
958         Vector3 PrimPos(Primitive prim)\r
959         {\r
960             Vector3 pos;\r
961             Quaternion rot;\r
962             PrimPosAndRot(prim, out pos, out rot);\r
963             return pos;\r
964         }\r
965 \r
966         SceneObject GetSceneObject(uint localID)\r
967         {\r
968             RenderPrimitive parent;\r
969             RenderAvatar avi;\r
970             if (Prims.TryGetValue(localID, out parent))\r
971             {\r
972                 return parent;\r
973             }\r
974             else if (Avatars.TryGetValue(localID, out avi))\r
975             {\r
976                 return avi;\r
977             }\r
978             return null;\r
979         }\r
980 \r
981         void PrimPosAndRot(Primitive prim, out Vector3 pos, out Quaternion rot)\r
982         {\r
983             if (prim.ParentID == 0)\r
984             {\r
985                 pos = prim.Position;\r
986                 rot = prim.Rotation;\r
987                 return;\r
988             }\r
989             else\r
990             {\r
991                 pos = new Vector3(99999f, 99999f, 99999f);\r
992                 rot = Quaternion.Identity;\r
993 \r
994                 SceneObject p = GetSceneObject(prim.ParentID);\r
995                 if (p == null) return;\r
996 \r
997                 Vector3 parentPos;\r
998                 Quaternion parentRot;\r
999                 if (p.PositionCalculated)\r
1000                 {\r
1001                     parentPos = p.SimPosition;\r
1002                     parentRot = p.SimRotation;\r
1003                 }\r
1004                 else\r
1005                 {\r
1006                     Primitive parentPrim = p.BasePrim;\r
1007                     PrimPosAndRot(parentPrim, out parentPos, out parentRot);\r
1008                     p.SimPosition = parentPos;\r
1009                     p.SimRotation = parentRot;\r
1010                     p.DistanceSquared = Vector3.DistanceSquared(Camera.RenderPosition, p.SimPosition);\r
1011                     p.PositionCalculated = true;\r
1012                 }\r
1013 \r
1014                 if (p is RenderPrimitive)\r
1015                 {\r
1016                     pos = parentPos + prim.Position * parentRot;\r
1017                     rot = parentRot * prim.Rotation;\r
1018                 }\r
1019                 else if (p is RenderAvatar)\r
1020                 {\r
1021                     RenderAvatar parentav = (RenderAvatar)p;\r
1022 \r
1023                     int attachment_index = (int)prim.PrimData.AttachmentPoint;\r
1024                     // Check for invalid LL attachment point\r
1025                     if (attachment_index > GLAvatar.attachment_points.Count()) return;\r
1026 \r
1027                     attachment_point apoint = GLAvatar.attachment_points[attachment_index];\r
1028                     Vector3 point = parentav.glavatar.skel.getOffset(apoint.joint) + apoint.position;\r
1029                     Quaternion qrot = parentav.glavatar.skel.getRotation(apoint.joint) * apoint.rotation;\r
1030 \r
1031                     pos = parentPos + point * parentRot + prim.Position * (parentRot * qrot);\r
1032                     rot = qrot * parentRot * prim.Rotation;\r
1033                 }\r
1034                 return;\r
1035             }\r
1036         }\r
1037 \r
1038         private void SetPerspective()\r
1039         {\r
1040             float dAspRat = (float)glControl.Width / (float)glControl.Height;\r
1041             GluPerspective(50.0f * Camera.Zoom, dAspRat, 0.1f, 1000f);\r
1042         }\r
1043 \r
1044 \r
1045 #pragma warning disable 0612\r
1046         OpenTK.Graphics.TextPrinter Printer = new OpenTK.Graphics.TextPrinter(OpenTK.Graphics.TextQuality.High);\r
1047 #pragma warning restore 0612\r
1048 \r
1049         private void RenderStats()\r
1050         {\r
1051             int posX = glControl.Width - 100;\r
1052             int posY = 0;\r
1053 \r
1054             // This is a FIR filter known as a MMA or Modified Mean Average, using a 20 point sampling width\r
1055             advTimerTick = ((19 * advTimerTick) + lastFrameTime) / 20;\r
1056 \r
1057             Printer.Begin();\r
1058             Printer.Print(String.Format("FPS {0:000.00}", 1d / advTimerTick), AvatarTagFont, Color.Orange,\r
1059                 new RectangleF(posX, posY, 100, 50),\r
1060                 OpenTK.Graphics.TextPrinterOptions.Default, OpenTK.Graphics.TextAlignment.Center);\r
1061             Printer.End();\r
1062         }\r
1063 \r
1064         private void RenderText()\r
1065         {\r
1066             lock (Avatars)\r
1067             {\r
1068 \r
1069                 GL.Color4(0f, 0f, 0f, 0.6f);\r
1070 \r
1071                 foreach (RenderAvatar av in Avatars.Values)\r
1072                 {\r
1073                     Vector3 avPos = PrimPos(av.avatar);\r
1074                     if (Vector3.Distance(avPos, Camera.Position) > 20f) continue;\r
1075 \r
1076                     OpenTK.Vector3 tagPos = RHelp.TKVector3(avPos);\r
1077                     tagPos.Z += 2.2f;\r
1078                     OpenTK.Vector3 screenPos;\r
1079                     if (!Math3D.GluProject(tagPos, ModelMatrix, ProjectionMatrix, Viewport, out screenPos)) continue;\r
1080 \r
1081                     string tagText = instance.Names.Get(av.avatar.ID, av.avatar.Name);\r
1082                     if (!string.IsNullOrEmpty(av.avatar.GroupName))\r
1083                     {\r
1084                         tagText = av.avatar.GroupName + "\n" + tagText;\r
1085                     }\r
1086                     var tSize = Printer.Measure(tagText, AvatarTagFont);\r
1087 \r
1088                     // Render tag backround\r
1089                     GL.Begin(BeginMode.Quads);\r
1090                     float halfWidth = tSize.BoundingBox.Width / 2 + 10;\r
1091                     float halfHeight = tSize.BoundingBox.Height / 2 + 5;\r
1092                     GL.Vertex2(screenPos.X - halfWidth, screenPos.Y - halfHeight);\r
1093                     GL.Vertex2(screenPos.X + halfWidth, screenPos.Y - halfHeight);\r
1094                     GL.Vertex2(screenPos.X + halfWidth, screenPos.Y + halfHeight);\r
1095                     GL.Vertex2(screenPos.X - halfWidth, screenPos.Y + halfHeight);\r
1096                     GL.End();\r
1097 \r
1098                     screenPos.Y = glControl.Height - screenPos.Y;\r
1099                     screenPos.X -= tSize.BoundingBox.Width / 2;\r
1100                     screenPos.Y -= tSize.BoundingBox.Height / 2;\r
1101 \r
1102                     if (screenPos.Y > 0)\r
1103                     {\r
1104                         Printer.Begin();\r
1105                         Printer.Print(tagText, AvatarTagFont, Color.Orange,\r
1106                             new RectangleF(screenPos.X, screenPos.Y, tSize.BoundingBox.Width, tSize.BoundingBox.Height),\r
1107                             OpenTK.Graphics.TextPrinterOptions.Default, OpenTK.Graphics.TextAlignment.Center);\r
1108                         Printer.End();\r
1109                     }\r
1110                 }\r
1111             }\r
1112 \r
1113             lock (Prims)\r
1114             {\r
1115                 int primNr = 0;\r
1116                 foreach (RenderPrimitive mesh in Prims.Values)\r
1117                 {\r
1118                     primNr++;\r
1119                     Primitive prim = mesh.Prim;\r
1120                     if (!string.IsNullOrEmpty(prim.Text))\r
1121                     {\r
1122                         string text = System.Text.RegularExpressions.Regex.Replace(prim.Text, "(\r?\n)+", "\n");\r
1123                         var newPrimPos = PrimPos(prim);\r
1124                         OpenTK.Vector3 primPos = new OpenTK.Vector3(newPrimPos.X, newPrimPos.Y, newPrimPos.Z);\r
1125                         var distance = Vector3.Distance(newPrimPos, Camera.Position);\r
1126 \r
1127                         // Display hovertext only on objects that are withing 12m of the camera\r
1128                         if (distance > 12) continue;\r
1129 \r
1130                         primPos.Z += prim.Scale.Z * 0.8f;\r
1131 \r
1132                         // Convert objects world position to 2D screen position in pixels\r
1133                         OpenTK.Vector3 screenPos;\r
1134                         if (!Math3D.GluProject(primPos, ModelMatrix, ProjectionMatrix, Viewport, out screenPos)) continue;\r
1135                         screenPos.Y = glControl.Height - screenPos.Y;\r
1136 \r
1137                         Printer.Begin();\r
1138 \r
1139                         Color color = Color.FromArgb((int)(prim.TextColor.A * 255), (int)(prim.TextColor.R * 255), (int)(prim.TextColor.G * 255), (int)(prim.TextColor.B * 255));\r
1140 \r
1141                         var size = Printer.Measure(text, HoverTextFont);\r
1142                         screenPos.X -= size.BoundingBox.Width / 2;\r
1143                         screenPos.Y -= size.BoundingBox.Height;\r
1144 \r
1145                         if (screenPos.Y > 0)\r
1146                         {\r
1147 \r
1148                             // Shadow\r
1149                             if (color != Color.Black)\r
1150                             {\r
1151                                 Printer.Print(text, HoverTextFont, Color.Black, new RectangleF(screenPos.X + 1, screenPos.Y + 1, size.BoundingBox.Width, size.BoundingBox.Height), OpenTK.Graphics.TextPrinterOptions.Default, OpenTK.Graphics.TextAlignment.Center);\r
1152                             }\r
1153                             // Text\r
1154                             Printer.Print(text, HoverTextFont, color, new RectangleF(screenPos.X, screenPos.Y, size.BoundingBox.Width, size.BoundingBox.Height), OpenTK.Graphics.TextPrinterOptions.Default, OpenTK.Graphics.TextAlignment.Center);\r
1155                         }\r
1156 \r
1157                         Printer.End();\r
1158                     }\r
1159                 }\r
1160             }\r
1161         }\r
1162 \r
1163         #region avatars\r
1164 \r
1165         private void AddAvatarToScene(Avatar av)\r
1166         {\r
1167             lock (Avatars)\r
1168             {\r
1169                 if (Vector3.Distance(PrimPos(av), Client.Self.SimPosition) > DrawDistance) return;\r
1170 \r
1171                 if (Avatars.ContainsKey(av.LocalID))\r
1172                 {\r
1173                     // flag we got an update??\r
1174                     updateAVtes(Avatars[av.LocalID]);\r
1175                 }\r
1176                 else\r
1177                 {\r
1178                     GLAvatar ga = new GLAvatar();\r
1179 \r
1180                     //ga.morph(av);\r
1181                     RenderAvatar ra = new Rendering.RenderAvatar();\r
1182                     ra.avatar = av;\r
1183                     ra.glavatar = ga;\r
1184                     updateAVtes(ra);\r
1185                     Avatars.Add(av.LocalID, ra);\r
1186                     ra.glavatar.morph(av);\r
1187 \r
1188                 }\r
1189             }\r
1190         }\r
1191 \r
1192         private void updateAVtes(RenderAvatar ra)\r
1193         {\r
1194             if (ra.avatar.Textures == null)\r
1195                 return;\r
1196 \r
1197             int[] tes = { 8, 9, 10, 11, 19, 20 };\r
1198 \r
1199             foreach (int fi in tes)\r
1200             {\r
1201                 Primitive.TextureEntryFace TEF = ra.avatar.Textures.FaceTextures[fi];\r
1202                 if (TEF == null)\r
1203                     continue;\r
1204 \r
1205                 if (ra.data[fi] == null || ra.data[fi].TextureInfo.TextureID != TEF.TextureID)\r
1206                 {\r
1207                     FaceData data = new FaceData();\r
1208                     ra.data[fi] = data;\r
1209                     data.TextureInfo.TextureID = TEF.TextureID;\r
1210 \r
1211                     DownloadTexture(new TextureLoadItem()\r
1212                     {\r
1213                         Data = data,\r
1214                         Prim = ra.avatar,\r
1215                         TeFace = ra.avatar.Textures.FaceTextures[fi]\r
1216                     });\r
1217                 }\r
1218             }\r
1219         }\r
1220 \r
1221         private void RenderAvatarsSkeleton(RenderPass pass)\r
1222         {\r
1223             if (!RenderAvatarSkeleton) return;\r
1224 \r
1225             lock (Avatars)\r
1226             {\r
1227                 foreach (RenderAvatar av in Avatars.Values)\r
1228                 {\r
1229                     // Individual prim matrix\r
1230                     GL.PushMatrix();\r
1231 \r
1232                     // Prim roation and position\r
1233                     Vector3 pos = av.avatar.Position;\r
1234                     pos.X += 1;\r
1235                     GL.MultMatrix(Math3D.CreateTranslationMatrix(pos));\r
1236                     GL.MultMatrix(Math3D.CreateRotationMatrix(av.avatar.Rotation));\r
1237 \r
1238                     GL.Begin(BeginMode.Lines);\r
1239 \r
1240                     GL.Color3(1.0, 0.0, 0.0);\r
1241 \r
1242                     foreach (Bone b in av.glavatar.skel.mBones.Values)\r
1243                     {\r
1244                         Vector3 newpos = b.getOffset();\r
1245 \r
1246                         if (b.parent != null)\r
1247                         {\r
1248                             Vector3 parentpos = b.parent.getOffset();\r
1249                             GL.Vertex3(parentpos.X, parentpos.Y, parentpos.Z);\r
1250                         }\r
1251                         else\r
1252                         {\r
1253                             GL.Vertex3(newpos.X, newpos.Y, newpos.Z);\r
1254                         }\r
1255 \r
1256                         GL.Vertex3(newpos.X, newpos.Y, newpos.Z);\r
1257 \r
1258                         //Mark the joints\r
1259 \r
1260 \r
1261                         newpos.X += 0.01f;\r
1262                         newpos.Y += 0.01f;\r
1263                         newpos.Z += 0.01f;\r
1264                         GL.Vertex3(newpos.X, newpos.Y, newpos.Z);\r
1265 \r
1266                         newpos.X -= 0.02f;\r
1267                         GL.Vertex3(newpos.X, newpos.Y, newpos.Z);\r
1268                         GL.Vertex3(newpos.X, newpos.Y, newpos.Z);\r
1269 \r
1270                         newpos.Y -= 0.02f;\r
1271                         GL.Vertex3(newpos.X, newpos.Y, newpos.Z);\r
1272                         GL.Vertex3(newpos.X, newpos.Y, newpos.Z);\r
1273 \r
1274                         newpos.X += 0.02f;\r
1275                         GL.Vertex3(newpos.X, newpos.Y, newpos.Z);\r
1276                         GL.Vertex3(newpos.X, newpos.Y, newpos.Z);\r
1277 \r
1278                         newpos.Y += 0.02f;\r
1279                         GL.Vertex3(newpos.X, newpos.Y, newpos.Z);\r
1280                         GL.Vertex3(newpos.X, newpos.Y, newpos.Z);\r
1281 \r
1282                         newpos.Z -= 0.02f;\r
1283                         GL.Vertex3(newpos.X, newpos.Y, newpos.Z);\r
1284                         GL.Vertex3(newpos.X, newpos.Y, newpos.Z);\r
1285 \r
1286                         newpos.Y -= 0.02f;\r
1287                         GL.Vertex3(newpos.X, newpos.Y, newpos.Z);\r
1288                         GL.Vertex3(newpos.X, newpos.Y, newpos.Z);\r
1289 \r
1290                         newpos.X -= 0.02f;\r
1291                         GL.Vertex3(newpos.X, newpos.Y, newpos.Z);\r
1292                         GL.Vertex3(newpos.X, newpos.Y, newpos.Z);\r
1293 \r
1294                         newpos.Y += 0.02f;\r
1295                         GL.Vertex3(newpos.X, newpos.Y, newpos.Z);\r
1296                         GL.Vertex3(newpos.X, newpos.Y, newpos.Z);\r
1297 \r
1298                         newpos.X += 0.02f;\r
1299                         GL.Vertex3(newpos.X, newpos.Y, newpos.Z);\r
1300                         GL.Vertex3(newpos.X, newpos.Y, newpos.Z);\r
1301 \r
1302                         newpos.Y -= 0.01f;\r
1303                         newpos.Z += 0.01f;\r
1304                         newpos.X -= 0.01f;\r
1305                         GL.Vertex3(newpos.X, newpos.Y, newpos.Z);\r
1306 \r
1307 \r
1308 \r
1309                     }\r
1310 \r
1311 \r
1312 \r
1313                     GL.Color3(0.0, 1.0, 0.0);\r
1314 \r
1315                     GL.End();\r
1316 \r
1317                     GL.PopMatrix();\r
1318                 }\r
1319             }\r
1320         }\r
1321 \r
1322         private void RenderAvatars(RenderPass pass)\r
1323         {\r
1324             if (!AvatarRenderingEnabled) return;\r
1325 \r
1326             lock (Avatars)\r
1327             {\r
1328                 GL.EnableClientState(ArrayCap.VertexArray);\r
1329                 GL.EnableClientState(ArrayCap.TextureCoordArray);\r
1330                 GL.EnableClientState(ArrayCap.NormalArray);\r
1331 \r
1332                 int avatarNr = 0;\r
1333                 foreach (RenderAvatar av in Avatars.Values)\r
1334                 {\r
1335                     avatarNr++;\r
1336 \r
1337                     if (av.glavatar._meshes.Count > 0)\r
1338                     {\r
1339                         int faceNr = 0;\r
1340                         foreach (GLMesh mesh in av.glavatar._meshes.Values)\r
1341                         {\r
1342                             faceNr++;\r
1343                             if (!av.glavatar._showSkirt && mesh.Name == "skirtMesh")\r
1344                                 continue;\r
1345 \r
1346                             if (mesh.Name == "hairMesh") // Don't render the hair mesh for the moment\r
1347                                 continue;\r
1348 \r
1349                             GL.Color3(1f, 1f, 1f);\r
1350 \r
1351                             // Individual prim matrix\r
1352                             GL.PushMatrix();\r
1353 \r
1354                             // Prim roation and position\r
1355                             //GL.MultMatrix(Math3D.CreateTranslationMatrix(av.avatar.Position));\r
1356                             //GL.MultMatrix(Math3D.CreateRotationMatrix(av.avatar.Rotation));\r
1357                             GL.MultMatrix(Math3D.CreateSRTMatrix(new Vector3(1, 1, 1), av.SimRotation, av.SimPosition));\r
1358 \r
1359                             // Special case for eyeballs we need to offset the mesh to the correct position\r
1360                             // We have manually added the eyeball offset based on the headbone when we\r
1361                             // constructed the meshes, but why are the position offsets we got when loading\r
1362                             // the other meshes <0,7,0> ?\r
1363                             if (mesh.Name == "eyeBallLeftMesh")\r
1364                             {\r
1365                                 // Mesh roation and position\r
1366                                 GL.MultMatrix(Math3D.CreateTranslationMatrix(av.glavatar.skel.getOffset("mEyeLeft")));\r
1367                                 GL.MultMatrix(Math3D.CreateRotationMatrix(av.glavatar.skel.getRotation("mEyeLeft")));\r
1368                             }\r
1369                             if (mesh.Name == "eyeBallRightMesh")\r
1370                             {\r
1371                                 // Mesh roation and position\r
1372                                 GL.MultMatrix(Math3D.CreateTranslationMatrix(av.glavatar.skel.getOffset("mEyeRight")));\r
1373                                 GL.MultMatrix(Math3D.CreateRotationMatrix(av.glavatar.skel.getRotation("mEyeRight")));\r
1374                             }\r
1375 \r
1376 \r
1377 \r
1378                             //Should we be offsetting the base meshs at all?\r
1379                             //if (mesh.Name == "headMesh")\r
1380                             //{\r
1381                             //    GL.MultMatrix(Math3D.CreateTranslationMatrix(av.glavatar.skel.getDeltaOffset("mHead")));\r
1382                             //}\r
1383 \r
1384 \r
1385                             //Gl.glTranslatef(mesh.Position.X, mesh.Position.Y, mesh.Position.Z);\r
1386 \r
1387                             GL.Rotate(mesh.RotationAngles.X, 1f, 0f, 0f);\r
1388                             GL.Rotate(mesh.RotationAngles.Y, 0f, 1f, 0f);\r
1389                             GL.Rotate(mesh.RotationAngles.Z, 0f, 0f, 1f);\r
1390 \r
1391                             GL.Scale(mesh.Scale.X, mesh.Scale.Y, mesh.Scale.Z);\r
1392 \r
1393                             if (pass == RenderPass.Picking)\r
1394                             {\r
1395                                 GL.Disable(EnableCap.Texture2D);\r
1396 \r
1397                                 for (int i = 0; i < av.data.Length; i++)\r
1398                                 {\r
1399                                     if (av.data[i] != null)\r
1400                                     {\r
1401                                         av.data[i].PickingID = avatarNr;\r
1402                                     }\r
1403                                 }\r
1404                                 byte[] primNrBytes = Utils.Int16ToBytes((short)avatarNr);\r
1405                                 byte[] faceColor = new byte[] { primNrBytes[0], primNrBytes[1], (byte)faceNr, 254 };\r
1406                                 GL.Color4(faceColor);\r
1407                             }\r
1408                             else\r
1409                             {\r
1410                                 if (av.data[mesh.teFaceID] == null)\r
1411                                 {\r
1412                                     GL.Disable(EnableCap.Texture2D);\r
1413                                 }\r
1414                                 else\r
1415                                 {\r
1416                                     if (mesh.teFaceID != 0)\r
1417                                     {\r
1418                                         GL.Enable(EnableCap.Texture2D);\r
1419                                         GL.BindTexture(TextureTarget.Texture2D, av.data[mesh.teFaceID].TextureInfo.TexturePointer);\r
1420                                     }\r
1421                                     else\r
1422                                     {\r
1423                                         GL.Disable(EnableCap.Texture2D);\r
1424                                     }\r
1425                                 }\r
1426                             }\r
1427 \r
1428                             GL.TexCoordPointer(2, TexCoordPointerType.Float, 0, mesh.RenderData.TexCoords);\r
1429                             GL.VertexPointer(3, VertexPointerType.Float, 0, mesh.RenderData.Vertices);\r
1430                             GL.NormalPointer(NormalPointerType.Float, 0, mesh.RenderData.Normals);\r
1431 \r
1432                             GL.DrawElements(BeginMode.Triangles, mesh.RenderData.Indices.Length, DrawElementsType.UnsignedShort, mesh.RenderData.Indices);\r
1433 \r
1434                             GL.BindTexture(TextureTarget.Texture2D, 0);\r
1435 \r
1436                             GL.PopMatrix();\r
1437 \r
1438                         }\r
1439                     }\r
1440                 }\r
1441                 GL.Disable(EnableCap.Texture2D);\r
1442                 GL.DisableClientState(ArrayCap.NormalArray);\r
1443                 GL.DisableClientState(ArrayCap.VertexArray);\r
1444                 GL.DisableClientState(ArrayCap.TextureCoordArray);\r
1445 \r
1446             }\r
1447         }\r
1448         #endregion avatars\r
1449 \r
1450         #region Terrian\r
1451         bool TerrainModified = true;\r
1452         float[,] heightTable = new float[256, 256];\r
1453         Face terrainFace;\r
1454         ushort[] terrainIndices;\r
1455         Vertex[] terrainVertices;\r
1456         int terrainTexture = -1;\r
1457         bool fetchingTerrainTexture = false;\r
1458         Bitmap terrainImage = null;\r
1459         int terrainVBO = -1;\r
1460         int terrainIndexVBO = -1;\r
1461 \r
1462         private void ResetTerrain()\r
1463         {\r
1464             ResetTerrain(true);\r
1465         }\r
1466 \r
1467         private void ResetTerrain(bool removeImage)\r
1468         {\r
1469             if (terrainImage != null)\r
1470             {\r
1471                 terrainImage.Dispose();\r
1472                 terrainImage = null;\r
1473             }\r
1474 \r
1475             if (terrainVBO != -1)\r
1476             {\r
1477                 GL.DeleteBuffers(1, ref terrainVBO);\r
1478                 terrainVBO = -1;\r
1479             }\r
1480 \r
1481             if (terrainIndexVBO != -1)\r
1482             {\r
1483                 GL.DeleteBuffers(1, ref terrainIndexVBO);\r
1484                 terrainIndexVBO = -1;\r
1485             }\r
1486 \r
1487             if (removeImage)\r
1488             {\r
1489                 if (terrainTexture != -1)\r
1490                 {\r
1491                     GL.DeleteTexture(terrainTexture);\r
1492                     terrainTexture = -1;\r
1493                 }\r
1494             }\r
1495 \r
1496             fetchingTerrainTexture = false;\r
1497             TerrainModified = true;\r
1498         }\r
1499 \r
1500         private void UpdateTerrain()\r
1501         {\r
1502             if (Client.Network.CurrentSim == null || Client.Network.CurrentSim.Terrain == null) return;\r
1503             int step = 1;\r
1504 \r
1505             for (int x = 0; x < 255; x += step)\r
1506             {\r
1507                 for (int y = 0; y < 255; y += step)\r
1508                 {\r
1509                     float z = 0;\r
1510                     int patchNr = ((int)x / 16) * 16 + (int)y / 16;\r
1511                     if (Client.Network.CurrentSim.Terrain[patchNr] != null\r
1512                         && Client.Network.CurrentSim.Terrain[patchNr].Data != null)\r
1513                     {\r
1514                         float[] data = Client.Network.CurrentSim.Terrain[patchNr].Data;\r
1515                         z = data[(int)x % 16 * 16 + (int)y % 16];\r
1516                     }\r
1517                     heightTable[x, y] = z;\r
1518                 }\r
1519             }\r
1520 \r
1521             terrainFace = renderer.TerrainMesh(heightTable, 0f, 255f, 0f, 255f);\r
1522             terrainVertices = terrainFace.Vertices.ToArray();\r
1523             terrainIndices = terrainFace.Indices.ToArray();\r
1524 \r
1525             TerrainModified = false;\r
1526         }\r
1527 \r
1528         void UpdateTerrainTexture()\r
1529         {\r
1530             if (!fetchingTerrainTexture)\r
1531             {\r
1532                 fetchingTerrainTexture = true;\r
1533                 ThreadPool.QueueUserWorkItem(sync =>\r
1534                 {\r
1535                     Simulator sim = Client.Network.CurrentSim;\r
1536                     terrainImage = TerrainSplat.Splat(instance, heightTable,\r
1537                         new UUID[] { sim.TerrainDetail0, sim.TerrainDetail1, sim.TerrainDetail2, sim.TerrainDetail3 },\r
1538                         new float[] { sim.TerrainStartHeight00, sim.TerrainStartHeight01, sim.TerrainStartHeight10, sim.TerrainStartHeight11 },\r
1539                         new float[] { sim.TerrainHeightRange00, sim.TerrainHeightRange01, sim.TerrainHeightRange10, sim.TerrainHeightRange11 },\r
1540                         Vector3.Zero);\r
1541 \r
1542                     fetchingTerrainTexture = false;\r
1543                 });\r
1544             }\r
1545         }\r
1546 \r
1547         private void RenderTerrain()\r
1548         {\r
1549             GL.Color3(1f, 1f, 1f);\r
1550             GL.EnableClientState(ArrayCap.VertexArray);\r
1551             GL.EnableClientState(ArrayCap.TextureCoordArray);\r
1552             GL.EnableClientState(ArrayCap.NormalArray);\r
1553 \r
1554             if (TerrainModified)\r
1555             {\r
1556                 ResetTerrain(false);\r
1557                 UpdateTerrain();\r
1558                 UpdateTerrainTexture();\r
1559             }\r
1560 \r
1561             if (terrainImage != null)\r
1562             {\r
1563                 if (terrainTexture != -1)\r
1564                 {\r
1565                     GL.DeleteTexture(terrainTexture);\r
1566                 }\r
1567 \r
1568                 terrainTexture = GLLoadImage(terrainImage, false);\r
1569                 terrainImage.Dispose();\r
1570                 terrainImage = null;\r
1571             }\r
1572 \r
1573             if (terrainTexture == -1)\r
1574             {\r
1575                 return;\r
1576             }\r
1577             else\r
1578             {\r
1579                 GL.Enable(EnableCap.Texture2D);\r
1580                 GL.BindTexture(TextureTarget.Texture2D, terrainTexture);\r
1581             }\r
1582 \r
1583             if (!useVBO)\r
1584             {\r
1585                 unsafe\r
1586                 {\r
1587                     fixed (float* normalPtr = &terrainVertices[0].Normal.X)\r
1588                     fixed (float* texPtr = &terrainVertices[0].TexCoord.X)\r
1589                     {\r
1590                         GL.NormalPointer(NormalPointerType.Float, FaceData.VertexSize, (IntPtr)normalPtr);\r
1591                         GL.TexCoordPointer(2, TexCoordPointerType.Float, FaceData.VertexSize, (IntPtr)texPtr);\r
1592                         GL.VertexPointer(3, VertexPointerType.Float, FaceData.VertexSize, terrainVertices);\r
1593                         GL.DrawElements(BeginMode.Triangles, terrainIndices.Length, DrawElementsType.UnsignedShort, terrainIndices);\r
1594                     }\r
1595                 }\r
1596             }\r
1597             else\r
1598             {\r
1599                 if (terrainVBO == -1)\r
1600                 {\r
1601                     GL.GenBuffers(1, out terrainVBO);\r
1602                     GL.BindBuffer(BufferTarget.ArrayBuffer, terrainVBO);\r
1603                     GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(terrainVertices.Length * FaceData.VertexSize), terrainVertices, BufferUsageHint.StaticDraw);\r
1604                 }\r
1605                 else\r
1606                 {\r
1607                     GL.BindBuffer(BufferTarget.ArrayBuffer, terrainVBO);\r
1608                 }\r
1609 \r
1610                 if (terrainIndexVBO == -1)\r
1611                 {\r
1612                     GL.GenBuffers(1, out terrainIndexVBO);\r
1613                     GL.BindBuffer(BufferTarget.ElementArrayBuffer, terrainIndexVBO);\r
1614                     GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(terrainIndices.Length * sizeof(ushort)), terrainIndices, BufferUsageHint.StaticDraw);\r
1615                 }\r
1616                 else\r
1617                 {\r
1618                     GL.BindBuffer(BufferTarget.ElementArrayBuffer, terrainIndexVBO);\r
1619                 }\r
1620 \r
1621                 GL.NormalPointer(NormalPointerType.Float, FaceData.VertexSize, (IntPtr)12);\r
1622                 GL.TexCoordPointer(2, TexCoordPointerType.Float, FaceData.VertexSize, (IntPtr)(24));\r
1623                 GL.VertexPointer(3, VertexPointerType.Float, FaceData.VertexSize, (IntPtr)(0));\r
1624 \r
1625                 GL.DrawElements(BeginMode.Triangles, terrainIndices.Length, DrawElementsType.UnsignedShort, IntPtr.Zero);\r
1626 \r
1627                 GL.BindBuffer(BufferTarget.ArrayBuffer, 0);\r
1628                 GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);\r
1629             }\r
1630             GL.BindTexture(TextureTarget.Texture2D, 0);\r
1631             GL.DisableClientState(ArrayCap.VertexArray);\r
1632             GL.DisableClientState(ArrayCap.TextureCoordArray);\r
1633             GL.DisableClientState(ArrayCap.NormalArray);\r
1634         }\r
1635         #endregion Terrain\r
1636 \r
1637         private void ResetMaterial()\r
1638         {\r
1639             GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Ambient, new float[] { 0.2f, 0.2f, 0.2f, 1.0f });\r
1640             GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Diffuse, new float[] { 0.8f, 0.8f, 0.8f, 1.0f });\r
1641             GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Specular, new float[] { 0f, 0f, 0f, 1.0f });\r
1642             GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Emission, new float[] { 0f, 0f, 0f, 1.0f });\r
1643             GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Shininess, 0f);\r
1644         }\r
1645 \r
1646         float LODFactor(float distance, Vector3 primScale, float radius)\r
1647         {\r
1648             float scale = primScale.X;\r
1649             if (primScale.Y > scale) scale = primScale.Y;\r
1650             if (primScale.Z > scale) scale = primScale.Z;\r
1651             return scale * radius * radius / distance;\r
1652         }\r
1653 \r
1654         void RenderSphere(float cx, float cy, float cz, float r, int p)\r
1655         {\r
1656             GL.PushAttrib(AttribMask.AllAttribBits);\r
1657             GL.Disable(EnableCap.Fog);\r
1658             GL.Disable(EnableCap.Texture2D);\r
1659             GL.Disable(EnableCap.Dither);\r
1660             GL.Disable(EnableCap.Lighting);\r
1661             GL.Disable(EnableCap.LineStipple);\r
1662             GL.Disable(EnableCap.PolygonStipple);\r
1663             GL.Disable(EnableCap.CullFace);\r
1664             GL.Disable(EnableCap.Blend);\r
1665             GL.Disable(EnableCap.AlphaTest);\r
1666             GL.Disable(EnableCap.DepthTest);\r
1667 \r
1668             const float TWOPI = 6.28318530717958f;\r
1669             const float PIDIV2 = 1.57079632679489f;\r
1670 \r
1671             float theta1 = 0.0f;\r
1672             float theta2 = 0.0f;\r
1673             float theta3 = 0.0f;\r
1674 \r
1675             float ex = 0.0f;\r
1676             float ey = 0.0f;\r
1677             float ez = 0.0f;\r
1678 \r
1679             float px = 0.0f;\r
1680             float py = 0.0f;\r
1681             float pz = 0.0f;\r
1682 \r
1683             // Disallow a negative number for radius.\r
1684             if (r < 0)\r
1685                 r = -r;\r
1686 \r
1687             // Disallow a negative number for precision.\r
1688             if (p < 0)\r
1689                 p = -p;\r
1690 \r
1691             // If the sphere is too small, just render a OpenGL point instead.\r
1692             if (p < 4 || r <= 0)\r
1693             {\r
1694                 GL.Begin(BeginMode.Points);\r
1695                 GL.Vertex3(cx, cy, cz);\r
1696                 GL.End();\r
1697                 return;\r
1698             }\r
1699 \r
1700             for (int i = 0; i < p / 2; ++i)\r
1701             {\r
1702                 theta1 = i * TWOPI / p - PIDIV2;\r
1703                 theta2 = (i + 1) * TWOPI / p - PIDIV2;\r
1704 \r
1705                 GL.Begin(BeginMode.TriangleStrip);\r
1706                 {\r
1707                     for (int j = 0; j <= p; ++j)\r
1708                     {\r
1709                         theta3 = j * TWOPI / p;\r
1710 \r
1711                         ex = (float)(Math.Cos(theta2) * Math.Cos(theta3));\r
1712                         ey = (float)Math.Sin(theta2);\r
1713                         ez = (float)(Math.Cos(theta2) * Math.Sin(theta3));\r
1714                         px = cx + r * ex;\r
1715                         py = cy + r * ey;\r
1716                         pz = cz + r * ez;\r
1717 \r
1718                         GL.Normal3(ex, ey, ez);\r
1719                         GL.TexCoord2(-(j / (float)p), 2 * (i + 1) / (float)p);\r
1720                         GL.Vertex3(px, py, pz);\r
1721 \r
1722                         ex = (float)(Math.Cos(theta1) * Math.Cos(theta3));\r
1723                         ey = (float)Math.Sin(theta1);\r
1724                         ez = (float)(Math.Cos(theta1) * Math.Sin(theta3));\r
1725                         px = cx + r * ex;\r
1726                         py = cy + r * ey;\r
1727                         pz = cz + r * ez;\r
1728 \r
1729                         GL.Normal3(ex, ey, ez);\r
1730                         GL.TexCoord2(-(j / (float)p), 2 * i / (float)p);\r
1731                         GL.Vertex3(px, py, pz);\r
1732                     }\r
1733                 }\r
1734                 GL.End();\r
1735             }\r
1736             GL.PopAttrib();\r
1737         }\r
1738 \r
1739 \r
1740         void RenderBoundingBox(SceneObject prim)\r
1741         {\r
1742             Vector3 scale = prim.BasePrim.Scale;\r
1743             BoundingVolume bbox = prim.BoundingVolume;\r
1744             GL.PushAttrib(AttribMask.AllAttribBits);\r
1745             GL.Disable(EnableCap.Fog);\r
1746             GL.Disable(EnableCap.Texture2D);\r
1747             GL.Disable(EnableCap.Lighting);\r
1748             GL.Disable(EnableCap.CullFace);\r
1749             GL.Disable(EnableCap.AlphaTest);\r
1750 \r
1751             GL.DepthMask(false);\r
1752             GL.ColorMask(false, false, false, false);\r
1753 \r
1754             GL.PushMatrix();\r
1755             GL.MultMatrix(Math3D.CreateTranslationMatrix(prim.SimPosition));\r
1756             GL.MultMatrix(Math3D.CreateRotationMatrix(prim.SimRotation));\r
1757             GL.Scale(scale.X, scale.Y, scale.Z);\r
1758             GL.Color3(1f, 0f, 0f);\r
1759             GL.Begin(BeginMode.Quads);\r
1760             var bmin = bbox.Min;\r
1761             var bmax = bbox.Max;\r
1762 \r
1763             //front\r
1764             GL.Vertex3(bmin.X, bmin.Y, bmin.Z);\r
1765             GL.Vertex3(bmax.X, bmin.Y, bmin.Z);\r
1766             GL.Vertex3(bmax.X, bmax.Y, bmin.Z);\r
1767             GL.Vertex3(bmin.X, bmax.Y, bmin.Z);\r
1768 \r
1769             // back\r
1770             GL.Vertex3(bmin.X, bmin.Y, bmax.Z);\r
1771             GL.Vertex3(bmax.X, bmin.Y, bmax.Z);\r
1772             GL.Vertex3(bmax.X, bmax.Y, bmax.Z);\r
1773             GL.Vertex3(bmin.X, bmax.Y, bmax.Z);\r
1774 \r
1775             // up\r
1776             GL.Vertex3(bmin.X, bmax.Y, bmax.Z);\r
1777             GL.Vertex3(bmax.X, bmax.Y, bmax.Z);\r
1778             GL.Vertex3(bmax.X, bmax.Y, bmin.Z);\r
1779             GL.Vertex3(bmin.X, bmax.Y, bmin.Z);\r
1780 \r
1781             // down\r
1782             GL.Vertex3(bmin.X, bmin.Y, bmax.Z);\r
1783             GL.Vertex3(bmax.X, bmin.Y, bmax.Z);\r
1784             GL.Vertex3(bmax.X, bmin.Y, bmin.Z);\r
1785             GL.Vertex3(bmin.X, bmin.Y, bmin.Z);\r
1786 \r
1787             // left side\r
1788             GL.Vertex3(bmin.X, bmin.Y, bmax.Z);\r
1789             GL.Vertex3(bmin.X, bmax.Y, bmax.Z);\r
1790             GL.Vertex3(bmin.X, bmax.Y, bmin.Z);\r
1791             GL.Vertex3(bmin.X, bmin.Y, bmin.Z);\r
1792 \r
1793             // rigth side\r
1794             GL.Vertex3(bmax.X, bmin.Y, bmax.Z);\r
1795             GL.Vertex3(bmax.X, bmax.Y, bmax.Z);\r
1796             GL.Vertex3(bmax.X, bmax.Y, bmin.Z);\r
1797             GL.Vertex3(bmax.X, bmin.Y, bmin.Z);\r
1798 \r
1799             GL.End();\r
1800             GL.PopMatrix();\r
1801 \r
1802             GL.ColorMask(true, true, true, true);\r
1803             GL.DepthMask(true);\r
1804 \r
1805             GL.PopAttrib();\r
1806         }\r
1807 \r
1808         void RenderPrim(RenderPrimitive mesh, RenderPass pass, int primNr)\r
1809         {\r
1810             // Do the stuff we need to do the first time we encouter the object\r
1811             if (!mesh.Initialized) mesh.Initialize();\r
1812 \r
1813             // Do any position interpolation\r
1814             mesh.Step(lastFrameTime);\r
1815 \r
1816             Primitive prim = mesh.Prim;\r
1817 \r
1818             // Individual prim matrix\r
1819             GL.PushMatrix();\r
1820 \r
1821             // Prim roation and position\r
1822             GL.MultMatrix(Math3D.CreateTranslationMatrix(mesh.RenderPosition));\r
1823             GL.MultMatrix(Math3D.CreateRotationMatrix(mesh.RenderRotation));\r
1824 \r
1825             // Prim scaling\r
1826             GL.Scale(prim.Scale.X, prim.Scale.Y, prim.Scale.Z);\r
1827 \r
1828             // Do we have animated texture on this face\r
1829             bool animatedTexture = false;\r
1830 \r
1831             // Draw the prim faces\r
1832             for (int j = 0; j < mesh.Faces.Count; j++)\r
1833             {\r
1834                 Primitive.TextureEntryFace teFace = mesh.Prim.Textures.FaceTextures[j];\r
1835                 Face face = mesh.Faces[j];\r
1836                 FaceData data = (FaceData)mesh.Faces[j].UserData;\r
1837 \r
1838                 if (data == null)\r
1839                     continue;\r
1840 \r
1841                 if (teFace == null)\r
1842                     teFace = mesh.Prim.Textures.DefaultTexture;\r
1843 \r
1844                 if (teFace == null)\r
1845                     continue;\r
1846 \r
1847                 // Don't render transparent faces\r
1848                 if (data.TextureInfo.FullAlpha || teFace.RGBA.A <= 0.01f) continue;\r
1849 \r
1850                 int lightsEnabled;\r
1851                 GL.GetInteger(GetPName.Lighting, out lightsEnabled);\r
1852 \r
1853                 if (pass != RenderPass.Picking)\r
1854                 {\r
1855                     bool belongToAlphaPass = (teFace.RGBA.A < 0.99) || data.TextureInfo.HasAlpha;\r
1856 \r
1857                     if (belongToAlphaPass && pass != RenderPass.Alpha) continue;\r
1858                     if (!belongToAlphaPass && pass == RenderPass.Alpha) continue;\r
1859 \r
1860                     if (teFace.Fullbright && lightsEnabled != 0)\r
1861                     {\r
1862                         GL.Disable(EnableCap.Lighting);\r
1863                     }\r
1864 \r
1865                     switch (teFace.Shiny)\r
1866                     {\r
1867                         case Shininess.High:\r
1868                             GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 0.94f);\r
1869                             break;\r
1870 \r
1871                         case Shininess.Medium:\r
1872                             GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 0.64f);\r
1873                             break;\r
1874 \r
1875                         case Shininess.Low:\r
1876                             GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 0.24f);\r
1877                             break;\r
1878 \r
1879 \r
1880                         case Shininess.None:\r
1881                         default:\r
1882                             GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 0f);\r
1883                             break;\r
1884                     }\r
1885 \r
1886                     var faceColor = new float[] { teFace.RGBA.R, teFace.RGBA.G, teFace.RGBA.B, teFace.RGBA.A };\r
1887                     GL.Color4(faceColor);\r
1888 \r
1889                     GL.Material(MaterialFace.Front, MaterialParameter.Specular, new float[] { 0.5f, 0.5f, 0.5f, 1f });\r
1890 \r
1891                     if (data.TextureInfo.TexturePointer != 0)\r
1892                     {\r
1893                         // Is this face using texture animation\r
1894                         if ((prim.TextureAnim.Flags & Primitive.TextureAnimMode.ANIM_ON) != 0\r
1895                             && (prim.TextureAnim.Face == j || prim.TextureAnim.Face == 255))\r
1896                         {\r
1897                             if (data.AnimInfo == null)\r
1898                             {\r
1899                                 data.AnimInfo = new TextureAnimationInfo();\r
1900                             }\r
1901                             data.AnimInfo.PrimAnimInfo = prim.TextureAnim;\r
1902                             data.AnimInfo.Step(lastFrameTime);\r
1903                             animatedTexture = true;\r
1904                         }\r
1905                         else if (data.AnimInfo != null) // Face texture not animated. Do we have previous anim setting?\r
1906                         {\r
1907                             data.AnimInfo = null;\r
1908                         }\r
1909 \r
1910                         GL.Enable(EnableCap.Texture2D);\r
1911                         GL.BindTexture(TextureTarget.Texture2D, data.TextureInfo.TexturePointer);\r
1912                     }\r
1913                     else\r
1914                     {\r
1915                         GL.Disable(EnableCap.Texture2D);\r
1916                     }\r
1917 \r
1918                 }\r
1919                 else\r
1920                 {\r
1921                     data.PickingID = primNr;\r
1922                     var primNrBytes = Utils.UInt16ToBytes((ushort)primNr);\r
1923                     var faceColor = new byte[] { primNrBytes[0], primNrBytes[1], (byte)j, 255 };\r
1924                     GL.Color4(faceColor);\r
1925                 }\r
1926 \r
1927                 if (!useVBO)\r
1928                 {\r
1929                     Vertex[] verts = face.Vertices.ToArray();\r
1930 \r
1931                     unsafe\r
1932                     {\r
1933                         fixed (float* normalPtr = &verts[0].Normal.X)\r
1934                         fixed (float* texPtr = &verts[0].TexCoord.X)\r
1935                         {\r
1936                             GL.NormalPointer(NormalPointerType.Float, FaceData.VertexSize, (IntPtr)normalPtr);\r
1937                             GL.TexCoordPointer(2, TexCoordPointerType.Float, FaceData.VertexSize, (IntPtr)texPtr);\r
1938                             GL.VertexPointer(3, VertexPointerType.Float, FaceData.VertexSize, verts);\r
1939                             GL.DrawElements(BeginMode.Triangles, data.Indices.Length, DrawElementsType.UnsignedShort, data.Indices);\r
1940                         }\r
1941                     }\r
1942                 }\r
1943                 else\r
1944                 {\r
1945                     data.CheckVBO(face);\r
1946                     GL.BindBuffer(BufferTarget.ArrayBuffer, data.VertexVBO);\r
1947                     GL.BindBuffer(BufferTarget.ElementArrayBuffer, data.IndexVBO);\r
1948                     GL.NormalPointer(NormalPointerType.Float, FaceData.VertexSize, (IntPtr)12);\r
1949                     GL.TexCoordPointer(2, TexCoordPointerType.Float, FaceData.VertexSize, (IntPtr)(24));\r
1950                     GL.VertexPointer(3, VertexPointerType.Float, FaceData.VertexSize, (IntPtr)(0));\r
1951 \r
1952                     GL.DrawElements(BeginMode.Triangles, face.Indices.Count, DrawElementsType.UnsignedShort, IntPtr.Zero);\r
1953 \r
1954                     GL.BindBuffer(BufferTarget.ArrayBuffer, 0);\r
1955                     GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);\r
1956 \r
1957                 }\r
1958 \r
1959                 if (teFace.Fullbright && lightsEnabled != 0)\r
1960                 {\r
1961                     GL.Enable(EnableCap.Lighting);\r
1962                 }\r
1963             }\r
1964 \r
1965             GL.BindTexture(TextureTarget.Texture2D, 0);\r
1966             ResetMaterial();\r
1967 \r
1968             // Reset texture coordinates if we modified them in texture animation\r
1969             if (animatedTexture)\r
1970             {\r
1971                 GL.MatrixMode(MatrixMode.Texture);\r
1972                 GL.LoadIdentity();\r
1973                 GL.MatrixMode(MatrixMode.Modelview);\r
1974             }\r
1975 \r
1976             // Pop the prim matrix\r
1977             GL.PopMatrix();\r
1978         }\r
1979 \r
1980         void SortObjects()\r
1981         {\r
1982             SortedObjects = new List<SceneObject>(Prims.Count);\r
1983             lock (Avatars)\r
1984             {\r
1985                 SortedObjects.AddRange(Avatars.Values.ToArray());\r
1986             }\r
1987 \r
1988             lock (Prims)\r
1989             {\r
1990                 SortedObjects.AddRange(Prims.Values.ToArray());\r
1991             }\r
1992 \r
1993             foreach (SceneObject obj in SortedObjects)\r
1994             {\r
1995                 obj.PositionCalculated = false;\r
1996             }\r
1997 \r
1998             foreach (SceneObject obj in SortedObjects)\r
1999             {\r
2000                 if (!obj.PositionCalculated)\r
2001                 {\r
2002                     PrimPosAndRot(obj.BasePrim, out obj.SimPosition, out obj.SimRotation);\r
2003                     obj.DistanceSquared = Vector3.DistanceSquared(Camera.RenderPosition, obj.SimPosition);\r
2004                     obj.PositionCalculated = true;\r
2005                 }\r
2006             }\r
2007 \r
2008             // RenderPrimitive class has IComparable implementation\r
2009             // that allows sorting by distance\r
2010             SortedObjects.Sort();\r
2011         }\r
2012 \r
2013         private void RenderObjects(RenderPass pass)\r
2014         {\r
2015             if (!PrimitiveRenderingEnabled) return;\r
2016 \r
2017             GL.EnableClientState(ArrayCap.VertexArray);\r
2018             GL.EnableClientState(ArrayCap.TextureCoordArray);\r
2019             GL.EnableClientState(ArrayCap.NormalArray);\r
2020 \r
2021             Vector3 myPos = Vector3.Zero;\r
2022             RenderAvatar me;\r
2023             if (Avatars.TryGetValue(Client.Self.LocalID, out me))\r
2024             {\r
2025                 myPos = me.SimPosition;\r
2026             }\r
2027             else\r
2028             {\r
2029                 myPos = Client.Self.SimPosition;\r
2030             }\r
2031 \r
2032             int nrPrims = SortedObjects.Count;\r
2033             for (int i = 0; i < nrPrims; i++)\r
2034             {\r
2035                 //RenderBoundingBox(SortedPrims[i]);\r
2036 \r
2037                 // When rendering alpha faces, draw from back towards the camers\r
2038                 // otherwise from those closest to camera, to the farthest\r
2039                 int ix = pass == RenderPass.Alpha ? nrPrims - i - 1 : i;\r
2040                 SceneObject obj = SortedObjects[ix];\r
2041 \r
2042                 if (obj is RenderPrimitive)\r
2043                 {\r
2044                     // Don't render objects that are outside the draw distane\r
2045                     if (Vector3.DistanceSquared(myPos, obj.SimPosition) > drawDistanceSquared) continue;\r
2046 \r
2047                     // Don't render objects too small to matter\r
2048                     if (LODFactor(obj.DistanceSquared, obj.BasePrim.Scale, obj.BoundingVolume.R) < minLODFactor) continue;\r
2049 \r
2050                     // Don't render objects not in the field of view\r
2051                     if (!Frustum.ObjectInFrustum(obj.SimPosition, obj.BoundingVolume, obj.BasePrim.Scale)) continue;\r
2052 \r
2053                     RenderPrim((RenderPrimitive)obj, pass, ix);\r
2054                 }\r
2055             }\r
2056 \r
2057             GL.Disable(EnableCap.Texture2D);\r
2058             GL.DisableClientState(ArrayCap.VertexArray);\r
2059             GL.DisableClientState(ArrayCap.TextureCoordArray);\r
2060             GL.DisableClientState(ArrayCap.NormalArray);\r
2061         }\r
2062 \r
2063         void DrawWaterQuad(float x, float y, float z)\r
2064         {\r
2065             GL.Vertex3(x, y, z);\r
2066             GL.Vertex3(x + 256f, y, z);\r
2067             GL.Vertex3(x + 256f, y + 256f, z);\r
2068             GL.Vertex3(x, y + 256f, z);\r
2069         }\r
2070 \r
2071         public void RenderWater()\r
2072         {\r
2073             float z = Client.Network.CurrentSim.WaterHeight;\r
2074 \r
2075             GL.Color4(0.09f, 0.28f, 0.63f, 0.84f);\r
2076 \r
2077             GL.Begin(BeginMode.Quads);\r
2078             for (float x = -256f * 2; x <= 256 * 2; x += 256f)\r
2079                 for (float y = -256f * 2; y <= 256 * 2; y += 256f)\r
2080                     DrawWaterQuad(x, y, z);\r
2081             GL.End();\r
2082         }\r
2083 \r
2084         private void Render(bool picking)\r
2085         {\r
2086             if (picking)\r
2087             {\r
2088                 GL.ClearColor(1f, 1f, 1f, 1f);\r
2089             }\r
2090             else\r
2091             {\r
2092                 GL.ClearColor(0.39f, 0.58f, 0.93f, 1.0f);\r
2093             }\r
2094 \r
2095             GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);\r
2096             GL.LoadIdentity();\r
2097 \r
2098             // Setup wireframe or solid fill drawing mode\r
2099             if (Wireframe && !picking)\r
2100             {\r
2101                 GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);\r
2102             }\r
2103             else\r
2104             {\r
2105                 GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);\r
2106             }\r
2107 \r
2108             var mLookAt = OpenTK.Matrix4d.LookAt(\r
2109                     Camera.RenderPosition.X, Camera.RenderPosition.Y, Camera.RenderPosition.Z,\r
2110                     Camera.RenderFocalPoint.X, Camera.RenderFocalPoint.Y, Camera.RenderFocalPoint.Z,\r
2111                     0d, 0d, 1d);\r
2112             GL.MultMatrix(ref mLookAt);\r
2113 \r
2114             GL.Light(LightName.Light0, LightParameter.Position, lightPos);\r
2115 \r
2116             // Push the world matrix\r
2117             GL.PushMatrix();\r
2118 \r
2119             if (Camera.Modified)\r
2120             {\r
2121                 GL.GetFloat(GetPName.ProjectionMatrix, out ProjectionMatrix);\r
2122                 GL.GetFloat(GetPName.ModelviewMatrix, out ModelMatrix);\r
2123                 GL.GetInteger(GetPName.Viewport, Viewport);\r
2124                 Frustum.CalculateFrustum(ProjectionMatrix, ModelMatrix);\r
2125                 UpdateCamera();\r
2126                 Camera.Modified = false;\r
2127                 Camera.Step(lastFrameTime);\r
2128             }\r
2129 \r
2130             SortObjects();\r
2131 \r
2132             if (picking)\r
2133             {\r
2134                 RenderObjects(RenderPass.Picking);\r
2135                 RenderAvatars(RenderPass.Picking);\r
2136             }\r
2137             else\r
2138             {\r
2139                 RenderTerrain();\r
2140                 RenderObjects(RenderPass.Simple);\r
2141                 RenderAvatarsSkeleton(RenderPass.Simple);\r
2142                 RenderAvatars(RenderPass.Simple);\r
2143 \r
2144                 GL.Disable(EnableCap.Lighting);\r
2145                 GL.DepthMask(false);\r
2146                 RenderWater();\r
2147                 RenderObjects(RenderPass.Alpha);\r
2148                 GL.DepthMask(true);\r
2149                 GL.Enable(EnableCap.Lighting);\r
2150 \r
2151                 GLHUDBegin();\r
2152                 RenderText();\r
2153                 RenderStats();\r
2154                 GLHUDEnd();\r
2155             }\r
2156 \r
2157             // Pop the world matrix\r
2158             GL.PopMatrix();\r
2159         }\r
2160 \r
2161         private void GluPerspective(float fovy, float aspect, float zNear, float zFar)\r
2162         {\r
2163             float fH = (float)Math.Tan(fovy / 360 * (float)Math.PI) * zNear;\r
2164             float fW = fH * aspect;\r
2165             GL.Frustum(-fW, fW, -fH, fH, zNear, zFar);\r
2166         }\r
2167 \r
2168         private bool TryPick(int x, int y, out object picked, out int faceID)\r
2169         {\r
2170             // Save old attributes\r
2171             GL.PushAttrib(AttribMask.AllAttribBits);\r
2172 \r
2173             // Disable some attributes to make the objects flat / solid color when they are drawn\r
2174             GL.Disable(EnableCap.Fog);\r
2175             GL.Disable(EnableCap.Texture2D);\r
2176             GL.Disable(EnableCap.Dither);\r
2177             GL.Disable(EnableCap.Lighting);\r
2178             GL.Disable(EnableCap.LineStipple);\r
2179             GL.Disable(EnableCap.PolygonStipple);\r
2180             GL.Disable(EnableCap.CullFace);\r
2181             GL.Disable(EnableCap.Blend);\r
2182             GL.Disable(EnableCap.AlphaTest);\r
2183 \r
2184             Render(true);\r
2185 \r
2186             byte[] color = new byte[4];\r
2187             GL.ReadPixels(x, glControl.Height - y, 1, 1, OpenTK.Graphics.OpenGL.PixelFormat.Rgba, PixelType.UnsignedByte, color);\r
2188 \r
2189             GL.PopAttrib();\r
2190 \r
2191             int primID = Utils.BytesToUInt16(color, 0);\r
2192             faceID = color[2];\r
2193 \r
2194             picked = null;\r
2195 \r
2196             if (color[3] == 254) // Avatar\r
2197             {\r
2198                 lock (Avatars)\r
2199                 {\r
2200                     foreach (var avatar in Avatars.Values)\r
2201                     {\r
2202                         for (int i = 0; i < avatar.data.Length; i++)\r
2203                         {\r
2204                             var face = avatar.data[i];\r
2205                             if (face != null && face.PickingID == primID)\r
2206                             {\r
2207                                 picked = avatar;\r
2208                                 break;\r
2209                             }\r
2210                         }\r
2211                     }\r
2212                 }\r
2213 \r
2214                 if (picked != null)\r
2215                 {\r
2216                     return true;\r
2217                 }\r
2218             }\r
2219 \r
2220             if (color[3] == 255) // Prim\r
2221             {\r
2222                 lock (Prims)\r
2223                 {\r
2224                     foreach (var mesh in Prims.Values)\r
2225                     {\r
2226                         foreach (var face in mesh.Faces)\r
2227                         {\r
2228                             if (face.UserData == null) continue;\r
2229                             if (((FaceData)face.UserData).PickingID == primID)\r
2230                             {\r
2231                                 picked = mesh;\r
2232                                 break;\r
2233                             }\r
2234                         }\r
2235 \r
2236                         if (picked != null) break;\r
2237                     }\r
2238                 }\r
2239             }\r
2240 \r
2241             return picked != null;\r
2242         }\r
2243 \r
2244         public void DownloadTexture(TextureLoadItem item)\r
2245         {\r
2246             lock (TexturesPtrMap)\r
2247             {\r
2248                 if (TexturesPtrMap.ContainsKey(item.TeFace.TextureID))\r
2249                 {\r
2250                     item.Data.TextureInfo = TexturesPtrMap[item.TeFace.TextureID];\r
2251                 }\r
2252                 else\r
2253                 {\r
2254                     TexturesPtrMap[item.TeFace.TextureID] = item.Data.TextureInfo;\r
2255 \r
2256                     if (item.TextureData == null)\r
2257                     {\r
2258                         ThreadPool.QueueUserWorkItem(sync =>\r
2259                         {\r
2260                             Client.Assets.RequestImage(item.TeFace.TextureID, (state, asset) =>\r
2261                             {\r
2262                                 if (state == TextureRequestState.Finished)\r
2263                                 {\r
2264                                     item.TextureData = asset.AssetData;\r
2265                                     PendingTextures.Enqueue(item);\r
2266                                 }\r
2267                             });\r
2268                         });\r
2269                     }\r
2270                     else\r
2271                     {\r
2272                         PendingTextures.Enqueue(item);\r
2273                     }\r
2274                 }\r
2275             }\r
2276         }\r
2277 \r
2278         private void MeshPrim(Primitive prim, RenderPrimitive rprim)\r
2279         {\r
2280             RenderPrimitive existingMesh = null;\r
2281 \r
2282             lock (Prims)\r
2283             {\r
2284                 if (Prims.ContainsKey(prim.LocalID))\r
2285                 {\r
2286                     existingMesh = Prims[prim.LocalID];\r
2287                 }\r
2288             }\r
2289 \r
2290             // Calculate bounding volumes for each prim and adjust textures\r
2291             rprim.BoundingVolume = new BoundingVolume();\r
2292             for (int j = 0; j < rprim.Faces.Count; j++)\r
2293             {\r
2294                 Primitive.TextureEntryFace teFace = prim.Textures.GetFace((uint)j);\r
2295                 if (teFace == null) continue;\r
2296 \r
2297                 Face face = rprim.Faces[j];\r
2298                 FaceData data = new FaceData();\r
2299 \r
2300                 data.BoundingVolume.CreateBoundingVolume(face);\r
2301                 rprim.BoundingVolume.AddVolume(data.BoundingVolume);\r
2302 \r
2303                 // With linear texture animation in effect, texture repeats and offset are ignored\r
2304                 if ((prim.TextureAnim.Flags & Primitive.TextureAnimMode.ANIM_ON) != 0\r
2305                     && (prim.TextureAnim.Flags & Primitive.TextureAnimMode.ROTATE) == 0\r
2306                     && (prim.TextureAnim.Face == 255 || prim.TextureAnim.Face == j))\r
2307                 {\r
2308                     teFace.RepeatU = 1;\r
2309                     teFace.RepeatV = 1;\r
2310                     teFace.OffsetU = 0;\r
2311                     teFace.OffsetV = 0;\r
2312                 }\r
2313 \r
2314                 // Need to adjust UV for spheres as they are sort of half-prim\r
2315                 if (prim.PrimData.ProfileCurve == ProfileCurve.HalfCircle)\r
2316                 {\r
2317                     teFace = new Primitive.TextureEntryFace(teFace);\r
2318                     teFace.RepeatV *= 2;\r
2319                     teFace.OffsetV += 0.5f;\r
2320                 }\r
2321 \r
2322                 // Texture transform for this face\r
2323                 renderer.TransformTexCoords(face.Vertices, face.Center, teFace);\r
2324 \r
2325                 // Set the UserData for this face to our FaceData struct\r
2326                 face.UserData = data;\r
2327                 rprim.Faces[j] = face;\r
2328 \r
2329                 if (existingMesh != null &&\r
2330                     j < existingMesh.Faces.Count &&\r
2331                     existingMesh.Faces[j].TextureFace.TextureID == teFace.TextureID &&\r
2332                     ((FaceData)existingMesh.Faces[j].UserData).TextureInfo.TexturePointer != 0\r
2333                     )\r
2334                 {\r
2335                     FaceData existingData = (FaceData)existingMesh.Faces[j].UserData;\r
2336                     data.TextureInfo = existingData.TextureInfo;\r
2337                 }\r
2338                 else\r
2339                 {\r
2340 \r
2341                     DownloadTexture(new TextureLoadItem()\r
2342                     {\r
2343                         Data = data,\r
2344                         Prim = prim,\r
2345                         TeFace = teFace\r
2346                     });\r
2347                 }\r
2348             }\r
2349 \r
2350             lock (Prims)\r
2351             {\r
2352                 Prims[prim.LocalID] = rprim;\r
2353             }\r
2354         }\r
2355 \r
2356         private void UpdatePrimBlocking(Primitive prim)\r
2357         {\r
2358             if (AvatarRenderingEnabled && prim.PrimData.PCode == PCode.Avatar)\r
2359             {\r
2360                 AddAvatarToScene(Client.Network.CurrentSim.ObjectsAvatars[prim.LocalID]);\r
2361                 return;\r
2362             }\r
2363 \r
2364             // Skip foliage\r
2365             if (prim.PrimData.PCode != PCode.Prim) return;\r
2366             if (!PrimitiveRenderingEnabled) return;\r
2367 \r
2368             if (prim.Textures == null) return;\r
2369 \r
2370             RenderPrimitive rPrim = null;\r
2371             if (!Prims.TryGetValue(prim.LocalID, out rPrim))\r
2372             {\r
2373                 rPrim = new RenderPrimitive();\r
2374             }\r
2375 \r
2376             // Regular prim\r
2377             if (prim.Sculpt == null || prim.Sculpt.SculptTexture == UUID.Zero)\r
2378             {\r
2379                 FacetedMesh mesh = renderer.GenerateFacetedMesh(prim, DetailLevel.High);\r
2380                 rPrim.Faces = mesh.Faces;\r
2381                 rPrim.Prim = prim;\r
2382                 MeshPrim(prim, rPrim);\r
2383             }\r
2384             else\r
2385             {\r
2386                 try\r
2387                 {\r
2388                     FacetedMesh mesh = null;\r
2389 \r
2390                     if (prim.Sculpt.Type != SculptType.Mesh)\r
2391                     { // Regular sculptie\r
2392                         Image img = null;\r
2393 \r
2394                         lock (sculptCache)\r
2395                         {\r
2396                             if (sculptCache.ContainsKey(prim.Sculpt.SculptTexture))\r
2397                             {\r
2398                                 img = sculptCache[prim.Sculpt.SculptTexture];\r
2399                             }\r
2400                         }\r
2401 \r
2402                         if (img == null)\r
2403                         {\r
2404                             if (LoadTexture(prim.Sculpt.SculptTexture, ref img, true))\r
2405                             {\r
2406                                 sculptCache[prim.Sculpt.SculptTexture] = (Bitmap)img;\r
2407                             }\r
2408                             else\r
2409                             {\r
2410                                 return;\r
2411                             }\r
2412                         }\r
2413 \r
2414                         mesh = renderer.GenerateFacetedSculptMesh(prim, (Bitmap)img, DetailLevel.High);\r
2415                     }\r
2416                     else\r
2417                     { // Mesh\r
2418                         AutoResetEvent gotMesh = new AutoResetEvent(false);\r
2419                         bool meshSuccess = false;\r
2420 \r
2421                         Client.Assets.RequestMesh(prim.Sculpt.SculptTexture, (success, meshAsset) =>\r
2422                             {\r
2423                                 if (!success || !FacetedMesh.TryDecodeFromAsset(prim, meshAsset, DetailLevel.Highest, out mesh))\r
2424                                 {\r
2425                                     Logger.Log("Failed to fetch or decode the mesh asset", Helpers.LogLevel.Warning, Client);\r
2426                                 }\r
2427                                 else\r
2428                                 {\r
2429                                     meshSuccess = true;\r
2430                                 }\r
2431                                 gotMesh.Set();\r
2432                             });\r
2433 \r
2434                         if (!gotMesh.WaitOne(20 * 1000, false)) return;\r
2435                         if (!meshSuccess) return;\r
2436                     }\r
2437 \r
2438                     if (mesh != null)\r
2439                     {\r
2440                         rPrim.Faces = mesh.Faces;\r
2441                         rPrim.Prim = prim;\r
2442                         MeshPrim(prim, rPrim);\r
2443                     }\r
2444                 }\r
2445                 catch\r
2446                 { }\r
2447             }\r
2448         }\r
2449 \r
2450         private bool LoadTexture(UUID textureID, ref Image texture, bool removeAlpha)\r
2451         {\r
2452             ManualResetEvent gotImage = new ManualResetEvent(false);\r
2453             Image img = null;\r
2454 \r
2455             try\r
2456             {\r
2457                 gotImage.Reset();\r
2458                 instance.Client.Assets.RequestImage(textureID, (TextureRequestState state, AssetTexture assetTexture) =>\r
2459                     {\r
2460                         if (state == TextureRequestState.Finished)\r
2461                         {\r
2462                             ManagedImage mi;\r
2463                             OpenJPEG.DecodeToImage(assetTexture.AssetData, out mi);\r
2464 \r
2465                             if (removeAlpha)\r
2466                             {\r
2467                                 if ((mi.Channels & ManagedImage.ImageChannels.Alpha) != 0)\r
2468                                 {\r
2469                                     mi.ConvertChannels(mi.Channels & ~ManagedImage.ImageChannels.Alpha);\r
2470                                 }\r
2471                             }\r
2472 \r
2473                             img = LoadTGAClass.LoadTGA(new MemoryStream(mi.ExportTGA()));\r
2474                         }\r
2475                         gotImage.Set();\r
2476                     }\r
2477                 );\r
2478                 gotImage.WaitOne(30 * 1000, false);\r
2479                 if (img != null)\r
2480                 {\r
2481                     texture = img;\r
2482                     return true;\r
2483                 }\r
2484                 return false;\r
2485             }\r
2486             catch (Exception e)\r
2487             {\r
2488                 Logger.Log(e.Message, Helpers.LogLevel.Error, instance.Client, e);\r
2489                 return false;\r
2490             }\r
2491         }\r
2492         #endregion Private methods (the meat)\r
2493 \r
2494         #region Form controls handlers\r
2495         private void chkWireFrame_CheckedChanged(object sender, EventArgs e)\r
2496         {\r
2497             Wireframe = chkWireFrame.Checked;\r
2498         }\r
2499 \r
2500         private void btnReset_Click(object sender, EventArgs e)\r
2501         {\r
2502             InitCamera();\r
2503         }\r
2504 \r
2505         private void cbAA_CheckedChanged(object sender, EventArgs e)\r
2506         {\r
2507             instance.GlobalSettings["use_multi_sampling"] = UseMultiSampling = cbAA.Checked;\r
2508             SetupGLControl();\r
2509         }\r
2510 \r
2511         #endregion Form controls handlers\r
2512 \r
2513         #region Context menu\r
2514         private void ctxObjects_Opening(object sender, System.ComponentModel.CancelEventArgs e)\r
2515         {\r
2516             if (instance.State.IsSitting)\r
2517             {\r
2518                 sitToolStripMenuItem.Text = "Stand up";\r
2519             }\r
2520             else if (RightclickedPrim.Prim.Properties != null\r
2521                 && !string.IsNullOrEmpty(RightclickedPrim.Prim.Properties.SitName))\r
2522             {\r
2523                 sitToolStripMenuItem.Text = RightclickedPrim.Prim.Properties.SitName;\r
2524             }\r
2525             else\r
2526             {\r
2527                 sitToolStripMenuItem.Text = "Sit";\r
2528             }\r
2529 \r
2530             if (RightclickedPrim.Prim.Properties != null\r
2531                 && !string.IsNullOrEmpty(RightclickedPrim.Prim.Properties.TouchName))\r
2532             {\r
2533                 touchToolStripMenuItem.Text = RightclickedPrim.Prim.Properties.TouchName;\r
2534             }\r
2535             else\r
2536             {\r
2537                 touchToolStripMenuItem.Text = "Touch";\r
2538             }\r
2539         }\r
2540 \r
2541         private void touchToolStripMenuItem_Click(object sender, EventArgs e)\r
2542         {\r
2543 \r
2544             Client.Self.Grab(RightclickedPrim.Prim.LocalID, Vector3.Zero, Vector3.Zero, Vector3.Zero, RightclickedFaceID, Vector3.Zero, Vector3.Zero, Vector3.Zero);\r
2545             Thread.Sleep(100);\r
2546             Client.Self.DeGrab(RightclickedPrim.Prim.LocalID);\r
2547         }\r
2548 \r
2549         private void sitToolStripMenuItem_Click(object sender, EventArgs e)\r
2550         {\r
2551             if (!instance.State.IsSitting)\r
2552             {\r
2553                 instance.State.SetSitting(true, RightclickedPrim.Prim.ID);\r
2554             }\r
2555             else\r
2556             {\r
2557                 instance.State.SetSitting(false, UUID.Zero);\r
2558             }\r
2559         }\r
2560 \r
2561         private void takeToolStripMenuItem_Click(object sender, EventArgs e)\r
2562         {\r
2563             instance.MediaManager.PlayUISound(UISounds.ObjectDelete);\r
2564             Client.Inventory.RequestDeRezToInventory(RightclickedPrim.Prim.LocalID);\r
2565             Close();\r
2566         }\r
2567 \r
2568         private void returnToolStripMenuItem_Click(object sender, EventArgs e)\r
2569         {\r
2570             instance.MediaManager.PlayUISound(UISounds.ObjectDelete);\r
2571             Client.Inventory.RequestDeRezToInventory(RightclickedPrim.Prim.LocalID, DeRezDestination.ReturnToOwner, UUID.Zero, UUID.Random());\r
2572             Close();\r
2573         }\r
2574 \r
2575         private void deleteToolStripMenuItem_Click(object sender, EventArgs e)\r
2576         {\r
2577             if (RightclickedPrim.Prim.Properties != null && RightclickedPrim.Prim.Properties.OwnerID != Client.Self.AgentID)\r
2578                 returnToolStripMenuItem_Click(sender, e);\r
2579             else\r
2580             {\r
2581                 instance.MediaManager.PlayUISound(UISounds.ObjectDelete);\r
2582                 Client.Inventory.RequestDeRezToInventory(RightclickedPrim.Prim.LocalID, DeRezDestination.AgentInventoryTake, Client.Inventory.FindFolderForType(AssetType.TrashFolder), UUID.Random());\r
2583             }\r
2584             Close();\r
2585         }\r
2586         #endregion Context menu\r
2587 \r
2588         private void hsAmbient_Scroll(object sender, ScrollEventArgs e)\r
2589         {\r
2590             ambient = (float)hsAmbient.Value / 100f;\r
2591             SetSun();\r
2592         }\r
2593 \r
2594         private void hsDiffuse_Scroll(object sender, ScrollEventArgs e)\r
2595         {\r
2596             difuse = (float)hsDiffuse.Value / 100f;\r
2597             SetSun();\r
2598         }\r
2599 \r
2600         private void hsSpecular_Scroll(object sender, ScrollEventArgs e)\r
2601         {\r
2602             specular = (float)hsSpecular.Value / 100f;\r
2603             SetSun();\r
2604         }\r
2605 \r
2606         private void hsLOD_Scroll(object sender, ScrollEventArgs e)\r
2607         {\r
2608             minLODFactor = (float)hsLOD.Value / 5000f;\r
2609         }\r
2610 \r
2611         private void button_vparam_Click(object sender, EventArgs e)\r
2612         {\r
2613             //int paramid = int.Parse(textBox_vparamid.Text);\r
2614             //float weight = (float)hScrollBar_weight.Value/100f;\r
2615             float weightx = float.Parse(textBox_x.Text);\r
2616             float weighty = float.Parse(textBox_y.Text);\r
2617             float weightz = float.Parse(textBox_z.Text);\r
2618 \r
2619             foreach (RenderAvatar av in Avatars.Values)\r
2620             {\r
2621                 //av.glavatar.morphtest(av.avatar,paramid,weight);\r
2622                 av.glavatar.skel.deformbone(comboBox1.Text, new Vector3(0, 0, 0), new Vector3(float.Parse(textBox_sx.Text), float.Parse(textBox_sy.Text), float.Parse(textBox_sz.Text)), Quaternion.CreateFromEulers((float)(Math.PI * (weightx / 180)), (float)(Math.PI * (weighty / 180)), (float)(Math.PI * (weightz / 180))));\r
2623 \r
2624                 foreach (GLMesh mesh in av.glavatar._meshes.Values)\r
2625                 {\r
2626                     mesh.applyjointweights();\r
2627                 }\r
2628 \r
2629             }\r
2630         }\r
2631 \r
2632         private void textBox_vparamid_TextChanged(object sender, EventArgs e)\r
2633         {\r
2634 \r
2635         }\r
2636 \r
2637         private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)\r
2638         {\r
2639 \r
2640             string bone = comboBox1.Text;\r
2641             foreach (RenderAvatar av in Avatars.Values)\r
2642             {\r
2643                 Bone b;\r
2644                 if (av.glavatar.skel.mBones.TryGetValue(bone, out b))\r
2645                 {\r
2646                     textBox_sx.Text = (b.scale.X - 1.0f).ToString();\r
2647                     textBox_sy.Text = (b.scale.Y - 1.0f).ToString();\r
2648                     textBox_sz.Text = (b.scale.Z - 1.0f).ToString();\r
2649 \r
2650                     float x, y, z;\r
2651                     b.rot.GetEulerAngles(out x, out y, out z);\r
2652                     textBox_x.Text = x.ToString();\r
2653                     textBox_y.Text = y.ToString();\r
2654                     textBox_z.Text = z.ToString();\r
2655 \r
2656                 }\r
2657 \r
2658             }\r
2659 \r
2660 \r
2661         }\r
2662 \r
2663         private void textBox_y_TextChanged(object sender, EventArgs e)\r
2664         {\r
2665 \r
2666         }\r
2667 \r
2668         private void textBox_z_TextChanged(object sender, EventArgs e)\r
2669         {\r
2670 \r
2671         }\r
2672 \r
2673         private void comboBox_morph_SelectedIndexChanged(object sender, EventArgs e)\r
2674         {\r
2675 \r
2676         }\r
2677 \r
2678         private void button1_Click(object sender, EventArgs e)\r
2679         {\r
2680             foreach (RenderAvatar av in Avatars.Values)\r
2681             {\r
2682                 int id = -1;\r
2683                 foreach (VisualParamEx vpe in VisualParamEx.morphParams.Values)\r
2684                 {\r
2685                     if (vpe.Name == comboBox_morph.Text)\r
2686                     {\r
2687                         id = vpe.ParamID;\r
2688                         break;\r
2689                     }\r
2690 \r
2691                 }\r
2692                 av.glavatar.morphtest(av.avatar, id, float.Parse(textBox_morphamount.Text));\r
2693 \r
2694                 foreach (GLMesh mesh in av.glavatar._meshes.Values)\r
2695                 {\r
2696                     mesh.applyjointweights();\r
2697                 }\r
2698 \r
2699             }\r
2700 \r
2701 \r
2702 \r
2703         }\r
2704 \r
2705         private void gbZoom_Enter(object sender, EventArgs e)\r
2706         {\r
2707 \r
2708         }\r
2709 \r
2710         private void button_driver_Click(object sender, EventArgs e)\r
2711         {\r
2712             foreach (RenderAvatar av in Avatars.Values)\r
2713             {\r
2714                 int id = -1;\r
2715                 foreach (VisualParamEx vpe in VisualParamEx.drivenParams.Values)\r
2716                 {\r
2717                     if (vpe.Name == comboBox_driver.Text)\r
2718                     {\r
2719                         id = vpe.ParamID;\r
2720                         break;\r
2721                     }\r
2722 \r
2723                 }\r
2724                 av.glavatar.morphtest(av.avatar, id, float.Parse(textBox_driveramount.Text));\r
2725 \r
2726                 foreach (GLMesh mesh in av.glavatar._meshes.Values)\r
2727                 {\r
2728                     mesh.applyjointweights();\r
2729                 }\r
2730 \r
2731             }\r
2732 \r
2733         }\r
2734 \r
2735         private void tbDrawDistance_Scroll(object sender, EventArgs e)\r
2736         {\r
2737             DrawDistance = (float)tbDrawDistance.Value;\r
2738             lblDrawDistance.Text = string.Format("Draw distance: {0}", tbDrawDistance.Value);\r
2739             UpdateCamera();\r
2740         }\r
2741 \r
2742         bool miscEnabled = true;\r
2743         private void cbMisc_CheckedChanged(object sender, EventArgs e)\r
2744         {\r
2745             miscEnabled = cbMisc.Checked;\r
2746         }\r
2747 \r
2748 \r
2749     }\r
2750 }\r