OSDN Git Service

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