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
\r
81 get { return drawDistance; }
\r
84 drawDistance = value;
\r
85 drawDistanceSquared = value * value;
\r
92 /// List of prims in the scene
\r
94 Dictionary<uint, RenderPrimitive> Prims = new Dictionary<uint, RenderPrimitive>();
\r
95 List<SceneObject> SortedObjects;
\r
96 Dictionary<uint, RenderAvatar> Avatars = new Dictionary<uint, RenderAvatar>();
\r
101 public bool PrimitiveRenderingEnabled = true;
\r
106 public bool AvatarRenderingEnabled = true;
\r
109 /// Show avatar skeloton
\r
111 public bool RenderAvatarSkeleton = false;
\r
113 #endregion Public fields
\r
115 #region Private fields
\r
118 Dictionary<UUID, TextureInfo> TexturesPtrMap = new Dictionary<UUID, TextureInfo>();
\r
119 RadegastInstance instance;
\r
120 MeshmerizerR renderer;
\r
121 OpenTK.Graphics.GraphicsMode GLMode = null;
\r
122 AutoResetEvent TextureThreadContextReady = new AutoResetEvent(false);
\r
123 BlockingQueue<TextureLoadItem> PendingTextures = new BlockingQueue<TextureLoadItem>();
\r
126 Font HoverTextFont = new Font(FontFamily.GenericSansSerif, 9f, FontStyle.Regular);
\r
127 Font AvatarTagFont = new Font(FontFamily.GenericSansSerif, 10f, FontStyle.Bold);
\r
128 Dictionary<UUID, Bitmap> sculptCache = new Dictionary<UUID, Bitmap>();
\r
129 OpenTK.Matrix4 ModelMatrix;
\r
130 OpenTK.Matrix4 ProjectionMatrix;
\r
131 int[] Viewport = new int[4];
\r
132 bool useVBO = true;
\r
133 System.Diagnostics.Stopwatch renderTimer;
\r
134 double lastFrameTime = 0d;
\r
135 double advTimerTick = 0d;
\r
136 float minLODFactor = 0.0001f;
\r
138 float[] lightPos = new float[] { 128f, 128f, 5000f, 0f };
\r
139 float ambient = 0.26f;
\r
140 float difuse = 0.27f;
\r
141 float specular = 0.20f;
\r
142 OpenTK.Vector4 ambientColor;
\r
143 OpenTK.Vector4 difuseColor;
\r
144 OpenTK.Vector4 specularColor;
\r
145 float drawDistance = 48f;
\r
146 float drawDistanceSquared = 48f * 48f;
\r
148 #endregion Private fields
\r
150 #region Construction and disposal
\r
151 public SceneWindow(RadegastInstance instance)
\r
154 InitializeComponent();
\r
155 Disposed += new EventHandler(frmPrimWorkshop_Disposed);
\r
156 AutoSavePosition = true;
\r
157 UseMultiSampling = cbAA.Checked = instance.GlobalSettings["use_multi_sampling"];
\r
158 cbAA.CheckedChanged += cbAA_CheckedChanged;
\r
160 this.instance = instance;
\r
162 renderer = new MeshmerizerR();
\r
163 renderTimer = new System.Diagnostics.Stopwatch();
\r
164 renderTimer.Start();
\r
166 // Camera initial setting
\r
167 Camera = new Camera();
\r
170 GLAvatar.loadlindenmeshes2("avatar_lad.xml");
\r
172 foreach (VisualParamEx vpe in VisualParamEx.morphParams.Values)
\r
174 comboBox_morph.Items.Add(vpe.Name);
\r
177 foreach (VisualParamEx vpe in VisualParamEx.drivenParams.Values)
\r
179 comboBox_driver.Items.Add(vpe.Name);
\r
182 tbDrawDistance.Value = (int)DrawDistance;
\r
183 lblDrawDistance.Text = string.Format("Draw distance: {0}", tbDrawDistance.Value);
\r
185 Client.Objects.TerseObjectUpdate += new EventHandler<TerseObjectUpdateEventArgs>(Objects_TerseObjectUpdate);
\r
186 Client.Objects.ObjectUpdate += new EventHandler<PrimEventArgs>(Objects_ObjectUpdate);
\r
187 Client.Objects.ObjectDataBlockUpdate += new EventHandler<ObjectDataBlockUpdateEventArgs>(Objects_ObjectDataBlockUpdate);
\r
188 Client.Objects.KillObject += new EventHandler<KillObjectEventArgs>(Objects_KillObject);
\r
189 Client.Network.SimChanged += new EventHandler<SimChangedEventArgs>(Network_SimChanged);
\r
190 Client.Self.TeleportProgress += new EventHandler<TeleportEventArgs>(Self_TeleportProgress);
\r
191 Client.Terrain.LandPatchReceived += new EventHandler<LandPatchReceivedEventArgs>(Terrain_LandPatchReceived);
\r
192 //Client.Avatars.AvatarAnimation += new EventHandler<AvatarAnimationEventArgs>(AvatarAnimationChanged);
\r
193 Client.Avatars.AvatarAppearance += new EventHandler<AvatarAppearanceEventArgs>(Avatars_AvatarAppearance);
\r
194 Instance.Netcom.ClientDisconnected += new EventHandler<DisconnectedEventArgs>(Netcom_ClientDisconnected);
\r
195 Application.Idle += new EventHandler(Application_Idle);
\r
199 void frmPrimWorkshop_Disposed(object sender, EventArgs e)
\r
201 RenderingEnabled = false;
\r
202 Application.Idle -= new EventHandler(Application_Idle);
\r
204 PendingTextures.Close();
\r
206 Client.Objects.TerseObjectUpdate -= new EventHandler<TerseObjectUpdateEventArgs>(Objects_TerseObjectUpdate);
\r
207 Client.Objects.ObjectUpdate -= new EventHandler<PrimEventArgs>(Objects_ObjectUpdate);
\r
208 Client.Objects.ObjectDataBlockUpdate -= new EventHandler<ObjectDataBlockUpdateEventArgs>(Objects_ObjectDataBlockUpdate);
\r
209 Client.Objects.KillObject -= new EventHandler<KillObjectEventArgs>(Objects_KillObject);
\r
210 Client.Network.SimChanged -= new EventHandler<SimChangedEventArgs>(Network_SimChanged);
\r
211 Client.Self.TeleportProgress -= new EventHandler<TeleportEventArgs>(Self_TeleportProgress);
\r
212 Client.Terrain.LandPatchReceived -= new EventHandler<LandPatchReceivedEventArgs>(Terrain_LandPatchReceived);
\r
213 //Client.Avatars.AvatarAnimation -= new EventHandler<AvatarAnimationEventArgs>(AvatarAnimationChanged);
\r
214 Client.Avatars.AvatarAppearance -= new EventHandler<AvatarAppearanceEventArgs>(Avatars_AvatarAppearance);
\r
216 if (instance.Netcom != null)
\r
218 Instance.Netcom.ClientDisconnected -= new EventHandler<DisconnectedEventArgs>(Netcom_ClientDisconnected);
\r
221 if (glControl != null)
\r
223 glControl.Dispose();
\r
229 foreach (var img in sculptCache.Values)
\r
231 sculptCache.Clear();
\r
234 lock (Prims) Prims.Clear();
\r
235 lock (Avatars) Avatars.Clear();
\r
237 TexturesPtrMap.Clear();
\r
241 void Application_Idle(object sender, EventArgs e)
\r
243 if (glControl != null && !glControl.IsDisposed && RenderingEnabled)
\r
247 while (glControl != null && glControl.IsIdle)
\r
250 if (instance.MonoRuntime)
\r
252 Application.DoEvents();
\r
256 catch (ObjectDisposedException)
\r
260 #endregion Construction and disposal
\r
262 #region Network messaage handlers
\r
263 void Terrain_LandPatchReceived(object sender, LandPatchReceivedEventArgs e)
\r
265 if (e.Simulator.Handle == Client.Network.CurrentSim.Handle)
\r
267 TerrainModified = true;
\r
271 void Netcom_ClientDisconnected(object sender, DisconnectedEventArgs e)
\r
273 if (InvokeRequired)
\r
275 if (IsHandleCreated || !instance.MonoRuntime)
\r
277 BeginInvoke(new MethodInvoker(() => Netcom_ClientDisconnected(sender, e)));
\r
285 void Self_TeleportProgress(object sender, TeleportEventArgs e)
\r
289 case TeleportStatus.Progress:
\r
290 case TeleportStatus.Start:
\r
291 RenderingEnabled = false;
\r
294 case TeleportStatus.Cancelled:
\r
295 case TeleportStatus.Failed:
\r
296 RenderingEnabled = true;
\r
299 case TeleportStatus.Finished:
\r
300 ThreadPool.QueueUserWorkItem(sync =>
\r
302 Thread.Sleep(3000);
\r
304 LoadCurrentPrims();
\r
305 RenderingEnabled = true;
\r
311 void Network_SimChanged(object sender, SimChangedEventArgs e)
\r
316 foreach (var img in sculptCache.Values)
\r
318 sculptCache.Clear();
\r
320 lock (Prims) Prims.Clear();
\r
323 void Objects_KillObject(object sender, KillObjectEventArgs e)
\r
325 if (e.Simulator.Handle != Client.Network.CurrentSim.Handle) return;
\r
326 // TODO: there should be really cleanup of resources when removing prims and avatars
\r
327 lock (Prims) Prims.Remove(e.ObjectLocalID);
\r
328 lock (Avatars) Avatars.Remove(e.ObjectLocalID);
\r
331 void Objects_TerseObjectUpdate(object sender, TerseObjectUpdateEventArgs e)
\r
333 if (e.Simulator.Handle != Client.Network.CurrentSim.Handle) return;
\r
334 UpdatePrimBlocking(e.Prim);
\r
337 void Objects_ObjectUpdate(object sender, PrimEventArgs e)
\r
339 if (e.Simulator.Handle != Client.Network.CurrentSim.Handle) return;
\r
340 UpdatePrimBlocking(e.Prim);
\r
343 void Objects_ObjectDataBlockUpdate(object sender, ObjectDataBlockUpdateEventArgs e)
\r
345 if (e.Simulator.Handle != Client.Network.CurrentSim.Handle) return;
\r
346 UpdatePrimBlocking(e.Prim);
\r
349 void AvatarAnimationChanged(object sender, AvatarAnimationEventArgs e)
\r
352 // We don't currently have UUID -> RenderAvatar mapping so we need to walk the list
\r
353 foreach (RenderAvatar av in Avatars.Values)
\r
355 if (av.avatar.ID == e.AvatarID)
\r
357 foreach (Animation anim in e.Animations)
\r
359 Client.Assets.RequestAsset(anim.AnimationID, AssetType.Animation, false, animRecievedCallback);
\r
360 av.animlist.Add(anim.AnimationID, anim);
\r
367 void animRecievedCallback(AssetDownload transfer, Asset asset)
\r
369 if (transfer.Success)
\r
371 BinBVHAnimationReader b = new BinBVHAnimationReader(asset.AssetData);
\r
376 void Avatars_AvatarAppearance(object sender, AvatarAppearanceEventArgs e)
\r
378 // We don't currently have UUID -> RenderAvatar mapping so we need to walk the list
\r
379 foreach (RenderAvatar av in Avatars.Values)
\r
381 if (av.avatar.ID == e.AvatarID)
\r
383 av.glavatar.morph(av.avatar);
\r
389 #endregion Network messaage handlers
\r
391 #region glControl setup and disposal
\r
392 public void SetupGLControl()
\r
394 RenderingEnabled = false;
\r
396 if (glControl != null)
\r
397 glControl.Dispose();
\r
404 if (!UseMultiSampling)
\r
406 GLMode = new OpenTK.Graphics.GraphicsMode(OpenTK.DisplayDevice.Default.BitsPerPixel, 24, 8, 0);
\r
410 for (int aa = 0; aa <= 4; aa += 2)
\r
412 var testMode = new OpenTK.Graphics.GraphicsMode(OpenTK.DisplayDevice.Default.BitsPerPixel, 24, 8, aa);
\r
413 if (testMode.Samples == aa)
\r
428 if (GLMode == null)
\r
430 // Try default mode
\r
431 glControl = new OpenTK.GLControl();
\r
435 glControl = new OpenTK.GLControl(GLMode);
\r
438 catch (Exception ex)
\r
440 Logger.Log(ex.Message, Helpers.LogLevel.Warning, Client);
\r
444 if (glControl == null)
\r
446 Logger.Log("Failed to initialize OpenGL control, cannot continue", Helpers.LogLevel.Error, Client);
\r
450 Logger.Log("Initializing OpenGL mode: " + GLMode.ToString(), Helpers.LogLevel.Info);
\r
452 glControl.Paint += glControl_Paint;
\r
453 glControl.Resize += glControl_Resize;
\r
454 glControl.MouseDown += glControl_MouseDown;
\r
455 glControl.MouseUp += glControl_MouseUp;
\r
456 glControl.MouseMove += glControl_MouseMove;
\r
457 glControl.MouseWheel += glControl_MouseWheel;
\r
458 glControl.Load += new EventHandler(glControl_Load);
\r
459 glControl.Disposed += new EventHandler(glControl_Disposed);
\r
460 glControl.Dock = DockStyle.Fill;
\r
461 glControl.VSync = false;
\r
462 Controls.Add(glControl);
\r
463 glControl.BringToFront();
\r
466 void glControl_Disposed(object sender, EventArgs e)
\r
468 TextureThreadRunning = false;
\r
469 PendingTextures.Close();
\r
470 glControl.Paint -= glControl_Paint;
\r
471 glControl.Resize -= glControl_Resize;
\r
472 glControl.MouseDown -= glControl_MouseDown;
\r
473 glControl.MouseUp -= glControl_MouseUp;
\r
474 glControl.MouseMove -= glControl_MouseMove;
\r
475 glControl.MouseWheel -= glControl_MouseWheel;
\r
476 glControl.Load -= new EventHandler(glControl_Load);
\r
477 glControl.Disposed -= glControl_Disposed;
\r
482 ambientColor = new OpenTK.Vector4(ambient, ambient, ambient, difuse);
\r
483 difuseColor = new OpenTK.Vector4(difuse, difuse, difuse, difuse);
\r
484 specularColor = new OpenTK.Vector4(specular, specular, specular, specular);
\r
485 GL.Light(LightName.Light0, LightParameter.Ambient, ambientColor);
\r
486 GL.Light(LightName.Light0, LightParameter.Diffuse, difuseColor);
\r
487 GL.Light(LightName.Light0, LightParameter.Specular, specularColor);
\r
488 GL.Light(LightName.Light0, LightParameter.Position, lightPos);
\r
491 void glControl_Load(object sender, EventArgs e)
\r
495 GL.ShadeModel(ShadingModel.Smooth);
\r
497 GL.Enable(EnableCap.Lighting);
\r
498 GL.Enable(EnableCap.Light0);
\r
501 GL.ClearDepth(1.0d);
\r
502 GL.Enable(EnableCap.DepthTest);
\r
503 GL.Enable(EnableCap.CullFace);
\r
504 GL.CullFace(CullFaceMode.Back);
\r
506 // GL.Color() tracks objects ambient and diffuse color
\r
507 GL.Enable(EnableCap.ColorMaterial);
\r
508 GL.ColorMaterial(MaterialFace.Front, ColorMaterialParameter.AmbientAndDiffuse);
\r
510 GL.DepthMask(true);
\r
511 GL.DepthFunc(DepthFunction.Lequal);
\r
512 GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);
\r
513 GL.MatrixMode(MatrixMode.Projection);
\r
515 GL.Enable(EnableCap.Blend);
\r
516 GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
\r
517 string glExtensions = GL.GetString(StringName.Extensions);
\r
518 hasMipmap = glExtensions.Contains("GL_SGIS_generate_mipmap");
\r
519 useVBO = glExtensions.Contains("ARB_vertex_buffer_object");
\r
521 // Double check if we have mipmap ability
\r
527 Bitmap testPic = new Bitmap(1, 1);
\r
528 BitmapData testData = testPic.LockBits(new Rectangle(0, 0, 1, 1), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
\r
529 GL.GenTextures(1, out testID);
\r
530 GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgb8, 1, 1, 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgr, PixelType.UnsignedByte, testData.Scan0);
\r
531 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.LinearMipmapLinear);
\r
532 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.GenerateMipmap, 1);
\r
533 GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
\r
534 testPic.UnlockBits(testData);
\r
536 GL.DeleteTexture(testID);
\r
540 Logger.DebugLog("Don't have glGenerateMipmap() after all");
\r
545 RenderingEnabled = true;
\r
546 // Call the resizing function which sets up the GL drawing window
\r
547 // and will also invalidate the GL control
\r
548 glControl_Resize(null, null);
\r
550 glControl.Context.MakeCurrent(null);
\r
551 TextureThreadContextReady.Reset();
\r
552 var textureThread = new Thread(() => TextureThread())
\r
554 IsBackground = true,
\r
555 Name = "TextureLoadingThread"
\r
557 textureThread.Start();
\r
558 TextureThreadContextReady.WaitOne(1000, false);
\r
559 glControl.MakeCurrent();
\r
561 catch (Exception ex)
\r
563 RenderingEnabled = false;
\r
564 Logger.Log("Failed to initialize OpenGL control", Helpers.LogLevel.Warning, Client, ex);
\r
567 #endregion glControl setup and disposal
\r
569 #region glControl paint and resize events
\r
570 private void MainRenderLoop()
\r
572 if (!RenderingEnabled) return;
\r
573 lastFrameTime = renderTimer.Elapsed.TotalSeconds;
\r
575 // Something went horribly wrong
\r
576 if (lastFrameTime < 0) return;
\r
578 // Stopwatch loses resolution if it runs for a long time, reset it
\r
579 renderTimer.Reset();
\r
580 renderTimer.Start();
\r
584 glControl.SwapBuffers();
\r
587 void glControl_Paint(object sender, EventArgs e)
\r
592 private void glControl_Resize(object sender, EventArgs e)
\r
594 if (!RenderingEnabled) return;
\r
595 glControl.MakeCurrent();
\r
597 GL.ClearColor(0.39f, 0.58f, 0.93f, 1.0f);
\r
599 GL.Viewport(0, 0, glControl.Width, glControl.Height);
\r
602 GL.MatrixMode(MatrixMode.Projection);
\r
607 GL.MatrixMode(MatrixMode.Modelview);
\r
610 #endregion glControl paint and resize events
\r
612 #region Mouse handling
\r
613 bool dragging = false;
\r
614 int dragX, dragY, downX, downY;
\r
616 private void glControl_MouseWheel(object sender, MouseEventArgs e)
\r
620 RenderPrimitive RightclickedPrim;
\r
621 int RightclickedFaceID;
\r
623 private void glControl_MouseDown(object sender, MouseEventArgs e)
\r
625 if (e.Button == MouseButtons.Left)
\r
628 downX = dragX = e.X;
\r
629 downY = dragY = e.Y;
\r
631 else if (e.Button == MouseButtons.Right)
\r
634 if (TryPick(e.X, e.Y, out picked, out RightclickedFaceID))
\r
636 if (picked is RenderPrimitive)
\r
638 RightclickedPrim = (RenderPrimitive)picked;
\r
639 ctxObjects.Show(glControl, e.X, e.Y);
\r
641 else if (picked is RenderAvatar)
\r
643 // TODO: add context menu when clicked on an avatar
\r
650 private void glControl_MouseMove(object sender, MouseEventArgs e)
\r
654 int deltaX = e.X - dragX;
\r
655 int deltaY = e.Y - dragY;
\r
656 float pixelToM = 1f / 75f;
\r
658 if (e.Button == MouseButtons.Left)
\r
661 if (ModifierKeys == Keys.Control || ModifierKeys == (Keys.Alt | Keys.Control | Keys.Shift))
\r
663 Vector3 direction = Camera.Position - Camera.FocalPoint;
\r
664 direction.Normalize();
\r
665 Vector3 vy = direction % new Vector3(0f, 0f, 1f);
\r
666 Vector3 vx = vy % direction;
\r
667 Vector3 vxy = vx * deltaY * pixelToM * 2 + vy * deltaX * pixelToM * 2;
\r
668 Camera.Position += vxy;
\r
669 Camera.FocalPoint += vxy;
\r
672 // Alt-zoom (up down move camera closer to target, left right rotate around target)
\r
673 if (ModifierKeys == Keys.Alt)
\r
675 Camera.Position += (Camera.Position - Camera.FocalPoint) * deltaY * pixelToM;
\r
676 var dx = -(deltaX * pixelToM);
\r
677 Camera.Position = Camera.FocalPoint + (Camera.Position - Camera.FocalPoint) * new Quaternion(0f, 0f, (float)Math.Sin(dx), (float)Math.Cos(dx));
\r
680 // Rotate camera in a vertical circle around target on up down mouse movement
\r
681 if (ModifierKeys == (Keys.Alt | Keys.Control))
\r
683 Camera.Position = Camera.FocalPoint +
\r
684 (Camera.Position - Camera.FocalPoint)
\r
685 * Quaternion.CreateFromAxisAngle((Camera.Position - Camera.FocalPoint) % new Vector3(0f, 0f, 1f), deltaY * pixelToM);
\r
686 var dx = -(deltaX * pixelToM);
\r
687 Camera.Position = Camera.FocalPoint + (Camera.Position - Camera.FocalPoint) * new Quaternion(0f, 0f, (float)Math.Sin(dx), (float)Math.Cos(dx));
\r
697 private void glControl_MouseUp(object sender, MouseEventArgs e)
\r
699 if (e.Button == MouseButtons.Left)
\r
703 if (e.X == downX && e.Y == downY) // click
\r
707 if (TryPick(e.X, e.Y, out clicked, out faceID))
\r
709 if (clicked is RenderPrimitive)
\r
711 RenderPrimitive picked = (RenderPrimitive)clicked;
\r
713 if (ModifierKeys == Keys.None)
\r
715 Client.Self.Grab(picked.Prim.LocalID, Vector3.Zero, Vector3.Zero, Vector3.Zero, faceID, Vector3.Zero, Vector3.Zero, Vector3.Zero);
\r
716 Client.Self.GrabUpdate(picked.Prim.ID, Vector3.Zero, Vector3.Zero, Vector3.Zero, Vector3.Zero, faceID, Vector3.Zero, Vector3.Zero, Vector3.Zero);
\r
717 Client.Self.DeGrab(picked.Prim.LocalID);
\r
719 else if (ModifierKeys == Keys.Alt)
\r
721 Camera.FocalPoint = picked.SimPosition;
\r
722 Cursor.Position = glControl.PointToScreen(new Point(glControl.Width / 2, glControl.Height / 2));
\r
725 else if (clicked is RenderAvatar)
\r
727 RenderAvatar av = (RenderAvatar)clicked;
\r
728 if (ModifierKeys == Keys.Alt)
\r
730 Vector3 pos = av.SimPosition;
\r
731 pos.Z += 1.5f; // focus roughly on the chest area
\r
732 Camera.FocalPoint = pos;
\r
733 Cursor.Position = glControl.PointToScreen(new Point(glControl.Width / 2, glControl.Height / 2));
\r
740 #endregion Mouse handling
\r
742 // Switch to ortho display mode for drawing hud
\r
743 public void GLHUDBegin()
\r
745 GL.Disable(EnableCap.DepthTest);
\r
746 GL.Disable(EnableCap.Lighting);
\r
747 GL.Disable(EnableCap.Light0);
\r
748 GL.MatrixMode(MatrixMode.Projection);
\r
751 GL.Ortho(0, glControl.Width, 0, glControl.Height, -5, 1);
\r
752 GL.MatrixMode(MatrixMode.Modelview);
\r
756 // Switch back to frustrum display mode
\r
757 public void GLHUDEnd()
\r
759 GL.Enable(EnableCap.DepthTest);
\r
760 GL.Enable(EnableCap.Lighting);
\r
761 GL.Enable(EnableCap.Light0);
\r
762 GL.MatrixMode(MatrixMode.Projection);
\r
764 GL.MatrixMode(MatrixMode.Modelview);
\r
767 public int GLLoadImage(Bitmap bitmap, bool hasAlpha)
\r
770 GL.GenTextures(1, out ret);
\r
771 GL.BindTexture(TextureTarget.Texture2D, ret);
\r
773 Rectangle rectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
\r
775 BitmapData bitmapData =
\r
778 ImageLockMode.ReadOnly,
\r
779 hasAlpha ? System.Drawing.Imaging.PixelFormat.Format32bppArgb : System.Drawing.Imaging.PixelFormat.Format24bppRgb);
\r
782 TextureTarget.Texture2D,
\r
784 hasAlpha ? PixelInternalFormat.Rgba : PixelInternalFormat.Rgb8,
\r
788 hasAlpha ? OpenTK.Graphics.OpenGL.PixelFormat.Bgra : OpenTK.Graphics.OpenGL.PixelFormat.Bgr,
\r
789 PixelType.UnsignedByte,
\r
792 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
\r
793 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
\r
794 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
\r
797 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.LinearMipmapLinear);
\r
798 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.GenerateMipmap, 1);
\r
799 GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
\r
803 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
\r
806 bitmap.UnlockBits(bitmapData);
\r
810 #region Texture thread
\r
811 bool TextureThreadRunning = true;
\r
813 void TextureThread()
\r
815 OpenTK.INativeWindow window = new OpenTK.NativeWindow();
\r
816 OpenTK.Graphics.IGraphicsContext context = new OpenTK.Graphics.GraphicsContext(GLMode, window.WindowInfo);
\r
817 context.MakeCurrent(window.WindowInfo);
\r
818 TextureThreadContextReady.Set();
\r
819 PendingTextures.Open();
\r
820 Logger.DebugLog("Started Texture Thread");
\r
822 while (window.Exists && TextureThreadRunning)
\r
824 window.ProcessEvents();
\r
826 TextureLoadItem item = null;
\r
828 if (!PendingTextures.Dequeue(Timeout.Infinite, ref item)) continue;
\r
830 // Already have this one loaded
\r
831 if (item.Data.TextureInfo.TexturePointer != 0) continue;
\r
833 if (item.TextureData != null)
\r
837 if (!OpenJPEG.DecodeToImage(item.TextureData, out mi)) continue;
\r
839 bool hasAlpha = false;
\r
840 bool fullAlpha = false;
\r
841 bool isMask = false;
\r
842 if ((mi.Channels & ManagedImage.ImageChannels.Alpha) != 0)
\r
847 // Do we really have alpha, is it all full alpha, or is it a mask
\r
848 for (int i = 0; i < mi.Alpha.Length; i++)
\r
850 if (mi.Alpha[i] < 255)
\r
854 if (mi.Alpha[i] != 0)
\r
858 if (mi.Alpha[i] != 0 && mi.Alpha[i] != 255)
\r
866 mi.ConvertChannels(mi.Channels & ~ManagedImage.ImageChannels.Alpha);
\r
870 using (MemoryStream byteData = new MemoryStream(mi.ExportTGA()))
\r
872 img = OpenMetaverse.Imaging.LoadTGAClass.LoadTGA(byteData);
\r
875 Bitmap bitmap = (Bitmap)img;
\r
877 item.Data.TextureInfo.HasAlpha = hasAlpha;
\r
878 item.Data.TextureInfo.FullAlpha = fullAlpha;
\r
879 item.Data.TextureInfo.IsMask = isMask;
\r
880 bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
\r
881 item.Data.TextureInfo.TexturePointer = GLLoadImage(bitmap, hasAlpha);
\r
883 item.TextureData = null;
\r
886 Logger.DebugLog("Texture thread exited");
\r
888 #endregion Texture thread
\r
890 void LoadCurrentPrims()
\r
892 if (!Client.Network.Connected) return;
\r
894 ThreadPool.QueueUserWorkItem(sync =>
\r
896 if (PrimitiveRenderingEnabled)
\r
898 List<Primitive> mainPrims = Client.Network.CurrentSim.ObjectsPrimitives.FindAll((Primitive root) => root.ParentID == 0);
\r
899 foreach (Primitive mainPrim in mainPrims)
\r
901 UpdatePrimBlocking(mainPrim);
\r
902 Client.Network.CurrentSim.ObjectsPrimitives
\r
903 .FindAll((Primitive child) => child.ParentID == mainPrim.LocalID)
\r
904 .ForEach((Primitive subPrim) => UpdatePrimBlocking(subPrim));
\r
908 if (AvatarRenderingEnabled)
\r
910 List<Avatar> avis = Client.Network.CurrentSim.ObjectsAvatars.FindAll((Avatar a) => true);
\r
911 foreach (Avatar avatar in avis)
\r
913 UpdatePrimBlocking(avatar);
\r
914 Client.Network.CurrentSim.ObjectsPrimitives
\r
915 .FindAll((Primitive child) => child.ParentID == avatar.LocalID)
\r
916 .ForEach((Primitive attachedPrim) =>
\r
918 UpdatePrimBlocking(attachedPrim);
\r
919 Client.Network.CurrentSim.ObjectsPrimitives
\r
920 .FindAll((Primitive child) => child.ParentID == attachedPrim.LocalID)
\r
921 .ForEach((Primitive attachedPrimChild) =>
\r
923 UpdatePrimBlocking(attachedPrimChild);
\r
931 private void frmPrimWorkshop_Shown(object sender, EventArgs e)
\r
934 LoadCurrentPrims();
\r
937 #region Private methods (the meat)
\r
938 private void UpdateCamera()
\r
940 if (Client != null)
\r
942 Client.Self.Movement.Camera.LookAt(Camera.Position, Camera.FocalPoint);
\r
943 Client.Self.Movement.Camera.Far = Camera.Far = DrawDistance;
\r
949 Vector3 camPos = Client.Self.SimPosition + new Vector3(-2, 0, 0) * Client.Self.Movement.BodyRotation;
\r
951 Camera.Position = camPos;
\r
952 Camera.FocalPoint = Client.Self.SimPosition + new Vector3(5, 0, 0) * Client.Self.Movement.BodyRotation;
\r
953 Camera.Zoom = 1.0f;
\r
954 Camera.Far = DrawDistance;
\r
958 Vector3 PrimPos(Primitive prim)
\r
962 PrimPosAndRot(prim, out pos, out rot);
\r
966 SceneObject GetSceneObject(uint localID)
\r
968 RenderPrimitive parent;
\r
970 if (Prims.TryGetValue(localID, out parent))
\r
974 else if (Avatars.TryGetValue(localID, out avi))
\r
981 void PrimPosAndRot(Primitive prim, out Vector3 pos, out Quaternion rot)
\r
983 if (prim.ParentID == 0)
\r
985 pos = prim.Position;
\r
986 rot = prim.Rotation;
\r
991 pos = new Vector3(99999f, 99999f, 99999f);
\r
992 rot = Quaternion.Identity;
\r
994 SceneObject p = GetSceneObject(prim.ParentID);
\r
995 if (p == null) return;
\r
998 Quaternion parentRot;
\r
999 if (p.PositionCalculated)
\r
1001 parentPos = p.SimPosition;
\r
1002 parentRot = p.SimRotation;
\r
1006 Primitive parentPrim = p.BasePrim;
\r
1007 PrimPosAndRot(parentPrim, out parentPos, out parentRot);
\r
1008 p.SimPosition = parentPos;
\r
1009 p.SimRotation = parentRot;
\r
1010 p.DistanceSquared = Vector3.DistanceSquared(Camera.RenderPosition, p.SimPosition);
\r
1011 p.PositionCalculated = true;
\r
1014 if (p is RenderPrimitive)
\r
1016 pos = parentPos + prim.Position * parentRot;
\r
1017 rot = parentRot * prim.Rotation;
\r
1019 else if (p is RenderAvatar)
\r
1021 RenderAvatar parentav = (RenderAvatar)p;
\r
1023 int attachment_index = (int)prim.PrimData.AttachmentPoint;
\r
1024 // Check for invalid LL attachment point
\r
1025 if (attachment_index > GLAvatar.attachment_points.Count()) return;
\r
1027 attachment_point apoint = GLAvatar.attachment_points[attachment_index];
\r
1028 Vector3 point = parentav.glavatar.skel.getOffset(apoint.joint) + apoint.position;
\r
1029 Quaternion qrot = parentav.glavatar.skel.getRotation(apoint.joint) * apoint.rotation;
\r
1031 pos = parentPos + point * parentRot + prim.Position * (parentRot * qrot);
\r
1032 rot = qrot * parentRot * prim.Rotation;
\r
1038 private void SetPerspective()
\r
1040 float dAspRat = (float)glControl.Width / (float)glControl.Height;
\r
1041 GluPerspective(50.0f * Camera.Zoom, dAspRat, 0.1f, 1000f);
\r
1045 #pragma warning disable 0612
\r
1046 OpenTK.Graphics.TextPrinter Printer = new OpenTK.Graphics.TextPrinter(OpenTK.Graphics.TextQuality.High);
\r
1047 #pragma warning restore 0612
\r
1049 private void RenderStats()
\r
1051 int posX = glControl.Width - 100;
\r
1054 // This is a FIR filter known as a MMA or Modified Mean Average, using a 20 point sampling width
\r
1055 advTimerTick = ((19 * advTimerTick) + lastFrameTime) / 20;
\r
1058 Printer.Print(String.Format("FPS {0:000.00}", 1d / advTimerTick), AvatarTagFont, Color.Orange,
\r
1059 new RectangleF(posX, posY, 100, 50),
\r
1060 OpenTK.Graphics.TextPrinterOptions.Default, OpenTK.Graphics.TextAlignment.Center);
\r
1064 private void RenderText()
\r
1069 GL.Color4(0f, 0f, 0f, 0.6f);
\r
1071 foreach (RenderAvatar av in Avatars.Values)
\r
1073 Vector3 avPos = PrimPos(av.avatar);
\r
1074 if (Vector3.Distance(avPos, Camera.Position) > 20f) continue;
\r
1076 OpenTK.Vector3 tagPos = RHelp.TKVector3(avPos);
\r
1078 OpenTK.Vector3 screenPos;
\r
1079 if (!Math3D.GluProject(tagPos, ModelMatrix, ProjectionMatrix, Viewport, out screenPos)) continue;
\r
1081 string tagText = instance.Names.Get(av.avatar.ID, av.avatar.Name);
\r
1082 if (!string.IsNullOrEmpty(av.avatar.GroupName))
\r
1084 tagText = av.avatar.GroupName + "\n" + tagText;
\r
1086 var tSize = Printer.Measure(tagText, AvatarTagFont);
\r
1088 // Render tag backround
\r
1089 GL.Begin(BeginMode.Quads);
\r
1090 float halfWidth = tSize.BoundingBox.Width / 2 + 10;
\r
1091 float halfHeight = tSize.BoundingBox.Height / 2 + 5;
\r
1092 GL.Vertex2(screenPos.X - halfWidth, screenPos.Y - halfHeight);
\r
1093 GL.Vertex2(screenPos.X + halfWidth, screenPos.Y - halfHeight);
\r
1094 GL.Vertex2(screenPos.X + halfWidth, screenPos.Y + halfHeight);
\r
1095 GL.Vertex2(screenPos.X - halfWidth, screenPos.Y + halfHeight);
\r
1098 screenPos.Y = glControl.Height - screenPos.Y;
\r
1099 screenPos.X -= tSize.BoundingBox.Width / 2;
\r
1100 screenPos.Y -= tSize.BoundingBox.Height / 2;
\r
1102 if (screenPos.Y > 0)
\r
1105 Printer.Print(tagText, AvatarTagFont, Color.Orange,
\r
1106 new RectangleF(screenPos.X, screenPos.Y, tSize.BoundingBox.Width, tSize.BoundingBox.Height),
\r
1107 OpenTK.Graphics.TextPrinterOptions.Default, OpenTK.Graphics.TextAlignment.Center);
\r
1116 foreach (RenderPrimitive mesh in Prims.Values)
\r
1119 Primitive prim = mesh.Prim;
\r
1120 if (!string.IsNullOrEmpty(prim.Text))
\r
1122 string text = System.Text.RegularExpressions.Regex.Replace(prim.Text, "(\r?\n)+", "\n");
\r
1123 var newPrimPos = PrimPos(prim);
\r
1124 OpenTK.Vector3 primPos = new OpenTK.Vector3(newPrimPos.X, newPrimPos.Y, newPrimPos.Z);
\r
1125 var distance = Vector3.Distance(newPrimPos, Camera.Position);
\r
1127 // Display hovertext only on objects that are withing 12m of the camera
\r
1128 if (distance > 12) continue;
\r
1130 primPos.Z += prim.Scale.Z * 0.8f;
\r
1132 // Convert objects world position to 2D screen position in pixels
\r
1133 OpenTK.Vector3 screenPos;
\r
1134 if (!Math3D.GluProject(primPos, ModelMatrix, ProjectionMatrix, Viewport, out screenPos)) continue;
\r
1135 screenPos.Y = glControl.Height - screenPos.Y;
\r
1139 Color color = Color.FromArgb((int)(prim.TextColor.A * 255), (int)(prim.TextColor.R * 255), (int)(prim.TextColor.G * 255), (int)(prim.TextColor.B * 255));
\r
1141 var size = Printer.Measure(text, HoverTextFont);
\r
1142 screenPos.X -= size.BoundingBox.Width / 2;
\r
1143 screenPos.Y -= size.BoundingBox.Height;
\r
1145 if (screenPos.Y > 0)
\r
1149 if (color != Color.Black)
\r
1151 Printer.Print(text, HoverTextFont, Color.Black, new RectangleF(screenPos.X + 1, screenPos.Y + 1, size.BoundingBox.Width, size.BoundingBox.Height), OpenTK.Graphics.TextPrinterOptions.Default, OpenTK.Graphics.TextAlignment.Center);
\r
1154 Printer.Print(text, HoverTextFont, color, new RectangleF(screenPos.X, screenPos.Y, size.BoundingBox.Width, size.BoundingBox.Height), OpenTK.Graphics.TextPrinterOptions.Default, OpenTK.Graphics.TextAlignment.Center);
\r
1165 private void AddAvatarToScene(Avatar av)
\r
1169 if (Vector3.Distance(PrimPos(av), Client.Self.SimPosition) > DrawDistance) return;
\r
1171 if (Avatars.ContainsKey(av.LocalID))
\r
1173 // flag we got an update??
\r
1174 updateAVtes(Avatars[av.LocalID]);
\r
1178 GLAvatar ga = new GLAvatar();
\r
1181 RenderAvatar ra = new Rendering.RenderAvatar();
\r
1185 Avatars.Add(av.LocalID, ra);
\r
1186 ra.glavatar.morph(av);
\r
1192 private void updateAVtes(RenderAvatar ra)
\r
1194 if (ra.avatar.Textures == null)
\r
1197 int[] tes = { 8, 9, 10, 11, 19, 20 };
\r
1199 foreach (int fi in tes)
\r
1201 Primitive.TextureEntryFace TEF = ra.avatar.Textures.FaceTextures[fi];
\r
1205 if (ra.data[fi] == null || ra.data[fi].TextureInfo.TextureID != TEF.TextureID)
\r
1207 FaceData data = new FaceData();
\r
1208 ra.data[fi] = data;
\r
1209 data.TextureInfo.TextureID = TEF.TextureID;
\r
1211 DownloadTexture(new TextureLoadItem()
\r
1215 TeFace = ra.avatar.Textures.FaceTextures[fi]
\r
1221 private void RenderAvatarsSkeleton(RenderPass pass)
\r
1223 if (!RenderAvatarSkeleton) return;
\r
1227 foreach (RenderAvatar av in Avatars.Values)
\r
1229 // Individual prim matrix
\r
1232 // Prim roation and position
\r
1233 Vector3 pos = av.avatar.Position;
\r
1235 GL.MultMatrix(Math3D.CreateTranslationMatrix(pos));
\r
1236 GL.MultMatrix(Math3D.CreateRotationMatrix(av.avatar.Rotation));
\r
1238 GL.Begin(BeginMode.Lines);
\r
1240 GL.Color3(1.0, 0.0, 0.0);
\r
1242 foreach (Bone b in av.glavatar.skel.mBones.Values)
\r
1244 Vector3 newpos = b.getOffset();
\r
1246 if (b.parent != null)
\r
1248 Vector3 parentpos = b.parent.getOffset();
\r
1249 GL.Vertex3(parentpos.X, parentpos.Y, parentpos.Z);
\r
1253 GL.Vertex3(newpos.X, newpos.Y, newpos.Z);
\r
1256 GL.Vertex3(newpos.X, newpos.Y, newpos.Z);
\r
1261 newpos.X += 0.01f;
\r
1262 newpos.Y += 0.01f;
\r
1263 newpos.Z += 0.01f;
\r
1264 GL.Vertex3(newpos.X, newpos.Y, newpos.Z);
\r
1266 newpos.X -= 0.02f;
\r
1267 GL.Vertex3(newpos.X, newpos.Y, newpos.Z);
\r
1268 GL.Vertex3(newpos.X, newpos.Y, newpos.Z);
\r
1270 newpos.Y -= 0.02f;
\r
1271 GL.Vertex3(newpos.X, newpos.Y, newpos.Z);
\r
1272 GL.Vertex3(newpos.X, newpos.Y, newpos.Z);
\r
1274 newpos.X += 0.02f;
\r
1275 GL.Vertex3(newpos.X, newpos.Y, newpos.Z);
\r
1276 GL.Vertex3(newpos.X, newpos.Y, newpos.Z);
\r
1278 newpos.Y += 0.02f;
\r
1279 GL.Vertex3(newpos.X, newpos.Y, newpos.Z);
\r
1280 GL.Vertex3(newpos.X, newpos.Y, newpos.Z);
\r
1282 newpos.Z -= 0.02f;
\r
1283 GL.Vertex3(newpos.X, newpos.Y, newpos.Z);
\r
1284 GL.Vertex3(newpos.X, newpos.Y, newpos.Z);
\r
1286 newpos.Y -= 0.02f;
\r
1287 GL.Vertex3(newpos.X, newpos.Y, newpos.Z);
\r
1288 GL.Vertex3(newpos.X, newpos.Y, newpos.Z);
\r
1290 newpos.X -= 0.02f;
\r
1291 GL.Vertex3(newpos.X, newpos.Y, newpos.Z);
\r
1292 GL.Vertex3(newpos.X, newpos.Y, newpos.Z);
\r
1294 newpos.Y += 0.02f;
\r
1295 GL.Vertex3(newpos.X, newpos.Y, newpos.Z);
\r
1296 GL.Vertex3(newpos.X, newpos.Y, newpos.Z);
\r
1298 newpos.X += 0.02f;
\r
1299 GL.Vertex3(newpos.X, newpos.Y, newpos.Z);
\r
1300 GL.Vertex3(newpos.X, newpos.Y, newpos.Z);
\r
1302 newpos.Y -= 0.01f;
\r
1303 newpos.Z += 0.01f;
\r
1304 newpos.X -= 0.01f;
\r
1305 GL.Vertex3(newpos.X, newpos.Y, newpos.Z);
\r
1313 GL.Color3(0.0, 1.0, 0.0);
\r
1322 private void RenderAvatars(RenderPass pass)
\r
1324 if (!AvatarRenderingEnabled) return;
\r
1328 GL.EnableClientState(ArrayCap.VertexArray);
\r
1329 GL.EnableClientState(ArrayCap.TextureCoordArray);
\r
1330 GL.EnableClientState(ArrayCap.NormalArray);
\r
1333 foreach (RenderAvatar av in Avatars.Values)
\r
1337 if (av.glavatar._meshes.Count > 0)
\r
1340 foreach (GLMesh mesh in av.glavatar._meshes.Values)
\r
1343 if (!av.glavatar._showSkirt && mesh.Name == "skirtMesh")
\r
1346 if (mesh.Name == "hairMesh") // Don't render the hair mesh for the moment
\r
1349 GL.Color3(1f, 1f, 1f);
\r
1351 // Individual prim matrix
\r
1354 // Prim roation and position
\r
1355 //GL.MultMatrix(Math3D.CreateTranslationMatrix(av.avatar.Position));
\r
1356 //GL.MultMatrix(Math3D.CreateRotationMatrix(av.avatar.Rotation));
\r
1357 GL.MultMatrix(Math3D.CreateSRTMatrix(new Vector3(1, 1, 1), av.SimRotation, av.SimPosition));
\r
1359 // Special case for eyeballs we need to offset the mesh to the correct position
\r
1360 // We have manually added the eyeball offset based on the headbone when we
\r
1361 // constructed the meshes, but why are the position offsets we got when loading
\r
1362 // the other meshes <0,7,0> ?
\r
1363 if (mesh.Name == "eyeBallLeftMesh")
\r
1365 // Mesh roation and position
\r
1366 GL.MultMatrix(Math3D.CreateTranslationMatrix(av.glavatar.skel.getOffset("mEyeLeft")));
\r
1367 GL.MultMatrix(Math3D.CreateRotationMatrix(av.glavatar.skel.getRotation("mEyeLeft")));
\r
1369 if (mesh.Name == "eyeBallRightMesh")
\r
1371 // Mesh roation and position
\r
1372 GL.MultMatrix(Math3D.CreateTranslationMatrix(av.glavatar.skel.getOffset("mEyeRight")));
\r
1373 GL.MultMatrix(Math3D.CreateRotationMatrix(av.glavatar.skel.getRotation("mEyeRight")));
\r
1378 //Should we be offsetting the base meshs at all?
\r
1379 //if (mesh.Name == "headMesh")
\r
1381 // GL.MultMatrix(Math3D.CreateTranslationMatrix(av.glavatar.skel.getDeltaOffset("mHead")));
\r
1385 //Gl.glTranslatef(mesh.Position.X, mesh.Position.Y, mesh.Position.Z);
\r
1387 GL.Rotate(mesh.RotationAngles.X, 1f, 0f, 0f);
\r
1388 GL.Rotate(mesh.RotationAngles.Y, 0f, 1f, 0f);
\r
1389 GL.Rotate(mesh.RotationAngles.Z, 0f, 0f, 1f);
\r
1391 GL.Scale(mesh.Scale.X, mesh.Scale.Y, mesh.Scale.Z);
\r
1393 if (pass == RenderPass.Picking)
\r
1395 GL.Disable(EnableCap.Texture2D);
\r
1397 for (int i = 0; i < av.data.Length; i++)
\r
1399 if (av.data[i] != null)
\r
1401 av.data[i].PickingID = avatarNr;
\r
1404 byte[] primNrBytes = Utils.Int16ToBytes((short)avatarNr);
\r
1405 byte[] faceColor = new byte[] { primNrBytes[0], primNrBytes[1], (byte)faceNr, 254 };
\r
1406 GL.Color4(faceColor);
\r
1410 if (av.data[mesh.teFaceID] == null)
\r
1412 GL.Disable(EnableCap.Texture2D);
\r
1416 if (mesh.teFaceID != 0)
\r
1418 GL.Enable(EnableCap.Texture2D);
\r
1419 GL.BindTexture(TextureTarget.Texture2D, av.data[mesh.teFaceID].TextureInfo.TexturePointer);
\r
1423 GL.Disable(EnableCap.Texture2D);
\r
1428 GL.TexCoordPointer(2, TexCoordPointerType.Float, 0, mesh.RenderData.TexCoords);
\r
1429 GL.VertexPointer(3, VertexPointerType.Float, 0, mesh.RenderData.Vertices);
\r
1430 GL.NormalPointer(NormalPointerType.Float, 0, mesh.RenderData.Normals);
\r
1432 GL.DrawElements(BeginMode.Triangles, mesh.RenderData.Indices.Length, DrawElementsType.UnsignedShort, mesh.RenderData.Indices);
\r
1434 GL.BindTexture(TextureTarget.Texture2D, 0);
\r
1441 GL.Disable(EnableCap.Texture2D);
\r
1442 GL.DisableClientState(ArrayCap.NormalArray);
\r
1443 GL.DisableClientState(ArrayCap.VertexArray);
\r
1444 GL.DisableClientState(ArrayCap.TextureCoordArray);
\r
1448 #endregion avatars
\r
1451 bool TerrainModified = true;
\r
1452 float[,] heightTable = new float[256, 256];
\r
1454 ushort[] terrainIndices;
\r
1455 Vertex[] terrainVertices;
\r
1456 int terrainTexture = -1;
\r
1457 bool fetchingTerrainTexture = false;
\r
1458 Bitmap terrainImage = null;
\r
1459 int terrainVBO = -1;
\r
1460 int terrainIndexVBO = -1;
\r
1462 private void ResetTerrain()
\r
1464 ResetTerrain(true);
\r
1467 private void ResetTerrain(bool removeImage)
\r
1469 if (terrainImage != null)
\r
1471 terrainImage.Dispose();
\r
1472 terrainImage = null;
\r
1475 if (terrainVBO != -1)
\r
1477 GL.DeleteBuffers(1, ref terrainVBO);
\r
1481 if (terrainIndexVBO != -1)
\r
1483 GL.DeleteBuffers(1, ref terrainIndexVBO);
\r
1484 terrainIndexVBO = -1;
\r
1489 if (terrainTexture != -1)
\r
1491 GL.DeleteTexture(terrainTexture);
\r
1492 terrainTexture = -1;
\r
1496 fetchingTerrainTexture = false;
\r
1497 TerrainModified = true;
\r
1500 private void UpdateTerrain()
\r
1502 if (Client.Network.CurrentSim == null || Client.Network.CurrentSim.Terrain == null) return;
\r
1505 for (int x = 0; x < 255; x += step)
\r
1507 for (int y = 0; y < 255; y += step)
\r
1510 int patchNr = ((int)x / 16) * 16 + (int)y / 16;
\r
1511 if (Client.Network.CurrentSim.Terrain[patchNr] != null
\r
1512 && Client.Network.CurrentSim.Terrain[patchNr].Data != null)
\r
1514 float[] data = Client.Network.CurrentSim.Terrain[patchNr].Data;
\r
1515 z = data[(int)x % 16 * 16 + (int)y % 16];
\r
1517 heightTable[x, y] = z;
\r
1521 terrainFace = renderer.TerrainMesh(heightTable, 0f, 255f, 0f, 255f);
\r
1522 terrainVertices = terrainFace.Vertices.ToArray();
\r
1523 terrainIndices = terrainFace.Indices.ToArray();
\r
1525 TerrainModified = false;
\r
1528 void UpdateTerrainTexture()
\r
1530 if (!fetchingTerrainTexture)
\r
1532 fetchingTerrainTexture = true;
\r
1533 ThreadPool.QueueUserWorkItem(sync =>
\r
1535 Simulator sim = Client.Network.CurrentSim;
\r
1536 terrainImage = TerrainSplat.Splat(instance, heightTable,
\r
1537 new UUID[] { sim.TerrainDetail0, sim.TerrainDetail1, sim.TerrainDetail2, sim.TerrainDetail3 },
\r
1538 new float[] { sim.TerrainStartHeight00, sim.TerrainStartHeight01, sim.TerrainStartHeight10, sim.TerrainStartHeight11 },
\r
1539 new float[] { sim.TerrainHeightRange00, sim.TerrainHeightRange01, sim.TerrainHeightRange10, sim.TerrainHeightRange11 },
\r
1542 fetchingTerrainTexture = false;
\r
1547 private void RenderTerrain()
\r
1549 GL.Color3(1f, 1f, 1f);
\r
1550 GL.EnableClientState(ArrayCap.VertexArray);
\r
1551 GL.EnableClientState(ArrayCap.TextureCoordArray);
\r
1552 GL.EnableClientState(ArrayCap.NormalArray);
\r
1554 if (TerrainModified)
\r
1556 ResetTerrain(false);
\r
1558 UpdateTerrainTexture();
\r
1561 if (terrainImage != null)
\r
1563 if (terrainTexture != -1)
\r
1565 GL.DeleteTexture(terrainTexture);
\r
1568 terrainTexture = GLLoadImage(terrainImage, false);
\r
1569 terrainImage.Dispose();
\r
1570 terrainImage = null;
\r
1573 if (terrainTexture == -1)
\r
1579 GL.Enable(EnableCap.Texture2D);
\r
1580 GL.BindTexture(TextureTarget.Texture2D, terrainTexture);
\r
1587 fixed (float* normalPtr = &terrainVertices[0].Normal.X)
\r
1588 fixed (float* texPtr = &terrainVertices[0].TexCoord.X)
\r
1590 GL.NormalPointer(NormalPointerType.Float, FaceData.VertexSize, (IntPtr)normalPtr);
\r
1591 GL.TexCoordPointer(2, TexCoordPointerType.Float, FaceData.VertexSize, (IntPtr)texPtr);
\r
1592 GL.VertexPointer(3, VertexPointerType.Float, FaceData.VertexSize, terrainVertices);
\r
1593 GL.DrawElements(BeginMode.Triangles, terrainIndices.Length, DrawElementsType.UnsignedShort, terrainIndices);
\r
1599 if (terrainVBO == -1)
\r
1601 GL.GenBuffers(1, out terrainVBO);
\r
1602 GL.BindBuffer(BufferTarget.ArrayBuffer, terrainVBO);
\r
1603 GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(terrainVertices.Length * FaceData.VertexSize), terrainVertices, BufferUsageHint.StaticDraw);
\r
1607 GL.BindBuffer(BufferTarget.ArrayBuffer, terrainVBO);
\r
1610 if (terrainIndexVBO == -1)
\r
1612 GL.GenBuffers(1, out terrainIndexVBO);
\r
1613 GL.BindBuffer(BufferTarget.ElementArrayBuffer, terrainIndexVBO);
\r
1614 GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(terrainIndices.Length * sizeof(ushort)), terrainIndices, BufferUsageHint.StaticDraw);
\r
1618 GL.BindBuffer(BufferTarget.ElementArrayBuffer, terrainIndexVBO);
\r
1621 GL.NormalPointer(NormalPointerType.Float, FaceData.VertexSize, (IntPtr)12);
\r
1622 GL.TexCoordPointer(2, TexCoordPointerType.Float, FaceData.VertexSize, (IntPtr)(24));
\r
1623 GL.VertexPointer(3, VertexPointerType.Float, FaceData.VertexSize, (IntPtr)(0));
\r
1625 GL.DrawElements(BeginMode.Triangles, terrainIndices.Length, DrawElementsType.UnsignedShort, IntPtr.Zero);
\r
1627 GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
\r
1628 GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
\r
1630 GL.BindTexture(TextureTarget.Texture2D, 0);
\r
1631 GL.DisableClientState(ArrayCap.VertexArray);
\r
1632 GL.DisableClientState(ArrayCap.TextureCoordArray);
\r
1633 GL.DisableClientState(ArrayCap.NormalArray);
\r
1635 #endregion Terrain
\r
1637 private void ResetMaterial()
\r
1639 GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Ambient, new float[] { 0.2f, 0.2f, 0.2f, 1.0f });
\r
1640 GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Diffuse, new float[] { 0.8f, 0.8f, 0.8f, 1.0f });
\r
1641 GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Specular, new float[] { 0f, 0f, 0f, 1.0f });
\r
1642 GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Emission, new float[] { 0f, 0f, 0f, 1.0f });
\r
1643 GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Shininess, 0f);
\r
1646 float LODFactor(float distance, Vector3 primScale, float radius)
\r
1648 float scale = primScale.X;
\r
1649 if (primScale.Y > scale) scale = primScale.Y;
\r
1650 if (primScale.Z > scale) scale = primScale.Z;
\r
1651 return scale * radius * radius / distance;
\r
1654 void RenderSphere(float cx, float cy, float cz, float r, int p)
\r
1656 GL.PushAttrib(AttribMask.AllAttribBits);
\r
1657 GL.Disable(EnableCap.Fog);
\r
1658 GL.Disable(EnableCap.Texture2D);
\r
1659 GL.Disable(EnableCap.Dither);
\r
1660 GL.Disable(EnableCap.Lighting);
\r
1661 GL.Disable(EnableCap.LineStipple);
\r
1662 GL.Disable(EnableCap.PolygonStipple);
\r
1663 GL.Disable(EnableCap.CullFace);
\r
1664 GL.Disable(EnableCap.Blend);
\r
1665 GL.Disable(EnableCap.AlphaTest);
\r
1666 GL.Disable(EnableCap.DepthTest);
\r
1668 const float TWOPI = 6.28318530717958f;
\r
1669 const float PIDIV2 = 1.57079632679489f;
\r
1671 float theta1 = 0.0f;
\r
1672 float theta2 = 0.0f;
\r
1673 float theta3 = 0.0f;
\r
1683 // Disallow a negative number for radius.
\r
1687 // Disallow a negative number for precision.
\r
1691 // If the sphere is too small, just render a OpenGL point instead.
\r
1692 if (p < 4 || r <= 0)
\r
1694 GL.Begin(BeginMode.Points);
\r
1695 GL.Vertex3(cx, cy, cz);
\r
1700 for (int i = 0; i < p / 2; ++i)
\r
1702 theta1 = i * TWOPI / p - PIDIV2;
\r
1703 theta2 = (i + 1) * TWOPI / p - PIDIV2;
\r
1705 GL.Begin(BeginMode.TriangleStrip);
\r
1707 for (int j = 0; j <= p; ++j)
\r
1709 theta3 = j * TWOPI / p;
\r
1711 ex = (float)(Math.Cos(theta2) * Math.Cos(theta3));
\r
1712 ey = (float)Math.Sin(theta2);
\r
1713 ez = (float)(Math.Cos(theta2) * Math.Sin(theta3));
\r
1718 GL.Normal3(ex, ey, ez);
\r
1719 GL.TexCoord2(-(j / (float)p), 2 * (i + 1) / (float)p);
\r
1720 GL.Vertex3(px, py, pz);
\r
1722 ex = (float)(Math.Cos(theta1) * Math.Cos(theta3));
\r
1723 ey = (float)Math.Sin(theta1);
\r
1724 ez = (float)(Math.Cos(theta1) * Math.Sin(theta3));
\r
1729 GL.Normal3(ex, ey, ez);
\r
1730 GL.TexCoord2(-(j / (float)p), 2 * i / (float)p);
\r
1731 GL.Vertex3(px, py, pz);
\r
1740 void RenderBoundingBox(SceneObject prim)
\r
1742 Vector3 scale = prim.BasePrim.Scale;
\r
1743 BoundingVolume bbox = prim.BoundingVolume;
\r
1744 GL.PushAttrib(AttribMask.AllAttribBits);
\r
1745 GL.Disable(EnableCap.Fog);
\r
1746 GL.Disable(EnableCap.Texture2D);
\r
1747 GL.Disable(EnableCap.Lighting);
\r
1748 GL.Disable(EnableCap.CullFace);
\r
1749 GL.Disable(EnableCap.AlphaTest);
\r
1751 GL.DepthMask(false);
\r
1752 GL.ColorMask(false, false, false, false);
\r
1755 GL.MultMatrix(Math3D.CreateTranslationMatrix(prim.SimPosition));
\r
1756 GL.MultMatrix(Math3D.CreateRotationMatrix(prim.SimRotation));
\r
1757 GL.Scale(scale.X, scale.Y, scale.Z);
\r
1758 GL.Color3(1f, 0f, 0f);
\r
1759 GL.Begin(BeginMode.Quads);
\r
1760 var bmin = bbox.Min;
\r
1761 var bmax = bbox.Max;
\r
1764 GL.Vertex3(bmin.X, bmin.Y, bmin.Z);
\r
1765 GL.Vertex3(bmax.X, bmin.Y, bmin.Z);
\r
1766 GL.Vertex3(bmax.X, bmax.Y, bmin.Z);
\r
1767 GL.Vertex3(bmin.X, bmax.Y, bmin.Z);
\r
1770 GL.Vertex3(bmin.X, bmin.Y, bmax.Z);
\r
1771 GL.Vertex3(bmax.X, bmin.Y, bmax.Z);
\r
1772 GL.Vertex3(bmax.X, bmax.Y, bmax.Z);
\r
1773 GL.Vertex3(bmin.X, bmax.Y, bmax.Z);
\r
1776 GL.Vertex3(bmin.X, bmax.Y, bmax.Z);
\r
1777 GL.Vertex3(bmax.X, bmax.Y, bmax.Z);
\r
1778 GL.Vertex3(bmax.X, bmax.Y, bmin.Z);
\r
1779 GL.Vertex3(bmin.X, bmax.Y, bmin.Z);
\r
1782 GL.Vertex3(bmin.X, bmin.Y, bmax.Z);
\r
1783 GL.Vertex3(bmax.X, bmin.Y, bmax.Z);
\r
1784 GL.Vertex3(bmax.X, bmin.Y, bmin.Z);
\r
1785 GL.Vertex3(bmin.X, bmin.Y, bmin.Z);
\r
1788 GL.Vertex3(bmin.X, bmin.Y, bmax.Z);
\r
1789 GL.Vertex3(bmin.X, bmax.Y, bmax.Z);
\r
1790 GL.Vertex3(bmin.X, bmax.Y, bmin.Z);
\r
1791 GL.Vertex3(bmin.X, bmin.Y, bmin.Z);
\r
1794 GL.Vertex3(bmax.X, bmin.Y, bmax.Z);
\r
1795 GL.Vertex3(bmax.X, bmax.Y, bmax.Z);
\r
1796 GL.Vertex3(bmax.X, bmax.Y, bmin.Z);
\r
1797 GL.Vertex3(bmax.X, bmin.Y, bmin.Z);
\r
1802 GL.ColorMask(true, true, true, true);
\r
1803 GL.DepthMask(true);
\r
1808 void RenderPrim(RenderPrimitive mesh, RenderPass pass, int primNr)
\r
1810 // Do the stuff we need to do the first time we encouter the object
\r
1811 if (!mesh.Initialized) mesh.Initialize();
\r
1813 // Do any position interpolation
\r
1814 mesh.Step(lastFrameTime);
\r
1816 Primitive prim = mesh.Prim;
\r
1818 // Individual prim matrix
\r
1821 // Prim roation and position
\r
1822 GL.MultMatrix(Math3D.CreateTranslationMatrix(mesh.RenderPosition));
\r
1823 GL.MultMatrix(Math3D.CreateRotationMatrix(mesh.RenderRotation));
\r
1826 GL.Scale(prim.Scale.X, prim.Scale.Y, prim.Scale.Z);
\r
1828 // Do we have animated texture on this face
\r
1829 bool animatedTexture = false;
\r
1831 // Draw the prim faces
\r
1832 for (int j = 0; j < mesh.Faces.Count; j++)
\r
1834 Primitive.TextureEntryFace teFace = mesh.Prim.Textures.FaceTextures[j];
\r
1835 Face face = mesh.Faces[j];
\r
1836 FaceData data = (FaceData)mesh.Faces[j].UserData;
\r
1841 if (teFace == null)
\r
1842 teFace = mesh.Prim.Textures.DefaultTexture;
\r
1844 if (teFace == null)
\r
1847 // Don't render transparent faces
\r
1848 if (data.TextureInfo.FullAlpha || teFace.RGBA.A <= 0.01f) continue;
\r
1850 int lightsEnabled;
\r
1851 GL.GetInteger(GetPName.Lighting, out lightsEnabled);
\r
1853 if (pass != RenderPass.Picking)
\r
1855 bool belongToAlphaPass = (teFace.RGBA.A < 0.99) || data.TextureInfo.HasAlpha;
\r
1857 if (belongToAlphaPass && pass != RenderPass.Alpha) continue;
\r
1858 if (!belongToAlphaPass && pass == RenderPass.Alpha) continue;
\r
1860 if (teFace.Fullbright && lightsEnabled != 0)
\r
1862 GL.Disable(EnableCap.Lighting);
\r
1865 switch (teFace.Shiny)
\r
1867 case Shininess.High:
\r
1868 GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 0.94f);
\r
1871 case Shininess.Medium:
\r
1872 GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 0.64f);
\r
1875 case Shininess.Low:
\r
1876 GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 0.24f);
\r
1880 case Shininess.None:
\r
1882 GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 0f);
\r
1886 var faceColor = new float[] { teFace.RGBA.R, teFace.RGBA.G, teFace.RGBA.B, teFace.RGBA.A };
\r
1887 GL.Color4(faceColor);
\r
1889 GL.Material(MaterialFace.Front, MaterialParameter.Specular, new float[] { 0.5f, 0.5f, 0.5f, 1f });
\r
1891 if (data.TextureInfo.TexturePointer != 0)
\r
1893 // Is this face using texture animation
\r
1894 if ((prim.TextureAnim.Flags & Primitive.TextureAnimMode.ANIM_ON) != 0
\r
1895 && (prim.TextureAnim.Face == j || prim.TextureAnim.Face == 255))
\r
1897 if (data.AnimInfo == null)
\r
1899 data.AnimInfo = new TextureAnimationInfo();
\r
1901 data.AnimInfo.PrimAnimInfo = prim.TextureAnim;
\r
1902 data.AnimInfo.Step(lastFrameTime);
\r
1903 animatedTexture = true;
\r
1905 else if (data.AnimInfo != null) // Face texture not animated. Do we have previous anim setting?
\r
1907 data.AnimInfo = null;
\r
1910 GL.Enable(EnableCap.Texture2D);
\r
1911 GL.BindTexture(TextureTarget.Texture2D, data.TextureInfo.TexturePointer);
\r
1915 GL.Disable(EnableCap.Texture2D);
\r
1921 data.PickingID = primNr;
\r
1922 var primNrBytes = Utils.UInt16ToBytes((ushort)primNr);
\r
1923 var faceColor = new byte[] { primNrBytes[0], primNrBytes[1], (byte)j, 255 };
\r
1924 GL.Color4(faceColor);
\r
1929 Vertex[] verts = face.Vertices.ToArray();
\r
1933 fixed (float* normalPtr = &verts[0].Normal.X)
\r
1934 fixed (float* texPtr = &verts[0].TexCoord.X)
\r
1936 GL.NormalPointer(NormalPointerType.Float, FaceData.VertexSize, (IntPtr)normalPtr);
\r
1937 GL.TexCoordPointer(2, TexCoordPointerType.Float, FaceData.VertexSize, (IntPtr)texPtr);
\r
1938 GL.VertexPointer(3, VertexPointerType.Float, FaceData.VertexSize, verts);
\r
1939 GL.DrawElements(BeginMode.Triangles, data.Indices.Length, DrawElementsType.UnsignedShort, data.Indices);
\r
1945 data.CheckVBO(face);
\r
1946 GL.BindBuffer(BufferTarget.ArrayBuffer, data.VertexVBO);
\r
1947 GL.BindBuffer(BufferTarget.ElementArrayBuffer, data.IndexVBO);
\r
1948 GL.NormalPointer(NormalPointerType.Float, FaceData.VertexSize, (IntPtr)12);
\r
1949 GL.TexCoordPointer(2, TexCoordPointerType.Float, FaceData.VertexSize, (IntPtr)(24));
\r
1950 GL.VertexPointer(3, VertexPointerType.Float, FaceData.VertexSize, (IntPtr)(0));
\r
1952 GL.DrawElements(BeginMode.Triangles, face.Indices.Count, DrawElementsType.UnsignedShort, IntPtr.Zero);
\r
1954 GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
\r
1955 GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);
\r
1959 if (teFace.Fullbright && lightsEnabled != 0)
\r
1961 GL.Enable(EnableCap.Lighting);
\r
1965 GL.BindTexture(TextureTarget.Texture2D, 0);
\r
1968 // Reset texture coordinates if we modified them in texture animation
\r
1969 if (animatedTexture)
\r
1971 GL.MatrixMode(MatrixMode.Texture);
\r
1972 GL.LoadIdentity();
\r
1973 GL.MatrixMode(MatrixMode.Modelview);
\r
1976 // Pop the prim matrix
\r
1980 void SortObjects()
\r
1982 SortedObjects = new List<SceneObject>(Prims.Count);
\r
1985 SortedObjects.AddRange(Avatars.Values.ToArray());
\r
1990 SortedObjects.AddRange(Prims.Values.ToArray());
\r
1993 foreach (SceneObject obj in SortedObjects)
\r
1995 obj.PositionCalculated = false;
\r
1998 foreach (SceneObject obj in SortedObjects)
\r
2000 if (!obj.PositionCalculated)
\r
2002 PrimPosAndRot(obj.BasePrim, out obj.SimPosition, out obj.SimRotation);
\r
2003 obj.DistanceSquared = Vector3.DistanceSquared(Camera.RenderPosition, obj.SimPosition);
\r
2004 obj.PositionCalculated = true;
\r
2008 // RenderPrimitive class has IComparable implementation
\r
2009 // that allows sorting by distance
\r
2010 SortedObjects.Sort();
\r
2013 private void RenderObjects(RenderPass pass)
\r
2015 if (!PrimitiveRenderingEnabled) return;
\r
2017 GL.EnableClientState(ArrayCap.VertexArray);
\r
2018 GL.EnableClientState(ArrayCap.TextureCoordArray);
\r
2019 GL.EnableClientState(ArrayCap.NormalArray);
\r
2021 Vector3 myPos = Vector3.Zero;
\r
2023 if (Avatars.TryGetValue(Client.Self.LocalID, out me))
\r
2025 myPos = me.SimPosition;
\r
2029 myPos = Client.Self.SimPosition;
\r
2032 int nrPrims = SortedObjects.Count;
\r
2033 for (int i = 0; i < nrPrims; i++)
\r
2035 //RenderBoundingBox(SortedPrims[i]);
\r
2037 // When rendering alpha faces, draw from back towards the camers
\r
2038 // otherwise from those closest to camera, to the farthest
\r
2039 int ix = pass == RenderPass.Alpha ? nrPrims - i - 1 : i;
\r
2040 SceneObject obj = SortedObjects[ix];
\r
2042 if (obj is RenderPrimitive)
\r
2044 // Don't render objects that are outside the draw distane
\r
2045 if (Vector3.DistanceSquared(myPos, obj.SimPosition) > drawDistanceSquared) continue;
\r
2047 // Don't render objects too small to matter
\r
2048 if (LODFactor(obj.DistanceSquared, obj.BasePrim.Scale, obj.BoundingVolume.R) < minLODFactor) continue;
\r
2050 // Don't render objects not in the field of view
\r
2051 if (!Frustum.ObjectInFrustum(obj.SimPosition, obj.BoundingVolume, obj.BasePrim.Scale)) continue;
\r
2053 RenderPrim((RenderPrimitive)obj, pass, ix);
\r
2057 GL.Disable(EnableCap.Texture2D);
\r
2058 GL.DisableClientState(ArrayCap.VertexArray);
\r
2059 GL.DisableClientState(ArrayCap.TextureCoordArray);
\r
2060 GL.DisableClientState(ArrayCap.NormalArray);
\r
2063 void DrawWaterQuad(float x, float y, float z)
\r
2065 GL.Vertex3(x, y, z);
\r
2066 GL.Vertex3(x + 256f, y, z);
\r
2067 GL.Vertex3(x + 256f, y + 256f, z);
\r
2068 GL.Vertex3(x, y + 256f, z);
\r
2071 public void RenderWater()
\r
2073 float z = Client.Network.CurrentSim.WaterHeight;
\r
2075 GL.Color4(0.09f, 0.28f, 0.63f, 0.84f);
\r
2077 GL.Begin(BeginMode.Quads);
\r
2078 for (float x = -256f * 2; x <= 256 * 2; x += 256f)
\r
2079 for (float y = -256f * 2; y <= 256 * 2; y += 256f)
\r
2080 DrawWaterQuad(x, y, z);
\r
2084 private void Render(bool picking)
\r
2088 GL.ClearColor(1f, 1f, 1f, 1f);
\r
2092 GL.ClearColor(0.39f, 0.58f, 0.93f, 1.0f);
\r
2095 GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
\r
2096 GL.LoadIdentity();
\r
2098 // Setup wireframe or solid fill drawing mode
\r
2099 if (Wireframe && !picking)
\r
2101 GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);
\r
2105 GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);
\r
2108 var mLookAt = OpenTK.Matrix4d.LookAt(
\r
2109 Camera.RenderPosition.X, Camera.RenderPosition.Y, Camera.RenderPosition.Z,
\r
2110 Camera.RenderFocalPoint.X, Camera.RenderFocalPoint.Y, Camera.RenderFocalPoint.Z,
\r
2112 GL.MultMatrix(ref mLookAt);
\r
2114 GL.Light(LightName.Light0, LightParameter.Position, lightPos);
\r
2116 // Push the world matrix
\r
2119 if (Camera.Modified)
\r
2121 GL.GetFloat(GetPName.ProjectionMatrix, out ProjectionMatrix);
\r
2122 GL.GetFloat(GetPName.ModelviewMatrix, out ModelMatrix);
\r
2123 GL.GetInteger(GetPName.Viewport, Viewport);
\r
2124 Frustum.CalculateFrustum(ProjectionMatrix, ModelMatrix);
\r
2126 Camera.Modified = false;
\r
2127 Camera.Step(lastFrameTime);
\r
2134 RenderObjects(RenderPass.Picking);
\r
2135 RenderAvatars(RenderPass.Picking);
\r
2140 RenderObjects(RenderPass.Simple);
\r
2141 RenderAvatarsSkeleton(RenderPass.Simple);
\r
2142 RenderAvatars(RenderPass.Simple);
\r
2144 GL.Disable(EnableCap.Lighting);
\r
2145 GL.DepthMask(false);
\r
2147 RenderObjects(RenderPass.Alpha);
\r
2148 GL.DepthMask(true);
\r
2149 GL.Enable(EnableCap.Lighting);
\r
2157 // Pop the world matrix
\r
2161 private void GluPerspective(float fovy, float aspect, float zNear, float zFar)
\r
2163 float fH = (float)Math.Tan(fovy / 360 * (float)Math.PI) * zNear;
\r
2164 float fW = fH * aspect;
\r
2165 GL.Frustum(-fW, fW, -fH, fH, zNear, zFar);
\r
2168 private bool TryPick(int x, int y, out object picked, out int faceID)
\r
2170 // Save old attributes
\r
2171 GL.PushAttrib(AttribMask.AllAttribBits);
\r
2173 // Disable some attributes to make the objects flat / solid color when they are drawn
\r
2174 GL.Disable(EnableCap.Fog);
\r
2175 GL.Disable(EnableCap.Texture2D);
\r
2176 GL.Disable(EnableCap.Dither);
\r
2177 GL.Disable(EnableCap.Lighting);
\r
2178 GL.Disable(EnableCap.LineStipple);
\r
2179 GL.Disable(EnableCap.PolygonStipple);
\r
2180 GL.Disable(EnableCap.CullFace);
\r
2181 GL.Disable(EnableCap.Blend);
\r
2182 GL.Disable(EnableCap.AlphaTest);
\r
2186 byte[] color = new byte[4];
\r
2187 GL.ReadPixels(x, glControl.Height - y, 1, 1, OpenTK.Graphics.OpenGL.PixelFormat.Rgba, PixelType.UnsignedByte, color);
\r
2191 int primID = Utils.BytesToUInt16(color, 0);
\r
2192 faceID = color[2];
\r
2196 if (color[3] == 254) // Avatar
\r
2200 foreach (var avatar in Avatars.Values)
\r
2202 for (int i = 0; i < avatar.data.Length; i++)
\r
2204 var face = avatar.data[i];
\r
2205 if (face != null && face.PickingID == primID)
\r
2214 if (picked != null)
\r
2220 if (color[3] == 255) // Prim
\r
2224 foreach (var mesh in Prims.Values)
\r
2226 foreach (var face in mesh.Faces)
\r
2228 if (face.UserData == null) continue;
\r
2229 if (((FaceData)face.UserData).PickingID == primID)
\r
2236 if (picked != null) break;
\r
2241 return picked != null;
\r
2244 public void DownloadTexture(TextureLoadItem item)
\r
2246 lock (TexturesPtrMap)
\r
2248 if (TexturesPtrMap.ContainsKey(item.TeFace.TextureID))
\r
2250 item.Data.TextureInfo = TexturesPtrMap[item.TeFace.TextureID];
\r
2254 TexturesPtrMap[item.TeFace.TextureID] = item.Data.TextureInfo;
\r
2256 if (item.TextureData == null)
\r
2258 ThreadPool.QueueUserWorkItem(sync =>
\r
2260 Client.Assets.RequestImage(item.TeFace.TextureID, (state, asset) =>
\r
2262 if (state == TextureRequestState.Finished)
\r
2264 item.TextureData = asset.AssetData;
\r
2265 PendingTextures.Enqueue(item);
\r
2272 PendingTextures.Enqueue(item);
\r
2278 private void MeshPrim(Primitive prim, RenderPrimitive rprim)
\r
2280 RenderPrimitive existingMesh = null;
\r
2284 if (Prims.ContainsKey(prim.LocalID))
\r
2286 existingMesh = Prims[prim.LocalID];
\r
2290 // Calculate bounding volumes for each prim and adjust textures
\r
2291 rprim.BoundingVolume = new BoundingVolume();
\r
2292 for (int j = 0; j < rprim.Faces.Count; j++)
\r
2294 Primitive.TextureEntryFace teFace = prim.Textures.GetFace((uint)j);
\r
2295 if (teFace == null) continue;
\r
2297 Face face = rprim.Faces[j];
\r
2298 FaceData data = new FaceData();
\r
2300 data.BoundingVolume.CreateBoundingVolume(face);
\r
2301 rprim.BoundingVolume.AddVolume(data.BoundingVolume);
\r
2303 // With linear texture animation in effect, texture repeats and offset are ignored
\r
2304 if ((prim.TextureAnim.Flags & Primitive.TextureAnimMode.ANIM_ON) != 0
\r
2305 && (prim.TextureAnim.Flags & Primitive.TextureAnimMode.ROTATE) == 0
\r
2306 && (prim.TextureAnim.Face == 255 || prim.TextureAnim.Face == j))
\r
2308 teFace.RepeatU = 1;
\r
2309 teFace.RepeatV = 1;
\r
2310 teFace.OffsetU = 0;
\r
2311 teFace.OffsetV = 0;
\r
2314 // Need to adjust UV for spheres as they are sort of half-prim
\r
2315 if (prim.PrimData.ProfileCurve == ProfileCurve.HalfCircle)
\r
2317 teFace = new Primitive.TextureEntryFace(teFace);
\r
2318 teFace.RepeatV *= 2;
\r
2319 teFace.OffsetV += 0.5f;
\r
2322 // Texture transform for this face
\r
2323 renderer.TransformTexCoords(face.Vertices, face.Center, teFace);
\r
2325 // Set the UserData for this face to our FaceData struct
\r
2326 face.UserData = data;
\r
2327 rprim.Faces[j] = face;
\r
2329 if (existingMesh != null &&
\r
2330 j < existingMesh.Faces.Count &&
\r
2331 existingMesh.Faces[j].TextureFace.TextureID == teFace.TextureID &&
\r
2332 ((FaceData)existingMesh.Faces[j].UserData).TextureInfo.TexturePointer != 0
\r
2335 FaceData existingData = (FaceData)existingMesh.Faces[j].UserData;
\r
2336 data.TextureInfo = existingData.TextureInfo;
\r
2341 DownloadTexture(new TextureLoadItem()
\r
2352 Prims[prim.LocalID] = rprim;
\r
2356 private void UpdatePrimBlocking(Primitive prim)
\r
2358 if (AvatarRenderingEnabled && prim.PrimData.PCode == PCode.Avatar)
\r
2360 AddAvatarToScene(Client.Network.CurrentSim.ObjectsAvatars[prim.LocalID]);
\r
2365 if (prim.PrimData.PCode != PCode.Prim) return;
\r
2366 if (!PrimitiveRenderingEnabled) return;
\r
2368 if (prim.Textures == null) return;
\r
2370 RenderPrimitive rPrim = null;
\r
2371 if (!Prims.TryGetValue(prim.LocalID, out rPrim))
\r
2373 rPrim = new RenderPrimitive();
\r
2377 if (prim.Sculpt == null || prim.Sculpt.SculptTexture == UUID.Zero)
\r
2379 FacetedMesh mesh = renderer.GenerateFacetedMesh(prim, DetailLevel.High);
\r
2380 rPrim.Faces = mesh.Faces;
\r
2381 rPrim.Prim = prim;
\r
2382 MeshPrim(prim, rPrim);
\r
2388 FacetedMesh mesh = null;
\r
2390 if (prim.Sculpt.Type != SculptType.Mesh)
\r
2391 { // Regular sculptie
\r
2394 lock (sculptCache)
\r
2396 if (sculptCache.ContainsKey(prim.Sculpt.SculptTexture))
\r
2398 img = sculptCache[prim.Sculpt.SculptTexture];
\r
2404 if (LoadTexture(prim.Sculpt.SculptTexture, ref img, true))
\r
2406 sculptCache[prim.Sculpt.SculptTexture] = (Bitmap)img;
\r
2414 mesh = renderer.GenerateFacetedSculptMesh(prim, (Bitmap)img, DetailLevel.High);
\r
2418 AutoResetEvent gotMesh = new AutoResetEvent(false);
\r
2419 bool meshSuccess = false;
\r
2421 Client.Assets.RequestMesh(prim.Sculpt.SculptTexture, (success, meshAsset) =>
\r
2423 if (!success || !FacetedMesh.TryDecodeFromAsset(prim, meshAsset, DetailLevel.Highest, out mesh))
\r
2425 Logger.Log("Failed to fetch or decode the mesh asset", Helpers.LogLevel.Warning, Client);
\r
2429 meshSuccess = true;
\r
2434 if (!gotMesh.WaitOne(20 * 1000, false)) return;
\r
2435 if (!meshSuccess) return;
\r
2440 rPrim.Faces = mesh.Faces;
\r
2441 rPrim.Prim = prim;
\r
2442 MeshPrim(prim, rPrim);
\r
2450 private bool LoadTexture(UUID textureID, ref Image texture, bool removeAlpha)
\r
2452 ManualResetEvent gotImage = new ManualResetEvent(false);
\r
2458 instance.Client.Assets.RequestImage(textureID, (TextureRequestState state, AssetTexture assetTexture) =>
\r
2460 if (state == TextureRequestState.Finished)
\r
2463 OpenJPEG.DecodeToImage(assetTexture.AssetData, out mi);
\r
2467 if ((mi.Channels & ManagedImage.ImageChannels.Alpha) != 0)
\r
2469 mi.ConvertChannels(mi.Channels & ~ManagedImage.ImageChannels.Alpha);
\r
2473 img = LoadTGAClass.LoadTGA(new MemoryStream(mi.ExportTGA()));
\r
2478 gotImage.WaitOne(30 * 1000, false);
\r
2486 catch (Exception e)
\r
2488 Logger.Log(e.Message, Helpers.LogLevel.Error, instance.Client, e);
\r
2492 #endregion Private methods (the meat)
\r
2494 #region Form controls handlers
\r
2495 private void chkWireFrame_CheckedChanged(object sender, EventArgs e)
\r
2497 Wireframe = chkWireFrame.Checked;
\r
2500 private void btnReset_Click(object sender, EventArgs e)
\r
2505 private void cbAA_CheckedChanged(object sender, EventArgs e)
\r
2507 instance.GlobalSettings["use_multi_sampling"] = UseMultiSampling = cbAA.Checked;
\r
2511 #endregion Form controls handlers
\r
2513 #region Context menu
\r
2514 private void ctxObjects_Opening(object sender, System.ComponentModel.CancelEventArgs e)
\r
2516 if (instance.State.IsSitting)
\r
2518 sitToolStripMenuItem.Text = "Stand up";
\r
2520 else if (RightclickedPrim.Prim.Properties != null
\r
2521 && !string.IsNullOrEmpty(RightclickedPrim.Prim.Properties.SitName))
\r
2523 sitToolStripMenuItem.Text = RightclickedPrim.Prim.Properties.SitName;
\r
2527 sitToolStripMenuItem.Text = "Sit";
\r
2530 if (RightclickedPrim.Prim.Properties != null
\r
2531 && !string.IsNullOrEmpty(RightclickedPrim.Prim.Properties.TouchName))
\r
2533 touchToolStripMenuItem.Text = RightclickedPrim.Prim.Properties.TouchName;
\r
2537 touchToolStripMenuItem.Text = "Touch";
\r
2541 private void touchToolStripMenuItem_Click(object sender, EventArgs e)
\r
2544 Client.Self.Grab(RightclickedPrim.Prim.LocalID, Vector3.Zero, Vector3.Zero, Vector3.Zero, RightclickedFaceID, Vector3.Zero, Vector3.Zero, Vector3.Zero);
\r
2545 Thread.Sleep(100);
\r
2546 Client.Self.DeGrab(RightclickedPrim.Prim.LocalID);
\r
2549 private void sitToolStripMenuItem_Click(object sender, EventArgs e)
\r
2551 if (!instance.State.IsSitting)
\r
2553 instance.State.SetSitting(true, RightclickedPrim.Prim.ID);
\r
2557 instance.State.SetSitting(false, UUID.Zero);
\r
2561 private void takeToolStripMenuItem_Click(object sender, EventArgs e)
\r
2563 instance.MediaManager.PlayUISound(UISounds.ObjectDelete);
\r
2564 Client.Inventory.RequestDeRezToInventory(RightclickedPrim.Prim.LocalID);
\r
2568 private void returnToolStripMenuItem_Click(object sender, EventArgs e)
\r
2570 instance.MediaManager.PlayUISound(UISounds.ObjectDelete);
\r
2571 Client.Inventory.RequestDeRezToInventory(RightclickedPrim.Prim.LocalID, DeRezDestination.ReturnToOwner, UUID.Zero, UUID.Random());
\r
2575 private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
\r
2577 if (RightclickedPrim.Prim.Properties != null && RightclickedPrim.Prim.Properties.OwnerID != Client.Self.AgentID)
\r
2578 returnToolStripMenuItem_Click(sender, e);
\r
2581 instance.MediaManager.PlayUISound(UISounds.ObjectDelete);
\r
2582 Client.Inventory.RequestDeRezToInventory(RightclickedPrim.Prim.LocalID, DeRezDestination.AgentInventoryTake, Client.Inventory.FindFolderForType(AssetType.TrashFolder), UUID.Random());
\r
2586 #endregion Context menu
\r
2588 private void hsAmbient_Scroll(object sender, ScrollEventArgs e)
\r
2590 ambient = (float)hsAmbient.Value / 100f;
\r
2594 private void hsDiffuse_Scroll(object sender, ScrollEventArgs e)
\r
2596 difuse = (float)hsDiffuse.Value / 100f;
\r
2600 private void hsSpecular_Scroll(object sender, ScrollEventArgs e)
\r
2602 specular = (float)hsSpecular.Value / 100f;
\r
2606 private void hsLOD_Scroll(object sender, ScrollEventArgs e)
\r
2608 minLODFactor = (float)hsLOD.Value / 5000f;
\r
2611 private void button_vparam_Click(object sender, EventArgs e)
\r
2613 //int paramid = int.Parse(textBox_vparamid.Text);
\r
2614 //float weight = (float)hScrollBar_weight.Value/100f;
\r
2615 float weightx = float.Parse(textBox_x.Text);
\r
2616 float weighty = float.Parse(textBox_y.Text);
\r
2617 float weightz = float.Parse(textBox_z.Text);
\r
2619 foreach (RenderAvatar av in Avatars.Values)
\r
2621 //av.glavatar.morphtest(av.avatar,paramid,weight);
\r
2622 av.glavatar.skel.deformbone(comboBox1.Text, new Vector3(0, 0, 0), new Vector3(float.Parse(textBox_sx.Text), float.Parse(textBox_sy.Text), float.Parse(textBox_sz.Text)), Quaternion.CreateFromEulers((float)(Math.PI * (weightx / 180)), (float)(Math.PI * (weighty / 180)), (float)(Math.PI * (weightz / 180))));
\r
2624 foreach (GLMesh mesh in av.glavatar._meshes.Values)
\r
2626 mesh.applyjointweights();
\r
2632 private void textBox_vparamid_TextChanged(object sender, EventArgs e)
\r
2637 private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
\r
2640 string bone = comboBox1.Text;
\r
2641 foreach (RenderAvatar av in Avatars.Values)
\r
2644 if (av.glavatar.skel.mBones.TryGetValue(bone, out b))
\r
2646 textBox_sx.Text = (b.scale.X - 1.0f).ToString();
\r
2647 textBox_sy.Text = (b.scale.Y - 1.0f).ToString();
\r
2648 textBox_sz.Text = (b.scale.Z - 1.0f).ToString();
\r
2651 b.rot.GetEulerAngles(out x, out y, out z);
\r
2652 textBox_x.Text = x.ToString();
\r
2653 textBox_y.Text = y.ToString();
\r
2654 textBox_z.Text = z.ToString();
\r
2663 private void textBox_y_TextChanged(object sender, EventArgs e)
\r
2668 private void textBox_z_TextChanged(object sender, EventArgs e)
\r
2673 private void comboBox_morph_SelectedIndexChanged(object sender, EventArgs e)
\r
2678 private void button1_Click(object sender, EventArgs e)
\r
2680 foreach (RenderAvatar av in Avatars.Values)
\r
2683 foreach (VisualParamEx vpe in VisualParamEx.morphParams.Values)
\r
2685 if (vpe.Name == comboBox_morph.Text)
\r
2692 av.glavatar.morphtest(av.avatar, id, float.Parse(textBox_morphamount.Text));
\r
2694 foreach (GLMesh mesh in av.glavatar._meshes.Values)
\r
2696 mesh.applyjointweights();
\r
2705 private void gbZoom_Enter(object sender, EventArgs e)
\r
2710 private void button_driver_Click(object sender, EventArgs e)
\r
2712 foreach (RenderAvatar av in Avatars.Values)
\r
2715 foreach (VisualParamEx vpe in VisualParamEx.drivenParams.Values)
\r
2717 if (vpe.Name == comboBox_driver.Text)
\r
2724 av.glavatar.morphtest(av.avatar, id, float.Parse(textBox_driveramount.Text));
\r
2726 foreach (GLMesh mesh in av.glavatar._meshes.Values)
\r
2728 mesh.applyjointweights();
\r
2735 private void tbDrawDistance_Scroll(object sender, EventArgs e)
\r
2737 DrawDistance = (float)tbDrawDistance.Value;
\r
2738 lblDrawDistance.Text = string.Format("Draw distance: {0}", tbDrawDistance.Value);
\r
2742 bool miscEnabled = true;
\r
2743 private void cbMisc_CheckedChanged(object sender, EventArgs e)
\r
2745 miscEnabled = cbMisc.Checked;
\r