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
50 namespace Radegast.Rendering
\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 /// Object from up to this distance from us will be rendered
\r
79 public float DrawDistance = 48f;
\r
82 /// List of prims in the scene
\r
84 Dictionary<uint, FacetedMesh> Prims = new Dictionary<uint, FacetedMesh>();
\r
86 Dictionary<uint, RenderAvatar> Avatars = new Dictionary<uint, RenderAvatar>();
\r
88 #endregion Public fields
\r
90 #region Private fields
\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
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
111 #endregion Private fields
\r
113 #region Construction and disposal
\r
114 public SceneWindow(RadegastInstance instance)
\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
123 this.instance = instance;
\r
125 renderer = new MeshmerizerR();
\r
127 // Camera initial setting
\r
128 Camera = new Camera();
\r
131 GLAvatar.loadlindenmeshes("avatar_lad.xml");
\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
144 void frmPrimWorkshop_Disposed(object sender, EventArgs e)
\r
146 Application.Idle -= new EventHandler(Application_Idle);
\r
148 PendingTextures.Close();
\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
159 if (glControl != null)
\r
161 glControl.Dispose();
\r
167 foreach (var img in sculptCache.Values)
\r
169 sculptCache.Clear();
\r
172 lock (Prims) Prims.Clear();
\r
173 lock (Avatars) Avatars.Clear();
\r
175 TexturesPtrMap.Clear();
\r
179 void Application_Idle(object sender, EventArgs e)
\r
181 if (glControl != null && !glControl.IsDisposed && RenderingEnabled)
\r
185 while (glControl != null && glControl.IsIdle)
\r
188 if (instance.MonoRuntime)
\r
190 Application.DoEvents();
\r
194 catch (ObjectDisposedException)
\r
198 #endregion Construction and disposal
\r
200 #region Network messaage handlers
\r
201 void Terrain_LandPatchReceived(object sender, LandPatchReceivedEventArgs e)
\r
203 if (e.Simulator.Handle == Client.Network.CurrentSim.Handle)
\r
205 TerrainModified = true;
\r
209 void Netcom_ClientDisconnected(object sender, DisconnectedEventArgs e)
\r
211 if (InvokeRequired)
\r
213 if (IsHandleCreated || !instance.MonoRuntime)
\r
215 BeginInvoke(new MethodInvoker(() => Netcom_ClientDisconnected(sender, e)));
\r
223 void Self_TeleportProgress(object sender, TeleportEventArgs e)
\r
227 case TeleportStatus.Progress:
\r
228 case TeleportStatus.Start:
\r
229 RenderingEnabled = false;
\r
232 case TeleportStatus.Cancelled:
\r
233 case TeleportStatus.Failed:
\r
234 RenderingEnabled = true;
\r
237 case TeleportStatus.Finished:
\r
238 ThreadPool.QueueUserWorkItem(sync =>
\r
240 Thread.Sleep(3000);
\r
242 LoadCurrentPrims();
\r
243 RenderingEnabled = true;
\r
249 void Network_SimChanged(object sender, SimChangedEventArgs e)
\r
254 foreach (var img in sculptCache.Values)
\r
256 sculptCache.Clear();
\r
258 lock (Prims) Prims.Clear();
\r
261 void Objects_KillObject(object sender, KillObjectEventArgs e)
\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
269 void Objects_TerseObjectUpdate(object sender, TerseObjectUpdateEventArgs e)
\r
271 if (e.Simulator.Handle != Client.Network.CurrentSim.Handle) return;
\r
272 UpdatePrimBlocking(e.Prim);
\r
275 void Objects_ObjectUpdate(object sender, PrimEventArgs e)
\r
277 if (e.Simulator.Handle != Client.Network.CurrentSim.Handle) return;
\r
278 UpdatePrimBlocking(e.Prim);
\r
281 void Objects_ObjectDataBlockUpdate(object sender, ObjectDataBlockUpdateEventArgs e)
\r
283 if (e.Simulator.Handle != Client.Network.CurrentSim.Handle) return;
\r
284 UpdatePrimBlocking(e.Prim);
\r
286 #endregion Network messaage handlers
\r
288 #region glControl setup and disposal
\r
289 public void SetupGLControl()
\r
291 RenderingEnabled = false;
\r
293 if (glControl != null)
\r
294 glControl.Dispose();
\r
301 if (!UseMultiSampling)
\r
303 GLMode = new OpenTK.Graphics.GraphicsMode(OpenTK.DisplayDevice.Default.BitsPerPixel, 24, 8, 0);
\r
307 for (int aa = 0; aa <= 4; aa += 2)
\r
309 var testMode = new OpenTK.Graphics.GraphicsMode(OpenTK.DisplayDevice.Default.BitsPerPixel, 24, 8, aa);
\r
310 if (testMode.Samples == aa)
\r
325 if (GLMode == null)
\r
327 // Try default mode
\r
328 glControl = new OpenTK.GLControl();
\r
332 glControl = new OpenTK.GLControl(GLMode);
\r
335 catch (Exception ex)
\r
337 Logger.Log(ex.Message, Helpers.LogLevel.Warning, Client);
\r
341 if (glControl == null)
\r
343 Logger.Log("Failed to initialize OpenGL control, cannot continue", Helpers.LogLevel.Error, Client);
\r
347 Logger.Log("Initializing OpenGL mode: " + GLMode.ToString(), Helpers.LogLevel.Info);
\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
363 void glControl_Disposed(object sender, EventArgs e)
\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
377 void glControl_Load(object sender, EventArgs e)
\r
381 GL.ShadeModel(ShadingModel.Smooth);
\r
383 //GL.LightModel(LightModelParameter.LightModelAmbient, new float[] { 0.5f, 0.5f, 0.5f, 1.0f });
\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
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
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
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
408 // Double check if we have mipmap ability
\r
413 GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
\r
417 Logger.DebugLog("Don't have glGenerateMipmap() after all");
\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
427 glControl.Context.MakeCurrent(null);
\r
428 TextureThreadContextReady.Reset();
\r
429 var textureThread = new Thread(() => TextureThread())
\r
431 IsBackground = true,
\r
432 Name = "TextureLoadingThread"
\r
434 textureThread.Start();
\r
435 TextureThreadContextReady.WaitOne(1000, false);
\r
436 this.lastTimerTick = Environment.TickCount;
\r
437 glControl.MakeCurrent();
\r
439 catch (Exception ex)
\r
441 RenderingEnabled = false;
\r
442 Logger.Log("Failed to initialize OpenGL control", Helpers.LogLevel.Warning, Client, ex);
\r
445 #endregion glControl setup and disposal
\r
447 #region glControl paint and resize events
\r
448 private void MainRenderLoop()
\r
450 if (!RenderingEnabled) return;
\r
454 glControl.SwapBuffers();
\r
457 void glControl_Paint(object sender, EventArgs e)
\r
462 private void glControl_Resize(object sender, EventArgs e)
\r
464 if (!RenderingEnabled) return;
\r
465 glControl.MakeCurrent();
\r
467 GL.ClearColor(0.39f, 0.58f, 0.93f, 1.0f);
\r
469 GL.Viewport(0, 0, glControl.Width, glControl.Height);
\r
472 GL.MatrixMode(MatrixMode.Projection);
\r
477 GL.MatrixMode(MatrixMode.Modelview);
\r
480 #endregion glControl paint and resize events
\r
482 #region Mouse handling
\r
483 bool dragging = false;
\r
484 int dragX, dragY, downX, downY;
\r
486 private void glControl_MouseWheel(object sender, MouseEventArgs e)
\r
488 int newVal = Utils.Clamp(scrollZoom.Value + e.Delta / 10, scrollZoom.Minimum, scrollZoom.Maximum);
\r
490 if (scrollZoom.Value != newVal)
\r
492 Camera.Zoom = 1f - (float)newVal / (float)scrollZoom.Minimum;
\r
493 scrollZoom.Value = newVal;
\r
494 glControl_Resize(null, null);
\r
498 FacetedMesh RightclickedPrim;
\r
499 int RightclickedFaceID;
\r
501 private void glControl_MouseDown(object sender, MouseEventArgs e)
\r
503 if (e.Button == MouseButtons.Left)
\r
506 downX = dragX = e.X;
\r
507 downY = dragY = e.Y;
\r
509 else if (e.Button == MouseButtons.Right)
\r
512 if (TryPick(e.X, e.Y, out picked, out RightclickedFaceID))
\r
514 if (picked is FacetedMesh)
\r
516 RightclickedPrim = (FacetedMesh)picked;
\r
517 ctxObjects.Show(glControl, e.X, e.Y);
\r
519 else if (picked is RenderAvatar)
\r
521 // TODO: add context menu when clicked on an avatar
\r
528 private void glControl_MouseMove(object sender, MouseEventArgs e)
\r
532 int deltaX = e.X - dragX;
\r
533 int deltaY = e.Y - dragY;
\r
534 float pixelToM = 1f / 75f;
\r
536 if (e.Button == MouseButtons.Left)
\r
539 if (ModifierKeys == Keys.Control || ModifierKeys == (Keys.Alt | Keys.Control | Keys.Shift))
\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
550 // Alt-zoom (up down move camera closer to target, left right rotate around target)
\r
551 if (ModifierKeys == Keys.Alt)
\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
558 // Rotate camera in a vertical circle around target on up down mouse movement
\r
559 if (ModifierKeys == (Keys.Alt | Keys.Control))
\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
575 private void glControl_MouseUp(object sender, MouseEventArgs e)
\r
577 if (e.Button == MouseButtons.Left)
\r
581 if (e.X == downX && e.Y == downY) // click
\r
585 if (TryPick(e.X, e.Y, out clicked, out faceID))
\r
587 if (clicked is FacetedMesh)
\r
589 FacetedMesh picked = (FacetedMesh)clicked;
\r
591 if (ModifierKeys == Keys.None)
\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
597 else if (ModifierKeys == Keys.Alt)
\r
599 Camera.FocalPoint = PrimPos(picked.Prim);
\r
600 Cursor.Position = glControl.PointToScreen(new Point(glControl.Width / 2, glControl.Height / 2));
\r
603 else if (clicked is RenderAvatar)
\r
605 RenderAvatar av = (RenderAvatar)clicked;
\r
606 if (ModifierKeys == Keys.Alt)
\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
618 #endregion Mouse handling
\r
620 // Switch to ortho display mode for drawing hud
\r
621 public void GLHUDBegin()
\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
629 GL.Ortho(0, glControl.Width, 0, glControl.Height, -5, 1);
\r
630 GL.MatrixMode(MatrixMode.Modelview);
\r
634 // Switch back to frustrum display mode
\r
635 public void GLHUDEnd()
\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
642 GL.MatrixMode(MatrixMode.Modelview);
\r
645 public int GLLoadImage(Bitmap bitmap, bool hasAlpha)
\r
648 GL.GenTextures(1, out ret);
\r
649 GL.BindTexture(TextureTarget.Texture2D, ret);
\r
651 Rectangle rectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
\r
653 BitmapData bitmapData =
\r
656 ImageLockMode.ReadOnly,
\r
657 hasAlpha ? System.Drawing.Imaging.PixelFormat.Format32bppArgb : System.Drawing.Imaging.PixelFormat.Format24bppRgb);
\r
660 TextureTarget.Texture2D,
\r
662 hasAlpha ? PixelInternalFormat.Rgba : PixelInternalFormat.Rgb8,
\r
666 hasAlpha ? OpenTK.Graphics.OpenGL.PixelFormat.Bgra : OpenTK.Graphics.OpenGL.PixelFormat.Bgr,
\r
667 PixelType.UnsignedByte,
\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
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
681 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
\r
684 bitmap.UnlockBits(bitmapData);
\r
689 #region Texture thread
\r
690 bool TextureThreadRunning = true;
\r
692 void TextureThread()
\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
701 while (window.Exists && TextureThreadRunning)
\r
703 window.ProcessEvents();
\r
705 TextureLoadItem item = null;
\r
707 if (!PendingTextures.Dequeue(Timeout.Infinite, ref item)) continue;
\r
709 if (TexturesPtrMap.ContainsKey(item.TeFace.TextureID))
\r
711 item.Info = TexturesPtrMap[item.TeFace.TextureID];
\r
712 GL.BindTexture(TextureTarget.Texture2D, item.Info.TexturePointer);
\r
717 if (LoadTexture(item.TeFace.TextureID, ref item.Info.Texture, false))
\r
719 Bitmap bitmap = (Bitmap)item.Info.Texture;
\r
722 if (item.Info.Texture.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb)
\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
736 item.Info.Texture.Dispose();
\r
737 item.Info.Texture = null;
\r
740 Logger.DebugLog("Texture thread exited");
\r
742 #endregion Texture thread
\r
744 void LoadCurrentPrims()
\r
746 if (!Client.Network.Connected) return;
\r
748 ThreadPool.QueueUserWorkItem(sync =>
\r
750 Client.Network.CurrentSim.ObjectsPrimitives.FindAll((Primitive root) => root.ParentID == 0).ForEach((Primitive mainPrim) =>
\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
761 private void frmPrimWorkshop_Shown(object sender, EventArgs e)
\r
764 LoadCurrentPrims();
\r
767 #region Private methods (the meat)
\r
768 private void UpdateCamera()
\r
770 if (Client != null)
\r
772 Client.Self.Movement.Camera.LookAt(Camera.Position, Camera.FocalPoint);
\r
773 //Client.Self.Movement.Camera.Far = (float)Camera.Far;
\r
779 Vector3 camPos = Client.Self.SimPosition + new Vector3(-2, 0, 0) * Client.Self.Movement.BodyRotation;
\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
787 Vector3 PrimPos(Primitive prim)
\r
789 if (prim.ParentID == 0)
\r
791 return prim.Position;
\r
795 FacetedMesh parent;
\r
796 RenderAvatar parentav;
\r
797 if (Prims.TryGetValue(prim.ParentID, out parent))
\r
799 return parent.Prim.Position + prim.Position * Matrix4.CreateFromQuaternion(parent.Prim.Rotation);
\r
800 //return parent.Position * prim.Position * prim.Rotation;
\r
802 else if (Avatars.TryGetValue(prim.ParentID, out parentav))
\r
804 var avPos = PrimPos(parentav.avatar);
\r
806 return avPos + prim.Position * Matrix4.CreateFromQuaternion(parentav.avatar.Rotation);
\r
810 return new Vector3(99999f, 99999f, 99999f);
\r
815 private void SetPerspective()
\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
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
826 private void RenderStats()
\r
828 int posX = glControl.Width - 100;
\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
836 GL.Color4(0f, 0f, 0f, 0.6f);
\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
844 private void RenderText()
\r
851 GL.Color4(0f, 0f, 0f, 0.6f);
\r
853 foreach (RenderAvatar av in Avatars.Values)
\r
855 Vector3 avPos = PrimPos(av.avatar);
\r
856 if (Vector3.Distance(avPos, Camera.Position) > 20f) continue;
\r
858 OpenTK.Vector3 tagPos = RHelp.TKVector3(avPos);
\r
860 OpenTK.Vector3 screenPos;
\r
861 if (!Math3D.GluProject(tagPos, ModelMatrix, ProjectionMatrix, Viewport, out screenPos)) continue;
\r
863 string tagText = instance.Names.Get(av.avatar.ID, av.avatar.Name);
\r
864 if (!string.IsNullOrEmpty(av.avatar.GroupName))
\r
866 tagText = av.avatar.GroupName + "\n" + tagText;
\r
868 var tSize = Printer.Measure(tagText, AvatarTagFont);
\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
880 screenPos.Y = glControl.Height - screenPos.Y;
\r
881 screenPos.X -= tSize.BoundingBox.Width / 2;
\r
882 screenPos.Y -= tSize.BoundingBox.Height / 2;
\r
884 if (screenPos.Y > 0)
\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
893 GL.Color3(1, 1, 1);
\r
899 foreach (FacetedMesh mesh in Prims.Values)
\r
902 Primitive prim = mesh.Prim;
\r
903 if (!string.IsNullOrEmpty(prim.Text))
\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
910 // Display hovertext only on objects that are withing 12m of the camera
\r
911 if (distance > 12) continue;
\r
913 primPos.Z += prim.Scale.Z * 0.8f;
\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
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
924 var size = Printer.Measure(text, HoverTextFont);
\r
925 screenPos.X -= size.BoundingBox.Width / 2;
\r
926 screenPos.Y -= size.BoundingBox.Height;
\r
928 if (screenPos.Y > 0)
\r
932 if (color != Color.Black)
\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
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
949 private void AddAvatarToScene(Avatar av)
\r
953 if (Vector3.Distance(PrimPos(av), Client.Self.SimPosition) > 32) return;
\r
955 if (Avatars.ContainsKey(av.LocalID))
\r
957 // flag we got an update??
\r
958 updateAVtes(Avatars[av.LocalID]);
\r
962 GLAvatar ga = new GLAvatar();
\r
963 RenderAvatar ra = new Rendering.RenderAvatar();
\r
967 Avatars.Add(av.LocalID, ra);
\r
972 private void updateAVtes(RenderAvatar ra)
\r
974 if (ra.avatar.Textures == null)
\r
977 int[] tes = { 8, 9, 10, 11, 19, 20 };
\r
979 foreach (int fi in tes)
\r
981 Primitive.TextureEntryFace TEF = ra.avatar.Textures.FaceTextures[fi];
\r
985 if (ra.data[fi] == null || ra.data[fi].TextureInfo.TextureID != TEF.TextureID)
\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
992 Info = data.TextureInfo,
\r
994 TeFace = ra.avatar.Textures.FaceTextures[fi]
\r
997 PendingTextures.Enqueue(textureItem);
\r
1002 private void RenderAvatars(RenderPass pass)
\r
1006 GL.EnableClientState(ArrayCap.VertexArray);
\r
1007 GL.EnableClientState(ArrayCap.TextureCoordArray);
\r
1010 foreach (RenderAvatar av in Avatars.Values)
\r
1014 if (GLAvatar._meshes.Count > 0)
\r
1017 foreach (GLMesh mesh in GLAvatar._meshes.Values)
\r
1020 if (!GLAvatar._showSkirt && mesh.Name == "skirtMesh")
\r
1023 GL.Color3(1f, 1f, 1f);
\r
1025 // Individual prim matrix
\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
1032 //Gl.glTranslatef(mesh.Position.X, mesh.Position.Y, mesh.Position.Z);
\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
1038 GL.Scale(mesh.Scale.X, mesh.Scale.Y, mesh.Scale.Z);
\r
1040 if (pass == RenderPass.Picking)
\r
1042 GL.Disable(EnableCap.Texture2D);
\r
1044 for (int i = 0; i < av.data.Length; i++)
\r
1046 if (av.data[i] != null)
\r
1048 av.data[i].PickingID = avatarNr;
\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
1057 if (av.data[mesh.teFaceID] == null)
\r
1059 GL.Disable(EnableCap.Texture2D);
\r
1063 if (mesh.teFaceID != 0)
\r
1065 GL.Enable(EnableCap.Texture2D);
\r
1066 GL.BindTexture(TextureTarget.Texture2D, av.data[mesh.teFaceID].TextureInfo.TexturePointer);
\r
1070 GL.Disable(EnableCap.Texture2D);
\r
1075 GL.TexCoordPointer(2, TexCoordPointerType.Float, 0, mesh.RenderData.TexCoords);
\r
1076 GL.VertexPointer(3, VertexPointerType.Float, 0, mesh.RenderData.Vertices);
\r
1078 GL.DrawElements(BeginMode.Triangles, mesh.RenderData.Indices.Length, DrawElementsType.UnsignedShort, mesh.RenderData.Indices);
\r
1080 GL.BindTexture(TextureTarget.Texture2D, 0);
\r
1087 GL.Color3(1f, 1f, 1f);
\r
1088 GL.DisableClientState(ArrayCap.VertexArray);
\r
1089 GL.DisableClientState(ArrayCap.TextureCoordArray);
\r
1092 #endregion avatars
\r
1095 bool TerrainModified = true;
\r
1096 float[,] heightTable = new float[256, 256];
\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
1106 private void ResetTerrain()
\r
1108 if (terrainImage != null)
\r
1110 terrainImage.Dispose();
\r
1111 terrainImage = null;
\r
1114 terrainTexture = -1;
\r
1116 if (terrainVBO == -1)
\r
1118 GL.DeleteBuffers(1, ref terrainVBO);
\r
1122 if (terrainIndexVBO == -1)
\r
1124 GL.DeleteBuffers(1, ref terrainIndexVBO);
\r
1125 terrainIndexVBO = -1;
\r
1128 fetchingTerrainTexture = false;
\r
1129 TerrainModified = true;
\r
1132 private void UpdateTerrain()
\r
1136 for (int x = 0; x < 255; x += step)
\r
1138 for (int y = 0; y < 255; y += step)
\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
1146 float[] data = Client.Network.CurrentSim.Terrain[patchNr].Data;
\r
1147 z = data[(int)x % 16 * 16 + (int)y % 16];
\r
1149 heightTable[x, y] = z;
\r
1153 terrainFace = renderer.TerrainMesh(heightTable, 0f, 255f, 0f, 255f);
\r
1154 terrainVertices = terrainFace.Vertices.ToArray();
\r
1155 terrainIndices = terrainFace.Indices.ToArray();
\r
1157 TerrainModified = false;
\r
1160 void CheckTerrainTexture()
\r
1162 if (terrainTexture != -1) return;
\r
1164 if (!fetchingTerrainTexture)
\r
1166 fetchingTerrainTexture = true;
\r
1167 ThreadPool.QueueUserWorkItem(sync =>
\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
1176 fetchingTerrainTexture = false;
\r
1179 else if (terrainImage != null)
\r
1181 terrainTexture = GLLoadImage(terrainImage, false);
\r
1186 private void RenderTerrain()
\r
1188 GL.EnableClientState(ArrayCap.VertexArray);
\r
1189 GL.EnableClientState(ArrayCap.TextureCoordArray);
\r
1190 GL.EnableClientState(ArrayCap.NormalArray);
\r
1192 if (TerrainModified)
\r
1198 CheckTerrainTexture();
\r
1200 if (terrainTexture == -1)
\r
1206 GL.Enable(EnableCap.Texture2D);
\r
1207 GL.BindTexture(TextureTarget.Texture2D, terrainTexture);
\r
1214 fixed (float* normalPtr = &terrainVertices[0].Normal.X)
\r
1215 fixed (float* texPtr = &terrainVertices[0].TexCoord.X)
\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
1226 if (terrainVBO == -1)
\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
1234 GL.BindBuffer(BufferTarget.ArrayBuffer, terrainVBO);
\r
1237 if (terrainIndexVBO == -1)
\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
1245 GL.BindBuffer(BufferTarget.ElementArrayBuffer, terrainIndexVBO);
\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
1252 GL.DrawElements(BeginMode.Triangles, terrainIndices.Length, DrawElementsType.UnsignedShort, IntPtr.Zero);
\r
1254 GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
\r
1255 GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
\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
1262 #endregion Terrain
\r
1264 private void ResetMaterial()
\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
1273 private void RenderObjects(RenderPass pass)
\r
1277 GL.EnableClientState(ArrayCap.VertexArray);
\r
1278 GL.EnableClientState(ArrayCap.TextureCoordArray);
\r
1279 GL.EnableClientState(ArrayCap.NormalArray);
\r
1281 GL.Enable(EnableCap.ColorMaterial);
\r
1283 foreach (FacetedMesh mesh in Prims.Values)
\r
1286 Primitive prim = mesh.Prim;
\r
1287 FacetedMesh parent = null;
\r
1288 RenderAvatar parentav = null;
\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
1293 // Individual prim matrix
\r
1296 if (prim.ParentID != 0)
\r
1298 if (parent != null)
\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
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
1310 int attachment_index = (int)prim.PrimData.AttachmentPoint;
\r
1311 if (attachment_index > GLAvatar.attachment_points.Count())
\r
1313 // invalid LL attachment point
\r
1317 attachment_point apoint = GLAvatar.attachment_points[attachment_index];
\r
1318 if (apoint.jointmesh == null)
\r
1320 //Arse-tachments for us then, things not decoded from avatar_lad fully.
\r
1324 Vector3 point = apoint.getposition();
\r
1325 Quaternion rot = apoint.getrotation();
\r
1327 GL.MultMatrix(Math3D.CreateTranslationMatrix(point));
\r
1328 GL.MultMatrix(Math3D.CreateRotationMatrix(rot));
\r
1333 // Prim roation and position
\r
1334 GL.MultMatrix(Math3D.CreateTranslationMatrix(prim.Position));
\r
1335 GL.MultMatrix(Math3D.CreateRotationMatrix(prim.Rotation));
\r
1338 GL.Scale(prim.Scale.X, prim.Scale.Y, prim.Scale.Z);
\r
1340 // Draw the prim faces
\r
1341 for (int j = 0; j < mesh.Faces.Count; j++)
\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
1348 if (teFace == null)
\r
1349 teFace = mesh.Prim.Textures.DefaultTexture;
\r
1351 if (teFace == null)
\r
1354 if (pass != RenderPass.Picking)
\r
1356 bool belongToAlphaPass = (teFace.RGBA.A < 0.99) || data.TextureInfo.HasAlpha;
\r
1358 if (belongToAlphaPass && pass != RenderPass.Alpha) continue;
\r
1359 if (!belongToAlphaPass && pass == RenderPass.Alpha) continue;
\r
1361 // Don't render transparent faces
\r
1362 if (teFace.RGBA.A <= 0.01f) continue;
\r
1364 switch (teFace.Shiny)
\r
1366 case Shininess.High:
\r
1367 GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 94f);
\r
1370 case Shininess.Medium:
\r
1371 GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 64f);
\r
1374 case Shininess.Low:
\r
1375 GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 24f);
\r
1379 case Shininess.None:
\r
1381 GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 0f);
\r
1385 var faceColor = new float[] { teFace.RGBA.R, teFace.RGBA.G, teFace.RGBA.B, teFace.RGBA.A };
\r
1387 GL.Color4(faceColor);
\r
1388 GL.Material(MaterialFace.Front, MaterialParameter.AmbientAndDiffuse, faceColor);
\r
1389 GL.Material(MaterialFace.Front, MaterialParameter.Specular, faceColor);
\r
1391 if (data.TextureInfo.TexturePointer != 0)
\r
1393 GL.Enable(EnableCap.Texture2D);
\r
1394 GL.BindTexture(TextureTarget.Texture2D, data.TextureInfo.TexturePointer);
\r
1398 GL.Disable(EnableCap.Texture2D);
\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
1408 GL.Color4(faceColor);
\r
1413 Vertex[] verts = face.Vertices.ToArray();
\r
1417 fixed (float* normalPtr = &verts[0].Normal.X)
\r
1418 fixed (float* texPtr = &verts[0].TexCoord.X)
\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
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
1436 GL.DrawElements(BeginMode.Triangles, face.Indices.Count, DrawElementsType.UnsignedShort, IntPtr.Zero);
\r
1438 GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
\r
1439 GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
\r
1444 GL.BindTexture(TextureTarget.Texture2D, 0);
\r
1445 GL.Color4(new byte[] { 255, 255, 255, 255 });
\r
1448 // Pop the prim matrix
\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
1458 void DrawWaterQuad(float x, float y, float z)
\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
1466 public void RenderWater()
\r
1468 float z = Client.Network.CurrentSim.WaterHeight;
\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
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
1480 GL.Color3(1f, 1f, 1f);
\r
1481 GL.Enable(EnableCap.Lighting);
\r
1482 GL.Disable(EnableCap.ColorMaterial);
\r
1485 private void Render(bool picking)
\r
1489 GL.ClearColor(1f, 1f, 1f, 1f);
\r
1493 GL.ClearColor(0.39f, 0.58f, 0.93f, 1.0f);
\r
1496 GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
\r
1497 GL.LoadIdentity();
\r
1499 // Setup wireframe or solid fill drawing mode
\r
1500 if (Wireframe && !picking)
\r
1502 GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);
\r
1506 GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);
\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
1513 GL.MultMatrix(ref mLookAt);
\r
1515 //GL.Light(LightName.Light0, LightParameter.Position, lightPos);
\r
1517 // Push the world matrix
\r
1520 if (Camera.Modified)
\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
1527 Camera.Modified = false;
\r
1532 RenderObjects(RenderPass.Picking);
\r
1533 RenderAvatars(RenderPass.Picking);
\r
1538 RenderObjects(RenderPass.Simple);
\r
1539 RenderAvatars(RenderPass.Simple);
\r
1542 RenderObjects(RenderPass.Alpha);
\r
1547 // Pop the world matrix
\r
1553 private void GluPerspective(float fovy, float aspect, float zNear, float zFar)
\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
1560 private bool TryPick(int x, int y, out object picked, out int faceID)
\r
1562 // Save old attributes
\r
1563 GL.PushAttrib(AttribMask.AllAttribBits);
\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
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
1583 int primID = Utils.BytesToUInt16(color, 0);
\r
1584 faceID = color[2];
\r
1588 if (color[3] == 254) // Avatar
\r
1592 foreach (var avatar in Avatars.Values)
\r
1594 for (int i = 0; i < avatar.data.Length; i++)
\r
1596 var face = avatar.data[i];
\r
1597 if (face != null && face.PickingID == primID)
\r
1606 if (picked != null)
\r
1612 if (color[3] == 255) // Prim
\r
1616 foreach (var mesh in Prims.Values)
\r
1618 foreach (var face in mesh.Faces)
\r
1620 if (face.UserData == null) continue;
\r
1621 if (((FaceData)face.UserData).PickingID == primID)
\r
1628 if (picked != null) break;
\r
1633 return picked != null;
\r
1637 private void MeshPrim(Primitive prim, FacetedMesh mesh)
\r
1639 FacetedMesh existingMesh = null;
\r
1643 if (Prims.ContainsKey(prim.LocalID))
\r
1645 existingMesh = Prims[prim.LocalID];
\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
1653 Primitive.TextureEntryFace teFace = prim.Textures.GetFace((uint)j);
\r
1654 if (teFace == null) continue;
\r
1656 Face face = mesh.Faces[j];
\r
1657 FaceData data = new FaceData();
\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
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
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
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
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
1681 data.BoundingSphere.R = (data.BoundingSphere.Max - data.BoundingSphere.Min).Length();
\r
1683 // Indices for this face
\r
1684 data.Indices = face.Indices.ToArray();
\r
1686 // Texture transform for this face
\r
1687 renderer.TransformTexCoords(face.Vertices, face.Center, teFace);
\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
1693 data.TexCoords[k * 2 + 0] = face.Vertices[k].TexCoord.X;
\r
1694 data.TexCoords[k * 2 + 1] = face.Vertices[k].TexCoord.Y;
\r
1697 // Set the UserData for this face to our FaceData struct
\r
1698 face.UserData = data;
\r
1699 mesh.Faces[j] = face;
\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
1708 FaceData existingData = (FaceData)existingMesh.Faces[j].UserData;
\r
1709 data.TextureInfo.TexturePointer = existingData.TextureInfo.TexturePointer;
\r
1714 var textureItem = new TextureLoadItem()
\r
1716 Info = data.TextureInfo,
\r
1721 PendingTextures.Enqueue(textureItem);
\r
1727 Prims[prim.LocalID] = mesh;
\r
1731 private void UpdatePrimBlocking(Primitive prim)
\r
1733 if (Vector3.Distance(PrimPos(prim), Client.Self.SimPosition) > DrawDistance && !Prims.ContainsKey(prim.ParentID) && !Avatars.ContainsKey(prim.ParentID)) return;
\r
1735 if (Client.Network.CurrentSim.ObjectsAvatars.ContainsKey(prim.LocalID))
\r
1737 AddAvatarToScene(Client.Network.CurrentSim.ObjectsAvatars[prim.LocalID]);
\r
1742 if (prim.PrimData.PCode != PCode.Prim) return;
\r
1744 if (prim.Textures == null) return;
\r
1747 if (prim.Sculpt == null || prim.Sculpt.SculptTexture == UUID.Zero)
\r
1749 MeshPrim(prim, renderer.GenerateFacetedMesh(prim, DetailLevel.High));
\r
1755 FacetedMesh mesh = null;
\r
1757 if (prim.Sculpt.Type != SculptType.Mesh)
\r
1758 { // Regular sculptie
\r
1761 lock (sculptCache)
\r
1763 if (sculptCache.ContainsKey(prim.Sculpt.SculptTexture))
\r
1765 img = sculptCache[prim.Sculpt.SculptTexture];
\r
1771 if (LoadTexture(prim.Sculpt.SculptTexture, ref img, true))
\r
1773 sculptCache[prim.Sculpt.SculptTexture] = (Bitmap)img;
\r
1781 mesh = renderer.GenerateFacetedSculptMesh(prim, (Bitmap)img, DetailLevel.High);
\r
1785 AutoResetEvent gotMesh = new AutoResetEvent(false);
\r
1786 bool meshSuccess = false;
\r
1788 Client.Assets.RequestMesh(prim.Sculpt.SculptTexture, (success, meshAsset) =>
\r
1790 if (!success || !FacetedMesh.TryDecodeFromAsset(prim, meshAsset, DetailLevel.Highest, out mesh))
\r
1792 Logger.Log("Failed to fetch or decode the mesh asset", Helpers.LogLevel.Warning, Client);
\r
1796 meshSuccess = true;
\r
1801 if (!gotMesh.WaitOne(20 * 1000, false)) return;
\r
1802 if (!meshSuccess) return;
\r
1807 MeshPrim(prim, mesh);
\r
1815 private bool LoadTexture(UUID textureID, ref Image texture, bool removeAlpha)
\r
1817 ManualResetEvent gotImage = new ManualResetEvent(false);
\r
1823 instance.Client.Assets.RequestImage(textureID, (TextureRequestState state, AssetTexture assetTexture) =>
\r
1825 if (state == TextureRequestState.Finished)
\r
1828 OpenJPEG.DecodeToImage(assetTexture.AssetData, out mi);
\r
1832 if ((mi.Channels & ManagedImage.ImageChannels.Alpha) != 0)
\r
1834 mi.ConvertChannels(mi.Channels & ~ManagedImage.ImageChannels.Alpha);
\r
1838 img = LoadTGAClass.LoadTGA(new MemoryStream(mi.ExportTGA()));
\r
1843 gotImage.WaitOne(30 * 1000, false);
\r
1851 catch (Exception e)
\r
1853 Logger.Log(e.Message, Helpers.LogLevel.Error, instance.Client, e);
\r
1857 #endregion Private methods (the meat)
\r
1859 #region Form controls handlers
\r
1860 private void scrollZoom_ValueChanged(object sender, EventArgs e)
\r
1862 Camera.Zoom = 1f - (float)scrollZoom.Value / (float)scrollZoom.Minimum;
\r
1863 glControl_Resize(null, null);
\r
1866 private void chkWireFrame_CheckedChanged(object sender, EventArgs e)
\r
1868 Wireframe = chkWireFrame.Checked;
\r
1871 private void btnReset_Click(object sender, EventArgs e)
\r
1874 scrollZoom.Value = 0;
\r
1877 private void cbAA_CheckedChanged(object sender, EventArgs e)
\r
1879 instance.GlobalSettings["use_multi_sampling"] = UseMultiSampling = cbAA.Checked;
\r
1883 #endregion Form controls handlers
\r
1885 #region Context menu
\r
1886 private void ctxObjects_Opening(object sender, System.ComponentModel.CancelEventArgs e)
\r
1888 if (instance.State.IsSitting)
\r
1890 sitToolStripMenuItem.Text = "Stand up";
\r
1892 else if (RightclickedPrim.Prim.Properties != null
\r
1893 && !string.IsNullOrEmpty(RightclickedPrim.Prim.Properties.SitName))
\r
1895 sitToolStripMenuItem.Text = RightclickedPrim.Prim.Properties.SitName;
\r
1899 sitToolStripMenuItem.Text = "Sit";
\r
1902 if (RightclickedPrim.Prim.Properties != null
\r
1903 && !string.IsNullOrEmpty(RightclickedPrim.Prim.Properties.TouchName))
\r
1905 touchToolStripMenuItem.Text = RightclickedPrim.Prim.Properties.TouchName;
\r
1909 touchToolStripMenuItem.Text = "Touch";
\r
1913 private void touchToolStripMenuItem_Click(object sender, EventArgs e)
\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
1921 private void sitToolStripMenuItem_Click(object sender, EventArgs e)
\r
1923 if (!instance.State.IsSitting)
\r
1925 instance.State.SetSitting(true, RightclickedPrim.Prim.ID);
\r
1929 instance.State.SetSitting(false, UUID.Zero);
\r
1933 private void takeToolStripMenuItem_Click(object sender, EventArgs e)
\r
1935 instance.MediaManager.PlayUISound(UISounds.ObjectDelete);
\r
1936 Client.Inventory.RequestDeRezToInventory(RightclickedPrim.Prim.LocalID);
\r
1940 private void returnToolStripMenuItem_Click(object sender, EventArgs e)
\r
1942 instance.MediaManager.PlayUISound(UISounds.ObjectDelete);
\r
1943 Client.Inventory.RequestDeRezToInventory(RightclickedPrim.Prim.LocalID, DeRezDestination.ReturnToOwner, UUID.Zero, UUID.Random());
\r
1947 private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
\r
1949 if (RightclickedPrim.Prim.Properties != null && RightclickedPrim.Prim.Properties.OwnerID != Client.Self.AgentID)
\r
1950 returnToolStripMenuItem_Click(sender, e);
\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
1958 #endregion Context menu
\r