OSDN Git Service

* Don't send the whole big data object for the texture queue (reduces memory usage)
[radegast/radegast.git] / Radegast / GUI / Rendering / Rendering.cs
1 // \r
2 // Radegast Metaverse Client\r
3 // Copyright (c) 2009-2011, Radegast Development Team\r
4 // All rights reserved.\r
5 // \r
6 // Redistribution and use in source and binary forms, with or without\r
7 // modification, are permitted provided that the following conditions are met:\r
8 // \r
9 //     * Redistributions of source code must retain the above copyright notice,\r
10 //       this list of conditions and the following disclaimer.\r
11 //     * Redistributions in binary form must reproduce the above copyright\r
12 //       notice, this list of conditions and the following disclaimer in the\r
13 //       documentation and/or other materials provided with the distribution.\r
14 //     * Neither the name of the application "Radegast", nor the names of its\r
15 //       contributors may be used to endorse or promote products derived from\r
16 //       this software without specific prior written permission.\r
17 // \r
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r
21 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\r
22 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
23 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\r
24 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\r
25 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\r
26 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
28 //\r
29 // $Id$\r
30 //\r
31 \r
32 #region Usings\r
33 using System;\r
34 using System.Collections.Generic;\r
35 using System.Drawing;\r
36 using System.Drawing.Imaging;\r
37 using System.IO;\r
38 using System.Windows.Forms;\r
39 using System.Text;\r
40 using System.Threading;\r
41 using System.Linq;\r
42 using OpenTK.Graphics.OpenGL;\r
43 using OpenMetaverse;\r
44 using OpenMetaverse.Rendering;\r
45 using OpenMetaverse.Assets;\r
46 using OpenMetaverse.Imaging;\r
47 using OpenMetaverse.StructuredData;\r
48 #endregion Usings\r
49 \r
50 namespace Radegast.Rendering\r
51 {\r
52 \r
53     public partial class SceneWindow : RadegastForm\r
54     {\r
55         #region Public fields\r
56         /// <summary>\r
57         /// The OpenGL surface\r
58         /// </summary>\r
59         public OpenTK.GLControl glControl = null;\r
60 \r
61         /// <summary>\r
62         /// Use multi sampling (anti aliasing)\r
63         /// </summary>\r
64         public bool UseMultiSampling = true;\r
65 \r
66         /// <summary>\r
67         /// Is rendering engine ready and enabled\r
68         /// </summary>\r
69         public bool RenderingEnabled = false;\r
70 \r
71         /// <summary>\r
72         /// Rednder in wireframe mode\r
73         /// </summary>\r
74         public bool Wireframe = false;\r
75 \r
76         /// <summary>\r
77         /// Object from up to this distance from us will be rendered\r
78         /// </summary>\r
79         public float DrawDistance = 48f;\r
80 \r
81         /// <summary>\r
82         /// List of prims in the scene\r
83         /// </summary>\r
84         Dictionary<uint, FacetedMesh> Prims = new Dictionary<uint, FacetedMesh>();\r
85 \r
86         Dictionary<uint, RenderAvatar> Avatars = new Dictionary<uint, RenderAvatar>();\r
87 \r
88         #endregion Public fields\r
89 \r
90         #region Private fields\r
91 \r
92         Camera Camera;\r
93         Dictionary<UUID, TextureInfo> TexturesPtrMap = new Dictionary<UUID, TextureInfo>();\r
94         RadegastInstance instance;\r
95         MeshmerizerR renderer;\r
96         OpenTK.Graphics.GraphicsMode GLMode = null;\r
97         AutoResetEvent TextureThreadContextReady = new AutoResetEvent(false);\r
98         BlockingQueue<TextureLoadItem> PendingTextures = new BlockingQueue<TextureLoadItem>();\r
99         float[] lightPos = new float[] { 0f, 0f, 1f, 0f };\r
100         bool hasMipmap;\r
101         Font HoverTextFont = new Font(FontFamily.GenericSansSerif, 9f, FontStyle.Regular);\r
102         Font AvatarTagFont = new Font(FontFamily.GenericSansSerif, 10f, FontStyle.Bold);\r
103         Dictionary<UUID, Bitmap> sculptCache = new Dictionary<UUID, Bitmap>();\r
104         OpenTK.Matrix4 ModelMatrix;\r
105         OpenTK.Matrix4 ProjectionMatrix;\r
106         int[] Viewport = new int[4];\r
107         bool useVBO = true;\r
108         int lastTimerTick;\r
109         int advTimerTick;\r
110 \r
111         #endregion Private fields\r
112 \r
113         #region Construction and disposal\r
114         public SceneWindow(RadegastInstance instance)\r
115             : base(instance)\r
116         {\r
117             InitializeComponent();\r
118             Disposed += new EventHandler(frmPrimWorkshop_Disposed);\r
119             AutoSavePosition = true;\r
120             UseMultiSampling = cbAA.Checked = instance.GlobalSettings["use_multi_sampling"];\r
121             cbAA.CheckedChanged += cbAA_CheckedChanged;\r
122 \r
123             this.instance = instance;\r
124 \r
125             renderer = new MeshmerizerR();\r
126 \r
127             // Camera initial setting\r
128             Camera = new Camera();\r
129             InitCamera();\r
130 \r
131             GLAvatar.loadlindenmeshes("avatar_lad.xml");\r
132 \r
133             Client.Objects.TerseObjectUpdate += new EventHandler<TerseObjectUpdateEventArgs>(Objects_TerseObjectUpdate);\r
134             Client.Objects.ObjectUpdate += new EventHandler<PrimEventArgs>(Objects_ObjectUpdate);\r
135             Client.Objects.ObjectDataBlockUpdate += new EventHandler<ObjectDataBlockUpdateEventArgs>(Objects_ObjectDataBlockUpdate);\r
136             Client.Objects.KillObject += new EventHandler<KillObjectEventArgs>(Objects_KillObject);\r
137             Client.Network.SimChanged += new EventHandler<SimChangedEventArgs>(Network_SimChanged);\r
138             Client.Self.TeleportProgress += new EventHandler<TeleportEventArgs>(Self_TeleportProgress);\r
139             Client.Terrain.LandPatchReceived += new EventHandler<LandPatchReceivedEventArgs>(Terrain_LandPatchReceived);\r
140             Instance.Netcom.ClientDisconnected += new EventHandler<DisconnectedEventArgs>(Netcom_ClientDisconnected);\r
141             Application.Idle += new EventHandler(Application_Idle);\r
142         }\r
143 \r
144         void frmPrimWorkshop_Disposed(object sender, EventArgs e)\r
145         {\r
146             Application.Idle -= new EventHandler(Application_Idle);\r
147 \r
148             PendingTextures.Close();\r
149 \r
150             Client.Objects.TerseObjectUpdate -= new EventHandler<TerseObjectUpdateEventArgs>(Objects_TerseObjectUpdate);\r
151             Client.Objects.ObjectUpdate -= new EventHandler<PrimEventArgs>(Objects_ObjectUpdate);\r
152             Client.Objects.ObjectDataBlockUpdate -= new EventHandler<ObjectDataBlockUpdateEventArgs>(Objects_ObjectDataBlockUpdate);\r
153             Client.Objects.KillObject -= new EventHandler<KillObjectEventArgs>(Objects_KillObject);\r
154             Client.Network.SimChanged -= new EventHandler<SimChangedEventArgs>(Network_SimChanged);\r
155             Client.Self.TeleportProgress -= new EventHandler<TeleportEventArgs>(Self_TeleportProgress);\r
156             Client.Terrain.LandPatchReceived -= new EventHandler<LandPatchReceivedEventArgs>(Terrain_LandPatchReceived);\r
157             Instance.Netcom.ClientDisconnected -= new EventHandler<DisconnectedEventArgs>(Netcom_ClientDisconnected);\r
158 \r
159             if (glControl != null)\r
160             {\r
161                 glControl.Dispose();\r
162             }\r
163             glControl = null;\r
164 \r
165             lock (sculptCache)\r
166             {\r
167                 foreach (var img in sculptCache.Values)\r
168                     img.Dispose();\r
169                 sculptCache.Clear();\r
170             }\r
171 \r
172             lock (Prims) Prims.Clear();\r
173             lock (Avatars) Avatars.Clear();\r
174 \r
175             TexturesPtrMap.Clear();\r
176             GC.Collect();\r
177         }\r
178 \r
179         void Application_Idle(object sender, EventArgs e)\r
180         {\r
181             if (glControl != null && !glControl.IsDisposed && RenderingEnabled)\r
182             {\r
183                 try\r
184                 {\r
185                     while (glControl != null && glControl.IsIdle)\r
186                     {\r
187                         MainRenderLoop();\r
188                         if (instance.MonoRuntime)\r
189                         {\r
190                             Application.DoEvents();\r
191                         }\r
192                     }\r
193                 }\r
194                 catch (ObjectDisposedException)\r
195                 { }\r
196             }\r
197         }\r
198         #endregion Construction and disposal\r
199 \r
200         #region Network messaage handlers\r
201         void Terrain_LandPatchReceived(object sender, LandPatchReceivedEventArgs e)\r
202         {\r
203             if (e.Simulator.Handle == Client.Network.CurrentSim.Handle)\r
204             {\r
205                 TerrainModified = true;\r
206             }\r
207         }\r
208 \r
209         void Netcom_ClientDisconnected(object sender, DisconnectedEventArgs e)\r
210         {\r
211             if (InvokeRequired)\r
212             {\r
213                 if (IsHandleCreated || !instance.MonoRuntime)\r
214                 {\r
215                     BeginInvoke(new MethodInvoker(() => Netcom_ClientDisconnected(sender, e)));\r
216                 }\r
217                 return;\r
218             }\r
219 \r
220             Dispose();\r
221         }\r
222 \r
223         void Self_TeleportProgress(object sender, TeleportEventArgs e)\r
224         {\r
225             switch (e.Status)\r
226             {\r
227                 case TeleportStatus.Progress:\r
228                 case TeleportStatus.Start:\r
229                     RenderingEnabled = false;\r
230                     break;\r
231 \r
232                 case TeleportStatus.Cancelled:\r
233                 case TeleportStatus.Failed:\r
234                     RenderingEnabled = true;\r
235                     break;\r
236 \r
237                 case TeleportStatus.Finished:\r
238                     ThreadPool.QueueUserWorkItem(sync =>\r
239                     {\r
240                         Thread.Sleep(3000);\r
241                         InitCamera();\r
242                         LoadCurrentPrims();\r
243                         RenderingEnabled = true;\r
244                     });\r
245                     break;\r
246             }\r
247         }\r
248 \r
249         void Network_SimChanged(object sender, SimChangedEventArgs e)\r
250         {\r
251             ResetTerrain();\r
252             lock (sculptCache)\r
253             {\r
254                 foreach (var img in sculptCache.Values)\r
255                     img.Dispose();\r
256                 sculptCache.Clear();\r
257             }\r
258             lock (Prims) Prims.Clear();\r
259         }\r
260 \r
261         void Objects_KillObject(object sender, KillObjectEventArgs e)\r
262         {\r
263             if (e.Simulator.Handle != Client.Network.CurrentSim.Handle) return;\r
264             // TODO: there should be really cleanup of resources when removing prims and avatars\r
265             lock (Prims) Prims.Remove(e.ObjectLocalID);\r
266             lock (Avatars) Avatars.Remove(e.ObjectLocalID);\r
267         }\r
268 \r
269         void Objects_TerseObjectUpdate(object sender, TerseObjectUpdateEventArgs e)\r
270         {\r
271             if (e.Simulator.Handle != Client.Network.CurrentSim.Handle) return;\r
272             UpdatePrimBlocking(e.Prim);\r
273         }\r
274 \r
275         void Objects_ObjectUpdate(object sender, PrimEventArgs e)\r
276         {\r
277             if (e.Simulator.Handle != Client.Network.CurrentSim.Handle) return;\r
278             UpdatePrimBlocking(e.Prim);\r
279         }\r
280 \r
281         void Objects_ObjectDataBlockUpdate(object sender, ObjectDataBlockUpdateEventArgs e)\r
282         {\r
283             if (e.Simulator.Handle != Client.Network.CurrentSim.Handle) return;\r
284             UpdatePrimBlocking(e.Prim);\r
285         }\r
286         #endregion Network messaage handlers\r
287 \r
288         #region glControl setup and disposal\r
289         public void SetupGLControl()\r
290         {\r
291             RenderingEnabled = false;\r
292 \r
293             if (glControl != null)\r
294                 glControl.Dispose();\r
295             glControl = null;\r
296 \r
297             GLMode = null;\r
298 \r
299             try\r
300             {\r
301                 if (!UseMultiSampling)\r
302                 {\r
303                     GLMode = new OpenTK.Graphics.GraphicsMode(OpenTK.DisplayDevice.Default.BitsPerPixel, 24, 8, 0);\r
304                 }\r
305                 else\r
306                 {\r
307                     for (int aa = 0; aa <= 4; aa += 2)\r
308                     {\r
309                         var testMode = new OpenTK.Graphics.GraphicsMode(OpenTK.DisplayDevice.Default.BitsPerPixel, 24, 8, aa);\r
310                         if (testMode.Samples == aa)\r
311                         {\r
312                             GLMode = testMode;\r
313                         }\r
314                     }\r
315                 }\r
316             }\r
317             catch\r
318             {\r
319                 GLMode = null;\r
320             }\r
321 \r
322 \r
323             try\r
324             {\r
325                 if (GLMode == null)\r
326                 {\r
327                     // Try default mode\r
328                     glControl = new OpenTK.GLControl();\r
329                 }\r
330                 else\r
331                 {\r
332                     glControl = new OpenTK.GLControl(GLMode);\r
333                 }\r
334             }\r
335             catch (Exception ex)\r
336             {\r
337                 Logger.Log(ex.Message, Helpers.LogLevel.Warning, Client);\r
338                 glControl = null;\r
339             }\r
340 \r
341             if (glControl == null)\r
342             {\r
343                 Logger.Log("Failed to initialize OpenGL control, cannot continue", Helpers.LogLevel.Error, Client);\r
344                 return;\r
345             }\r
346 \r
347             Logger.Log("Initializing OpenGL mode: " + GLMode.ToString(), Helpers.LogLevel.Info);\r
348 \r
349             glControl.Paint += glControl_Paint;\r
350             glControl.Resize += glControl_Resize;\r
351             glControl.MouseDown += glControl_MouseDown;\r
352             glControl.MouseUp += glControl_MouseUp;\r
353             glControl.MouseMove += glControl_MouseMove;\r
354             glControl.MouseWheel += glControl_MouseWheel;\r
355             glControl.Load += new EventHandler(glControl_Load);\r
356             glControl.Disposed += new EventHandler(glControl_Disposed);\r
357             glControl.Dock = DockStyle.Fill;\r
358             glControl.VSync = false;\r
359             Controls.Add(glControl);\r
360             glControl.BringToFront();\r
361         }\r
362 \r
363         void glControl_Disposed(object sender, EventArgs e)\r
364         {\r
365             TextureThreadRunning = false;\r
366             PendingTextures.Close();\r
367             glControl.Paint -= glControl_Paint;\r
368             glControl.Resize -= glControl_Resize;\r
369             glControl.MouseDown -= glControl_MouseDown;\r
370             glControl.MouseUp -= glControl_MouseUp;\r
371             glControl.MouseMove -= glControl_MouseMove;\r
372             glControl.MouseWheel -= glControl_MouseWheel;\r
373             glControl.Load -= new EventHandler(glControl_Load);\r
374             glControl.Disposed -= glControl_Disposed;\r
375         }\r
376 \r
377         void glControl_Load(object sender, EventArgs e)\r
378         {\r
379             try\r
380             {\r
381                 GL.ShadeModel(ShadingModel.Smooth);\r
382 \r
383                 //GL.LightModel(LightModelParameter.LightModelAmbient, new float[] { 0.5f, 0.5f, 0.5f, 1.0f });\r
384 \r
385                 GL.Enable(EnableCap.Lighting);\r
386                 GL.Enable(EnableCap.Light0);\r
387                 GL.Light(LightName.Light0, LightParameter.Ambient, new float[] { 0.5f, 0.5f, 0.5f, 1f });\r
388                 GL.Light(LightName.Light0, LightParameter.Diffuse, new float[] { 0.3f, 0.3f, 0.3f, 1f });\r
389                 GL.Light(LightName.Light0, LightParameter.Specular, new float[] { 0.8f, 0.8f, 0.8f, 1.0f });\r
390                 GL.Light(LightName.Light0, LightParameter.Position, lightPos);\r
391 \r
392                 GL.ClearDepth(1.0d);\r
393                 GL.Enable(EnableCap.DepthTest);\r
394                 GL.Enable(EnableCap.CullFace);\r
395                 GL.CullFace(CullFaceMode.Back);\r
396                 GL.ColorMaterial(MaterialFace.Front, ColorMaterialParameter.AmbientAndDiffuse);\r
397                 GL.ColorMaterial(MaterialFace.Front, ColorMaterialParameter.Specular);\r
398 \r
399                 GL.DepthMask(true);\r
400                 GL.DepthFunc(DepthFunction.Lequal);\r
401                 GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);\r
402                 GL.MatrixMode(MatrixMode.Projection);\r
403 \r
404                 GL.Enable(EnableCap.Blend);\r
405                 GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);\r
406                 hasMipmap = GL.GetString(StringName.Extensions).Contains("GL_SGIS_generate_mipmap");\r
407 \r
408                 // Double check if we have mipmap ability\r
409                 if (hasMipmap)\r
410                 {\r
411                     try\r
412                     {\r
413                         GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);\r
414                     }\r
415                     catch\r
416                     {\r
417                         Logger.DebugLog("Don't have glGenerateMipmap() after all");\r
418                         hasMipmap = false;\r
419                     }\r
420                 }\r
421 \r
422                 RenderingEnabled = true;\r
423                 // Call the resizing function which sets up the GL drawing window\r
424                 // and will also invalidate the GL control\r
425                 glControl_Resize(null, null);\r
426 \r
427                 glControl.Context.MakeCurrent(null);\r
428                 TextureThreadContextReady.Reset();\r
429                 var textureThread = new Thread(() => TextureThread())\r
430                 {\r
431                     IsBackground = true,\r
432                     Name = "TextureLoadingThread"\r
433                 };\r
434                 textureThread.Start();\r
435                 TextureThreadContextReady.WaitOne(1000, false);\r
436                 this.lastTimerTick = Environment.TickCount;\r
437                 glControl.MakeCurrent();\r
438             }\r
439             catch (Exception ex)\r
440             {\r
441                 RenderingEnabled = false;\r
442                 Logger.Log("Failed to initialize OpenGL control", Helpers.LogLevel.Warning, Client, ex);\r
443             }\r
444         }\r
445         #endregion glControl setup and disposal\r
446 \r
447         #region glControl paint and resize events\r
448         private void MainRenderLoop()\r
449         {\r
450             if (!RenderingEnabled) return;\r
451 \r
452             Render(false);\r
453 \r
454             glControl.SwapBuffers();\r
455         }\r
456 \r
457         void glControl_Paint(object sender, EventArgs e)\r
458         {\r
459             MainRenderLoop();\r
460         }\r
461 \r
462         private void glControl_Resize(object sender, EventArgs e)\r
463         {\r
464             if (!RenderingEnabled) return;\r
465             glControl.MakeCurrent();\r
466 \r
467             GL.ClearColor(0.39f, 0.58f, 0.93f, 1.0f);\r
468 \r
469             GL.Viewport(0, 0, glControl.Width, glControl.Height);\r
470 \r
471             GL.PushMatrix();\r
472             GL.MatrixMode(MatrixMode.Projection);\r
473             GL.LoadIdentity();\r
474 \r
475             SetPerspective();\r
476 \r
477             GL.MatrixMode(MatrixMode.Modelview);\r
478             GL.PopMatrix();\r
479         }\r
480         #endregion glControl paint and resize events\r
481 \r
482         #region Mouse handling\r
483         bool dragging = false;\r
484         int dragX, dragY, downX, downY;\r
485 \r
486         private void glControl_MouseWheel(object sender, MouseEventArgs e)\r
487         {\r
488             int newVal = Utils.Clamp(scrollZoom.Value + e.Delta / 10, scrollZoom.Minimum, scrollZoom.Maximum);\r
489 \r
490             if (scrollZoom.Value != newVal)\r
491             {\r
492                 Camera.Zoom = 1f - (float)newVal / (float)scrollZoom.Minimum;\r
493                 scrollZoom.Value = newVal;\r
494                 glControl_Resize(null, null);\r
495             }\r
496         }\r
497 \r
498         FacetedMesh RightclickedPrim;\r
499         int RightclickedFaceID;\r
500 \r
501         private void glControl_MouseDown(object sender, MouseEventArgs e)\r
502         {\r
503             if (e.Button == MouseButtons.Left)\r
504             {\r
505                 dragging = true;\r
506                 downX = dragX = e.X;\r
507                 downY = dragY = e.Y;\r
508             }\r
509             else if (e.Button == MouseButtons.Right)\r
510             {\r
511                 object picked;\r
512                 if (TryPick(e.X, e.Y, out picked, out RightclickedFaceID))\r
513                 {\r
514                     if (picked is FacetedMesh)\r
515                     {\r
516                         RightclickedPrim = (FacetedMesh)picked;\r
517                         ctxObjects.Show(glControl, e.X, e.Y);\r
518                     }\r
519                     else if (picked is RenderAvatar)\r
520                     {\r
521                         // TODO: add context menu when clicked on an avatar\r
522                     }\r
523                 }\r
524             }\r
525 \r
526         }\r
527 \r
528         private void glControl_MouseMove(object sender, MouseEventArgs e)\r
529         {\r
530             if (dragging)\r
531             {\r
532                 int deltaX = e.X - dragX;\r
533                 int deltaY = e.Y - dragY;\r
534                 float pixelToM = 1f / 75f;\r
535 \r
536                 if (e.Button == MouseButtons.Left)\r
537                 {\r
538                     // Pan\r
539                     if (ModifierKeys == Keys.Control || ModifierKeys == (Keys.Alt | Keys.Control | Keys.Shift))\r
540                     {\r
541                         Vector3 direction = Camera.Position - Camera.FocalPoint;\r
542                         direction.Normalize();\r
543                         Vector3 vy = direction % new Vector3(0f, 0f, 1f);\r
544                         Vector3 vx = vy % direction;\r
545                         Vector3 vxy = vx * deltaY * pixelToM + vy * deltaX * pixelToM;\r
546                         Camera.Position += vxy;\r
547                         Camera.FocalPoint += vxy;\r
548                     }\r
549 \r
550                     // Alt-zoom (up down move camera closer to target, left right rotate around target)\r
551                     if (ModifierKeys == Keys.Alt)\r
552                     {\r
553                         Camera.Position += (Camera.Position - Camera.FocalPoint) * deltaY * pixelToM;\r
554                         var dx = -(deltaX * pixelToM);\r
555                         Camera.Position = Camera.FocalPoint + (Camera.Position - Camera.FocalPoint) * new Quaternion(0f, 0f, (float)Math.Sin(dx), (float)Math.Cos(dx));\r
556                     }\r
557 \r
558                     // Rotate camera in a vertical circle around target on up down mouse movement\r
559                     if (ModifierKeys == (Keys.Alt | Keys.Control))\r
560                     {\r
561                         Camera.Position = Camera.FocalPoint +\r
562                             (Camera.Position - Camera.FocalPoint)\r
563                             * Quaternion.CreateFromAxisAngle((Camera.Position - Camera.FocalPoint) % new Vector3(0f, 0f, 1f), deltaY * pixelToM);\r
564                         var dx = -(deltaX * pixelToM);\r
565                         Camera.Position = Camera.FocalPoint + (Camera.Position - Camera.FocalPoint) * new Quaternion(0f, 0f, (float)Math.Sin(dx), (float)Math.Cos(dx));\r
566                     }\r
567 \r
568                 }\r
569 \r
570                 dragX = e.X;\r
571                 dragY = e.Y;\r
572             }\r
573         }\r
574 \r
575         private void glControl_MouseUp(object sender, MouseEventArgs e)\r
576         {\r
577             if (e.Button == MouseButtons.Left)\r
578             {\r
579                 dragging = false;\r
580 \r
581                 if (e.X == downX && e.Y == downY) // click\r
582                 {\r
583                     object clicked;\r
584                     int faceID;\r
585                     if (TryPick(e.X, e.Y, out clicked, out faceID))\r
586                     {\r
587                         if (clicked is FacetedMesh)\r
588                         {\r
589                             FacetedMesh picked = (FacetedMesh)clicked;\r
590 \r
591                             if (ModifierKeys == Keys.None)\r
592                             {\r
593                                 Client.Self.Grab(picked.Prim.LocalID, Vector3.Zero, Vector3.Zero, Vector3.Zero, faceID, Vector3.Zero, Vector3.Zero, Vector3.Zero);\r
594                                 Client.Self.GrabUpdate(picked.Prim.ID, Vector3.Zero, Vector3.Zero, Vector3.Zero, Vector3.Zero, faceID, Vector3.Zero, Vector3.Zero, Vector3.Zero);\r
595                                 Client.Self.DeGrab(picked.Prim.LocalID);\r
596                             }\r
597                             else if (ModifierKeys == Keys.Alt)\r
598                             {\r
599                                 Camera.FocalPoint = PrimPos(picked.Prim);\r
600                                 Cursor.Position = glControl.PointToScreen(new Point(glControl.Width / 2, glControl.Height / 2));\r
601                             }\r
602                         }\r
603                         else if (clicked is RenderAvatar)\r
604                         {\r
605                             RenderAvatar av = (RenderAvatar)clicked;\r
606                             if (ModifierKeys == Keys.Alt)\r
607                             {\r
608                                 Vector3 pos = PrimPos(av.avatar);\r
609                                 pos.Z += 1.5f; // focus roughly on the chest area\r
610                                 Camera.FocalPoint = pos;\r
611                                 Cursor.Position = glControl.PointToScreen(new Point(glControl.Width / 2, glControl.Height / 2));\r
612                             }\r
613                         }\r
614                     }\r
615                 }\r
616             }\r
617         }\r
618         #endregion Mouse handling\r
619 \r
620         // Switch to ortho display mode for drawing hud\r
621         public void GLHUDBegin()\r
622         {\r
623             GL.Disable(EnableCap.DepthTest);\r
624             GL.Disable(EnableCap.Lighting);\r
625             GL.Disable(EnableCap.Light0);\r
626             GL.MatrixMode(MatrixMode.Projection);\r
627             GL.PushMatrix();\r
628             GL.LoadIdentity();\r
629             GL.Ortho(0, glControl.Width, 0, glControl.Height, -5, 1);\r
630             GL.MatrixMode(MatrixMode.Modelview);\r
631             GL.LoadIdentity();\r
632         }\r
633 \r
634         // Switch back to frustrum display mode\r
635         public void GLHUDEnd()\r
636         {\r
637             GL.Enable(EnableCap.DepthTest);\r
638             GL.Enable(EnableCap.Lighting);\r
639             GL.Enable(EnableCap.Light0);\r
640             GL.MatrixMode(MatrixMode.Projection);\r
641             GL.PopMatrix();\r
642             GL.MatrixMode(MatrixMode.Modelview);\r
643         }\r
644 \r
645         public int GLLoadImage(Bitmap bitmap, bool hasAlpha)\r
646         {\r
647             int ret = -1;\r
648             GL.GenTextures(1, out ret);\r
649             GL.BindTexture(TextureTarget.Texture2D, ret);\r
650 \r
651             Rectangle rectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height);\r
652 \r
653             BitmapData bitmapData =\r
654                 bitmap.LockBits(\r
655                 rectangle,\r
656                 ImageLockMode.ReadOnly,\r
657                 hasAlpha ? System.Drawing.Imaging.PixelFormat.Format32bppArgb : System.Drawing.Imaging.PixelFormat.Format24bppRgb);\r
658 \r
659             GL.TexImage2D(\r
660                 TextureTarget.Texture2D,\r
661                 0,\r
662                 hasAlpha ? PixelInternalFormat.Rgba : PixelInternalFormat.Rgb8,\r
663                 bitmap.Width,\r
664                 bitmap.Height,\r
665                 0,\r
666                 hasAlpha ? OpenTK.Graphics.OpenGL.PixelFormat.Bgra : OpenTK.Graphics.OpenGL.PixelFormat.Bgr,\r
667                 PixelType.UnsignedByte,\r
668                 bitmapData.Scan0);\r
669 \r
670             GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);\r
671             GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);\r
672             GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);\r
673             if (hasMipmap)\r
674             {\r
675                 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.LinearMipmapLinear);\r
676                 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.GenerateMipmap, 1);\r
677                 GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);\r
678             }\r
679             else\r
680             {\r
681                 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);\r
682             }\r
683 \r
684             bitmap.UnlockBits(bitmapData);\r
685             GL.Flush();\r
686             return ret;\r
687         }\r
688 \r
689         #region Texture thread\r
690         bool TextureThreadRunning = true;\r
691 \r
692         void TextureThread()\r
693         {\r
694             OpenTK.INativeWindow window = new OpenTK.NativeWindow();\r
695             OpenTK.Graphics.IGraphicsContext context = new OpenTK.Graphics.GraphicsContext(GLMode, window.WindowInfo);\r
696             context.MakeCurrent(window.WindowInfo);\r
697             TextureThreadContextReady.Set();\r
698             PendingTextures.Open();\r
699             Logger.DebugLog("Started Texture Thread");\r
700 \r
701             while (window.Exists && TextureThreadRunning)\r
702             {\r
703                 window.ProcessEvents();\r
704 \r
705                 TextureLoadItem item = null;\r
706 \r
707                 if (!PendingTextures.Dequeue(Timeout.Infinite, ref item)) continue;\r
708 \r
709                 if (TexturesPtrMap.ContainsKey(item.TeFace.TextureID))\r
710                 {\r
711                     item.Info = TexturesPtrMap[item.TeFace.TextureID];\r
712                     GL.BindTexture(TextureTarget.Texture2D, item.Info.TexturePointer);\r
713                     \r
714                     continue;\r
715                 }\r
716 \r
717                 if (LoadTexture(item.TeFace.TextureID, ref item.Info.Texture, false))\r
718                 {\r
719                     Bitmap bitmap = (Bitmap)item.Info.Texture;\r
720 \r
721                     bool hasAlpha;\r
722                     if (item.Info.Texture.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb)\r
723                     {\r
724                         hasAlpha = true;\r
725                     }\r
726                     else\r
727                     {\r
728                         hasAlpha = false;\r
729                     }\r
730 \r
731                     item.Info.HasAlpha = hasAlpha;\r
732                     bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);\r
733                     item.Info.TexturePointer = GLLoadImage(bitmap, hasAlpha);\r
734                     TexturesPtrMap[item.TeFace.TextureID] = item.Info;\r
735 \r
736                     item.Info.Texture.Dispose();\r
737                     item.Info.Texture = null;\r
738                 }\r
739             }\r
740             Logger.DebugLog("Texture thread exited");\r
741         }\r
742         #endregion Texture thread\r
743 \r
744         void LoadCurrentPrims()\r
745         {\r
746             if (!Client.Network.Connected) return;\r
747 \r
748             ThreadPool.QueueUserWorkItem(sync =>\r
749                 {\r
750                     Client.Network.CurrentSim.ObjectsPrimitives.FindAll((Primitive root) => root.ParentID == 0).ForEach((Primitive mainPrim) =>\r
751                     {\r
752                         UpdatePrimBlocking(mainPrim);\r
753                         Client.Network.CurrentSim.ObjectsPrimitives.FindAll((Primitive p) => { return p.ParentID == mainPrim.LocalID; })\r
754                             .FindAll((Primitive child) => child.ParentID == mainPrim.LocalID)\r
755                             .ForEach((Primitive subPrim) => UpdatePrimBlocking(subPrim));\r
756                     });\r
757                 }\r
758             );\r
759         }\r
760 \r
761         private void frmPrimWorkshop_Shown(object sender, EventArgs e)\r
762         {\r
763             SetupGLControl();\r
764             LoadCurrentPrims();\r
765         }\r
766 \r
767         #region Private methods (the meat)\r
768         private void UpdateCamera()\r
769         {\r
770             if (Client != null)\r
771             {\r
772                 Client.Self.Movement.Camera.LookAt(Camera.Position, Camera.FocalPoint);\r
773                 //Client.Self.Movement.Camera.Far = (float)Camera.Far;\r
774             }\r
775         }\r
776 \r
777         void InitCamera()\r
778         {\r
779             Vector3 camPos = Client.Self.SimPosition + new Vector3(-2, 0, 0) * Client.Self.Movement.BodyRotation;\r
780             camPos.Z += 2f;\r
781             Camera.Position = camPos;\r
782             Camera.FocalPoint = Client.Self.SimPosition + new Vector3(5, 0, 0) * Client.Self.Movement.BodyRotation;\r
783             Camera.Zoom = 1.0f;\r
784             Camera.Far = 128.0f;\r
785         }\r
786 \r
787         Vector3 PrimPos(Primitive prim)\r
788         {\r
789             if (prim.ParentID == 0)\r
790             {\r
791                 return prim.Position;\r
792             }\r
793             else\r
794             {\r
795                 FacetedMesh parent;\r
796                 RenderAvatar parentav;\r
797                 if (Prims.TryGetValue(prim.ParentID, out parent))\r
798                 {\r
799                     return parent.Prim.Position + prim.Position * Matrix4.CreateFromQuaternion(parent.Prim.Rotation);\r
800                     //return parent.Position * prim.Position * prim.Rotation;\r
801                 }\r
802                 else if (Avatars.TryGetValue(prim.ParentID, out parentav))\r
803                 {\r
804                     var avPos = PrimPos(parentav.avatar);\r
805                     \r
806                     return avPos + prim.Position * Matrix4.CreateFromQuaternion(parentav.avatar.Rotation);\r
807                 }\r
808                 else\r
809                 {\r
810                     return new Vector3(99999f, 99999f, 99999f);\r
811                 }\r
812             }\r
813         }\r
814 \r
815         private void SetPerspective()\r
816         {\r
817             float dAspRat = (float)glControl.Width / (float)glControl.Height;\r
818             GluPerspective(50.0f * Camera.Zoom, dAspRat, 0.1f, (float)Camera.Far * 3);\r
819         }\r
820 \r
821 \r
822 #pragma warning disable 0612\r
823         OpenTK.Graphics.TextPrinter Printer = new OpenTK.Graphics.TextPrinter(OpenTK.Graphics.TextQuality.High);\r
824 #pragma warning restore 0612\r
825 \r
826         private void RenderStats()\r
827         {\r
828             int posX = glControl.Width - 100;\r
829             int posY = 0;\r
830 \r
831             int elapsedTime = Environment.TickCount - lastTimerTick;\r
832             lastTimerTick = Environment.TickCount;\r
833             // This is a FIR filter known as a MMA or Modified Mean Average, using a 20 point sampling width\r
834             advTimerTick = ((19 * advTimerTick) + elapsedTime) / 20;\r
835 \r
836             GL.Color4(0f, 0f, 0f, 0.6f);\r
837             Printer.Begin();\r
838             Printer.Print(String.Format("FPS {0:000.00}",1000.0f/(float)advTimerTick), AvatarTagFont, Color.Orange,\r
839                 new RectangleF(posX, posY, 100, 50),\r
840                 OpenTK.Graphics.TextPrinterOptions.Default, OpenTK.Graphics.TextAlignment.Center);\r
841             Printer.End();\r
842         }\r
843 \r
844         private void RenderText()\r
845         {\r
846             GLHUDBegin();\r
847 \r
848             lock (Avatars)\r
849             {\r
850 \r
851                 GL.Color4(0f, 0f, 0f, 0.6f);\r
852 \r
853                 foreach (RenderAvatar av in Avatars.Values)\r
854                 {\r
855                     Vector3 avPos = PrimPos(av.avatar);\r
856                     if (Vector3.Distance(avPos, Camera.Position) > 20f) continue;\r
857 \r
858                     OpenTK.Vector3 tagPos = RHelp.TKVector3(avPos);\r
859                     tagPos.Z += 2.2f;\r
860                     OpenTK.Vector3 screenPos;\r
861                     if (!Math3D.GluProject(tagPos, ModelMatrix, ProjectionMatrix, Viewport, out screenPos)) continue;\r
862 \r
863                     string tagText = instance.Names.Get(av.avatar.ID, av.avatar.Name);\r
864                     if (!string.IsNullOrEmpty(av.avatar.GroupName))\r
865                     {\r
866                         tagText = av.avatar.GroupName + "\n" + tagText;\r
867                     }\r
868                     var tSize = Printer.Measure(tagText, AvatarTagFont);\r
869 \r
870                     // Render tag backround\r
871                     GL.Begin(BeginMode.Quads);\r
872                     float halfWidth = tSize.BoundingBox.Width / 2 + 10;\r
873                     float halfHeight = tSize.BoundingBox.Height / 2 + 5;\r
874                     GL.Vertex2(screenPos.X - halfWidth, screenPos.Y - halfHeight);\r
875                     GL.Vertex2(screenPos.X + halfWidth, screenPos.Y - halfHeight);\r
876                     GL.Vertex2(screenPos.X + halfWidth, screenPos.Y + halfHeight);\r
877                     GL.Vertex2(screenPos.X - halfWidth, screenPos.Y + halfHeight);\r
878                     GL.End();\r
879 \r
880                     screenPos.Y = glControl.Height - screenPos.Y;\r
881                     screenPos.X -= tSize.BoundingBox.Width / 2;\r
882                     screenPos.Y -= tSize.BoundingBox.Height / 2;\r
883 \r
884                     if (screenPos.Y > 0)\r
885                     {\r
886                         Printer.Begin();\r
887                         Printer.Print(tagText, AvatarTagFont, Color.Orange,\r
888                             new RectangleF(screenPos.X, screenPos.Y, tSize.BoundingBox.Width, tSize.BoundingBox.Height),\r
889                             OpenTK.Graphics.TextPrinterOptions.Default, OpenTK.Graphics.TextAlignment.Center);\r
890                         Printer.End();\r
891                     }\r
892                 }\r
893                 GL.Color3(1, 1, 1);\r
894             }\r
895 \r
896             lock (Prims)\r
897             {\r
898                 int primNr = 0;\r
899                 foreach (FacetedMesh mesh in Prims.Values)\r
900                 {\r
901                     primNr++;\r
902                     Primitive prim = mesh.Prim;\r
903                     if (!string.IsNullOrEmpty(prim.Text))\r
904                     {\r
905                         string text = System.Text.RegularExpressions.Regex.Replace(prim.Text, "(\r?\n)+", "\n");\r
906                         var newPrimPos = PrimPos(prim);\r
907                         OpenTK.Vector3 primPos = new OpenTK.Vector3(newPrimPos.X, newPrimPos.Y, newPrimPos.Z);\r
908                         var distance = Vector3.Distance(newPrimPos, Camera.Position);\r
909 \r
910                         // Display hovertext only on objects that are withing 12m of the camera\r
911                         if (distance > 12) continue;\r
912 \r
913                         primPos.Z += prim.Scale.Z * 0.8f;\r
914 \r
915                         // Convert objects world position to 2D screen position in pixels\r
916                         OpenTK.Vector3 screenPos;\r
917                         if (!Math3D.GluProject(primPos, ModelMatrix, ProjectionMatrix, Viewport, out screenPos)) continue;\r
918                         screenPos.Y = glControl.Height - screenPos.Y;\r
919 \r
920                         Printer.Begin();\r
921 \r
922                         Color color = Color.FromArgb((int)(prim.TextColor.A * 255), (int)(prim.TextColor.R * 255), (int)(prim.TextColor.G * 255), (int)(prim.TextColor.B * 255));\r
923 \r
924                         var size = Printer.Measure(text, HoverTextFont);\r
925                         screenPos.X -= size.BoundingBox.Width / 2;\r
926                         screenPos.Y -= size.BoundingBox.Height;\r
927 \r
928                         if (screenPos.Y > 0)\r
929                         {\r
930 \r
931                             // Shadow\r
932                             if (color != Color.Black)\r
933                             {\r
934                                 Printer.Print(text, HoverTextFont, Color.Black, new RectangleF(screenPos.X + 1, screenPos.Y + 1, size.BoundingBox.Width, size.BoundingBox.Height), OpenTK.Graphics.TextPrinterOptions.Default, OpenTK.Graphics.TextAlignment.Center);\r
935                             }\r
936                             // Text\r
937                             Printer.Print(text, HoverTextFont, color, new RectangleF(screenPos.X, screenPos.Y, size.BoundingBox.Width, size.BoundingBox.Height), OpenTK.Graphics.TextPrinterOptions.Default, OpenTK.Graphics.TextAlignment.Center);\r
938                         }\r
939 \r
940                         Printer.End();\r
941                     }\r
942                 }\r
943             }\r
944             GLHUDEnd();\r
945         }\r
946 \r
947         #region avatars\r
948 \r
949         private void AddAvatarToScene(Avatar av)\r
950         {\r
951             lock (Avatars)\r
952             {\r
953                 if (Vector3.Distance(PrimPos(av), Client.Self.SimPosition) > 32) return;\r
954 \r
955                 if (Avatars.ContainsKey(av.LocalID))\r
956                 {\r
957                     // flag we got an update??\r
958                     updateAVtes(Avatars[av.LocalID]);\r
959                 }\r
960                 else\r
961                 {\r
962                     GLAvatar ga = new GLAvatar();\r
963                     RenderAvatar ra = new Rendering.RenderAvatar();\r
964                     ra.avatar = av;\r
965                     ra.glavatar = ga;\r
966                     updateAVtes(ra);\r
967                     Avatars.Add(av.LocalID, ra);\r
968                 }\r
969             }\r
970         }\r
971 \r
972         private void updateAVtes(RenderAvatar ra)\r
973         {\r
974             if (ra.avatar.Textures == null)\r
975                 return;\r
976 \r
977             int[] tes = { 8, 9, 10, 11, 19, 20 };\r
978 \r
979             foreach (int fi in tes)\r
980             {\r
981                 Primitive.TextureEntryFace TEF = ra.avatar.Textures.FaceTextures[fi];\r
982                 if (TEF == null)\r
983                     continue;\r
984 \r
985                 if (ra.data[fi] == null || ra.data[fi].TextureInfo.TextureID != TEF.TextureID)\r
986                 {\r
987                     FaceData data = new FaceData();\r
988                     ra.data[fi] = data;\r
989                     data.TextureInfo.TextureID = TEF.TextureID;\r
990                     var textureItem = new TextureLoadItem()\r
991                     {\r
992                         Info = data.TextureInfo,\r
993                         Prim = ra.avatar,\r
994                         TeFace = ra.avatar.Textures.FaceTextures[fi]\r
995                     };\r
996 \r
997                     PendingTextures.Enqueue(textureItem);\r
998                 }\r
999             }\r
1000         }\r
1001 \r
1002         private void RenderAvatars(RenderPass pass)\r
1003         {\r
1004             lock (Avatars)\r
1005             {\r
1006                 GL.EnableClientState(ArrayCap.VertexArray);\r
1007                 GL.EnableClientState(ArrayCap.TextureCoordArray);\r
1008 \r
1009                 int avatarNr = 0;\r
1010                 foreach (RenderAvatar av in Avatars.Values)\r
1011                 {\r
1012                     avatarNr++;\r
1013 \r
1014                     if (GLAvatar._meshes.Count > 0)\r
1015                     {\r
1016                         int faceNr = 0;\r
1017                         foreach (GLMesh mesh in GLAvatar._meshes.Values)\r
1018                         {\r
1019                             faceNr++;\r
1020                             if (!GLAvatar._showSkirt && mesh.Name == "skirtMesh")\r
1021                                 continue;\r
1022 \r
1023                             GL.Color3(1f, 1f, 1f);\r
1024 \r
1025                             // Individual prim matrix\r
1026                             GL.PushMatrix();\r
1027 \r
1028                             // Prim roation and position\r
1029                             GL.MultMatrix(Math3D.CreateTranslationMatrix(av.avatar.Position));\r
1030                             GL.MultMatrix(Math3D.CreateRotationMatrix(av.avatar.Rotation));\r
1031 \r
1032                             //Gl.glTranslatef(mesh.Position.X, mesh.Position.Y, mesh.Position.Z);\r
1033 \r
1034                             GL.Rotate(mesh.RotationAngles.X, 1f, 0f, 0f);\r
1035                             GL.Rotate(mesh.RotationAngles.Y, 0f, 1f, 0f);\r
1036                             GL.Rotate(mesh.RotationAngles.Z, 0f, 0f, 1f);\r
1037 \r
1038                             GL.Scale(mesh.Scale.X, mesh.Scale.Y, mesh.Scale.Z);\r
1039 \r
1040                             if (pass == RenderPass.Picking)\r
1041                             {\r
1042                                 GL.Disable(EnableCap.Texture2D);\r
1043 \r
1044                                 for (int i = 0; i < av.data.Length; i++)\r
1045                                 {\r
1046                                     if (av.data[i] != null)\r
1047                                     {\r
1048                                         av.data[i].PickingID = avatarNr;\r
1049                                     }\r
1050                                 }\r
1051                                 byte[] primNrBytes = Utils.Int16ToBytes((short)avatarNr);\r
1052                                 byte[] faceColor = new byte[] { primNrBytes[0], primNrBytes[1], (byte)faceNr, 254 };\r
1053                                 GL.Color4(faceColor);\r
1054                             }\r
1055                             else\r
1056                             {\r
1057                                 if (av.data[mesh.teFaceID] == null)\r
1058                                 {\r
1059                                     GL.Disable(EnableCap.Texture2D);\r
1060                                 }\r
1061                                 else\r
1062                                 {\r
1063                                     if (mesh.teFaceID != 0)\r
1064                                     {\r
1065                                         GL.Enable(EnableCap.Texture2D);\r
1066                                         GL.BindTexture(TextureTarget.Texture2D, av.data[mesh.teFaceID].TextureInfo.TexturePointer);\r
1067                                     }\r
1068                                     else\r
1069                                     {\r
1070                                         GL.Disable(EnableCap.Texture2D);\r
1071                                     }\r
1072                                 }\r
1073                             }\r
1074 \r
1075                             GL.TexCoordPointer(2, TexCoordPointerType.Float, 0, mesh.RenderData.TexCoords);\r
1076                             GL.VertexPointer(3, VertexPointerType.Float, 0, mesh.RenderData.Vertices);\r
1077 \r
1078                             GL.DrawElements(BeginMode.Triangles, mesh.RenderData.Indices.Length, DrawElementsType.UnsignedShort, mesh.RenderData.Indices);\r
1079 \r
1080                             GL.BindTexture(TextureTarget.Texture2D, 0);\r
1081 \r
1082                             GL.PopMatrix();\r
1083 \r
1084                         }\r
1085                     }\r
1086                 }\r
1087                 GL.Color3(1f, 1f, 1f);\r
1088                 GL.DisableClientState(ArrayCap.VertexArray);\r
1089                 GL.DisableClientState(ArrayCap.TextureCoordArray);\r
1090             }\r
1091         }\r
1092         #endregion avatars\r
1093 \r
1094         #region Terrian\r
1095         bool TerrainModified = true;\r
1096         float[,] heightTable = new float[256, 256];\r
1097         Face terrainFace;\r
1098         ushort[] terrainIndices;\r
1099         Vertex[] terrainVertices;\r
1100         int terrainTexture = -1;\r
1101         bool fetchingTerrainTexture = false;\r
1102         Bitmap terrainImage = null;\r
1103         int terrainVBO = -1;\r
1104         int terrainIndexVBO = -1;\r
1105 \r
1106         private void ResetTerrain()\r
1107         {\r
1108             if (terrainImage != null)\r
1109             {\r
1110                 terrainImage.Dispose();\r
1111                 terrainImage = null;\r
1112             }\r
1113 \r
1114             terrainTexture = -1;\r
1115 \r
1116             if (terrainVBO == -1)\r
1117             {\r
1118                 GL.DeleteBuffers(1, ref terrainVBO);\r
1119                 terrainVBO = -1;\r
1120             }\r
1121 \r
1122             if (terrainIndexVBO == -1)\r
1123             {\r
1124                 GL.DeleteBuffers(1, ref terrainIndexVBO);\r
1125                 terrainIndexVBO = -1;\r
1126             }\r
1127 \r
1128             fetchingTerrainTexture = false;\r
1129             TerrainModified = true;\r
1130         }\r
1131 \r
1132         private void UpdateTerrain()\r
1133         {\r
1134             int step = 1;\r
1135 \r
1136             for (int x = 0; x < 255; x += step)\r
1137             {\r
1138                 for (int y = 0; y < 255; y += step)\r
1139                 {\r
1140                     float z = 0;\r
1141                     int patchNr = ((int)x / 16) * 16 + (int)y / 16;\r
1142                     if (Client.Network.CurrentSim.Terrain != null\r
1143                         && Client.Network.CurrentSim.Terrain[patchNr] != null\r
1144                         && Client.Network.CurrentSim.Terrain[patchNr].Data != null)\r
1145                     {\r
1146                         float[] data = Client.Network.CurrentSim.Terrain[patchNr].Data;\r
1147                         z = data[(int)x % 16 * 16 + (int)y % 16];\r
1148                     }\r
1149                     heightTable[x, y] = z;\r
1150                 }\r
1151             }\r
1152 \r
1153             terrainFace = renderer.TerrainMesh(heightTable, 0f, 255f, 0f, 255f);\r
1154             terrainVertices = terrainFace.Vertices.ToArray();\r
1155             terrainIndices = terrainFace.Indices.ToArray();\r
1156 \r
1157             TerrainModified = false;\r
1158         }\r
1159 \r
1160         void CheckTerrainTexture()\r
1161         {\r
1162             if (terrainTexture != -1) return;\r
1163 \r
1164             if (!fetchingTerrainTexture)\r
1165             {\r
1166                 fetchingTerrainTexture = true;\r
1167                 ThreadPool.QueueUserWorkItem(sync =>\r
1168                 {\r
1169                     Simulator sim = Client.Network.CurrentSim;\r
1170                     terrainImage = TerrainSplat.Splat(instance, heightTable,\r
1171                         new UUID[] { sim.TerrainDetail0, sim.TerrainDetail1, sim.TerrainDetail2, sim.TerrainDetail3 },\r
1172                         new float[] { sim.TerrainStartHeight00, sim.TerrainStartHeight01, sim.TerrainStartHeight10, sim.TerrainStartHeight11 },\r
1173                         new float[] { sim.TerrainHeightRange00, sim.TerrainHeightRange01, sim.TerrainHeightRange10, sim.TerrainHeightRange11 },\r
1174                         Vector3.Zero);\r
1175 \r
1176                     fetchingTerrainTexture = false;\r
1177                 });\r
1178             }\r
1179             else if (terrainImage != null)\r
1180             {\r
1181                 terrainTexture = GLLoadImage(terrainImage, false);\r
1182             }\r
1183 \r
1184         }\r
1185 \r
1186         private void RenderTerrain()\r
1187         {\r
1188             GL.EnableClientState(ArrayCap.VertexArray);\r
1189             GL.EnableClientState(ArrayCap.TextureCoordArray);\r
1190             GL.EnableClientState(ArrayCap.NormalArray);\r
1191 \r
1192             if (TerrainModified)\r
1193             {\r
1194                 ResetTerrain();\r
1195                 UpdateTerrain();\r
1196             }\r
1197 \r
1198             CheckTerrainTexture();\r
1199 \r
1200             if (terrainTexture == -1)\r
1201             {\r
1202                 return;\r
1203             }\r
1204             else\r
1205             {\r
1206                 GL.Enable(EnableCap.Texture2D);\r
1207                 GL.BindTexture(TextureTarget.Texture2D, terrainTexture);\r
1208             }\r
1209 \r
1210             if (!useVBO)\r
1211             {\r
1212                 unsafe\r
1213                 {\r
1214                     fixed (float* normalPtr = &terrainVertices[0].Normal.X)\r
1215                     fixed (float* texPtr = &terrainVertices[0].TexCoord.X)\r
1216                     {\r
1217                         GL.NormalPointer(NormalPointerType.Float, FaceData.VertexSize, (IntPtr)normalPtr);\r
1218                         GL.TexCoordPointer(2, TexCoordPointerType.Float, FaceData.VertexSize, (IntPtr)texPtr);\r
1219                         GL.VertexPointer(3, VertexPointerType.Float, FaceData.VertexSize, terrainVertices);\r
1220                         GL.DrawElements(BeginMode.Triangles, terrainIndices.Length, DrawElementsType.UnsignedShort, terrainIndices);\r
1221                     }\r
1222                 }\r
1223             }\r
1224             else\r
1225             {\r
1226                 if (terrainVBO == -1)\r
1227                 {\r
1228                     GL.GenBuffers(1, out terrainVBO);\r
1229                     GL.BindBuffer(BufferTarget.ArrayBuffer, terrainVBO);\r
1230                     GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(terrainVertices.Length * FaceData.VertexSize), terrainVertices, BufferUsageHint.StaticDraw);\r
1231                 }\r
1232                 else\r
1233                 {\r
1234                     GL.BindBuffer(BufferTarget.ArrayBuffer, terrainVBO);\r
1235                 }\r
1236 \r
1237                 if (terrainIndexVBO == -1)\r
1238                 {\r
1239                     GL.GenBuffers(1, out terrainIndexVBO);\r
1240                     GL.BindBuffer(BufferTarget.ElementArrayBuffer, terrainIndexVBO);\r
1241                     GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(terrainIndices.Length * sizeof(ushort)), terrainIndices, BufferUsageHint.StaticDraw);\r
1242                 }\r
1243                 else\r
1244                 {\r
1245                     GL.BindBuffer(BufferTarget.ElementArrayBuffer, terrainIndexVBO);\r
1246                 }\r
1247 \r
1248                 GL.NormalPointer(NormalPointerType.Float, FaceData.VertexSize, (IntPtr)12);\r
1249                 GL.TexCoordPointer(2, TexCoordPointerType.Float, FaceData.VertexSize, (IntPtr)(24));\r
1250                 GL.VertexPointer(3, VertexPointerType.Float, FaceData.VertexSize, (IntPtr)(0));\r
1251 \r
1252                 GL.DrawElements(BeginMode.Triangles, terrainIndices.Length, DrawElementsType.UnsignedShort, IntPtr.Zero);\r
1253 \r
1254                 GL.BindBuffer(BufferTarget.ArrayBuffer, 0);\r
1255                 GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);\r
1256             }\r
1257             GL.BindTexture(TextureTarget.Texture2D, 0);\r
1258             GL.DisableClientState(ArrayCap.VertexArray);\r
1259             GL.DisableClientState(ArrayCap.TextureCoordArray);\r
1260             GL.DisableClientState(ArrayCap.NormalArray);\r
1261         }\r
1262         #endregion Terrain\r
1263 \r
1264         private void ResetMaterial()\r
1265         {\r
1266             GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Ambient, new float[] { 0.2f, 0.2f, 0.2f, 1.0f });\r
1267             GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Diffuse, new float[] { 0.8f, 0.8f, 0.8f, 1.0f });\r
1268             GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Specular, new float[] { 0f, 0f, 0f, 1.0f });\r
1269             GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Emission, new float[] { 0f, 0f, 0f, 1.0f });\r
1270             GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Shininess, 0f);\r
1271         }\r
1272 \r
1273         private void RenderObjects(RenderPass pass)\r
1274         {\r
1275             lock (Prims)\r
1276             {\r
1277                 GL.EnableClientState(ArrayCap.VertexArray);\r
1278                 GL.EnableClientState(ArrayCap.TextureCoordArray);\r
1279                 GL.EnableClientState(ArrayCap.NormalArray);\r
1280 \r
1281                 GL.Enable(EnableCap.ColorMaterial);\r
1282                 int primNr = 0;\r
1283                 foreach (FacetedMesh mesh in Prims.Values)\r
1284                 {\r
1285                     primNr++;\r
1286                     Primitive prim = mesh.Prim;\r
1287                     FacetedMesh parent = null;\r
1288                     RenderAvatar parentav = null;\r
1289 \r
1290                     if (prim.ParentID != 0 && !Prims.TryGetValue(prim.ParentID, out parent) && !Avatars.TryGetValue(prim.ParentID, out parentav)) continue;\r
1291                     Vector3 primPos = PrimPos(prim);\r
1292 \r
1293                     // Individual prim matrix\r
1294                     GL.PushMatrix();\r
1295 \r
1296                     if (prim.ParentID != 0)\r
1297                     {\r
1298                         if (parent != null)\r
1299                         {\r
1300                             // Apply prim translation and rotation relative to the root prim\r
1301                             GL.MultMatrix(Math3D.CreateTranslationMatrix(parent.Prim.Position));\r
1302                             GL.MultMatrix(Math3D.CreateRotationMatrix(parent.Prim.Rotation));\r
1303                         }\r
1304                         else\r
1305                         {\r
1306                             // Apply prim translation and rotation relative to the root prim\r
1307                             GL.MultMatrix(Math3D.CreateTranslationMatrix(parentav.avatar.Position));\r
1308                             GL.MultMatrix(Math3D.CreateRotationMatrix(parentav.avatar.Rotation));\r
1309 \r
1310                             int attachment_index = (int)prim.PrimData.AttachmentPoint;\r
1311                             if (attachment_index > GLAvatar.attachment_points.Count())\r
1312                             {\r
1313                                 // invalid LL attachment point\r
1314                                 continue;\r
1315                             }\r
1316 \r
1317                             attachment_point apoint = GLAvatar.attachment_points[attachment_index];\r
1318                             if (apoint.jointmesh == null)\r
1319                             {\r
1320                                 //Arse-tachments for us then, things not decoded from avatar_lad fully.\r
1321                             }\r
1322                             else\r
1323                             {\r
1324                                 Vector3 point = apoint.getposition();\r
1325                                 Quaternion rot = apoint.getrotation();\r
1326 \r
1327                                 GL.MultMatrix(Math3D.CreateTranslationMatrix(point));\r
1328                                 GL.MultMatrix(Math3D.CreateRotationMatrix(rot));\r
1329                             }\r
1330                         }\r
1331                     }\r
1332 \r
1333                     // Prim roation and position\r
1334                     GL.MultMatrix(Math3D.CreateTranslationMatrix(prim.Position));\r
1335                     GL.MultMatrix(Math3D.CreateRotationMatrix(prim.Rotation));\r
1336 \r
1337                     // Prim scaling\r
1338                     GL.Scale(prim.Scale.X, prim.Scale.Y, prim.Scale.Z);\r
1339 \r
1340                     // Draw the prim faces\r
1341                     for (int j = 0; j < mesh.Faces.Count; j++)\r
1342                     {\r
1343                         Primitive.TextureEntryFace teFace = mesh.Prim.Textures.FaceTextures[j];\r
1344                         Face face = mesh.Faces[j];\r
1345                         FaceData data = (FaceData)face.UserData;\r
1346                         if (!Frustum.ObjectInFrustum(primPos, data.BoundingSphere, prim.Scale)) continue;\r
1347 \r
1348                         if (teFace == null)\r
1349                             teFace = mesh.Prim.Textures.DefaultTexture;\r
1350 \r
1351                         if (teFace == null)\r
1352                             continue;\r
1353 \r
1354                         if (pass != RenderPass.Picking)\r
1355                         {\r
1356                             bool belongToAlphaPass = (teFace.RGBA.A < 0.99) || data.TextureInfo.HasAlpha;\r
1357 \r
1358                             if (belongToAlphaPass && pass != RenderPass.Alpha) continue;\r
1359                             if (!belongToAlphaPass && pass == RenderPass.Alpha) continue;\r
1360 \r
1361                             // Don't render transparent faces\r
1362                             if (teFace.RGBA.A <= 0.01f) continue;\r
1363 \r
1364                             switch (teFace.Shiny)\r
1365                             {\r
1366                                 case Shininess.High:\r
1367                                     GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 94f);\r
1368                                     break;\r
1369 \r
1370                                 case Shininess.Medium:\r
1371                                     GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 64f);\r
1372                                     break;\r
1373 \r
1374                                 case Shininess.Low:\r
1375                                     GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 24f);\r
1376                                     break;\r
1377 \r
1378 \r
1379                                 case Shininess.None:\r
1380                                 default:\r
1381                                     GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 0f);\r
1382                                     break;\r
1383                             }\r
1384 \r
1385                             var faceColor = new float[] { teFace.RGBA.R, teFace.RGBA.G, teFace.RGBA.B, teFace.RGBA.A };\r
1386 \r
1387                             GL.Color4(faceColor);\r
1388                             GL.Material(MaterialFace.Front, MaterialParameter.AmbientAndDiffuse, faceColor);\r
1389                             GL.Material(MaterialFace.Front, MaterialParameter.Specular, faceColor);\r
1390 \r
1391                             if (data.TextureInfo.TexturePointer != 0)\r
1392                             {\r
1393                                 GL.Enable(EnableCap.Texture2D);\r
1394                                 GL.BindTexture(TextureTarget.Texture2D, data.TextureInfo.TexturePointer);\r
1395                             }\r
1396                             else\r
1397                             {\r
1398                                 GL.Disable(EnableCap.Texture2D);\r
1399                             }\r
1400 \r
1401                         }\r
1402                         else\r
1403                         {\r
1404                             data.PickingID = primNr;\r
1405                             var primNrBytes = Utils.Int16ToBytes((short)primNr);\r
1406                             var faceColor = new byte[] { primNrBytes[0], primNrBytes[1], (byte)j, 255 };\r
1407 \r
1408                             GL.Color4(faceColor);\r
1409                         }\r
1410 \r
1411                         if (!useVBO)\r
1412                         {\r
1413                             Vertex[] verts = face.Vertices.ToArray();\r
1414 \r
1415                             unsafe\r
1416                             {\r
1417                                 fixed (float* normalPtr = &verts[0].Normal.X)\r
1418                                 fixed (float* texPtr = &verts[0].TexCoord.X)\r
1419                                 {\r
1420                                     GL.NormalPointer(NormalPointerType.Float, FaceData.VertexSize, (IntPtr)normalPtr);\r
1421                                     GL.TexCoordPointer(2, TexCoordPointerType.Float, FaceData.VertexSize, (IntPtr)texPtr);\r
1422                                     GL.VertexPointer(3, VertexPointerType.Float, FaceData.VertexSize, verts);\r
1423                                     GL.DrawElements(BeginMode.Triangles, data.Indices.Length, DrawElementsType.UnsignedShort, data.Indices);\r
1424                                 }\r
1425                             }\r
1426                         }\r
1427                         else\r
1428                         {\r
1429                             data.CheckVBO(face);\r
1430                             GL.BindBuffer(BufferTarget.ArrayBuffer, data.VertexVBO);\r
1431                             GL.BindBuffer(BufferTarget.ElementArrayBuffer, data.IndexVBO);\r
1432                             GL.NormalPointer(NormalPointerType.Float, FaceData.VertexSize, (IntPtr)12);\r
1433                             GL.TexCoordPointer(2, TexCoordPointerType.Float, FaceData.VertexSize, (IntPtr)(24));\r
1434                             GL.VertexPointer(3, VertexPointerType.Float, FaceData.VertexSize, (IntPtr)(0));\r
1435 \r
1436                             GL.DrawElements(BeginMode.Triangles, face.Indices.Count, DrawElementsType.UnsignedShort, IntPtr.Zero);\r
1437 \r
1438                             GL.BindBuffer(BufferTarget.ArrayBuffer, 0);\r
1439                             GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);\r
1440 \r
1441                         }\r
1442                     }\r
1443 \r
1444                     GL.BindTexture(TextureTarget.Texture2D, 0);\r
1445                     GL.Color4(new byte[] { 255, 255, 255, 255 });\r
1446                     ResetMaterial();\r
1447 \r
1448                     // Pop the prim matrix\r
1449                     GL.PopMatrix();\r
1450                 }\r
1451                 GL.Disable(EnableCap.ColorMaterial);\r
1452                 GL.DisableClientState(ArrayCap.VertexArray);\r
1453                 GL.DisableClientState(ArrayCap.TextureCoordArray);\r
1454                 GL.DisableClientState(ArrayCap.NormalArray);\r
1455             }\r
1456         }\r
1457 \r
1458         void DrawWaterQuad(float x, float y, float z)\r
1459         {\r
1460             GL.Vertex3(x, y, z);\r
1461             GL.Vertex3(x + 256f, y, z);\r
1462             GL.Vertex3(x + 256f, y + 256f, z);\r
1463             GL.Vertex3(x, y + 256f, z);\r
1464         }\r
1465 \r
1466         public void RenderWater()\r
1467         {\r
1468             float z = Client.Network.CurrentSim.WaterHeight;\r
1469 \r
1470             GL.Disable(EnableCap.Lighting);\r
1471             GL.Enable(EnableCap.ColorMaterial);\r
1472             GL.Color4(0.09f, 0.28f, 0.63f, 0.84f);\r
1473 \r
1474             GL.Begin(BeginMode.Quads);\r
1475             for (float x = -256f * 2; x <= 256 * 2; x += 256f)\r
1476                 for (float y = -256f * 2; y <= 256 * 2; y += 256f)\r
1477                     DrawWaterQuad(x, y, z);\r
1478             GL.End();\r
1479 \r
1480             GL.Color3(1f, 1f, 1f);\r
1481             GL.Enable(EnableCap.Lighting);\r
1482             GL.Disable(EnableCap.ColorMaterial);\r
1483         }\r
1484 \r
1485         private void Render(bool picking)\r
1486         {\r
1487             if (picking)\r
1488             {\r
1489                 GL.ClearColor(1f, 1f, 1f, 1f);\r
1490             }\r
1491             else\r
1492             {\r
1493                 GL.ClearColor(0.39f, 0.58f, 0.93f, 1.0f);\r
1494             }\r
1495 \r
1496             GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);\r
1497             GL.LoadIdentity();\r
1498 \r
1499             // Setup wireframe or solid fill drawing mode\r
1500             if (Wireframe && !picking)\r
1501             {\r
1502                 GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);\r
1503             }\r
1504             else\r
1505             {\r
1506                 GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);\r
1507             }\r
1508 \r
1509             var mLookAt = OpenTK.Matrix4d.LookAt(\r
1510                     Camera.Position.X, Camera.Position.Y, Camera.Position.Z,\r
1511                     Camera.FocalPoint.X, Camera.FocalPoint.Y, Camera.FocalPoint.Z,\r
1512                     0d, 0d, 1d);\r
1513             GL.MultMatrix(ref mLookAt);\r
1514 \r
1515             //GL.Light(LightName.Light0, LightParameter.Position, lightPos);\r
1516 \r
1517             // Push the world matrix\r
1518             GL.PushMatrix();\r
1519 \r
1520             if (Camera.Modified)\r
1521             {\r
1522                 GL.GetFloat(GetPName.ProjectionMatrix, out ProjectionMatrix);\r
1523                 GL.GetFloat(GetPName.ModelviewMatrix, out ModelMatrix);\r
1524                 GL.GetInteger(GetPName.Viewport, Viewport);\r
1525                 Frustum.CalculateFrustum(ProjectionMatrix, ModelMatrix);\r
1526                 UpdateCamera();\r
1527                 Camera.Modified = false;\r
1528             }\r
1529 \r
1530             if (picking)\r
1531             {\r
1532                 RenderObjects(RenderPass.Picking);\r
1533                 RenderAvatars(RenderPass.Picking);\r
1534             }\r
1535             else\r
1536             {\r
1537                 RenderTerrain();\r
1538                 RenderObjects(RenderPass.Simple);\r
1539                 RenderAvatars(RenderPass.Simple);\r
1540 \r
1541                 RenderWater();\r
1542                 RenderObjects(RenderPass.Alpha);\r
1543                 RenderText();\r
1544                 RenderStats();\r
1545             }\r
1546 \r
1547             // Pop the world matrix\r
1548             GL.PopMatrix();\r
1549             GL.Flush();\r
1550 \r
1551         }\r
1552 \r
1553         private void GluPerspective(float fovy, float aspect, float zNear, float zFar)\r
1554         {\r
1555             float fH = (float)Math.Tan(fovy / 360 * (float)Math.PI) * zNear;\r
1556             float fW = fH * aspect;\r
1557             GL.Frustum(-fW, fW, -fH, fH, zNear, zFar);\r
1558         }\r
1559 \r
1560         private bool TryPick(int x, int y, out object picked, out int faceID)\r
1561         {\r
1562             // Save old attributes\r
1563             GL.PushAttrib(AttribMask.AllAttribBits);\r
1564 \r
1565             // Disable some attributes to make the objects flat / solid color when they are drawn\r
1566             GL.Disable(EnableCap.Fog);\r
1567             GL.Disable(EnableCap.Texture2D);\r
1568             GL.Disable(EnableCap.Dither);\r
1569             GL.Disable(EnableCap.Lighting);\r
1570             GL.Disable(EnableCap.LineStipple);\r
1571             GL.Disable(EnableCap.PolygonStipple);\r
1572             GL.Disable(EnableCap.CullFace);\r
1573             GL.Disable(EnableCap.Blend);\r
1574             GL.Disable(EnableCap.AlphaTest);\r
1575 \r
1576             Render(true);\r
1577 \r
1578             byte[] color = new byte[4];\r
1579             GL.ReadPixels(x, glControl.Height - y, 1, 1, OpenTK.Graphics.OpenGL.PixelFormat.Rgba, PixelType.UnsignedByte, color);\r
1580 \r
1581             GL.PopAttrib();\r
1582 \r
1583             int primID = Utils.BytesToUInt16(color, 0);\r
1584             faceID = color[2];\r
1585 \r
1586             picked = null;\r
1587 \r
1588             if (color[3] == 254) // Avatar\r
1589             {\r
1590                 lock (Avatars)\r
1591                 {\r
1592                     foreach (var avatar in Avatars.Values)\r
1593                     {\r
1594                         for (int i = 0; i < avatar.data.Length; i++)\r
1595                         {\r
1596                             var face = avatar.data[i];\r
1597                             if (face != null && face.PickingID == primID)\r
1598                             {\r
1599                                 picked = avatar;\r
1600                                 break;\r
1601                             }\r
1602                         }\r
1603                     }\r
1604                 }\r
1605 \r
1606                 if (picked != null)\r
1607                 {\r
1608                     return true;\r
1609                 }\r
1610             }\r
1611 \r
1612             if (color[3] == 255) // Prim\r
1613             {\r
1614                 lock (Prims)\r
1615                 {\r
1616                     foreach (var mesh in Prims.Values)\r
1617                     {\r
1618                         foreach (var face in mesh.Faces)\r
1619                         {\r
1620                             if (face.UserData == null) continue;\r
1621                             if (((FaceData)face.UserData).PickingID == primID)\r
1622                             {\r
1623                                 picked = mesh;\r
1624                                 break;\r
1625                             }\r
1626                         }\r
1627 \r
1628                         if (picked != null) break;\r
1629                     }\r
1630                 }\r
1631             }\r
1632 \r
1633             return picked != null;\r
1634         }\r
1635 \r
1636 \r
1637         private void MeshPrim(Primitive prim, FacetedMesh mesh)\r
1638         {\r
1639             FacetedMesh existingMesh = null;\r
1640 \r
1641             lock (Prims)\r
1642             {\r
1643                 if (Prims.ContainsKey(prim.LocalID))\r
1644                 {\r
1645                     existingMesh = Prims[prim.LocalID];\r
1646                 }\r
1647             }\r
1648 \r
1649             // Create a FaceData struct for each face that stores the 3D data\r
1650             // in a OpenGL friendly format\r
1651             for (int j = 0; j < mesh.Faces.Count; j++)\r
1652             {\r
1653                 Primitive.TextureEntryFace teFace = prim.Textures.GetFace((uint)j);\r
1654                 if (teFace == null) continue;\r
1655 \r
1656                 Face face = mesh.Faces[j];\r
1657                 FaceData data = new FaceData();\r
1658 \r
1659                 // Vertices for this face\r
1660                 data.Vertices = new float[face.Vertices.Count * 3];\r
1661                 data.Normals = new float[face.Vertices.Count * 3];\r
1662                 for (int k = 0; k < face.Vertices.Count; k++)\r
1663                 {\r
1664                     data.Vertices[k * 3 + 0] = face.Vertices[k].Position.X;\r
1665                     data.Vertices[k * 3 + 1] = face.Vertices[k].Position.Y;\r
1666                     data.Vertices[k * 3 + 2] = face.Vertices[k].Position.Z;\r
1667 \r
1668                     if (data.Vertices[k * 3 + 0] < data.BoundingSphere.Min.X) data.BoundingSphere.Min.X = data.Vertices[k * 3 + 0];\r
1669                     if (data.Vertices[k * 3 + 1] < data.BoundingSphere.Min.Y) data.BoundingSphere.Min.Y = data.Vertices[k * 3 + 1];\r
1670                     if (data.Vertices[k * 3 + 2] < data.BoundingSphere.Min.Z) data.BoundingSphere.Min.Z = data.Vertices[k * 3 + 2];\r
1671 \r
1672                     if (data.Vertices[k * 3 + 0] > data.BoundingSphere.Max.X) data.BoundingSphere.Max.X = data.Vertices[k * 3 + 0];\r
1673                     if (data.Vertices[k * 3 + 1] > data.BoundingSphere.Max.Y) data.BoundingSphere.Max.Y = data.Vertices[k * 3 + 1];\r
1674                     if (data.Vertices[k * 3 + 2] > data.BoundingSphere.Max.Z) data.BoundingSphere.Max.Z = data.Vertices[k * 3 + 2];\r
1675 \r
1676                     data.Normals[k * 3 + 0] = face.Vertices[k].Normal.X;\r
1677                     data.Normals[k * 3 + 1] = face.Vertices[k].Normal.Y;\r
1678                     data.Normals[k * 3 + 2] = face.Vertices[k].Normal.Z;\r
1679                 }\r
1680 \r
1681                 data.BoundingSphere.R = (data.BoundingSphere.Max - data.BoundingSphere.Min).Length();\r
1682 \r
1683                 // Indices for this face\r
1684                 data.Indices = face.Indices.ToArray();\r
1685 \r
1686                 // Texture transform for this face\r
1687                 renderer.TransformTexCoords(face.Vertices, face.Center, teFace);\r
1688 \r
1689                 // Texcoords for this face\r
1690                 data.TexCoords = new float[face.Vertices.Count * 2];\r
1691                 for (int k = 0; k < face.Vertices.Count; k++)\r
1692                 {\r
1693                     data.TexCoords[k * 2 + 0] = face.Vertices[k].TexCoord.X;\r
1694                     data.TexCoords[k * 2 + 1] = face.Vertices[k].TexCoord.Y;\r
1695                 }\r
1696 \r
1697                 // Set the UserData for this face to our FaceData struct\r
1698                 face.UserData = data;\r
1699                 mesh.Faces[j] = face;\r
1700 \r
1701 \r
1702                 if (existingMesh != null &&\r
1703                     j < existingMesh.Faces.Count &&\r
1704                     existingMesh.Faces[j].TextureFace.TextureID == teFace.TextureID &&\r
1705                     ((FaceData)existingMesh.Faces[j].UserData).TextureInfo.TexturePointer != 0\r
1706                     )\r
1707                 {\r
1708                     FaceData existingData = (FaceData)existingMesh.Faces[j].UserData;\r
1709                     data.TextureInfo.TexturePointer = existingData.TextureInfo.TexturePointer;\r
1710                 }\r
1711                 else\r
1712                 {\r
1713 \r
1714                     var textureItem = new TextureLoadItem()\r
1715                     {\r
1716                         Info = data.TextureInfo,\r
1717                         Prim = prim,\r
1718                         TeFace = teFace\r
1719                     };\r
1720 \r
1721                     PendingTextures.Enqueue(textureItem);\r
1722                 }\r
1723             }\r
1724 \r
1725             lock (Prims)\r
1726             {\r
1727                 Prims[prim.LocalID] = mesh;\r
1728             }\r
1729         }\r
1730 \r
1731         private void UpdatePrimBlocking(Primitive prim)\r
1732         {\r
1733             if (Vector3.Distance(PrimPos(prim), Client.Self.SimPosition) > DrawDistance && !Prims.ContainsKey(prim.ParentID) && !Avatars.ContainsKey(prim.ParentID)) return;\r
1734 \r
1735             if (Client.Network.CurrentSim.ObjectsAvatars.ContainsKey(prim.LocalID))\r
1736             {\r
1737                 AddAvatarToScene(Client.Network.CurrentSim.ObjectsAvatars[prim.LocalID]);\r
1738                 return;\r
1739             }\r
1740 \r
1741             // Skip foliage\r
1742             if (prim.PrimData.PCode != PCode.Prim) return;\r
1743 \r
1744             if (prim.Textures == null) return;\r
1745 \r
1746             // Regular prim\r
1747             if (prim.Sculpt == null || prim.Sculpt.SculptTexture == UUID.Zero)\r
1748             {\r
1749                 MeshPrim(prim, renderer.GenerateFacetedMesh(prim, DetailLevel.High));\r
1750             }\r
1751             else\r
1752             {\r
1753                 try\r
1754                 {\r
1755                     FacetedMesh mesh = null;\r
1756 \r
1757                     if (prim.Sculpt.Type != SculptType.Mesh)\r
1758                     { // Regular sculptie\r
1759                         Image img = null;\r
1760 \r
1761                         lock (sculptCache)\r
1762                         {\r
1763                             if (sculptCache.ContainsKey(prim.Sculpt.SculptTexture))\r
1764                             {\r
1765                                 img = sculptCache[prim.Sculpt.SculptTexture];\r
1766                             }\r
1767                         }\r
1768 \r
1769                         if (img == null)\r
1770                         {\r
1771                             if (LoadTexture(prim.Sculpt.SculptTexture, ref img, true))\r
1772                             {\r
1773                                 sculptCache[prim.Sculpt.SculptTexture] = (Bitmap)img;\r
1774                             }\r
1775                             else\r
1776                             {\r
1777                                 return;\r
1778                             }\r
1779                         }\r
1780 \r
1781                         mesh = renderer.GenerateFacetedSculptMesh(prim, (Bitmap)img, DetailLevel.High);\r
1782                     }\r
1783                     else\r
1784                     { // Mesh\r
1785                         AutoResetEvent gotMesh = new AutoResetEvent(false);\r
1786                         bool meshSuccess = false;\r
1787 \r
1788                         Client.Assets.RequestMesh(prim.Sculpt.SculptTexture, (success, meshAsset) =>\r
1789                             {\r
1790                                 if (!success || !FacetedMesh.TryDecodeFromAsset(prim, meshAsset, DetailLevel.Highest, out mesh))\r
1791                                 {\r
1792                                     Logger.Log("Failed to fetch or decode the mesh asset", Helpers.LogLevel.Warning, Client);\r
1793                                 }\r
1794                                 else\r
1795                                 {\r
1796                                     meshSuccess = true;\r
1797                                 }\r
1798                                 gotMesh.Set();\r
1799                             });\r
1800 \r
1801                         if (!gotMesh.WaitOne(20 * 1000, false)) return;\r
1802                         if (!meshSuccess) return;\r
1803                     }\r
1804 \r
1805                     if (mesh != null)\r
1806                     {\r
1807                         MeshPrim(prim, mesh);\r
1808                     }\r
1809                 }\r
1810                 catch\r
1811                 { }\r
1812             }\r
1813         }\r
1814 \r
1815         private bool LoadTexture(UUID textureID, ref Image texture, bool removeAlpha)\r
1816         {\r
1817             ManualResetEvent gotImage = new ManualResetEvent(false);\r
1818             Image img = null;\r
1819 \r
1820             try\r
1821             {\r
1822                 gotImage.Reset();\r
1823                 instance.Client.Assets.RequestImage(textureID, (TextureRequestState state, AssetTexture assetTexture) =>\r
1824                     {\r
1825                         if (state == TextureRequestState.Finished)\r
1826                         {\r
1827                             ManagedImage mi;\r
1828                             OpenJPEG.DecodeToImage(assetTexture.AssetData, out mi);\r
1829 \r
1830                             if (removeAlpha)\r
1831                             {\r
1832                                 if ((mi.Channels & ManagedImage.ImageChannels.Alpha) != 0)\r
1833                                 {\r
1834                                     mi.ConvertChannels(mi.Channels & ~ManagedImage.ImageChannels.Alpha);\r
1835                                 }\r
1836                             }\r
1837 \r
1838                             img = LoadTGAClass.LoadTGA(new MemoryStream(mi.ExportTGA()));\r
1839                         }\r
1840                         gotImage.Set();\r
1841                     }\r
1842                 );\r
1843                 gotImage.WaitOne(30 * 1000, false);\r
1844                 if (img != null)\r
1845                 {\r
1846                     texture = img;\r
1847                     return true;\r
1848                 }\r
1849                 return false;\r
1850             }\r
1851             catch (Exception e)\r
1852             {\r
1853                 Logger.Log(e.Message, Helpers.LogLevel.Error, instance.Client, e);\r
1854                 return false;\r
1855             }\r
1856         }\r
1857         #endregion Private methods (the meat)\r
1858 \r
1859         #region Form controls handlers\r
1860         private void scrollZoom_ValueChanged(object sender, EventArgs e)\r
1861         {\r
1862             Camera.Zoom = 1f - (float)scrollZoom.Value / (float)scrollZoom.Minimum;\r
1863             glControl_Resize(null, null);\r
1864         }\r
1865 \r
1866         private void chkWireFrame_CheckedChanged(object sender, EventArgs e)\r
1867         {\r
1868             Wireframe = chkWireFrame.Checked;\r
1869         }\r
1870 \r
1871         private void btnReset_Click(object sender, EventArgs e)\r
1872         {\r
1873             InitCamera();\r
1874             scrollZoom.Value = 0;\r
1875         }\r
1876 \r
1877         private void cbAA_CheckedChanged(object sender, EventArgs e)\r
1878         {\r
1879             instance.GlobalSettings["use_multi_sampling"] = UseMultiSampling = cbAA.Checked;\r
1880             SetupGLControl();\r
1881         }\r
1882 \r
1883         #endregion Form controls handlers\r
1884 \r
1885         #region Context menu\r
1886         private void ctxObjects_Opening(object sender, System.ComponentModel.CancelEventArgs e)\r
1887         {\r
1888             if (instance.State.IsSitting)\r
1889             {\r
1890                 sitToolStripMenuItem.Text = "Stand up";\r
1891             }\r
1892             else if (RightclickedPrim.Prim.Properties != null\r
1893                 && !string.IsNullOrEmpty(RightclickedPrim.Prim.Properties.SitName))\r
1894             {\r
1895                 sitToolStripMenuItem.Text = RightclickedPrim.Prim.Properties.SitName;\r
1896             }\r
1897             else\r
1898             {\r
1899                 sitToolStripMenuItem.Text = "Sit";\r
1900             }\r
1901 \r
1902             if (RightclickedPrim.Prim.Properties != null\r
1903                 && !string.IsNullOrEmpty(RightclickedPrim.Prim.Properties.TouchName))\r
1904             {\r
1905                 touchToolStripMenuItem.Text = RightclickedPrim.Prim.Properties.TouchName;\r
1906             }\r
1907             else\r
1908             {\r
1909                 touchToolStripMenuItem.Text = "Touch";\r
1910             }\r
1911         }\r
1912 \r
1913         private void touchToolStripMenuItem_Click(object sender, EventArgs e)\r
1914         {\r
1915 \r
1916             Client.Self.Grab(RightclickedPrim.Prim.LocalID, Vector3.Zero, Vector3.Zero, Vector3.Zero, RightclickedFaceID, Vector3.Zero, Vector3.Zero, Vector3.Zero);\r
1917             Thread.Sleep(100);\r
1918             Client.Self.DeGrab(RightclickedPrim.Prim.LocalID);\r
1919         }\r
1920 \r
1921         private void sitToolStripMenuItem_Click(object sender, EventArgs e)\r
1922         {\r
1923             if (!instance.State.IsSitting)\r
1924             {\r
1925                 instance.State.SetSitting(true, RightclickedPrim.Prim.ID);\r
1926             }\r
1927             else\r
1928             {\r
1929                 instance.State.SetSitting(false, UUID.Zero);\r
1930             }\r
1931         }\r
1932 \r
1933         private void takeToolStripMenuItem_Click(object sender, EventArgs e)\r
1934         {\r
1935             instance.MediaManager.PlayUISound(UISounds.ObjectDelete);\r
1936             Client.Inventory.RequestDeRezToInventory(RightclickedPrim.Prim.LocalID);\r
1937             Close();\r
1938         }\r
1939 \r
1940         private void returnToolStripMenuItem_Click(object sender, EventArgs e)\r
1941         {\r
1942             instance.MediaManager.PlayUISound(UISounds.ObjectDelete);\r
1943             Client.Inventory.RequestDeRezToInventory(RightclickedPrim.Prim.LocalID, DeRezDestination.ReturnToOwner, UUID.Zero, UUID.Random());\r
1944             Close();\r
1945         }\r
1946 \r
1947         private void deleteToolStripMenuItem_Click(object sender, EventArgs e)\r
1948         {\r
1949             if (RightclickedPrim.Prim.Properties != null && RightclickedPrim.Prim.Properties.OwnerID != Client.Self.AgentID)\r
1950                 returnToolStripMenuItem_Click(sender, e);\r
1951             else\r
1952             {\r
1953                 instance.MediaManager.PlayUISound(UISounds.ObjectDelete);\r
1954                 Client.Inventory.RequestDeRezToInventory(RightclickedPrim.Prim.LocalID, DeRezDestination.AgentInventoryTake, Client.Inventory.FindFolderForType(AssetType.TrashFolder), UUID.Random());\r
1955             }\r
1956             Close();\r
1957         }\r
1958         #endregion Context menu\r
1959     }\r
1960 \r
1961 }\r