2 // Radegast Metaverse Client
3 // Copyright (c) 2009-2011, Radegast Development Team
4 // All rights reserved.
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
9 // * Redistributions of source code must retain the above copyright notice,
10 // this list of conditions and the following disclaimer.
11 // * Redistributions in binary form must reproduce the above copyright
12 // notice, this list of conditions and the following disclaimer in the
13 // documentation and/or other materials provided with the distribution.
14 // * Neither the name of the application "Radegast", nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 using System.Collections.Generic;
36 using System.Drawing.Imaging;
38 using System.Windows.Forms;
40 using System.Threading;
42 using OpenTK.Graphics.OpenGL;
44 using OpenMetaverse.Rendering;
45 using OpenMetaverse.Assets;
46 using OpenMetaverse.Imaging;
47 using OpenMetaverse.StructuredData;
50 namespace Radegast.Rendering
53 public partial class frmPrimWorkshop : RadegastForm
57 /// The OpenGL surface
59 public OpenTK.GLControl glControl = null;
62 /// Use multi sampling (anti aliasing)
64 public bool UseMultiSampling = true;
67 /// Is rendering engine ready and enabled
69 public bool RenderingEnabled = false;
72 /// Rednder in wireframe mode
74 public bool Wireframe = false;
77 /// List of prims in the scene
79 Dictionary<uint, FacetedMesh> Prims = new Dictionary<uint, FacetedMesh>();
82 /// Local ID of the root prim
84 public uint RootPrimLocalID = 0;
89 public Vector3 Center = Vector3.Zero;
90 #endregion Public fields
92 #region Private fields
94 Dictionary<UUID, TextureInfo> TexturesPtrMap = new Dictionary<UUID, TextureInfo>();
95 RadegastInstance instance;
96 MeshmerizerR renderer;
97 OpenTK.Graphics.GraphicsMode GLMode = null;
98 AutoResetEvent TextureThreadContextReady = new AutoResetEvent(false);
99 BlockingQueue<TextureLoadItem> PendingTextures = new BlockingQueue<TextureLoadItem>();
100 float[] lightPos = new float[] { 0f, 0f, 1f, 0f };
103 #endregion Private fields
105 #region Construction and disposal
106 public frmPrimWorkshop(RadegastInstance instance, uint rootLocalID)
109 this.RootPrimLocalID = rootLocalID;
111 InitializeComponent();
112 Disposed += new EventHandler(frmPrimWorkshop_Disposed);
113 AutoSavePosition = true;
114 UseMultiSampling = cbAA.Checked = instance.GlobalSettings["use_multi_sampling"];
115 cbAA.CheckedChanged += cbAA_CheckedChanged;
117 this.instance = instance;
119 renderer = new MeshmerizerR();
121 Client.Objects.TerseObjectUpdate += new EventHandler<TerseObjectUpdateEventArgs>(Objects_TerseObjectUpdate);
122 Client.Objects.ObjectUpdate += new EventHandler<PrimEventArgs>(Objects_ObjectUpdate);
123 Client.Objects.ObjectDataBlockUpdate += new EventHandler<ObjectDataBlockUpdateEventArgs>(Objects_ObjectDataBlockUpdate);
126 void frmPrimWorkshop_Disposed(object sender, EventArgs e)
128 if (glControl != null)
133 Client.Objects.TerseObjectUpdate -= new EventHandler<TerseObjectUpdateEventArgs>(Objects_TerseObjectUpdate);
134 Client.Objects.ObjectUpdate -= new EventHandler<PrimEventArgs>(Objects_ObjectUpdate);
135 Client.Objects.ObjectDataBlockUpdate -= new EventHandler<ObjectDataBlockUpdateEventArgs>(Objects_ObjectDataBlockUpdate);
137 #endregion Construction and disposal
139 #region Network messaage handlers
140 void Objects_TerseObjectUpdate(object sender, TerseObjectUpdateEventArgs e)
142 if (Prims.ContainsKey(e.Prim.LocalID))
144 UpdatePrimBlocking(e.Prim);
148 void Objects_ObjectUpdate(object sender, PrimEventArgs e)
150 if (Prims.ContainsKey(e.Prim.LocalID) || Prims.ContainsKey(e.Prim.ParentID))
152 UpdatePrimBlocking(e.Prim);
156 void Objects_ObjectDataBlockUpdate(object sender, ObjectDataBlockUpdateEventArgs e)
158 if (Prims.ContainsKey(e.Prim.LocalID))
160 UpdatePrimBlocking(e.Prim);
163 #endregion Network messaage handlers
165 #region glControl setup and disposal
166 public void SetupGLControl()
168 RenderingEnabled = false;
170 if (glControl != null)
178 if (!UseMultiSampling)
180 GLMode = new OpenTK.Graphics.GraphicsMode(OpenTK.DisplayDevice.Default.BitsPerPixel, 24, 8, 0);
184 for (int aa = 0; aa <= 4; aa += 2)
186 var testMode = new OpenTK.Graphics.GraphicsMode(OpenTK.DisplayDevice.Default.BitsPerPixel, 24, 8, aa);
187 if (testMode.Samples == aa)
205 glControl = new OpenTK.GLControl();
209 glControl = new OpenTK.GLControl(GLMode);
214 Logger.Log(ex.Message, Helpers.LogLevel.Warning, Client);
218 if (glControl == null)
220 Logger.Log("Failed to initialize OpenGL control, cannot continue", Helpers.LogLevel.Error, Client);
224 Logger.Log("Initializing OpenGL mode: " + GLMode.ToString(), Helpers.LogLevel.Info);
226 glControl.Paint += glControl_Paint;
227 glControl.Resize += glControl_Resize;
228 glControl.MouseDown += glControl_MouseDown;
229 glControl.MouseUp += glControl_MouseUp;
230 glControl.MouseMove += glControl_MouseMove;
231 glControl.MouseWheel += glControl_MouseWheel;
232 glControl.Load += new EventHandler(glControl_Load);
233 glControl.Disposed += new EventHandler(glControl_Disposed);
234 glControl.Dock = DockStyle.Fill;
235 Controls.Add(glControl);
236 glControl.BringToFront();
239 void glControl_Disposed(object sender, EventArgs e)
241 TextureThreadRunning = false;
242 PendingTextures.Close();
245 void glControl_Load(object sender, EventArgs e)
249 GL.ShadeModel(ShadingModel.Smooth);
250 GL.ClearColor(0f, 0f, 0f, 0f);
252 //GL.LightModel(LightModelParameter.LightModelAmbient, new float[] { 0.5f, 0.5f, 0.5f, 1.0f });
254 GL.Enable(EnableCap.Lighting);
255 GL.Enable(EnableCap.Light0);
256 GL.Light(LightName.Light0, LightParameter.Ambient, new float[] { 0.5f, 0.5f, 0.5f, 1f });
257 GL.Light(LightName.Light0, LightParameter.Diffuse, new float[] { 0.3f, 0.3f, 0.3f, 1f });
258 GL.Light(LightName.Light0, LightParameter.Specular, new float[] { 0.8f, 0.8f, 0.8f, 1.0f });
259 GL.Light(LightName.Light0, LightParameter.Position, lightPos);
262 GL.Enable(EnableCap.DepthTest);
263 GL.Enable(EnableCap.ColorMaterial);
264 GL.Enable(EnableCap.CullFace);
265 GL.ColorMaterial(MaterialFace.Front, ColorMaterialParameter.AmbientAndDiffuse);
266 GL.ColorMaterial(MaterialFace.Front, ColorMaterialParameter.Specular);
269 GL.DepthFunc(DepthFunction.Lequal);
270 GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);
271 GL.MatrixMode(MatrixMode.Projection);
273 GL.Enable(EnableCap.Blend);
274 GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
275 hasMipmap = GL.GetString(StringName.Extensions).Contains("GL_SGIS_generate_mipmap");
277 RenderingEnabled = true;
278 // Call the resizing function which sets up the GL drawing window
279 // and will also invalidate the GL control
280 glControl_Resize(null, null);
282 glControl.Context.MakeCurrent(null);
283 TextureThreadContextReady.Reset();
284 var textureThread = new Thread(() => TextureThread())
287 Name = "TextureLoadingThread"
289 textureThread.Start();
290 TextureThreadContextReady.WaitOne(1000, false);
291 glControl.MakeCurrent();
295 RenderingEnabled = false;
296 Logger.Log("Failed to initialize OpenGL control", Helpers.LogLevel.Warning, Client, ex);
299 #endregion glControl setup and disposal
301 #region glControl paint and resize events
302 private void glControl_Paint(object sender, PaintEventArgs e)
304 if (!RenderingEnabled) return;
308 glControl.SwapBuffers();
311 private void glControl_Resize(object sender, EventArgs e)
313 if (!RenderingEnabled) return;
314 glControl.MakeCurrent();
316 GL.ClearColor(0.39f, 0.58f, 0.93f, 1.0f);
318 GL.Viewport(0, 0, glControl.Width, glControl.Height);
321 GL.MatrixMode(MatrixMode.Projection);
324 float dAspRat = (float)glControl.Width / (float)glControl.Height;
325 GluPerspective(50f, dAspRat, 0.1f, 100.0f);
327 GL.MatrixMode(MatrixMode.Modelview);
330 #endregion glControl paint and resize events
332 #region Mouse handling
333 bool dragging = false;
334 int dragX, dragY, downX, downY;
336 private void glControl_MouseWheel(object sender, MouseEventArgs e)
338 int newVal = Utils.Clamp(scrollZoom.Value + e.Delta / 10, scrollZoom.Minimum, scrollZoom.Maximum);
340 if (scrollZoom.Value != newVal)
342 scrollZoom.Value = newVal;
343 glControl_Resize(null, null);
348 FacetedMesh RightclickedPrim;
349 int RightclickedFaceID;
351 private void glControl_MouseDown(object sender, MouseEventArgs e)
353 if (e.Button == MouseButtons.Left || e.Button == MouseButtons.Middle)
359 else if (e.Button == MouseButtons.Right)
361 if (TryPick(e.X, e.Y, out RightclickedPrim, out RightclickedFaceID))
363 ctxObjects.Show(glControl, e.X, e.Y);
369 private void glControl_MouseMove(object sender, MouseEventArgs e)
373 int deltaX = e.X - dragX;
374 int deltaY = e.Y - dragY;
376 if (e.Button == MouseButtons.Left)
378 if (ModifierKeys == Keys.Control || ModifierKeys == (Keys.Alt | Keys.Control | Keys.Shift))
380 Center.X -= deltaX / 100f;
381 Center.Z += deltaY / 100f;
384 if (ModifierKeys == Keys.Alt)
386 Center.Y -= deltaY / 25f;
388 int newYaw = scrollYaw.Value + deltaX;
389 if (newYaw < 0) newYaw += 360;
390 if (newYaw > 360) newYaw -= 360;
392 scrollYaw.Value = newYaw;
396 if (ModifierKeys == Keys.None || ModifierKeys == (Keys.Alt | Keys.Control))
398 int newRoll = scrollRoll.Value + deltaY;
399 if (newRoll < 0) newRoll += 360;
400 if (newRoll > 360) newRoll -= 360;
402 scrollRoll.Value = newRoll;
405 int newYaw = scrollYaw.Value + deltaX;
406 if (newYaw < 0) newYaw += 360;
407 if (newYaw > 360) newYaw -= 360;
409 scrollYaw.Value = newYaw;
413 else if (e.Button == MouseButtons.Middle)
415 Center.X -= deltaX / 100f;
416 Center.Z += deltaY / 100f;
427 private void glControl_MouseUp(object sender, MouseEventArgs e)
429 if (e.Button == MouseButtons.Left)
433 if (e.X == downX && e.Y == downY) // click
437 if (TryPick(e.X, e.Y, out picked, out faceID))
439 Client.Self.Grab(picked.Prim.LocalID, Vector3.Zero, Vector3.Zero, Vector3.Zero, faceID, Vector3.Zero, Vector3.Zero, Vector3.Zero);
440 Client.Self.DeGrab(picked.Prim.LocalID);
446 #endregion Mouse handling
448 #region Texture thread
449 bool TextureThreadRunning = true;
453 OpenTK.INativeWindow window = new OpenTK.NativeWindow();
454 OpenTK.Graphics.IGraphicsContext context = new OpenTK.Graphics.GraphicsContext(GLMode, window.WindowInfo);
455 context.MakeCurrent(window.WindowInfo);
456 TextureThreadContextReady.Set();
457 PendingTextures.Open();
458 Logger.DebugLog("Started Texture Thread");
460 while (window.Exists && TextureThreadRunning)
462 window.ProcessEvents();
464 TextureLoadItem item = null;
466 if (!PendingTextures.Dequeue(Timeout.Infinite, ref item)) continue;
468 if (TexturesPtrMap.ContainsKey(item.TeFace.TextureID))
470 item.Data.TextureInfo = TexturesPtrMap[item.TeFace.TextureID];
471 GL.BindTexture(TextureTarget.Texture2D, item.Data.TextureInfo.TexturePointer);
475 if (LoadTexture(item.TeFace.TextureID, ref item.Data.TextureInfo.Texture, false))
477 GL.GenTextures(1, out item.Data.TextureInfo.TexturePointer);
478 GL.BindTexture(TextureTarget.Texture2D, item.Data.TextureInfo.TexturePointer);
480 Bitmap bitmap = (Bitmap)item.Data.TextureInfo.Texture;
483 if (item.Data.TextureInfo.Texture.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb)
491 item.Data.TextureInfo.HasAlpha = hasAlpha;
493 bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
494 Rectangle rectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
496 BitmapData bitmapData =
499 ImageLockMode.ReadOnly,
500 hasAlpha ? System.Drawing.Imaging.PixelFormat.Format32bppArgb : System.Drawing.Imaging.PixelFormat.Format24bppRgb);
503 TextureTarget.Texture2D,
505 hasAlpha ? PixelInternalFormat.Rgba : PixelInternalFormat.Rgb8,
509 hasAlpha ? OpenTK.Graphics.OpenGL.PixelFormat.Bgra : OpenTK.Graphics.OpenGL.PixelFormat.Bgr,
510 PixelType.UnsignedByte,
513 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
514 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
515 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
518 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.LinearMipmapLinear);
519 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.GenerateMipmap, 1);
520 GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
524 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
527 TexturesPtrMap[item.TeFace.TextureID] = item.Data.TextureInfo;
529 bitmap.UnlockBits(bitmapData);
531 item.Data.TextureInfo.Texture = null;
537 Logger.DebugLog("Texture thread exited");
539 #endregion Texture thread
541 private void frmPrimWorkshop_Shown(object sender, EventArgs e)
545 ThreadPool.QueueUserWorkItem(sync =>
547 if (Client.Network.CurrentSim.ObjectsPrimitives.ContainsKey(RootPrimLocalID))
549 UpdatePrimBlocking(Client.Network.CurrentSim.ObjectsPrimitives[RootPrimLocalID]);
550 var children = Client.Network.CurrentSim.ObjectsPrimitives.FindAll((Primitive p) => { return p.ParentID == RootPrimLocalID; });
551 children.ForEach(p => UpdatePrimBlocking(p));
558 #region Public methods
559 public void SetView(Vector3 center, int roll, int pitch, int yaw, int zoom)
561 this.Center = center;
562 scrollRoll.Value = roll;
563 scrollPitch.Value = pitch;
564 scrollYaw.Value = yaw;
565 scrollZoom.Value = zoom;
567 #endregion Public methods
569 #region Private methods (the meat)
570 private OpenTK.Vector3 WorldToScreen(OpenTK.Vector3 world)
572 OpenTK.Vector3 screen;
573 double[] ModelViewMatrix = new double[16];
574 double[] ProjectionMatrix = new double[16];
575 int[] Viewport = new int[4];
577 GL.GetInteger(GetPName.Viewport, Viewport);
578 GL.GetDouble(GetPName.ModelviewMatrix, ModelViewMatrix);
579 GL.GetDouble(GetPName.ProjectionMatrix, ProjectionMatrix);
581 #pragma warning disable 0618
582 OpenTK.Graphics.Glu.Project(world,
587 #pragma warning restore 0618
589 screen.Y = glControl.Height - screen.Y;
593 #pragma warning disable 0612
594 OpenTK.Graphics.TextPrinter Printer = new OpenTK.Graphics.TextPrinter(OpenTK.Graphics.TextQuality.High);
595 #pragma warning restore 0612
596 private void RenderText()
601 foreach (FacetedMesh mesh in Prims.Values)
604 Primitive prim = mesh.Prim;
605 if (!string.IsNullOrEmpty(prim.Text))
607 string text = System.Text.RegularExpressions.Regex.Replace(prim.Text, "(\r?\n)+", "\n");
608 OpenTK.Vector3 screenPos = OpenTK.Vector3.Zero;
609 OpenTK.Vector3 primPos = OpenTK.Vector3.Zero;
612 FacetedMesh parent = null;
613 if (Prims.TryGetValue(prim.ParentID, out parent))
615 var newPrimPos = prim.Position * Matrix4.CreateFromQuaternion(parent.Prim.Rotation);
616 primPos = new OpenTK.Vector3(newPrimPos.X, newPrimPos.Y, newPrimPos.Z);
619 primPos.Z += prim.Scale.Z * 0.7f;
620 screenPos = WorldToScreen(primPos);
623 Color color = Color.FromArgb((int)(prim.TextColor.A * 255), (int)(prim.TextColor.R * 255), (int)(prim.TextColor.G * 255), (int)(prim.TextColor.B * 255));
625 using (Font f = new Font(FontFamily.GenericSansSerif, 10f, FontStyle.Bold))
627 var size = Printer.Measure(text, f);
628 screenPos.X -= size.BoundingBox.Width / 2;
629 screenPos.Y -= size.BoundingBox.Height;
632 if (color != Color.Black)
634 Printer.Print(text, f, Color.Black, new RectangleF(screenPos.X + 1, screenPos.Y + 1, size.BoundingBox.Width, size.BoundingBox.Height), OpenTK.Graphics.TextPrinterOptions.Default, OpenTK.Graphics.TextAlignment.Center);
636 Printer.Print(text, f, color, new RectangleF(screenPos.X, screenPos.Y, size.BoundingBox.Width, size.BoundingBox.Height), OpenTK.Graphics.TextPrinterOptions.Default, OpenTK.Graphics.TextAlignment.Center);
644 private void RenderObjects(RenderPass pass)
649 foreach (FacetedMesh mesh in Prims.Values)
652 Primitive prim = mesh.Prim;
653 // Individual prim matrix
656 if (prim.ParentID == RootPrimLocalID)
658 FacetedMesh parent = null;
659 if (Prims.TryGetValue(prim.ParentID, out parent))
661 // Apply prim translation and rotation relative to the root prim
662 GL.MultMatrix(Math3D.CreateRotationMatrix(parent.Prim.Rotation));
663 //GL.MultMatrixf(Math3D.CreateTranslationMatrix(parent.Prim.Position));
666 // Prim roation relative to root
667 GL.MultMatrix(Math3D.CreateTranslationMatrix(prim.Position));
671 GL.MultMatrix(Math3D.CreateRotationMatrix(prim.Rotation));
674 GL.Scale(prim.Scale.X, prim.Scale.Y, prim.Scale.Z);
676 // Draw the prim faces
677 for (int j = 0; j < mesh.Faces.Count; j++)
679 Primitive.TextureEntryFace teFace = mesh.Prim.Textures.FaceTextures[j];
680 Face face = mesh.Faces[j];
681 FaceData data = (FaceData)face.UserData;
684 teFace = mesh.Prim.Textures.DefaultTexture;
686 if (pass != RenderPass.Picking)
688 bool belongToAlphaPass = (teFace.RGBA.A < 0.99) || data.TextureInfo.HasAlpha;
690 if (belongToAlphaPass && pass != RenderPass.Alpha) continue;
691 if (!belongToAlphaPass && pass == RenderPass.Alpha) continue;
693 // Don't render transparent faces
694 if (teFace.RGBA.A <= 0.01f) continue;
696 switch (teFace.Shiny)
699 GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 94f);
702 case Shininess.Medium:
703 GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 64f);
707 GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 24f);
713 GL.Material(MaterialFace.Front, MaterialParameter.Shininess, 0f);
717 var faceColor = new float[] { teFace.RGBA.R, teFace.RGBA.G, teFace.RGBA.B, teFace.RGBA.A };
719 GL.Color4(faceColor);
720 GL.Material(MaterialFace.Front, MaterialParameter.AmbientAndDiffuse, faceColor);
721 GL.Material(MaterialFace.Front, MaterialParameter.Specular, faceColor);
723 if (data.TextureInfo.TexturePointer != 0)
725 GL.Enable(EnableCap.Texture2D);
729 GL.Disable(EnableCap.Texture2D);
733 GL.BindTexture(TextureTarget.Texture2D, data.TextureInfo.TexturePointer);
737 data.PickingID = primNr;
738 var primNrBytes = Utils.Int16ToBytes((short)primNr);
739 var faceColor = new byte[] { primNrBytes[0], primNrBytes[1], (byte)j, 255 };
741 GL.Color4(faceColor);
744 GL.TexCoordPointer(2, TexCoordPointerType.Float, 0, data.TexCoords);
745 GL.VertexPointer(3, VertexPointerType.Float, 0, data.Vertices);
746 GL.NormalPointer(NormalPointerType.Float, 0, data.Normals);
747 GL.DrawElements(BeginMode.Triangles, data.Indices.Length, DrawElementsType.UnsignedShort, data.Indices);
751 // Pop the prim matrix
757 private void Render(bool picking)
759 glControl.MakeCurrent();
762 GL.ClearColor(1f, 1f, 1f, 1f);
766 GL.ClearColor(0.39f, 0.58f, 0.93f, 1.0f);
769 GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
772 // Setup wireframe or solid fill drawing mode
773 if (Wireframe && !picking)
775 GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Line);
779 GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill);
782 var mLookAt = OpenTK.Matrix4d.LookAt(
783 Center.X, (double)scrollZoom.Value * 0.1d + Center.Y, Center.Z,
784 Center.X, Center.Y, Center.Z,
786 GL.MultMatrix(ref mLookAt);
788 //OpenTK.Graphics.Glu.LookAt(
789 // Center.X, (double)scrollZoom.Value * 0.1d + Center.Y, Center.Z,
790 // Center.X, Center.Y, Center.Z,
793 //GL.Light(LightName.Light0, LightParameter.Position, lightPos);
795 // Push the world matrix
798 GL.EnableClientState(ArrayCap.VertexArray);
799 GL.EnableClientState(ArrayCap.TextureCoordArray);
800 GL.EnableClientState(ArrayCap.NormalArray);
803 GL.Rotate((float)scrollRoll.Value, 1f, 0f, 0f);
804 GL.Rotate((float)scrollPitch.Value, 0f, 1f, 0f);
805 GL.Rotate((float)scrollYaw.Value, 0f, 0f, 1f);
809 RenderObjects(RenderPass.Picking);
813 RenderObjects(RenderPass.Simple);
814 RenderObjects(RenderPass.Alpha);
818 // Pop the world matrix
821 GL.DisableClientState(ArrayCap.TextureCoordArray);
822 GL.DisableClientState(ArrayCap.VertexArray);
823 GL.DisableClientState(ArrayCap.NormalArray);
828 private void GluPerspective(float fovy, float aspect, float zNear, float zFar)
830 float fH = (float)Math.Tan(fovy / 360 * (float)Math.PI) * zNear;
831 float fW = fH * aspect;
832 GL.Frustum(-fW, fW, -fH, fH, zNear, zFar);
835 private bool TryPick(int x, int y, out FacetedMesh picked, out int faceID)
837 // Save old attributes
838 GL.PushAttrib(AttribMask.AllAttribBits);
840 // Disable some attributes to make the objects flat / solid color when they are drawn
841 GL.Disable(EnableCap.Fog);
842 GL.Disable(EnableCap.Texture2D);
843 GL.Disable(EnableCap.Dither);
844 GL.Disable(EnableCap.Lighting);
845 GL.Disable(EnableCap.LineStipple);
846 GL.Disable(EnableCap.PolygonStipple);
847 GL.Disable(EnableCap.CullFace);
848 GL.Disable(EnableCap.Blend);
849 GL.Disable(EnableCap.AlphaTest);
853 byte[] color = new byte[4];
854 GL.ReadPixels(x, glControl.Height - y, 1, 1, OpenTK.Graphics.OpenGL.PixelFormat.Rgba, PixelType.UnsignedByte, color);
858 int primID = Utils.BytesToUInt16(color, 0);
865 foreach (var mesh in Prims.Values)
867 foreach (var face in mesh.Faces)
869 if (((FaceData)face.UserData).PickingID == primID)
876 if (picked != null) break;
880 return picked != null;
884 private void UpdatePrimBlocking(Primitive prim)
887 FacetedMesh mesh = null;
888 FacetedMesh existingMesh = null;
892 if (Prims.ContainsKey(prim.LocalID))
894 existingMesh = Prims[prim.LocalID];
898 if (prim.Textures == null)
903 if (prim.Sculpt != null && prim.Sculpt.SculptTexture != UUID.Zero)
905 if (prim.Sculpt.Type != SculptType.Mesh)
906 { // Regular sculptie
908 if (!LoadTexture(prim.Sculpt.SculptTexture, ref img, true))
910 mesh = renderer.GenerateFacetedSculptMesh(prim, (Bitmap)img, DetailLevel.Highest);
914 AutoResetEvent gotMesh = new AutoResetEvent(false);
915 bool meshSuccess = false;
917 Client.Assets.RequestMesh(prim.Sculpt.SculptTexture, (success, meshAsset) =>
919 if (!success || !FacetedMesh.TryDecodeFromAsset(prim, meshAsset, DetailLevel.Highest, out mesh))
921 Logger.Log("Failed to fetch or decode the mesh asset", Helpers.LogLevel.Warning, Client);
930 if (!gotMesh.WaitOne(20 * 1000, false)) return;
931 if (!meshSuccess) return;
936 mesh = renderer.GenerateFacetedMesh(prim, DetailLevel.Highest);
944 // Create a FaceData struct for each face that stores the 3D data
945 // in a OpenGL friendly format
946 for (int j = 0; j < mesh.Faces.Count; j++)
948 Face face = mesh.Faces[j];
949 FaceData data = new FaceData();
951 // Vertices for this face
952 data.Vertices = new float[face.Vertices.Count * 3];
953 data.Normals = new float[face.Vertices.Count * 3];
954 for (int k = 0; k < face.Vertices.Count; k++)
956 data.Vertices[k * 3 + 0] = face.Vertices[k].Position.X;
957 data.Vertices[k * 3 + 1] = face.Vertices[k].Position.Y;
958 data.Vertices[k * 3 + 2] = face.Vertices[k].Position.Z;
960 data.Normals[k * 3 + 0] = face.Vertices[k].Normal.X;
961 data.Normals[k * 3 + 1] = face.Vertices[k].Normal.Y;
962 data.Normals[k * 3 + 2] = face.Vertices[k].Normal.Z;
965 // Indices for this face
966 data.Indices = face.Indices.ToArray();
968 // Texture transform for this face
969 Primitive.TextureEntryFace teFace = prim.Textures.GetFace((uint)j);
970 renderer.TransformTexCoords(face.Vertices, face.Center, teFace);
972 // Texcoords for this face
973 data.TexCoords = new float[face.Vertices.Count * 2];
974 for (int k = 0; k < face.Vertices.Count; k++)
976 data.TexCoords[k * 2 + 0] = face.Vertices[k].TexCoord.X;
977 data.TexCoords[k * 2 + 1] = face.Vertices[k].TexCoord.Y;
980 // Set the UserData for this face to our FaceData struct
981 face.UserData = data;
982 mesh.Faces[j] = face;
985 if (existingMesh != null &&
986 j < existingMesh.Faces.Count &&
987 existingMesh.Faces[j].TextureFace.TextureID == teFace.TextureID &&
988 ((FaceData)existingMesh.Faces[j].UserData).TextureInfo.TexturePointer != 0
991 FaceData existingData = (FaceData)existingMesh.Faces[j].UserData;
992 data.TextureInfo.TexturePointer = existingData.TextureInfo.TexturePointer;
997 var textureItem = new TextureLoadItem()
1004 PendingTextures.Enqueue(textureItem);
1011 Prims[prim.LocalID] = mesh;
1016 private bool LoadTexture(UUID textureID, ref Image texture, bool removeAlpha)
1018 ManualResetEvent gotImage = new ManualResetEvent(false);
1024 instance.Client.Assets.RequestImage(textureID, (TextureRequestState state, AssetTexture assetTexture) =>
1026 if (state == TextureRequestState.Finished)
1029 OpenJPEG.DecodeToImage(assetTexture.AssetData, out mi);
1033 if ((mi.Channels & ManagedImage.ImageChannels.Alpha) != 0)
1035 mi.ConvertChannels(mi.Channels & ~ManagedImage.ImageChannels.Alpha);
1039 img = LoadTGAClass.LoadTGA(new MemoryStream(mi.ExportTGA()));
1044 gotImage.WaitOne(30 * 1000, false);
1054 Logger.Log(e.Message, Helpers.LogLevel.Error, instance.Client, e);
1059 private void SafeInvalidate()
1061 if (glControl == null || !RenderingEnabled) return;
1065 if (!instance.MonoRuntime || IsHandleCreated)
1067 BeginInvoke(new MethodInvoker(() => SafeInvalidate()));
1072 glControl.Invalidate();
1074 #endregion Private methods (the meat)
1076 #region Form controls handlers
1077 private void scroll_ValueChanged(object sender, EventArgs e)
1082 private void scrollZoom_ValueChanged(object sender, EventArgs e)
1084 glControl_Resize(null, null);
1088 private void chkWireFrame_CheckedChanged(object sender, EventArgs e)
1090 Wireframe = chkWireFrame.Checked;
1094 private void btnReset_Click(object sender, EventArgs e)
1096 scrollYaw.Value = 90;
1097 scrollPitch.Value = 0;
1098 scrollRoll.Value = 0;
1099 scrollZoom.Value = -30;
1100 Center = Vector3.Zero;
1105 private void oBJToolStripMenuItem_Click(object sender, EventArgs e)
1107 SaveFileDialog dialog = new SaveFileDialog();
1108 dialog.Filter = "OBJ files (*.obj)|*.obj";
1110 if (dialog.ShowDialog() == DialogResult.OK)
1112 if (!MeshToOBJ.MeshesToOBJ(Prims, dialog.FileName))
1114 MessageBox.Show("Failed to save file " + dialog.FileName +
1115 ". Ensure that you have permission to write to that file and it is currently not in use");
1120 private void cbAA_CheckedChanged(object sender, EventArgs e)
1122 instance.GlobalSettings["use_multi_sampling"] = UseMultiSampling = cbAA.Checked;
1126 #endregion Form controls handlers
1128 #region Context menu
1129 private void ctxObjects_Opening(object sender, System.ComponentModel.CancelEventArgs e)
1131 if (instance.State.IsSitting)
1133 sitToolStripMenuItem.Text = "Stand up";
1135 else if (RightclickedPrim.Prim.Properties != null
1136 && !string.IsNullOrEmpty(RightclickedPrim.Prim.Properties.SitName))
1138 sitToolStripMenuItem.Text = RightclickedPrim.Prim.Properties.SitName;
1142 sitToolStripMenuItem.Text = "Sit";
1145 if (RightclickedPrim.Prim.Properties != null
1146 && !string.IsNullOrEmpty(RightclickedPrim.Prim.Properties.TouchName))
1148 touchToolStripMenuItem.Text = RightclickedPrim.Prim.Properties.TouchName;
1152 touchToolStripMenuItem.Text = "Touch";
1156 private void touchToolStripMenuItem_Click(object sender, EventArgs e)
1159 Client.Self.Grab(RightclickedPrim.Prim.LocalID, Vector3.Zero, Vector3.Zero, Vector3.Zero, RightclickedFaceID, Vector3.Zero, Vector3.Zero, Vector3.Zero);
1161 Client.Self.DeGrab(RightclickedPrim.Prim.LocalID);
1164 private void sitToolStripMenuItem_Click(object sender, EventArgs e)
1166 if (!instance.State.IsSitting)
1168 instance.State.SetSitting(true, RightclickedPrim.Prim.ID);
1172 instance.State.SetSitting(false, UUID.Zero);
1176 private void takeToolStripMenuItem_Click(object sender, EventArgs e)
1178 instance.MediaManager.PlayUISound(UISounds.ObjectDelete);
1179 Client.Inventory.RequestDeRezToInventory(RightclickedPrim.Prim.LocalID);
1183 private void returnToolStripMenuItem_Click(object sender, EventArgs e)
1185 instance.MediaManager.PlayUISound(UISounds.ObjectDelete);
1186 Client.Inventory.RequestDeRezToInventory(RightclickedPrim.Prim.LocalID, DeRezDestination.ReturnToOwner, UUID.Zero, UUID.Random());
1190 private void deleteToolStripMenuItem_Click(object sender, EventArgs e)
1192 if (RightclickedPrim.Prim.Properties != null && RightclickedPrim.Prim.Properties.OwnerID != Client.Self.AgentID)
1193 returnToolStripMenuItem_Click(sender, e);
1196 instance.MediaManager.PlayUISound(UISounds.ObjectDelete);
1197 Client.Inventory.RequestDeRezToInventory(RightclickedPrim.Prim.LocalID, DeRezDestination.AgentInventoryTake, Client.Inventory.FindFolderForType(AssetType.TrashFolder), UUID.Random());
1201 #endregion Context menu