2 // Radegast Metaverse Client
\r
3 // Copyright (c) 2009-2011, Radegast Development Team
\r
4 // All rights reserved.
\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
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
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
34 using System.Collections.Generic;
\r
35 using System.Drawing;
\r
36 using System.Drawing.Imaging;
\r
38 using System.Windows.Forms;
\r
40 using System.Threading;
\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
53 public partial class SceneWindow : RadegastForm
\r
55 #region Public fields
\r
57 /// The OpenGL surface
\r
59 public OpenTK.GLControl glControl = null;
\r
62 /// Use multi sampling (anti aliasing)
\r
64 public bool UseMultiSampling = true;
\r
67 /// Is rendering engine ready and enabled
\r
69 public bool RenderingEnabled = false;
\r
72 /// Rednder in wireframe mode
\r
74 public bool Wireframe = false;
\r
77 /// List of prims in the scene
\r
79 Dictionary<uint, FacetedMesh> Prims = new Dictionary<uint, FacetedMesh>();
\r
81 #endregion Public fields
\r
83 #region Private fields
\r
86 Dictionary<UUID, TextureInfo> TexturesPtrMap = new Dictionary<UUID, TextureInfo>();
\r
87 RadegastInstance instance;
\r
88 MeshmerizerR renderer;
\r
89 OpenTK.Graphics.GraphicsMode GLMode = null;
\r
90 AutoResetEvent TextureThreadContextReady = new AutoResetEvent(false);
\r
91 BlockingQueue<TextureLoadItem> PendingTextures = new BlockingQueue<TextureLoadItem>();
\r
92 float[] lightPos = new float[] { 0f, 0f, 1f, 0f };
\r
95 #endregion Private fields
\r
97 #region Construction and disposal
\r
98 public SceneWindow(RadegastInstance instance)
\r
101 InitializeComponent();
\r
102 Disposed += new EventHandler(frmPrimWorkshop_Disposed);
\r
103 AutoSavePosition = true;
\r
104 UseMultiSampling = cbAA.Checked = instance.GlobalSettings["use_multi_sampling"];
\r
105 cbAA.CheckedChanged += cbAA_CheckedChanged;
\r
107 this.instance = instance;
\r
109 renderer = new MeshmerizerR();
\r
111 // Camera initial setting
\r
112 Camera = new Camera();
\r
115 Client.Objects.TerseObjectUpdate += new EventHandler<TerseObjectUpdateEventArgs>(Objects_TerseObjectUpdate);
\r
116 Client.Objects.ObjectUpdate += new EventHandler<PrimEventArgs>(Objects_ObjectUpdate);
\r
117 Client.Objects.ObjectDataBlockUpdate += new EventHandler<ObjectDataBlockUpdateEventArgs>(Objects_ObjectDataBlockUpdate);
\r
118 Client.Objects.KillObject += new EventHandler<KillObjectEventArgs>(Objects_KillObject);
\r
119 Client.Network.SimChanged += new EventHandler<SimChangedEventArgs>(Network_SimChanged);
\r
120 Client.Self.TeleportProgress += new EventHandler<TeleportEventArgs>(Self_TeleportProgress);
\r
121 Instance.Netcom.ClientDisconnected += new EventHandler<DisconnectedEventArgs>(Netcom_ClientDisconnected);
\r
122 Application.Idle += new EventHandler(Application_Idle);
\r
125 void frmPrimWorkshop_Disposed(object sender, EventArgs e)
\r
127 Application.Idle -= new EventHandler(Application_Idle);
\r
128 Client.Objects.TerseObjectUpdate -= new EventHandler<TerseObjectUpdateEventArgs>(Objects_TerseObjectUpdate);
\r
129 Client.Objects.ObjectUpdate -= new EventHandler<PrimEventArgs>(Objects_ObjectUpdate);
\r
130 Client.Objects.ObjectDataBlockUpdate -= new EventHandler<ObjectDataBlockUpdateEventArgs>(Objects_ObjectDataBlockUpdate);
\r
131 Client.Objects.KillObject -= new EventHandler<KillObjectEventArgs>(Objects_KillObject);
\r
132 Client.Network.SimChanged -= new EventHandler<SimChangedEventArgs>(Network_SimChanged);
\r
133 Client.Self.TeleportProgress -= new EventHandler<TeleportEventArgs>(Self_TeleportProgress);
\r
134 Instance.Netcom.ClientDisconnected -= new EventHandler<DisconnectedEventArgs>(Netcom_ClientDisconnected);
\r
136 if (glControl != null)
\r
138 glControl.Dispose();
\r
141 lock (Prims) Prims.Clear();
\r
142 TexturesPtrMap.Clear();
\r
146 void Application_Idle(object sender, EventArgs e)
\r
148 if (glControl != null && !glControl.IsDisposed && RenderingEnabled)
\r
150 while (glControl.IsIdle)
\r
156 #endregion Construction and disposal
\r
158 #region Network messaage handlers
\r
159 void Netcom_ClientDisconnected(object sender, DisconnectedEventArgs e)
\r
161 if (InvokeRequired)
\r
163 if (IsHandleCreated || !instance.MonoRuntime)
\r
165 BeginInvoke(new MethodInvoker(() => Netcom_ClientDisconnected(sender, e)));
\r
173 void Self_TeleportProgress(object sender, TeleportEventArgs e)
\r
177 case TeleportStatus.Progress:
\r
178 case TeleportStatus.Start:
\r
179 RenderingEnabled = false;
\r
182 case TeleportStatus.Cancelled:
\r
183 case TeleportStatus.Failed:
\r
184 RenderingEnabled = true;
\r
187 case TeleportStatus.Finished:
\r
188 ThreadPool.QueueUserWorkItem(sync =>
\r
190 Thread.Sleep(3000);
\r
192 LoadCurrentPrims();
\r
193 RenderingEnabled = true;
\r
199 void Network_SimChanged(object sender, SimChangedEventArgs e)
\r
201 lock (Prims) Prims.Clear();
\r
204 void Objects_KillObject(object sender, KillObjectEventArgs e)
\r
206 if (e.Simulator.Handle != Client.Network.CurrentSim.Handle) return;
\r
207 lock (Prims) Prims.Remove(e.ObjectLocalID);
\r
210 void Objects_TerseObjectUpdate(object sender, TerseObjectUpdateEventArgs e)
\r
212 if (e.Simulator.Handle != Client.Network.CurrentSim.Handle) return;
\r
213 UpdatePrimBlocking(e.Prim);
\r
216 void Objects_ObjectUpdate(object sender, PrimEventArgs e)
\r
218 if (e.Simulator.Handle != Client.Network.CurrentSim.Handle) return;
\r
219 UpdatePrimBlocking(e.Prim);
\r
222 void Objects_ObjectDataBlockUpdate(object sender, ObjectDataBlockUpdateEventArgs e)
\r
224 if (e.Simulator.Handle != Client.Network.CurrentSim.Handle) return;
\r
225 UpdatePrimBlocking(e.Prim);
\r
227 #endregion Network messaage handlers
\r
229 #region glControl setup and disposal
\r
230 public void SetupGLControl()
\r
232 RenderingEnabled = false;
\r
234 if (glControl != null)
\r
235 glControl.Dispose();
\r
242 if (!UseMultiSampling)
\r
244 GLMode = new OpenTK.Graphics.GraphicsMode(OpenTK.DisplayDevice.Default.BitsPerPixel, 24, 8, 0);
\r
248 for (int aa = 0; aa <= 4; aa += 2)
\r
250 var testMode = new OpenTK.Graphics.GraphicsMode(OpenTK.DisplayDevice.Default.BitsPerPixel, 24, 8, aa);
\r
251 if (testMode.Samples == aa)
\r
266 if (GLMode == null)
\r
268 // Try default mode
\r
269 glControl = new OpenTK.GLControl();
\r
273 glControl = new OpenTK.GLControl(GLMode);
\r
276 catch (Exception ex)
\r
278 Logger.Log(ex.Message, Helpers.LogLevel.Warning, Client);
\r
282 if (glControl == null)
\r
284 Logger.Log("Failed to initialize OpenGL control, cannot continue", Helpers.LogLevel.Error, Client);
\r
288 Logger.Log("Initializing OpenGL mode: " + GLMode.ToString(), Helpers.LogLevel.Info);
\r
290 glControl.Paint += glControl_Paint;
\r
291 glControl.Resize += glControl_Resize;
\r
292 glControl.MouseDown += glControl_MouseDown;
\r
293 glControl.MouseUp += glControl_MouseUp;
\r
294 glControl.MouseMove += glControl_MouseMove;
\r
295 glControl.MouseWheel += glControl_MouseWheel;
\r
296 glControl.Load += new EventHandler(glControl_Load);
\r
297 glControl.Disposed += new EventHandler(glControl_Disposed);
\r
298 glControl.Dock = DockStyle.Fill;
\r
299 Controls.Add(glControl);
\r
300 glControl.BringToFront();
\r
303 void glControl_Disposed(object sender, EventArgs e)
\r
305 TextureThreadRunning = false;
\r
306 PendingTextures.Close();
\r
307 glControl.Paint -= glControl_Paint;
\r
308 glControl.Resize -= glControl_Resize;
\r
309 glControl.MouseDown -= glControl_MouseDown;
\r
310 glControl.MouseUp -= glControl_MouseUp;
\r
311 glControl.MouseMove -= glControl_MouseMove;
\r
312 glControl.MouseWheel -= glControl_MouseWheel;
\r
313 glControl.Load -= new EventHandler(glControl_Load);
\r
314 glControl.Disposed -= glControl_Disposed;
\r
317 void glControl_Load(object sender, EventArgs e)
\r
321 GL.ShadeModel(ShadingModel.Smooth);
\r
322 GL.ClearColor(0f, 0f, 0f, 0f);
\r
324 //GL.LightModel(LightModelParameter.LightModelAmbient, new float[] { 0.5f, 0.5f, 0.5f, 1.0f });
\r
326 GL.Enable(EnableCap.Lighting);
\r
327 GL.Enable(EnableCap.Light0);
\r
328 GL.Light(LightName.Light0, LightParameter.Ambient, new float[] { 0.5f, 0.5f, 0.5f, 1f });
\r
329 GL.Light(LightName.Light0, LightParameter.Diffuse, new float[] { 0.3f, 0.3f, 0.3f, 1f });
\r
330 GL.Light(LightName.Light0, LightParameter.Specular, new float[] { 0.8f, 0.8f, 0.8f, 1.0f });
\r
331 GL.Light(LightName.Light0, LightParameter.Position, lightPos);
\r
333 GL.ClearDepth(1.0d);
\r
334 GL.Enable(EnableCap.DepthTest);
\r
335 GL.Enable(EnableCap.ColorMaterial);
\r
336 GL.Enable(EnableCap.CullFace);
\r
337 GL.ColorMaterial(MaterialFace.Front, ColorMaterialParameter.AmbientAndDiffuse);
\r
338 GL.ColorMaterial(MaterialFace.Front, ColorMaterialParameter.Specular);
\r
340 GL.DepthMask(true);
\r
341 GL.DepthFunc(DepthFunction.Lequal);
\r
342 GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);
\r
343 GL.MatrixMode(MatrixMode.Projection);
\r
345 GL.Enable(EnableCap.Blend);
\r
346 GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
\r
347 hasMipmap = GL.GetString(StringName.Extensions).Contains("GL_SGIS_generate_mipmap");
\r
349 RenderingEnabled = true;
\r
350 // Call the resizing function which sets up the GL drawing window
\r
351 // and will also invalidate the GL control
\r
352 glControl_Resize(null, null);
\r
354 glControl.Context.MakeCurrent(null);
\r
355 TextureThreadContextReady.Reset();
\r
356 var textureThread = new Thread(() => TextureThread())
\r
358 IsBackground = true,
\r
359 Name = "TextureLoadingThread"
\r
361 textureThread.Start();
\r
362 TextureThreadContextReady.WaitOne(1000, false);
\r
363 glControl.MakeCurrent();
\r
365 catch (Exception ex)
\r
367 RenderingEnabled = false;
\r
368 Logger.Log("Failed to initialize OpenGL control", Helpers.LogLevel.Warning, Client, ex);
\r
371 #endregion glControl setup and disposal
\r
373 #region glControl paint and resize events
\r
374 private void MainRenderLoop()
\r
376 if (!RenderingEnabled) return;
\r
380 glControl.SwapBuffers();
\r
383 void glControl_Paint(object sender, EventArgs e)
\r
388 private void glControl_Resize(object sender, EventArgs e)
\r
390 if (!RenderingEnabled) return;
\r
391 glControl.MakeCurrent();
\r
393 GL.ClearColor(0.39f, 0.58f, 0.93f, 1.0f);
\r
395 GL.Viewport(0, 0, glControl.Width, glControl.Height);
\r
398 GL.MatrixMode(MatrixMode.Projection);
\r
403 GL.MatrixMode(MatrixMode.Modelview);
\r
406 #endregion glControl paint and resize events
\r
408 #region Mouse handling
\r
409 bool dragging = false;
\r
410 int dragX, dragY, downX, downY;
\r
412 private void glControl_MouseWheel(object sender, MouseEventArgs e)
\r
414 int newVal = Utils.Clamp(scrollZoom.Value + e.Delta / 10, scrollZoom.Minimum, scrollZoom.Maximum);
\r
416 if (scrollZoom.Value != newVal)
\r
418 Camera.Zoom = 1f - (float)newVal / (float)scrollZoom.Minimum;
\r
419 scrollZoom.Value = newVal;
\r
420 glControl_Resize(null, null);
\r
424 FacetedMesh RightclickedPrim;
\r
425 int RightclickedFaceID;
\r
427 private void glControl_MouseDown(object sender, MouseEventArgs e)
\r
429 if (e.Button == MouseButtons.Left || e.Button == MouseButtons.Middle)
\r
432 downX = dragX = e.X;
\r
433 downY = dragY = e.Y;
\r
435 else if (e.Button == MouseButtons.Right)
\r
437 if (TryPick(e.X, e.Y, out RightclickedPrim, out RightclickedFaceID))
\r
439 ctxObjects.Show(glControl, e.X, e.Y);
\r
445 private void glControl_MouseMove(object sender, MouseEventArgs e)
\r
449 int deltaX = e.X - dragX;
\r
450 int deltaY = e.Y - dragY;
\r
451 float pixelToM = 1f / 75f;
\r
453 if (e.Button == MouseButtons.Left)
\r
456 if (ModifierKeys == Keys.Control || ModifierKeys == (Keys.Alt | Keys.Control | Keys.Shift))
\r
458 Vector3 direction = Camera.Position - Camera.FocalPoint;
\r
459 direction.Normalize();
\r
460 Vector3 vy = direction % new Vector3(0f, 0f, 1f);
\r
461 Vector3 vx = vy % direction;
\r
462 Vector3 vxy = vx * deltaY * pixelToM + vy * deltaX * pixelToM;
\r
463 Camera.Position += vxy;
\r
464 Camera.FocalPoint += vxy;
\r
467 // Alt-zoom (up down move camera closer to target, left right rotate around target)
\r
468 if (ModifierKeys == Keys.Alt)
\r
470 Camera.Position += (Camera.Position - Camera.FocalPoint) * deltaY * pixelToM;
\r
471 var dx = -(deltaX * pixelToM);
\r
472 Camera.Position = Camera.FocalPoint + (Camera.Position - Camera.FocalPoint) * new Quaternion(0f, 0f, (float)Math.Sin(dx), (float)Math.Cos(dx));
\r
475 // Rotate camera in a vertical circle around target on up down mouse movement
\r
476 if (ModifierKeys == (Keys.Alt | Keys.Control))
\r
478 Camera.Position = Camera.FocalPoint +
\r
479 (Camera.Position - Camera.FocalPoint)
\r
480 * Quaternion.CreateFromAxisAngle((Camera.Position - Camera.FocalPoint) % new Vector3(0f, 0f, 1f), deltaY * pixelToM);
\r
481 var dx = -(deltaX * pixelToM);
\r
482 Camera.Position = Camera.FocalPoint + (Camera.Position - Camera.FocalPoint) * new Quaternion(0f, 0f, (float)Math.Sin(dx), (float)Math.Cos(dx));
\r
492 private void glControl_MouseUp(object sender, MouseEventArgs e)
\r
494 if (e.Button == MouseButtons.Left)
\r
498 if (e.X == downX && e.Y == downY) // click
\r
500 FacetedMesh picked;
\r
502 if (TryPick(e.X, e.Y, out picked, out faceID))
\r
504 if (ModifierKeys == Keys.None)
\r
506 Client.Self.Grab(picked.Prim.LocalID, Vector3.Zero, Vector3.Zero, Vector3.Zero, faceID, Vector3.Zero, Vector3.Zero, Vector3.Zero);
\r
507 Client.Self.GrabUpdate(picked.Prim.ID, Vector3.Zero, Vector3.Zero, Vector3.Zero, Vector3.Zero, faceID, Vector3.Zero, Vector3.Zero, Vector3.Zero);
\r
508 Client.Self.DeGrab(picked.Prim.LocalID);
\r
510 else if (ModifierKeys == Keys.Alt)
\r
512 Camera.FocalPoint = PrimPos(picked.Prim);
\r
519 #endregion Mouse handling
\r
521 #region Texture thread
\r
522 bool TextureThreadRunning = true;
\r
524 void TextureThread()
\r
526 OpenTK.INativeWindow window = new OpenTK.NativeWindow();
\r
527 OpenTK.Graphics.IGraphicsContext context = new OpenTK.Graphics.GraphicsContext(GLMode, window.WindowInfo);
\r
528 context.MakeCurrent(window.WindowInfo);
\r
529 TextureThreadContextReady.Set();
\r
530 PendingTextures.Open();
\r
531 Logger.DebugLog("Started Texture Thread");
\r
533 while (window.Exists && TextureThreadRunning)
\r
535 window.ProcessEvents();
\r
537 TextureLoadItem item = null;
\r
539 if (!PendingTextures.Dequeue(Timeout.Infinite, ref item)) continue;
\r
541 if (TexturesPtrMap.ContainsKey(item.TeFace.TextureID))
\r
543 item.Data.TextureInfo = TexturesPtrMap[item.TeFace.TextureID];
\r
544 GL.BindTexture(TextureTarget.Texture2D, item.Data.TextureInfo.TexturePointer);
\r
548 if (LoadTexture(item.TeFace.TextureID, ref item.Data.TextureInfo.Texture, false))
\r
550 GL.GenTextures(1, out item.Data.TextureInfo.TexturePointer);
\r
551 GL.BindTexture(TextureTarget.Texture2D, item.Data.TextureInfo.TexturePointer);
\r
553 Bitmap bitmap = (Bitmap)item.Data.TextureInfo.Texture;
\r
556 if (item.Data.TextureInfo.Texture.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb)
\r
564 item.Data.TextureInfo.HasAlpha = hasAlpha;
\r
566 bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
\r
567 Rectangle rectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
\r
569 BitmapData bitmapData =
\r
572 ImageLockMode.ReadOnly,
\r
573 hasAlpha ? System.Drawing.Imaging.PixelFormat.Format32bppArgb : System.Drawing.Imaging.PixelFormat.Format24bppRgb);
\r
576 TextureTarget.Texture2D,
\r
578 hasAlpha ? PixelInternalFormat.Rgba : PixelInternalFormat.Rgb8,
\r
582 hasAlpha ? OpenTK.Graphics.OpenGL.PixelFormat.Bgra : OpenTK.Graphics.OpenGL.PixelFormat.Bgr,
\r
583 PixelType.UnsignedByte,
\r
586 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
\r
587 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
\r
588 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
\r
591 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.LinearMipmapLinear);
\r
592 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.GenerateMipmap, 1);
\r
593 GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
\r
597 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
\r
600 TexturesPtrMap[item.TeFace.TextureID] = item.Data.TextureInfo;
\r
602 bitmap.UnlockBits(bitmapData);
\r
604 item.Data.TextureInfo.Texture = null;
\r
609 Logger.DebugLog("Texture thread exited");
\r
611 #endregion Texture thread
\r
613 void LoadCurrentPrims()
\r
615 ThreadPool.QueueUserWorkItem(sync =>
\r
617 Client.Network.CurrentSim.ObjectsPrimitives.FindAll((Primitive root) => root.ParentID == 0).ForEach((Primitive mainPrim) =>
\r
619 UpdatePrimBlocking(Client.Network.CurrentSim.ObjectsPrimitives[mainPrim.LocalID]);
\r
620 Client.Network.CurrentSim.ObjectsPrimitives.FindAll((Primitive p) => { return p.ParentID == mainPrim.LocalID; })
\r
621 .FindAll((Primitive child) => child.ParentID == mainPrim.LocalID)
\r
622 .ForEach((Primitive subPrim) => UpdatePrimBlocking(subPrim));
\r
628 private void frmPrimWorkshop_Shown(object sender, EventArgs e)
\r
632 LoadCurrentPrims();
\r
635 #region Private methods (the meat)
\r
636 private void UpdateCamera()
\r
638 if (Client != null)
\r
640 Client.Self.Movement.Camera.LookAt(Camera.Position, Camera.FocalPoint);
\r
641 //Client.Self.Movement.Camera.Far = (float)Camera.Far;
\r
644 if (RenderingEnabled)
\r
647 GL.MatrixMode(MatrixMode.Projection);
\r
652 GL.MatrixMode(MatrixMode.Modelview);
\r
659 Camera.Position = Client.Self.SimPosition + new Vector3(-2, 0, 0) * Client.Self.Movement.BodyRotation;
\r
660 Camera.Position.Z += 2f;
\r
661 Camera.FocalPoint = Client.Self.SimPosition + new Vector3(5, 0, 0) * Client.Self.Movement.BodyRotation;
\r
662 Camera.Zoom = 1.0f;
\r
663 Camera.Far = 128.0f;
\r
666 Vector3 PrimPos(Primitive prim)
\r
668 if (prim.ParentID == 0)
\r
670 return prim.Position;
\r
675 if (Client.Network.CurrentSim.ObjectsPrimitives.TryGetValue(prim.ParentID, out parent))
\r
677 return parent.Position + prim.Position * Matrix4.CreateFromQuaternion(parent.Rotation);
\r
678 //return parent.Position * prim.Position * prim.Rotation;
\r
682 return Vector3.Zero;
\r
687 private void SetPerspective()
\r
689 float dAspRat = (float)glControl.Width / (float)glControl.Height;
\r
690 GluPerspective(50.0f * Camera.Zoom, dAspRat, 0.1f, (float)Camera.Far);
\r
693 private OpenTK.Vector3 WorldToScreen(OpenTK.Vector3 world)
\r
695 OpenTK.Vector3 screen;
\r
696 double[] ModelViewMatrix = new double[16];
\r
697 double[] ProjectionMatrix = new double[16];
\r
698 int[] Viewport = new int[4];
\r
700 GL.GetInteger(GetPName.Viewport, Viewport);
\r
701 GL.GetDouble(GetPName.ModelviewMatrix, ModelViewMatrix);
\r
702 GL.GetDouble(GetPName.ProjectionMatrix, ProjectionMatrix);
\r
704 #pragma warning disable 0618
\r
705 OpenTK.Graphics.Glu.Project(world,
\r
710 #pragma warning restore 0618
\r
712 screen.Y = glControl.Height - screen.Y;
\r
716 #pragma warning disable 0612
\r
717 OpenTK.Graphics.TextPrinter Printer = new OpenTK.Graphics.TextPrinter(OpenTK.Graphics.TextQuality.High);
\r
718 #pragma warning restore 0612
\r
719 private void RenderText()
\r
724 foreach (FacetedMesh mesh in Prims.Values)
\r
727 Primitive prim = mesh.Prim;
\r
728 if (!string.IsNullOrEmpty(prim.Text))
\r
730 string text = System.Text.RegularExpressions.Regex.Replace(prim.Text, "(\r?\n)+", "\n");
\r
731 OpenTK.Vector3 screenPos = OpenTK.Vector3.Zero;
\r
732 OpenTK.Vector3 primPos = OpenTK.Vector3.Zero;
\r
734 // Is it child prim
\r
735 FacetedMesh parent = null;
\r
736 if (Prims.TryGetValue(prim.ParentID, out parent))
\r
738 var newPrimPos = prim.Position * Matrix4.CreateFromQuaternion(parent.Prim.Rotation);
\r
739 primPos = new OpenTK.Vector3(newPrimPos.X, newPrimPos.Y, newPrimPos.Z);
\r
742 primPos.Z += prim.Scale.Z * 0.7f;
\r
743 screenPos = WorldToScreen(primPos);
\r
746 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
748 using (Font f = new Font(FontFamily.GenericSansSerif, 10f, FontStyle.Bold))
\r
750 var size = Printer.Measure(text, f);
\r
751 screenPos.X -= size.BoundingBox.Width / 2;
\r
752 screenPos.Y -= size.BoundingBox.Height;
\r
755 if (color != Color.Black)
\r
757 Printer.Print(text, f, Color.Black, new RectangleF(screenPos.X + 1, screenPos.Y + 1, size.BoundingBox.Width, size.BoundingBox.Height), OpenTK.Graphics.TextPrinterOptions.Default, OpenTK.Graphics.TextAlignment.Center);
\r
759 Printer.Print(text, f, color, new RectangleF(screenPos.X, screenPos.Y, size.BoundingBox.Width, size.BoundingBox.Height), OpenTK.Graphics.TextPrinterOptions.Default, OpenTK.Graphics.TextAlignment.Center);
\r
767 private void RenderObjects(RenderPass pass)
\r
772 foreach (FacetedMesh mesh in Prims.Values)
\r
775 Primitive prim = mesh.Prim;
\r
776 Primitive parent = null;
\r
777 if (prim.ParentID != 0 && !Client.Network.CurrentSim.ObjectsPrimitives.TryGetValue(prim.ParentID, out parent)) continue;
\r
779 // Individual prim matrix
\r
782 if (prim.ParentID != 0)
\r
784 // Apply prim translation and rotation relative to the root prim
\r
785 GL.MultMatrix(Math3D.CreateTranslationMatrix(parent.Position));
\r
786 GL.MultMatrix(Math3D.CreateRotationMatrix(parent.Rotation));
\r
789 // Prim roation and position
\r
790 GL.MultMatrix(Math3D.CreateTranslationMatrix(prim.Position));
\r
791 GL.MultMatrix(Math3D.CreateRotationMatrix(prim.Rotation));
\r
794 GL.Scale(prim.Scale.X, prim.Scale.Y, prim.Scale.Z);
\r
796 // Draw the prim faces
\r
797 for (int j = 0; j < mesh.Faces.Count; j++)
\r
799 Primitive.TextureEntryFace teFace = mesh.Prim.Textures.FaceTextures[j];
\r
800 Face face = mesh.Faces[j];
\r
801 FaceData data = (FaceData)face.UserData;
\r
803 if (teFace == null)
\r
804 teFace = mesh.Prim.Textures.DefaultTexture;
\r
806 if (pass != RenderPass.Picking)
\r
808 bool belongToAlphaPass = (teFace.RGBA.A < 0.99) || data.TextureInfo.HasAlpha;
\r
810 if (belongToAlphaPass && pass != RenderPass.Alpha) continue;
\r
811 if (!belongToAlphaPass && pass == RenderPass.Alpha) continue;
\r
813 // Don't render transparent faces
\r
814 if (teFace.RGBA.A <= 0.01f) continue;
\r
816 switch (teFace.Shiny)
\r
818 case Shininess.High:
\r
819 GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 94f);
\r
822 case Shininess.Medium:
\r
823 GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 64f);
\r
826 case Shininess.Low:
\r
827 GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 24f);
\r
831 case Shininess.None:
\r
833 GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 0f);
\r
837 var faceColor = new float[] { teFace.RGBA.R, teFace.RGBA.G, teFace.RGBA.B, teFace.RGBA.A };
\r
839 GL.Color4(faceColor);
\r
840 GL.Material(MaterialFace.Front, MaterialParameter.AmbientAndDiffuse, faceColor);
\r
841 GL.Material(MaterialFace.Front, MaterialParameter.Specular, faceColor);
\r
843 if (data.TextureInfo.TexturePointer != 0)
\r
845 GL.Enable(EnableCap.Texture2D);
\r
849 GL.Disable(EnableCap.Texture2D);
\r
852 // Bind the texture
\r
853 GL.BindTexture(TextureTarget.Texture2D, data.TextureInfo.TexturePointer);
\r
857 data.PickingID = primNr;
\r
858 var primNrBytes = Utils.Int16ToBytes((short)primNr);
\r
859 var faceColor = new byte[] { primNrBytes[0], primNrBytes[1], (byte)j, 255 };
\r
861 GL.Color4(faceColor);
\r
864 GL.TexCoordPointer(2, TexCoordPointerType.Float, 0, data.TexCoords);
\r
865 GL.VertexPointer(3, VertexPointerType.Float, 0, data.Vertices);
\r
866 GL.NormalPointer(NormalPointerType.Float, 0, data.Normals);
\r
867 GL.DrawElements(BeginMode.Triangles, data.Indices.Length, DrawElementsType.UnsignedShort, data.Indices);
\r
871 // Pop the prim matrix
\r
877 private void Render(bool picking)
\r
879 glControl.MakeCurrent();
\r
882 GL.ClearColor(1f, 1f, 1f, 1f);
\r
886 GL.ClearColor(0.39f, 0.58f, 0.93f, 1.0f);
\r
889 GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
\r
892 // Setup wireframe or solid fill drawing mode
\r
893 if (Wireframe && !picking)
\r
895 GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);
\r
899 GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);
\r
902 var mLookAt = OpenTK.Matrix4d.LookAt(
\r
903 Camera.Position.X, Camera.Position.Y, Camera.Position.Z,
\r
904 Camera.FocalPoint.X, Camera.FocalPoint.Y, Camera.FocalPoint.Z,
\r
906 GL.MultMatrix(ref mLookAt);
\r
908 //GL.Light(LightName.Light0, LightParameter.Position, lightPos);
\r
910 // Push the world matrix
\r
913 GL.EnableClientState(ArrayCap.VertexArray);
\r
914 GL.EnableClientState(ArrayCap.TextureCoordArray);
\r
915 GL.EnableClientState(ArrayCap.NormalArray);
\r
920 RenderObjects(RenderPass.Picking);
\r
924 RenderObjects(RenderPass.Simple);
\r
925 RenderObjects(RenderPass.Alpha);
\r
929 // Pop the world matrix
\r
932 GL.DisableClientState(ArrayCap.TextureCoordArray);
\r
933 GL.DisableClientState(ArrayCap.VertexArray);
\r
934 GL.DisableClientState(ArrayCap.NormalArray);
\r
939 private void GluPerspective(float fovy, float aspect, float zNear, float zFar)
\r
941 float fH = (float)Math.Tan(fovy / 360 * (float)Math.PI) * zNear;
\r
942 float fW = fH * aspect;
\r
943 GL.Frustum(-fW, fW, -fH, fH, zNear, zFar);
\r
946 private bool TryPick(int x, int y, out FacetedMesh picked, out int faceID)
\r
948 // Save old attributes
\r
949 GL.PushAttrib(AttribMask.AllAttribBits);
\r
951 // Disable some attributes to make the objects flat / solid color when they are drawn
\r
952 GL.Disable(EnableCap.Fog);
\r
953 GL.Disable(EnableCap.Texture2D);
\r
954 GL.Disable(EnableCap.Dither);
\r
955 GL.Disable(EnableCap.Lighting);
\r
956 GL.Disable(EnableCap.LineStipple);
\r
957 GL.Disable(EnableCap.PolygonStipple);
\r
958 GL.Disable(EnableCap.CullFace);
\r
959 GL.Disable(EnableCap.Blend);
\r
960 GL.Disable(EnableCap.AlphaTest);
\r
964 byte[] color = new byte[4];
\r
965 GL.ReadPixels(x, glControl.Height - y, 1, 1, OpenTK.Graphics.OpenGL.PixelFormat.Rgba, PixelType.UnsignedByte, color);
\r
969 int primID = Utils.BytesToUInt16(color, 0);
\r
976 foreach (var mesh in Prims.Values)
\r
978 foreach (var face in mesh.Faces)
\r
980 if (face.UserData == null) continue;
\r
981 if (((FaceData)face.UserData).PickingID == primID)
\r
988 if (picked != null) break;
\r
992 return picked != null;
\r
996 private void UpdatePrimBlocking(Primitive prim)
\r
998 // Don't render avatars for now
\r
999 if (Client.Network.CurrentSim.ObjectsAvatars.ContainsKey(prim.LocalID)) return;
\r
1001 if (Vector3.Distance(PrimPos(prim), Client.Self.SimPosition) > 32 && !Prims.ContainsKey(prim.ParentID)) return;
\r
1003 FacetedMesh mesh = null;
\r
1004 FacetedMesh existingMesh = null;
\r
1008 if (Prims.ContainsKey(prim.LocalID))
\r
1010 existingMesh = Prims[prim.LocalID];
\r
1014 if (prim.Textures == null)
\r
1019 if (prim.Sculpt != null && prim.Sculpt.SculptTexture != UUID.Zero)
\r
1021 if (prim.Sculpt.Type != SculptType.Mesh)
\r
1022 { // Regular sculptie
\r
1024 if (!LoadTexture(prim.Sculpt.SculptTexture, ref img, true))
\r
1026 mesh = renderer.GenerateFacetedSculptMesh(prim, (Bitmap)img, DetailLevel.Highest);
\r
1030 AutoResetEvent gotMesh = new AutoResetEvent(false);
\r
1031 bool meshSuccess = false;
\r
1033 Client.Assets.RequestMesh(prim.Sculpt.SculptTexture, (success, meshAsset) =>
\r
1035 if (!success || !FacetedMesh.TryDecodeFromAsset(prim, meshAsset, DetailLevel.Highest, out mesh))
\r
1037 Logger.Log("Failed to fetch or decode the mesh asset", Helpers.LogLevel.Warning, Client);
\r
1041 meshSuccess = true;
\r
1046 if (!gotMesh.WaitOne(20 * 1000, false)) return;
\r
1047 if (!meshSuccess) return;
\r
1052 mesh = renderer.GenerateFacetedMesh(prim, DetailLevel.Highest);
\r
1060 // Create a FaceData struct for each face that stores the 3D data
\r
1061 // in a OpenGL friendly format
\r
1062 for (int j = 0; j < mesh.Faces.Count; j++)
\r
1064 Primitive.TextureEntryFace teFace = prim.Textures.GetFace((uint)j);
\r
1066 Face face = mesh.Faces[j];
\r
1067 FaceData data = new FaceData();
\r
1069 // Vertices for this face
\r
1070 data.Vertices = new float[face.Vertices.Count * 3];
\r
1071 data.Normals = new float[face.Vertices.Count * 3];
\r
1072 for (int k = 0; k < face.Vertices.Count; k++)
\r
1074 data.Vertices[k * 3 + 0] = face.Vertices[k].Position.X;
\r
1075 data.Vertices[k * 3 + 1] = face.Vertices[k].Position.Y;
\r
1076 data.Vertices[k * 3 + 2] = face.Vertices[k].Position.Z;
\r
1078 data.Normals[k * 3 + 0] = face.Vertices[k].Normal.X;
\r
1079 data.Normals[k * 3 + 1] = face.Vertices[k].Normal.Y;
\r
1080 data.Normals[k * 3 + 2] = face.Vertices[k].Normal.Z;
\r
1083 // Indices for this face
\r
1084 data.Indices = face.Indices.ToArray();
\r
1086 // Texture transform for this face
\r
1087 renderer.TransformTexCoords(face.Vertices, face.Center, teFace);
\r
1089 // Texcoords for this face
\r
1090 data.TexCoords = new float[face.Vertices.Count * 2];
\r
1091 for (int k = 0; k < face.Vertices.Count; k++)
\r
1093 data.TexCoords[k * 2 + 0] = face.Vertices[k].TexCoord.X;
\r
1094 data.TexCoords[k * 2 + 1] = face.Vertices[k].TexCoord.Y;
\r
1097 // Set the UserData for this face to our FaceData struct
\r
1098 face.UserData = data;
\r
1099 mesh.Faces[j] = face;
\r
1102 if (existingMesh != null &&
\r
1103 j < existingMesh.Faces.Count &&
\r
1104 existingMesh.Faces[j].TextureFace.TextureID == teFace.TextureID &&
\r
1105 ((FaceData)existingMesh.Faces[j].UserData).TextureInfo.TexturePointer != 0
\r
1108 FaceData existingData = (FaceData)existingMesh.Faces[j].UserData;
\r
1109 data.TextureInfo.TexturePointer = existingData.TextureInfo.TexturePointer;
\r
1114 var textureItem = new TextureLoadItem()
\r
1121 PendingTextures.Enqueue(textureItem);
\r
1128 Prims[prim.LocalID] = mesh;
\r
1132 private bool LoadTexture(UUID textureID, ref Image texture, bool removeAlpha)
\r
1134 ManualResetEvent gotImage = new ManualResetEvent(false);
\r
1140 instance.Client.Assets.RequestImage(textureID, (TextureRequestState state, AssetTexture assetTexture) =>
\r
1142 if (state == TextureRequestState.Finished)
\r
1145 OpenJPEG.DecodeToImage(assetTexture.AssetData, out mi);
\r
1149 if ((mi.Channels & ManagedImage.ImageChannels.Alpha) != 0)
\r
1151 mi.ConvertChannels(mi.Channels & ~ManagedImage.ImageChannels.Alpha);
\r
1155 img = LoadTGAClass.LoadTGA(new MemoryStream(mi.ExportTGA()));
\r
1160 gotImage.WaitOne(30 * 1000, false);
\r
1168 catch (Exception e)
\r
1170 Logger.Log(e.Message, Helpers.LogLevel.Error, instance.Client, e);
\r
1174 #endregion Private methods (the meat)
\r
1176 #region Form controls handlers
\r
1177 private void scrollZoom_ValueChanged(object sender, EventArgs e)
\r
1179 Camera.Zoom = 1f - (float)scrollZoom.Value / (float)scrollZoom.Minimum;
\r
1180 glControl_Resize(null, null);
\r
1183 private void chkWireFrame_CheckedChanged(object sender, EventArgs e)
\r
1185 Wireframe = chkWireFrame.Checked;
\r
1188 private void btnReset_Click(object sender, EventArgs e)
\r
1192 scrollZoom.Value = 0;
\r
1195 private void cbAA_CheckedChanged(object sender, EventArgs e)
\r
1197 instance.GlobalSettings["use_multi_sampling"] = UseMultiSampling = cbAA.Checked;
\r
1201 #endregion Form controls handlers
\r
1203 #region Context menu
\r
1204 private void ctxObjects_Opening(object sender, System.ComponentModel.CancelEventArgs e)
\r
1206 if (instance.State.IsSitting)
\r
1208 sitToolStripMenuItem.Text = "Stand up";
\r
1210 else if (RightclickedPrim.Prim.Properties != null
\r
1211 && !string.IsNullOrEmpty(RightclickedPrim.Prim.Properties.SitName))
\r
1213 sitToolStripMenuItem.Text = RightclickedPrim.Prim.Properties.SitName;
\r
1217 sitToolStripMenuItem.Text = "Sit";
\r
1220 if (RightclickedPrim.Prim.Properties != null
\r
1221 && !string.IsNullOrEmpty(RightclickedPrim.Prim.Properties.TouchName))
\r
1223 touchToolStripMenuItem.Text = RightclickedPrim.Prim.Properties.TouchName;
\r
1227 touchToolStripMenuItem.Text = "Touch";
\r
1231 private void touchToolStripMenuItem_Click(object sender, EventArgs e)
\r
1234 Client.Self.Grab(RightclickedPrim.Prim.LocalID, Vector3.Zero, Vector3.Zero, Vector3.Zero, RightclickedFaceID, Vector3.Zero, Vector3.Zero, Vector3.Zero);
\r
1235 Thread.Sleep(100);
\r
1236 Client.Self.DeGrab(RightclickedPrim.Prim.LocalID);
\r
1239 private void sitToolStripMenuItem_Click(object sender, EventArgs e)
\r
1241 if (!instance.State.IsSitting)
\r
1243 instance.State.SetSitting(true, RightclickedPrim.Prim.ID);
\r
1247 instance.State.SetSitting(false, UUID.Zero);
\r
1251 private void takeToolStripMenuItem_Click(object sender, EventArgs e)
\r
1253 instance.MediaManager.PlayUISound(UISounds.ObjectDelete);
\r
1254 Client.Inventory.RequestDeRezToInventory(RightclickedPrim.Prim.LocalID);
\r
1258 private void returnToolStripMenuItem_Click(object sender, EventArgs e)
\r
1260 instance.MediaManager.PlayUISound(UISounds.ObjectDelete);
\r
1261 Client.Inventory.RequestDeRezToInventory(RightclickedPrim.Prim.LocalID, DeRezDestination.ReturnToOwner, UUID.Zero, UUID.Random());
\r
1265 private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
\r
1267 if (RightclickedPrim.Prim.Properties != null && RightclickedPrim.Prim.Properties.OwnerID != Client.Self.AgentID)
\r
1268 returnToolStripMenuItem_Click(sender, e);
\r
1271 instance.MediaManager.PlayUISound(UISounds.ObjectDelete);
\r
1272 Client.Inventory.RequestDeRezToInventory(RightclickedPrim.Prim.LocalID, DeRezDestination.AgentInventoryTake, Client.Inventory.FindFolderForType(AssetType.TrashFolder), UUID.Random());
\r
1276 #endregion Context menu
\r