diff --git a/Programs/Baker/Baker.csproj b/Programs/Baker/Baker.csproj
deleted file mode 100644
index d3d4b594..00000000
--- a/Programs/Baker/Baker.csproj
+++ /dev/null
@@ -1,117 +0,0 @@
-
-
- Local
- 8.0.50727
- 2.0
- {7744EF85-0000-0000-0000-000000000000}
- Debug
- AnyCPU
-
-
-
-
- Baker
- JScript
- Grid
- IE50
- false
- Exe
-
-
- Baker
-
-
-
-
-
-
- True
- 285212672
- False
-
-
- TRACE;DEBUG
-
-
- True
- 4096
- False
- ..\..\bin\
- False
- False
- False
- 4
-
-
-
-
- True
- 285212672
- False
-
-
- TRACE
-
-
- False
- 4096
- True
- ..\..\bin\
- False
- False
- False
- 4
-
-
-
-
-
- System.dll
- False
-
-
- System.Data.dll
- False
-
-
- System.Drawing.dll
- False
-
-
- System.Windows.Forms.dll
- False
-
-
-
-
- OpenMetaverse
- {3164F259-0000-0000-0000-000000000000}
- {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
- False
-
-
-
-
-
-
- Code
-
-
- Form
-
-
- frmBaker.cs
- Code
-
-
- Code
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Programs/PrimWorkshop/Program.cs b/Programs/PrimWorkshop/Program.cs
new file mode 100644
index 00000000..bcaf7ec7
--- /dev/null
+++ b/Programs/PrimWorkshop/Program.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Windows.Forms;
+
+namespace primpreview
+{
+ static class Program
+ {
+ ///
+ /// The main entry point for the application.
+ ///
+ [STAThread]
+ static void Main()
+ {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ Application.Run(new frmPrimPreview());
+ }
+ }
+}
\ No newline at end of file
diff --git a/Programs/PrimWorkshop/Properties/AssemblyInfo.cs b/Programs/PrimWorkshop/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..6d23e9f2
--- /dev/null
+++ b/Programs/PrimWorkshop/Properties/AssemblyInfo.cs
@@ -0,0 +1,33 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("primpreview")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Metaverse Industries LLC")]
+[assembly: AssemblyProduct("primpreview")]
+[assembly: AssemblyCopyright("Copyright © Metaverse Industries LLC 2007")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("8b942505-1bf6-4efe-afef-afde490540d5")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Programs/PrimWorkshop/Properties/Resources.Designer.cs b/Programs/PrimWorkshop/Properties/Resources.Designer.cs
new file mode 100644
index 00000000..76c2b5f8
--- /dev/null
+++ b/Programs/PrimWorkshop/Properties/Resources.Designer.cs
@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.42
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace primpreview.Properties
+{
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources
+ {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources()
+ {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager
+ {
+ get
+ {
+ if ((resourceMan == null))
+ {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("primpreview.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture
+ {
+ get
+ {
+ return resourceCulture;
+ }
+ set
+ {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/Programs/PrimWorkshop/Properties/Resources.resx b/Programs/PrimWorkshop/Properties/Resources.resx
new file mode 100644
index 00000000..ffecec85
--- /dev/null
+++ b/Programs/PrimWorkshop/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Programs/PrimWorkshop/Properties/Settings.Designer.cs b/Programs/PrimWorkshop/Properties/Settings.Designer.cs
new file mode 100644
index 00000000..967209ff
--- /dev/null
+++ b/Programs/PrimWorkshop/Properties/Settings.Designer.cs
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:2.0.50727.42
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace primpreview.Properties
+{
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "8.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+ {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default
+ {
+ get
+ {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/Programs/PrimWorkshop/Properties/Settings.settings b/Programs/PrimWorkshop/Properties/Settings.settings
new file mode 100644
index 00000000..abf36c5d
--- /dev/null
+++ b/Programs/PrimWorkshop/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/Programs/PrimWorkshop/TexturePipeline.cs b/Programs/PrimWorkshop/TexturePipeline.cs
new file mode 100644
index 00000000..0409133e
--- /dev/null
+++ b/Programs/PrimWorkshop/TexturePipeline.cs
@@ -0,0 +1,341 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+using libsecondlife;
+/*
+ * [14:09] the onnewprim function will add missing texture uuids to the download queue,
+ * and a separate thread will pull entries off that queue.
+ * if they exist in the cache it will add that texture to a dictionary that the rendering loop accesses,
+ * otherwise it will start the download.
+ * the ondownloaded function will put the new texture in the same dictionary
+ *
+ *
+ * Easy Start:
+ * subscribe to OnImageRenderReady event
+ * send request with RequestTexture()
+ *
+ * when OnImageRenderReady fires:
+ * request image data with GetTextureToRender() using key returned in OnImageRenderReady event
+ * (optionally) use RemoveFromPipeline() with key to cleanup dictionary
+ */
+namespace primpreview
+{
+ class TaskInfo
+ {
+ public LLUUID RequestID;
+ public int RequestNbr;
+
+
+ public TaskInfo(LLUUID reqID, int reqNbr)
+ {
+ RequestID = reqID;
+ RequestNbr = reqNbr;
+ }
+ }
+
+ ///
+ /// Texture request download handler, allows a configurable number of download slots
+ ///
+ public class TexturePipeline
+ {
+ private static SecondLife Client;
+
+ // queue for requested images
+ private Queue RequestQueue;
+
+ // list of current requests in process
+ private Dictionary CurrentRequests;
+
+ private static AutoResetEvent[] resetEvents;
+
+ private static int[] threadpoolSlots;
+
+ ///
+ /// For keeping track of active threads available/downloading textures
+ ///
+ public static int[] ThreadpoolSlots
+ {
+ get { lock (threadpoolSlots) { return threadpoolSlots; }}
+ set { lock (threadpoolSlots) { threadpoolSlots = value; } }
+ }
+
+ // storage for images ready to render
+ private Dictionary RenderReady;
+
+ // maximum allowed concurrent requests at once
+ const int MAX_TEXTURE_REQUESTS = 3;
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public delegate void DownloadFinishedCallback(LLUUID id, bool success);
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public delegate void DownloadProgressCallback(LLUUID image, int recieved, int total);
+
+ /// Fired when a texture download completes
+ public event DownloadFinishedCallback OnDownloadFinished;
+ ///
+ public event DownloadProgressCallback OnDownloadProgress;
+
+ private Thread downloadMaster;
+ private bool Running;
+
+ private AssetManager.ImageReceivedCallback DownloadCallback;
+ private AssetManager.ImageReceiveProgressCallback DownloadProgCallback;
+
+ ///
+ /// Default constructor
+ ///
+ /// Reference to SecondLife client
+ public TexturePipeline(SecondLife client)
+ {
+ Running = true;
+
+ RequestQueue = new Queue();
+ CurrentRequests = new Dictionary(MAX_TEXTURE_REQUESTS);
+
+ RenderReady = new Dictionary();
+
+ resetEvents = new AutoResetEvent[MAX_TEXTURE_REQUESTS];
+ threadpoolSlots = new int[MAX_TEXTURE_REQUESTS];
+
+ // pre-configure autoreset events/download slots
+ for (int i = 0; i < MAX_TEXTURE_REQUESTS; i++)
+ {
+ resetEvents[i] = new AutoResetEvent(false);
+ threadpoolSlots[i] = -1;
+ }
+
+ Client = client;
+
+ DownloadCallback = new AssetManager.ImageReceivedCallback(Assets_OnImageReceived);
+ DownloadProgCallback = new AssetManager.ImageReceiveProgressCallback(Assets_OnImageReceiveProgress);
+ Client.Assets.OnImageReceived += DownloadCallback;
+ Client.Assets.OnImageReceiveProgress += DownloadProgCallback;
+
+ // Fire up the texture download thread
+ downloadMaster = new Thread(new ThreadStart(DownloadThread));
+ downloadMaster.Start();
+ }
+
+ public void Shutdown()
+ {
+ Client.Assets.OnImageReceived -= DownloadCallback;
+ Client.Assets.OnImageReceiveProgress -= DownloadProgCallback;
+
+ RequestQueue.Clear();
+
+ for (int i = 0; i < resetEvents.Length; i++)
+ if (resetEvents[i] != null)
+ resetEvents[i].Set();
+
+ Running = false;
+ }
+
+ ///
+ /// Request a texture be downloaded, once downloaded OnImageRenderReady event will be fired
+ /// containing texture key which can be used to retrieve texture with GetTextureToRender method
+ ///
+ /// id of Texture to request
+ public void RequestTexture(LLUUID textureID)
+ {
+ if (Client.Assets.Cache.HasImage(textureID))
+ {
+ // Add to rendering dictionary
+ lock (RenderReady)
+ {
+ if (!RenderReady.ContainsKey(textureID))
+ {
+ RenderReady.Add(textureID, Client.Assets.Cache.GetCachedImage(textureID));
+
+ // Let any subscribers know about it
+ if (OnDownloadFinished != null)
+ {
+ OnDownloadFinished(textureID, true);
+ }
+ }
+ else
+ {
+ // This image has already been served up, ignore this request
+ }
+ }
+ }
+ else
+ {
+ lock (RequestQueue)
+ {
+ // Make sure we aren't already downloading the texture
+ if (!RequestQueue.Contains(textureID) && !CurrentRequests.ContainsKey(textureID))
+ {
+ RequestQueue.Enqueue(textureID);
+ }
+ }
+ }
+ }
+
+ ///
+ /// retrieve texture information from dictionary
+ ///
+ /// Texture ID
+ /// ImageDownload object
+ public ImageDownload GetTextureToRender(LLUUID textureID)
+ {
+ ImageDownload renderable = new ImageDownload();
+ lock (RenderReady)
+ {
+ if (RenderReady.ContainsKey(textureID))
+ {
+ renderable = RenderReady[textureID];
+ }
+ else
+ {
+ Logger.Log("Requested texture data for texture that does not exist in dictionary", Helpers.LogLevel.Warning);
+ }
+ return renderable;
+ }
+ }
+
+ ///
+ /// Remove no longer necessary texture from dictionary
+ ///
+ ///
+ public void RemoveFromPipeline(LLUUID textureID)
+ {
+ lock (RenderReady)
+ {
+ if (RenderReady.ContainsKey(textureID))
+ RenderReady.Remove(textureID);
+ }
+ }
+
+ ///
+ /// Master Download Thread, Queues up downloads in the threadpool
+ ///
+ private void DownloadThread()
+ {
+ int reqNbr;
+
+ while (Running)
+ {
+ if (RequestQueue.Count > 0)
+ {
+ reqNbr = -1;
+ // find available slot for reset event
+ for (int i = 0; i < threadpoolSlots.Length; i++)
+ {
+ if (threadpoolSlots[i] == -1)
+ {
+ threadpoolSlots[i] = 1;
+ reqNbr = i;
+ break;
+ }
+ }
+
+ if (reqNbr != -1)
+ {
+ LLUUID requestID;
+ lock (RequestQueue)
+ requestID = RequestQueue.Dequeue();
+
+ Logger.DebugLog(String.Format("Sending Worker thread new download request {0}", reqNbr));
+ ThreadPool.QueueUserWorkItem(new WaitCallback(textureRequestDoWork), new TaskInfo(requestID, reqNbr));
+
+ continue;
+ }
+ }
+
+ // Queue was empty, let's give up some CPU time
+ Thread.Sleep(500);
+ }
+ }
+
+ void textureRequestDoWork(Object threadContext)
+ {
+ TaskInfo ti = (TaskInfo)threadContext;
+
+ lock (CurrentRequests)
+ {
+ if (CurrentRequests.ContainsKey(ti.RequestID))
+ {
+ threadpoolSlots[ti.RequestNbr] = -1;
+ return;
+ }
+ else
+ {
+ CurrentRequests.Add(ti.RequestID, ti.RequestNbr);
+ }
+ }
+
+ Logger.DebugLog(String.Format("Worker {0} Requesting {1}", ti.RequestNbr, ti.RequestID));
+
+ resetEvents[ti.RequestNbr].Reset();
+ Client.Assets.RequestImage(ti.RequestID, ImageType.Normal);
+
+ // don't release this worker slot until texture is downloaded or timeout occurs
+ if (!resetEvents[ti.RequestNbr].WaitOne(30 * 1000, false))
+ {
+ // Timed out
+ Logger.Log("Worker " + ti.RequestNbr + " Timeout waiting for Texture " + ti.RequestID + " to Download", Helpers.LogLevel.Warning);
+
+ lock (CurrentRequests)
+ CurrentRequests.Remove(ti.RequestID);
+ }
+
+ // free up this download slot
+ threadpoolSlots[ti.RequestNbr] = -1;
+ }
+
+ private void Assets_OnImageReceived(ImageDownload image, AssetTexture asset)
+ {
+ // Free up this slot in the ThreadPool
+ lock (CurrentRequests)
+ {
+ int requestNbr;
+ if (asset != null && CurrentRequests.TryGetValue(image.ID, out requestNbr))
+ {
+ Logger.DebugLog(String.Format("Worker {0} Downloaded texture {1}", requestNbr, image.ID));
+ resetEvents[requestNbr].Set();
+ CurrentRequests.Remove(image.ID);
+ }
+ }
+
+ if (image.Success)
+ {
+ lock (RenderReady)
+ {
+ if (!RenderReady.ContainsKey(image.ID))
+ {
+ // Add to rendering dictionary
+ RenderReady.Add(image.ID, image);
+ }
+ }
+ }
+ else
+ {
+ Console.WriteLine(String.Format("Download of texture {0} failed. NotFound={1}", image.ID, image.NotFound));
+ }
+
+ // Let any subscribers know about it
+ if (OnDownloadFinished != null)
+ {
+ OnDownloadFinished(image.ID, image.Success);
+ }
+ }
+
+ private void Assets_OnImageReceiveProgress(LLUUID image, int recieved, int total)
+ {
+ if (OnDownloadProgress != null)
+ {
+ OnDownloadProgress(image, recieved, total);
+ }
+ }
+ }
+}
diff --git a/Programs/PrimWorkshop/frmBrowser.Designer.cs b/Programs/PrimWorkshop/frmBrowser.Designer.cs
new file mode 100644
index 00000000..882eb74e
--- /dev/null
+++ b/Programs/PrimWorkshop/frmBrowser.Designer.cs
@@ -0,0 +1,391 @@
+namespace primpreview
+{
+ partial class frmBrowser
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ GlacialComponents.Controls.GLColumn glColumn1 = new GlacialComponents.Controls.GLColumn();
+ GlacialComponents.Controls.GLColumn glColumn2 = new GlacialComponents.Controls.GLColumn();
+ this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
+ this.glControl = new Tao.Platform.Windows.SimpleOpenGlControl();
+ this.panel1 = new System.Windows.Forms.Panel();
+ this.txtPass = new System.Windows.Forms.TextBox();
+ this.txtLast = new System.Windows.Forms.TextBox();
+ this.txtFirst = new System.Windows.Forms.TextBox();
+ this.cmdLogin = new System.Windows.Forms.Button();
+ this.lstDownloads = new GlacialComponents.Controls.GlacialList();
+ this.progPrims = new System.Windows.Forms.ProgressBar();
+ this.lblPrims = new System.Windows.Forms.Label();
+ this.cboServer = new System.Windows.Forms.ComboBox();
+ this.panel2 = new System.Windows.Forms.Panel();
+ this.txtSim = new System.Windows.Forms.TextBox();
+ this.label2 = new System.Windows.Forms.Label();
+ this.label3 = new System.Windows.Forms.Label();
+ this.label4 = new System.Windows.Forms.Label();
+ this.label5 = new System.Windows.Forms.Label();
+ this.txtX = new System.Windows.Forms.TextBox();
+ this.txtY = new System.Windows.Forms.TextBox();
+ this.txtZ = new System.Windows.Forms.TextBox();
+ this.cmdTeleport = new System.Windows.Forms.Button();
+ this.tableLayoutPanel1.SuspendLayout();
+ this.panel1.SuspendLayout();
+ this.panel2.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // tableLayoutPanel1
+ //
+ this.tableLayoutPanel1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.tableLayoutPanel1.ColumnCount = 3;
+ this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 130F));
+ this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
+ this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 175F));
+ this.tableLayoutPanel1.Controls.Add(this.glControl, 0, 0);
+ this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 2);
+ this.tableLayoutPanel1.Controls.Add(this.lstDownloads, 0, 3);
+ this.tableLayoutPanel1.Controls.Add(this.progPrims, 1, 1);
+ this.tableLayoutPanel1.Controls.Add(this.lblPrims, 0, 1);
+ this.tableLayoutPanel1.Controls.Add(this.panel2, 2, 0);
+ this.tableLayoutPanel1.Location = new System.Drawing.Point(1, 1);
+ this.tableLayoutPanel1.Name = "tableLayoutPanel1";
+ this.tableLayoutPanel1.RowCount = 4;
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30F));
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 45F));
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 120F));
+ this.tableLayoutPanel1.Size = new System.Drawing.Size(799, 612);
+ this.tableLayoutPanel1.TabIndex = 0;
+ //
+ // glControl
+ //
+ this.glControl.AccumBits = ((byte)(0));
+ this.glControl.AutoCheckErrors = false;
+ this.glControl.AutoFinish = false;
+ this.glControl.AutoMakeCurrent = true;
+ this.glControl.AutoSwapBuffers = true;
+ this.glControl.BackColor = System.Drawing.Color.Black;
+ this.glControl.ColorBits = ((byte)(32));
+ this.tableLayoutPanel1.SetColumnSpan(this.glControl, 2);
+ this.glControl.DepthBits = ((byte)(16));
+ this.glControl.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.glControl.Location = new System.Drawing.Point(3, 3);
+ this.glControl.Name = "glControl";
+ this.glControl.Size = new System.Drawing.Size(618, 411);
+ this.glControl.StencilBits = ((byte)(0));
+ this.glControl.TabIndex = 7;
+ this.glControl.TabStop = false;
+ this.glControl.MouseMove += new System.Windows.Forms.MouseEventHandler(this.glControl_MouseMove);
+ this.glControl.MouseClick += new System.Windows.Forms.MouseEventHandler(this.glControl_MouseClick);
+ this.glControl.MouseDown += new System.Windows.Forms.MouseEventHandler(this.glControl_MouseDown);
+ this.glControl.Resize += new System.EventHandler(this.glControl_Resize);
+ this.glControl.MouseUp += new System.Windows.Forms.MouseEventHandler(this.glControl_MouseUp);
+ //
+ // panel1
+ //
+ this.panel1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.tableLayoutPanel1.SetColumnSpan(this.panel1, 2);
+ this.panel1.Controls.Add(this.cboServer);
+ this.panel1.Controls.Add(this.txtPass);
+ this.panel1.Controls.Add(this.txtLast);
+ this.panel1.Controls.Add(this.txtFirst);
+ this.panel1.Controls.Add(this.cmdLogin);
+ this.panel1.Location = new System.Drawing.Point(3, 450);
+ this.panel1.Name = "panel1";
+ this.panel1.Size = new System.Drawing.Size(618, 39);
+ this.panel1.TabIndex = 8;
+ //
+ // txtPass
+ //
+ this.txtPass.Location = new System.Drawing.Point(215, 11);
+ this.txtPass.Name = "txtPass";
+ this.txtPass.Size = new System.Drawing.Size(100, 20);
+ this.txtPass.TabIndex = 2;
+ this.txtPass.UseSystemPasswordChar = true;
+ this.txtPass.Enter += new System.EventHandler(this.txtLogin_Enter);
+ //
+ // txtLast
+ //
+ this.txtLast.Location = new System.Drawing.Point(109, 11);
+ this.txtLast.Name = "txtLast";
+ this.txtLast.Size = new System.Drawing.Size(100, 20);
+ this.txtLast.TabIndex = 1;
+ this.txtLast.Enter += new System.EventHandler(this.txtLogin_Enter);
+ //
+ // txtFirst
+ //
+ this.txtFirst.Location = new System.Drawing.Point(3, 11);
+ this.txtFirst.Name = "txtFirst";
+ this.txtFirst.Size = new System.Drawing.Size(100, 20);
+ this.txtFirst.TabIndex = 0;
+ this.txtFirst.Enter += new System.EventHandler(this.txtLogin_Enter);
+ //
+ // cmdLogin
+ //
+ this.cmdLogin.Location = new System.Drawing.Point(321, 9);
+ this.cmdLogin.Name = "cmdLogin";
+ this.cmdLogin.Size = new System.Drawing.Size(75, 23);
+ this.cmdLogin.TabIndex = 3;
+ this.cmdLogin.Text = "Login";
+ this.cmdLogin.UseVisualStyleBackColor = true;
+ this.cmdLogin.Click += new System.EventHandler(this.cmdLogin_Click);
+ //
+ // lstDownloads
+ //
+ this.lstDownloads.AllowColumnResize = true;
+ this.lstDownloads.AllowMultiselect = false;
+ this.lstDownloads.AlternateBackground = System.Drawing.Color.DarkGreen;
+ this.lstDownloads.AlternatingColors = false;
+ this.lstDownloads.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.lstDownloads.AutoHeight = true;
+ this.lstDownloads.BackColor = System.Drawing.SystemColors.ControlLightLight;
+ this.lstDownloads.BackgroundStretchToFit = true;
+ glColumn1.ActivatedEmbeddedType = GlacialComponents.Controls.GLActivatedEmbeddedTypes.None;
+ glColumn1.CheckBoxes = false;
+ glColumn1.ImageIndex = -1;
+ glColumn1.Name = "colTextureID";
+ glColumn1.NumericSort = false;
+ glColumn1.Text = "Texture ID";
+ glColumn1.TextAlignment = System.Drawing.ContentAlignment.MiddleLeft;
+ glColumn1.Width = 220;
+ glColumn2.ActivatedEmbeddedType = GlacialComponents.Controls.GLActivatedEmbeddedTypes.None;
+ glColumn2.CheckBoxes = false;
+ glColumn2.ImageIndex = -1;
+ glColumn2.Name = "colProgress";
+ glColumn2.NumericSort = false;
+ glColumn2.Text = "Download Progress";
+ glColumn2.TextAlignment = System.Drawing.ContentAlignment.MiddleLeft;
+ glColumn2.Width = 220;
+ this.lstDownloads.Columns.AddRange(new GlacialComponents.Controls.GLColumn[] {
+ glColumn1,
+ glColumn2});
+ this.tableLayoutPanel1.SetColumnSpan(this.lstDownloads, 2);
+ this.lstDownloads.ControlStyle = GlacialComponents.Controls.GLControlStyles.Normal;
+ this.lstDownloads.FullRowSelect = true;
+ this.lstDownloads.GridColor = System.Drawing.Color.LightGray;
+ this.lstDownloads.GridLines = GlacialComponents.Controls.GLGridLines.gridBoth;
+ this.lstDownloads.GridLineStyle = GlacialComponents.Controls.GLGridLineStyles.gridSolid;
+ this.lstDownloads.GridTypes = GlacialComponents.Controls.GLGridTypes.gridOnExists;
+ this.lstDownloads.HeaderHeight = 22;
+ this.lstDownloads.HeaderVisible = true;
+ this.lstDownloads.HeaderWordWrap = false;
+ this.lstDownloads.HotColumnTracking = false;
+ this.lstDownloads.HotItemTracking = false;
+ this.lstDownloads.HotTrackingColor = System.Drawing.Color.LightGray;
+ this.lstDownloads.HoverEvents = false;
+ this.lstDownloads.HoverTime = 1;
+ this.lstDownloads.ImageList = null;
+ this.lstDownloads.ItemHeight = 17;
+ this.lstDownloads.ItemWordWrap = false;
+ this.lstDownloads.Location = new System.Drawing.Point(3, 495);
+ this.lstDownloads.Name = "lstDownloads";
+ this.lstDownloads.Selectable = true;
+ this.lstDownloads.SelectedTextColor = System.Drawing.Color.White;
+ this.lstDownloads.SelectionColor = System.Drawing.Color.DarkBlue;
+ this.lstDownloads.ShowBorder = true;
+ this.lstDownloads.ShowFocusRect = false;
+ this.lstDownloads.Size = new System.Drawing.Size(618, 114);
+ this.lstDownloads.SortType = GlacialComponents.Controls.SortTypes.InsertionSort;
+ this.lstDownloads.SuperFlatHeaderColor = System.Drawing.Color.White;
+ this.lstDownloads.TabIndex = 9;
+ this.lstDownloads.Text = "Texture Downloads";
+ //
+ // progPrims
+ //
+ this.progPrims.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.progPrims.Location = new System.Drawing.Point(133, 420);
+ this.progPrims.Name = "progPrims";
+ this.progPrims.Size = new System.Drawing.Size(488, 24);
+ this.progPrims.TabIndex = 10;
+ //
+ // lblPrims
+ //
+ this.lblPrims.Anchor = System.Windows.Forms.AnchorStyles.Left;
+ this.lblPrims.AutoSize = true;
+ this.lblPrims.Location = new System.Drawing.Point(3, 425);
+ this.lblPrims.Name = "lblPrims";
+ this.lblPrims.Size = new System.Drawing.Size(61, 13);
+ this.lblPrims.TabIndex = 11;
+ this.lblPrims.Text = "Prims: 0 / 0";
+ //
+ // cboServer
+ //
+ this.cboServer.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.cboServer.FormattingEnabled = true;
+ this.cboServer.Location = new System.Drawing.Point(405, 11);
+ this.cboServer.Name = "cboServer";
+ this.cboServer.Size = new System.Drawing.Size(204, 21);
+ this.cboServer.TabIndex = 4;
+ //
+ // panel2
+ //
+ this.panel2.Controls.Add(this.cmdTeleport);
+ this.panel2.Controls.Add(this.txtZ);
+ this.panel2.Controls.Add(this.txtY);
+ this.panel2.Controls.Add(this.txtX);
+ this.panel2.Controls.Add(this.label5);
+ this.panel2.Controls.Add(this.label4);
+ this.panel2.Controls.Add(this.label3);
+ this.panel2.Controls.Add(this.label2);
+ this.panel2.Controls.Add(this.txtSim);
+ this.panel2.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.panel2.Location = new System.Drawing.Point(627, 3);
+ this.panel2.Name = "panel2";
+ this.tableLayoutPanel1.SetRowSpan(this.panel2, 4);
+ this.panel2.Size = new System.Drawing.Size(169, 606);
+ this.panel2.TabIndex = 12;
+ //
+ // txtSim
+ //
+ this.txtSim.Location = new System.Drawing.Point(36, 8);
+ this.txtSim.Name = "txtSim";
+ this.txtSim.Size = new System.Drawing.Size(126, 20);
+ this.txtSim.TabIndex = 1;
+ //
+ // label2
+ //
+ this.label2.AutoSize = true;
+ this.label2.Location = new System.Drawing.Point(3, 11);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(27, 13);
+ this.label2.TabIndex = 2;
+ this.label2.Text = "Sim:";
+ //
+ // label3
+ //
+ this.label3.AutoSize = true;
+ this.label3.Location = new System.Drawing.Point(5, 37);
+ this.label3.Name = "label3";
+ this.label3.Size = new System.Drawing.Size(17, 13);
+ this.label3.TabIndex = 3;
+ this.label3.Text = "X:";
+ //
+ // label4
+ //
+ this.label4.AutoSize = true;
+ this.label4.Location = new System.Drawing.Point(58, 37);
+ this.label4.Name = "label4";
+ this.label4.Size = new System.Drawing.Size(17, 13);
+ this.label4.TabIndex = 4;
+ this.label4.Text = "Y:";
+ //
+ // label5
+ //
+ this.label5.AutoSize = true;
+ this.label5.Location = new System.Drawing.Point(112, 37);
+ this.label5.Name = "label5";
+ this.label5.Size = new System.Drawing.Size(17, 13);
+ this.label5.TabIndex = 5;
+ this.label5.Text = "Z:";
+ //
+ // txtX
+ //
+ this.txtX.Location = new System.Drawing.Point(24, 34);
+ this.txtX.MaxLength = 3;
+ this.txtX.Name = "txtX";
+ this.txtX.Size = new System.Drawing.Size(30, 20);
+ this.txtX.TabIndex = 6;
+ this.txtX.Text = "128";
+ //
+ // txtY
+ //
+ this.txtY.Location = new System.Drawing.Point(78, 34);
+ this.txtY.MaxLength = 3;
+ this.txtY.Name = "txtY";
+ this.txtY.Size = new System.Drawing.Size(30, 20);
+ this.txtY.TabIndex = 7;
+ this.txtY.Text = "128";
+ //
+ // txtZ
+ //
+ this.txtZ.Location = new System.Drawing.Point(132, 34);
+ this.txtZ.MaxLength = 3;
+ this.txtZ.Name = "txtZ";
+ this.txtZ.Size = new System.Drawing.Size(30, 20);
+ this.txtZ.TabIndex = 8;
+ this.txtZ.Text = "0";
+ //
+ // cmdTeleport
+ //
+ this.cmdTeleport.Enabled = false;
+ this.cmdTeleport.Location = new System.Drawing.Point(87, 60);
+ this.cmdTeleport.Name = "cmdTeleport";
+ this.cmdTeleport.Size = new System.Drawing.Size(75, 23);
+ this.cmdTeleport.TabIndex = 9;
+ this.cmdTeleport.Text = "Teleport";
+ this.cmdTeleport.UseVisualStyleBackColor = true;
+ this.cmdTeleport.Click += new System.EventHandler(this.cmdTeleport_Click);
+ //
+ // frmBrowser
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(800, 612);
+ this.Controls.Add(this.tableLayoutPanel1);
+ this.Name = "frmBrowser";
+ this.Text = "World Browser";
+ this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.frmBrowser_FormClosing);
+ this.tableLayoutPanel1.ResumeLayout(false);
+ this.tableLayoutPanel1.PerformLayout();
+ this.panel1.ResumeLayout(false);
+ this.panel1.PerformLayout();
+ this.panel2.ResumeLayout(false);
+ this.panel2.PerformLayout();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
+ private Tao.Platform.Windows.SimpleOpenGlControl glControl;
+ private System.Windows.Forms.Panel panel1;
+ private System.Windows.Forms.TextBox txtPass;
+ private System.Windows.Forms.TextBox txtLast;
+ private System.Windows.Forms.TextBox txtFirst;
+ private System.Windows.Forms.Button cmdLogin;
+ private GlacialComponents.Controls.GlacialList lstDownloads;
+ private System.Windows.Forms.ProgressBar progPrims;
+ private System.Windows.Forms.Label lblPrims;
+ private System.Windows.Forms.ComboBox cboServer;
+ private System.Windows.Forms.Panel panel2;
+ private System.Windows.Forms.TextBox txtZ;
+ private System.Windows.Forms.TextBox txtY;
+ private System.Windows.Forms.TextBox txtX;
+ private System.Windows.Forms.Label label5;
+ private System.Windows.Forms.Label label4;
+ private System.Windows.Forms.Label label3;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.TextBox txtSim;
+ private System.Windows.Forms.Button cmdTeleport;
+
+ }
+}
\ No newline at end of file
diff --git a/Programs/PrimWorkshop/frmBrowser.cs b/Programs/PrimWorkshop/frmBrowser.cs
new file mode 100644
index 00000000..ac672de1
--- /dev/null
+++ b/Programs/PrimWorkshop/frmBrowser.cs
@@ -0,0 +1,1954 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.Windows.Forms;
+using System.IO;
+using System.Runtime.InteropServices;
+using Tao.OpenGl;
+using Tao.Platform.Windows;
+using ICSharpCode.SharpZipLib.Zip;
+using libsecondlife;
+using libsecondlife.StructuredData;
+using libsecondlife.Imaging;
+using libsecondlife.Rendering;
+
+namespace primpreview
+{
+ public partial class frmBrowser : Form
+ {
+ const float DEG_TO_RAD = 0.0174532925f;
+ const uint TERRAIN_START = (uint)Int32.MaxValue + 1;
+
+ ContextMenu ExportPrimMenu;
+ ContextMenu ExportTerrainMenu;
+
+ SecondLife Client;
+ Camera Camera;
+ Dictionary RenderFoliageList = new Dictionary();
+ Dictionary RenderPrimList = new Dictionary();
+ Dictionary DownloadList = new Dictionary();
+ EventHandler IdleEvent;
+
+ System.Timers.Timer ProgressTimer;
+ int TotalPrims;
+
+ // Textures
+ TexturePipeline TextureDownloader;
+ Dictionary Textures = new Dictionary();
+
+ // Terrain
+ float MaxHeight = 0.1f;
+ libsecondlife.TerrainManager.Patch[,] Heightmap;
+ HeightmapLookupValue[] LookupHeightTable;
+
+ // Picking globals
+ bool Clicked = false;
+ int ClickX = 0;
+ int ClickY = 0;
+ uint LastHit = 0;
+
+ //
+ LLVector3 PivotPosition = LLVector3.Zero;
+ bool Pivoting = false;
+ Point LastPivot;
+
+ //
+ const int SELECT_BUFSIZE = 512;
+ uint[] SelectBuffer = new uint[SELECT_BUFSIZE];
+
+ //
+ NativeMethods.Message msg;
+ private bool AppStillIdle
+ {
+ get { return !NativeMethods.PeekMessage(out msg, IntPtr.Zero, 0, 0, 0); }
+ }
+
+ ///
+ /// Default constructor
+ ///
+ public frmBrowser()
+ {
+ InitializeComponent();
+
+ // Setup OpenGL
+ glControl.InitializeContexts();
+ glControl.SwapBuffers();
+ glControl.MouseWheel += new MouseEventHandler(glControl_MouseWheel);
+
+ // Login server URLs
+ cboServer.Items.Add(Settings.AGNI_LOGIN_SERVER);
+ cboServer.Items.Add(Settings.ADITI_LOGIN_SERVER);
+ cboServer.Items.Add("http://osgrid.org:8002/");
+ cboServer.SelectedIndex = 0;
+
+ // Context menus
+ ExportPrimMenu = new ContextMenu();
+ ExportPrimMenu.MenuItems.Add("Download", new EventHandler(DownloadMenu_Clicked));
+ ExportPrimMenu.MenuItems.Add("Download All Objects", new EventHandler(DownloadAllMenu_Clicked));
+ ExportTerrainMenu = new ContextMenu();
+ ExportTerrainMenu.MenuItems.Add("Teleport", new EventHandler(TeleportMenu_Clicked));
+ ExportTerrainMenu.MenuItems.Add("Export Terrain", new EventHandler(ExportTerrainMenu_Clicked));
+ ExportTerrainMenu.MenuItems.Add("Import Object", new EventHandler(ImportObjectMenu_Clicked));
+ ExportTerrainMenu.MenuItems.Add("Import Sim", new EventHandler(ImportSimMenu_Clicked));
+
+ // Setup a timer for updating the progress bar
+ ProgressTimer = new System.Timers.Timer(250);
+ ProgressTimer.Elapsed +=
+ delegate(object sender, System.Timers.ElapsedEventArgs e)
+ {
+ UpdatePrimProgress();
+ };
+ ProgressTimer.Start();
+
+ IdleEvent = new EventHandler(Application_Idle);
+ Application.Idle += IdleEvent;
+
+ // Show a flat sim before login so the screen isn't so boring
+ InitOpenGL();
+ InitCamera();
+ InitHeightmap();
+
+ glControl_Resize(null, null);
+ }
+
+ private void InitLists()
+ {
+ TotalPrims = 0;
+
+ lock (Textures)
+ {
+ foreach (TextureInfo tex in Textures.Values)
+ {
+ int id = tex.ID;
+ Gl.glDeleteTextures(1, ref id);
+ }
+
+ Textures.Clear();
+ }
+
+ lock (RenderPrimList) RenderPrimList.Clear();
+ lock (RenderFoliageList) RenderFoliageList.Clear();
+ }
+
+ private void InitializeObjects()
+ {
+ InitHeightmap();
+ InitLists();
+
+ if (DownloadList != null)
+ lock (DownloadList)
+ DownloadList.Clear();
+
+ // Initialize the SL client
+ Client = new SecondLife();
+ Client.Settings.MULTIPLE_SIMS = false;
+ Client.Settings.ALWAYS_DECODE_OBJECTS = true;
+ Client.Settings.ALWAYS_REQUEST_OBJECTS = true;
+ Client.Settings.SEND_AGENT_UPDATES = true;
+ Client.Settings.DISABLE_AGENT_UPDATE_DUPLICATE_CHECK = true;
+ Client.Settings.USE_TEXTURE_CACHE = true;
+ Client.Settings.TEXTURE_CACHE_DIR = Application.StartupPath + System.IO.Path.DirectorySeparatorChar + "cache";
+ Client.Settings.ALWAYS_REQUEST_PARCEL_ACL = false;
+ Client.Settings.ALWAYS_REQUEST_PARCEL_DWELL = false;
+ // Crank up the throttle on texture downloads
+ Client.Throttle.Texture = 446000.0f;
+
+ // FIXME: Write our own avatar tracker so we don't double store prims
+ Client.Settings.OBJECT_TRACKING = false; // We use our own object tracking system
+ Client.Settings.AVATAR_TRACKING = true; //but we want to use the libsl avatar system
+
+ Client.Network.OnLogin += new NetworkManager.LoginCallback(Network_OnLogin);
+ Client.Network.OnDisconnected += new NetworkManager.DisconnectedCallback(Network_OnDisconnected);
+ Client.Network.OnCurrentSimChanged += new NetworkManager.CurrentSimChangedCallback(Network_OnCurrentSimChanged);
+ Client.Network.OnEventQueueRunning += new NetworkManager.EventQueueRunningCallback(Network_OnEventQueueRunning);
+ Client.Objects.OnNewPrim += new ObjectManager.NewPrimCallback(Objects_OnNewPrim);
+ Client.Objects.OnNewFoliage += new ObjectManager.NewFoliageCallback(Objects_OnNewFoliage);
+ Client.Objects.OnObjectKilled += new ObjectManager.KillObjectCallback(Objects_OnObjectKilled);
+ Client.Terrain.OnLandPatch += new TerrainManager.LandPatchCallback(Terrain_OnLandPatch);
+ Client.Parcels.OnSimParcelsDownloaded += new ParcelManager.SimParcelsDownloaded(Parcels_OnSimParcelsDownloaded);
+
+ // Initialize the texture download pipeline
+ if (TextureDownloader != null)
+ TextureDownloader.Shutdown();
+ TextureDownloader = new TexturePipeline(Client);
+ TextureDownloader.OnDownloadFinished += new TexturePipeline.DownloadFinishedCallback(TextureDownloader_OnDownloadFinished);
+ TextureDownloader.OnDownloadProgress += new TexturePipeline.DownloadProgressCallback(TextureDownloader_OnDownloadProgress);
+
+ // Initialize the camera object
+ InitCamera();
+
+ // Setup the libsl camera to match our Camera struct
+ UpdateCamera();
+ glControl_Resize(null, null);
+
+ /*
+ // Enable lighting
+ Gl.glEnable(Gl.GL_LIGHTING);
+ Gl.glEnable(Gl.GL_LIGHT0);
+ float[] lightPosition = { 128.0f, 64.0f, 96.0f, 0.0f };
+ Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_POSITION, lightPosition);
+
+ // Setup ambient property
+ float[] ambientLight = { 0.2f, 0.2f, 0.2f, 0.0f };
+ Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_AMBIENT, ambientLight);
+
+ // Setup specular property
+ float[] specularLight = { 0.5f, 0.5f, 0.5f, 0.0f };
+ Gl.glLightfv(Gl.GL_LIGHT0, Gl.GL_SPECULAR, specularLight);
+ */
+ }
+
+ private void InitOpenGL()
+ {
+ Gl.glShadeModel(Gl.GL_SMOOTH);
+
+ Gl.glClearDepth(1.0f);
+ Gl.glEnable(Gl.GL_DEPTH_TEST);
+ Gl.glDepthMask(Gl.GL_TRUE);
+ Gl.glDepthFunc(Gl.GL_LEQUAL);
+ Gl.glHint(Gl.GL_PERSPECTIVE_CORRECTION_HINT, Gl.GL_NICEST);
+ }
+
+ private void InitHeightmap()
+ {
+ // Initialize the heightmap
+ Heightmap = new TerrainManager.Patch[16, 16];
+ for (int y = 0; y < 16; y++)
+ {
+ for (int x = 0; x < 16; x++)
+ {
+ Heightmap[y, x] = new TerrainManager.Patch();
+ Heightmap[y, x].Heightmap = new float[16 * 16];
+ }
+ }
+
+ // Speed up terrain exports with a lookup table
+ LookupHeightTable = new HeightmapLookupValue[256 * 256];
+ for (int i = 0; i < 256; i++)
+ {
+ for (int j = 0; j < 256; j++)
+ {
+ LookupHeightTable[i + (j * 256)] = new HeightmapLookupValue(i + (j * 256), ((float)i * ((float)j / 127.0f)));
+ }
+ }
+ Array.Sort(LookupHeightTable);
+ }
+
+ private void InitCamera()
+ {
+ Camera = new Camera();
+ Camera.Position = new LLVector3(128f, -192f, 90f);
+ Camera.FocalPoint = new LLVector3(128f, 128f, 0f);
+ Camera.Zoom = 1.0d;
+ Camera.Far = 512.0d;
+ }
+
+ private void UpdatePrimProgress()
+ {
+ if (this.InvokeRequired)
+ {
+ BeginInvoke((MethodInvoker)delegate() { UpdatePrimProgress(); });
+ }
+ else
+ {
+ try
+ {
+ if (RenderPrimList != null && RenderFoliageList != null)
+ {
+ int count = RenderPrimList.Count + RenderFoliageList.Count;
+
+ lblPrims.Text = String.Format("Prims: {0} / {1}", count, TotalPrims);
+ progPrims.Maximum = (TotalPrims > count) ? TotalPrims : count;
+ progPrims.Value = count;
+ }
+ else
+ {
+ lblPrims.Text = String.Format("Prims: 0 / {0}", TotalPrims);
+ progPrims.Maximum = TotalPrims;
+ progPrims.Value = 0;
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine(ex);
+ }
+ }
+ }
+
+ private void UpdateCamera()
+ {
+ if (Client != null)
+ {
+ Client.Self.Movement.Camera.LookAt(Camera.Position, Camera.FocalPoint);
+ Client.Self.Movement.Camera.Far = (float)Camera.Far;
+ }
+
+ Gl.glPushMatrix();
+ Gl.glMatrixMode(Gl.GL_PROJECTION);
+ Gl.glLoadIdentity();
+
+ SetPerspective();
+
+ Gl.glMatrixMode(Gl.GL_MODELVIEW);
+ Gl.glPopMatrix();
+ }
+
+ private bool ExportObject(RenderablePrim parent, string fileName, out int prims, out int textures, out string error)
+ {
+ // Build a list of primitives (parent+children) to export
+ List primList = new List();
+ primList.Add(parent.Prim);
+
+ lock (RenderPrimList)
+ {
+ foreach (RenderablePrim render in RenderPrimList.Values)
+ {
+ if (render.Prim.ParentID == parent.Prim.LocalID)
+ primList.Add(render.Prim);
+ }
+ }
+
+ return ExportObjects(primList, fileName, out prims, out textures, out error);
+ }
+
+ private bool ExportSim(string fileName, out int prims, out int textures, out string error)
+ {
+ // Add all of the prims in this sim to the export list
+ List primList = new List();
+
+ lock (RenderPrimList)
+ {
+ foreach (RenderablePrim render in RenderPrimList.Values)
+ {
+ primList.Add(render.Prim);
+ }
+ }
+
+ return ExportObjects(primList, fileName, out prims, out textures, out error);
+ }
+
+ private bool ExportObjects(List primList, string fileName, out int prims, out int textures, out string error)
+ {
+ List textureList = new List();
+ prims = 0;
+ textures = 0;
+
+ // Write the LLSD to the hard drive in XML format
+ string output = LLSDParser.SerializeXmlString(Helpers.PrimListToLLSD(primList));
+ try
+ {
+ // Create a temporary directory
+ string tempPath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetRandomFileName());
+ Directory.CreateDirectory(tempPath);
+
+ // Write the prim XML file
+ File.WriteAllText(System.IO.Path.Combine(tempPath, "prims.xml"), output);
+ prims = primList.Count;
+
+ // Build a list of all the referenced textures in this prim list
+ foreach (Primitive prim in primList)
+ {
+ for (int i = 0; i < prim.Textures.FaceTextures.Length; i++)
+ {
+ LLObject.TextureEntryFace face = prim.Textures.FaceTextures[i];
+ if (face != null && face.TextureID != LLUUID.Zero && face.TextureID != LLObject.TextureEntry.WHITE_TEXTURE)
+ {
+ if (!textureList.Contains(face.TextureID))
+ textureList.Add(face.TextureID);
+ }
+ }
+ }
+
+ // Copy all of relevant textures from the cache to the temp directory
+ foreach (LLUUID texture in textureList)
+ {
+ string tempFileName = Client.Assets.Cache.ImageFileName(texture);
+
+ if (!String.IsNullOrEmpty(tempFileName))
+ {
+ File.Copy(tempFileName, System.IO.Path.Combine(tempPath, texture.ToString() + ".jp2"));
+ ++textures;
+ }
+ else
+ {
+ Console.WriteLine("Missing texture file during download: " + texture.ToString());
+ }
+ }
+
+ // Zip up the directory
+ string[] filenames = Directory.GetFiles(tempPath);
+ using (ZipOutputStream s = new ZipOutputStream(File.Create(fileName)))
+ {
+ s.SetLevel(9);
+ byte[] buffer = new byte[4096];
+
+ foreach (string file in filenames)
+ {
+ ZipEntry entry = new ZipEntry(System.IO.Path.GetFileName(file));
+ entry.DateTime = DateTime.Now;
+ s.PutNextEntry(entry);
+
+ using (FileStream fs = File.OpenRead(file))
+ {
+ int sourceBytes;
+ do
+ {
+ sourceBytes = fs.Read(buffer, 0, buffer.Length);
+ s.Write(buffer, 0, sourceBytes);
+ } while (sourceBytes > 0);
+ }
+ }
+
+ s.Finish();
+ s.Close();
+ }
+
+ error = null;
+ return true;
+ }
+ catch (Exception ex)
+ {
+ error = ex.Message;
+ return false;
+ }
+ }
+
+ private List ImportObjects(string fileName, out string tempPath, out string error)
+ {
+ tempPath = null;
+ error = null;
+ string primFile = null;
+
+ try
+ {
+ // Create a temporary directory
+ tempPath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetRandomFileName());
+ Directory.CreateDirectory(tempPath);
+
+ // Unzip the primpackage
+ using (ZipInputStream s = new ZipInputStream(File.OpenRead(fileName)))
+ {
+ ZipEntry theEntry;
+
+ // Loop through and confirm there is a prims.xml file
+ while ((theEntry = s.GetNextEntry()) != null)
+ {
+ if (String.Equals("prims.xml", theEntry.Name.ToLower()))
+ {
+ primFile = theEntry.Name;
+ break;
+ }
+ }
+
+ if (primFile != null)
+ {
+ // Prepend the path to the primFile (that will be created in the next loop)
+ primFile = System.IO.Path.Combine(tempPath, primFile);
+ }
+ else
+ {
+ // Didn't find a prims.xml file, bail out
+ error = "No prims.xml file found in the archive";
+ return null;
+ }
+
+ // Reset to the beginning of the zip file
+ s.Seek(0, SeekOrigin.Begin);
+
+ Logger.DebugLog("Unpacking archive to " + tempPath);
+
+ // Unzip all of the texture and xml files
+ while ((theEntry = s.GetNextEntry()) != null)
+ {
+ string directory = System.IO.Path.GetDirectoryName(theEntry.Name);
+ string file = System.IO.Path.GetFileName(theEntry.Name);
+
+ // Skip directories
+ if (directory.Length > 0)
+ continue;
+
+ if (!String.IsNullOrEmpty(file))
+ {
+ string filelow = file.ToLower();
+
+ if (filelow.EndsWith(".jp2") || filelow.EndsWith(".tga") || filelow.EndsWith(".xml"))
+ {
+ Logger.DebugLog("Unpacking " + file);
+
+ // Create the full path from the temp path and new filename
+ string filePath = System.IO.Path.Combine(tempPath, file);
+
+ using (FileStream streamWriter = File.Create(filePath))
+ {
+ const int READ_BUFFER_SIZE = 2048;
+ int size = READ_BUFFER_SIZE;
+ byte[] data = new byte[READ_BUFFER_SIZE];
+
+ while (true)
+ {
+ size = s.Read(data, 0, data.Length);
+ if (size > 0)
+ streamWriter.Write(data, 0, size);
+ else
+ break;
+ }
+ }
+ }
+ else
+ {
+ Logger.Log("Skipping file " + file, Helpers.LogLevel.Info);
+ }
+ }
+ }
+ }
+
+ // Decode the .prims file
+ string raw = File.ReadAllText(primFile);
+ LLSD llsd = LLSDParser.DeserializeXml(raw);
+ return Helpers.LLSDToPrimList(llsd);
+ }
+ catch (Exception e)
+ {
+ error = e.Message;
+ return null;
+ }
+ }
+
+ private void DownloadMenu_Clicked(object sender, EventArgs e)
+ {
+ // Confirm that there actually is a selected object
+ RenderablePrim parent;
+ if (RenderPrimList.TryGetValue(LastHit, out parent))
+ {
+ if (parent.Prim.ParentID == 0)
+ {
+ // Valid parent prim is selected, throw up the save file dialog
+ SaveFileDialog dialog = new SaveFileDialog();
+ dialog.Filter = "Prim Package (*.zip)|*.zip";
+
+ if (dialog.ShowDialog() == DialogResult.OK)
+ {
+ string error;
+ int prims, textures;
+ if (ExportObject(parent, dialog.FileName, out prims, out textures, out error))
+ MessageBox.Show(String.Format("Exported {0} prims and {1} textures", prims, textures));
+ else
+ MessageBox.Show("Export failed: " + error);
+ }
+ }
+ else
+ {
+ // This should have already been fixed in the picking processing code
+ Console.WriteLine("Download menu clicked when a child prim is selected!");
+ glControl.ContextMenu = null;
+ LastHit = 0;
+ }
+ }
+ else
+ {
+ Console.WriteLine("Download menu clicked when there is no selected prim!");
+ glControl.ContextMenu = null;
+ LastHit = 0;
+ }
+ }
+
+ private void DownloadAllMenu_Clicked(object sender, EventArgs e)
+ {
+ SaveFileDialog dialog = new SaveFileDialog();
+ dialog.Filter = "Prim Package (*.zip)|*.zip";
+
+ if (dialog.ShowDialog() == DialogResult.OK)
+ {
+ string error;
+ int prims, textures;
+ if (ExportSim(dialog.FileName, out prims, out textures, out error))
+ MessageBox.Show(String.Format("Exported {0} prims and {1} textures", prims, textures));
+ else
+ MessageBox.Show("Export failed: " + error);
+ }
+ }
+
+ private void ExportTerrainMenu_Clicked(object sender, EventArgs e)
+ {
+ // Valid parent prim is selected, throw up the save file dialog
+ SaveFileDialog dialog = new SaveFileDialog();
+ dialog.Filter = "Terrain RAW (*.raw)|*.raw";
+
+ if (dialog.ShowDialog() == DialogResult.OK)
+ {
+ BackgroundWorker worker = new BackgroundWorker();
+ worker.DoWork += delegate(object obj, DoWorkEventArgs args)
+ {
+ byte red, green, blue, alpha1, alpha2, alpha3, alpha4, alpha5, alpha6, alpha7, alpha8, alpha9, alpha10;
+
+ try
+ {
+ FileInfo file = new FileInfo(dialog.FileName);
+ FileStream s = file.Open(FileMode.OpenOrCreate, FileAccess.Write);
+ BinaryWriter binStream = new BinaryWriter(s);
+
+ for (int y = 0; y < 256; y++)
+ {
+ for (int x = 0; x < 256; x++)
+ {
+ int xBlock = x / 16;
+ int yBlock = y / 16;
+ int xOff = x - (xBlock * 16);
+ int yOff = y - (yBlock * 16);
+
+ float t = Heightmap[yBlock, xBlock].Heightmap[yOff * 16 + xOff];
+ //float min = Single.MaxValue;
+ int index = 0;
+
+ // The lookup table is pre-sorted, so we either find an exact match or
+ // the next closest (smaller) match with a binary search
+ index = Array.BinarySearch(LookupHeightTable, new HeightmapLookupValue(0, t));
+ if (index < 0)
+ index = ~index - 1;
+
+ index = LookupHeightTable[index].Index;
+
+ /*for (int i = 0; i < 65536; i++)
+ {
+ if (Math.Abs(t - LookupHeightTable[i].Value) < min)
+ {
+ min = Math.Abs(t - LookupHeightTable[i].Value);
+ index = i;
+ }
+ }*/
+
+ red = (byte)(index & 0xFF);
+ green = (byte)((index >> 8) & 0xFF);
+ blue = 20;
+ alpha1 = 0; // Land Parcels
+ alpha2 = 0; // For Sale Land
+ alpha3 = 0; // Public Edit Object
+ alpha4 = 0; // Public Edit Land
+ alpha5 = 255; // Safe Land
+ alpha6 = 255; // Flying Allowed
+ alpha7 = 255; // Create Landmark
+ alpha8 = 255; // Outside Scripts
+ alpha9 = red;
+ alpha10 = green;
+
+ binStream.Write(red);
+ binStream.Write(green);
+ binStream.Write(blue);
+ binStream.Write(alpha1);
+ binStream.Write(alpha2);
+ binStream.Write(alpha3);
+ binStream.Write(alpha4);
+ binStream.Write(alpha5);
+ binStream.Write(alpha6);
+ binStream.Write(alpha7);
+ binStream.Write(alpha8);
+ binStream.Write(alpha9);
+ binStream.Write(alpha10);
+ }
+ }
+
+ binStream.Close();
+ s.Close();
+
+ BeginInvoke((MethodInvoker)delegate() { System.Windows.Forms.MessageBox.Show("Exported heightmap"); });
+ }
+ catch (Exception ex)
+ {
+ BeginInvoke((MethodInvoker)delegate() { System.Windows.Forms.MessageBox.Show("Error exporting heightmap: " + ex.Message); });
+ }
+ };
+
+ worker.RunWorkerAsync();
+ }
+ }
+
+ private void TeleportMenu_Clicked(object sender, EventArgs e)
+ {
+ if (Client != null && Client.Network.CurrentSim != null)
+ {
+ if (LastHit >= TERRAIN_START)
+ {
+ // Determine which piece of terrain was clicked on
+ int y = (int)(LastHit - TERRAIN_START) / 16;
+ int x = (int)(LastHit - (TERRAIN_START + (y * 16)));
+
+ LLVector3 targetPos = new LLVector3(x * 16 + 8, y * 16 + 8, 0f);
+
+ Console.WriteLine("Starting local teleport to " + targetPos.ToString());
+ Client.Self.RequestTeleport(Client.Network.CurrentSim.Handle, targetPos);
+ }
+ else
+ {
+ // This shouldn't have happened...
+ glControl.ContextMenu = null;
+ }
+ }
+ }
+
+ private void ImportObjectMenu_Clicked(object sender, EventArgs e)
+ {
+ OpenFileDialog dialog = new OpenFileDialog();
+ dialog.Filter = "Prim Package (*.zip,*.primpackage)|*.zip;*.primpackage";
+
+ if (dialog.ShowDialog() == DialogResult.OK)
+ {
+ // FIXME: Disable any further imports or exports until this is finished
+
+ // Import the prims
+ string error, texturePath;
+ List primList = ImportObjects(dialog.FileName, out texturePath, out error);
+ if (primList != null)
+ {
+ // Determine the total height of the object
+ float minHeight = Single.MaxValue;
+ float maxHeight = Single.MinValue;
+ float totalHeight = 0f;
+
+ for (int i = 0; i < primList.Count; i++)
+ {
+ Primitive prim = primList[i];
+
+ // Find the largest scale dimension (quick cheat to avoid figuring in the rotation)
+ float scale = prim.Scale.X;
+ if (prim.Scale.Y > scale) scale = prim.Scale.Y;
+ if (prim.Scale.Z > scale) scale = prim.Scale.Z;
+
+ float top = prim.Position.Z + (scale * 0.5f);
+ float bottom = top - scale;
+
+ if (top > maxHeight) maxHeight = top;
+ if (bottom < minHeight) minHeight = bottom;
+ }
+
+ totalHeight = maxHeight - minHeight;
+
+ // Create a progress bar for the import process
+ ProgressBar prog = new ProgressBar();
+ prog.Minimum = 0;
+ prog.Maximum = primList.Count;
+ prog.Value = 0;
+
+ // List item
+ GlacialComponents.Controls.GLItem item = new GlacialComponents.Controls.GLItem();
+ item.SubItems[0].Text = "Import process";
+ item.SubItems[1].Control = prog;
+
+ lstDownloads.Items.Add(item);
+ lstDownloads.Invalidate();
+
+ // Start the import process in the background
+ BackgroundWorker worker = new BackgroundWorker();
+
+ worker.DoWork += delegate(object s, DoWorkEventArgs ea)
+ {
+ // Set the spot choosing state
+
+ // Wait for a spot to be chosen
+
+ // mouse2dto3d()
+
+ // Add (0, 0, totalHeight * 0.5f) to the clicked position
+
+ for (int i = 0; i < primList.Count; i++)
+ {
+ Primitive prim = primList[i];
+
+ for (int j = 0; j < prim.Textures.FaceTextures.Length; j++)
+ {
+ // Check if this texture exists
+
+ // If not, wait while it uploads
+ }
+
+ // Create this prim (using weird SL math to get the correct position)
+
+ // Wait for the callback to fire for this prim being created
+
+ // Add this prim's localID to a list
+
+ // Set any additional properties. If this is the root prim, do not apply rotation
+
+ // Update the progress bar
+ BeginInvoke((MethodInvoker)delegate() { prog.Value = i; });
+ }
+
+ // Link all of the prims together
+
+ // Apply root prim rotation
+ };
+
+ worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs ea)
+ {
+ BeginInvoke(
+ (MethodInvoker)delegate()
+ {
+ lstDownloads.Items.Remove(item);
+ lstDownloads.Invalidate();
+ });
+ };
+
+ worker.RunWorkerAsync();
+ }
+ else
+ {
+ // FIXME: Re-enable imports and exports
+
+ MessageBox.Show(error);
+ return;
+ }
+ }
+ }
+
+ private void ImportSimMenu_Clicked(object sender, EventArgs e)
+ {
+ }
+
+ private void SetPerspective()
+ {
+ Glu.gluPerspective(50.0d * Camera.Zoom, 1.0d, 0.1d, Camera.Far);
+ }
+
+ private void StartPicking(int cursorX, int cursorY)
+ {
+ int[] viewport = new int[4];
+
+ Gl.glSelectBuffer(SELECT_BUFSIZE, SelectBuffer);
+ Gl.glRenderMode(Gl.GL_SELECT);
+
+ Gl.glMatrixMode(Gl.GL_PROJECTION);
+ Gl.glPushMatrix();
+ Gl.glLoadIdentity();
+
+ Gl.glGetIntegerv(Gl.GL_VIEWPORT, viewport);
+ Glu.gluPickMatrix(cursorX, viewport[3] - cursorY, 5, 5, viewport);
+
+ SetPerspective();
+
+ Gl.glMatrixMode(Gl.GL_MODELVIEW);
+
+ Gl.glInitNames();
+ }
+
+ private void StopPicking()
+ {
+ int hits;
+
+ // Resotre the original projection matrix
+ Gl.glMatrixMode(Gl.GL_PROJECTION);
+ Gl.glPopMatrix();
+ Gl.glMatrixMode(Gl.GL_MODELVIEW);
+ Gl.glFlush();
+
+ // Return to normal rendering mode
+ hits = Gl.glRenderMode(Gl.GL_RENDER);
+
+ // If there are hits process them
+ if (hits != 0)
+ {
+ ProcessHits(hits, SelectBuffer);
+ }
+ else
+ {
+ LastHit = 0;
+ glControl.ContextMenu = null;
+ }
+ }
+
+ private void ProcessHits(int hits, uint[] selectBuffer)
+ {
+ uint names = 0;
+ uint numNames = 0;
+ uint minZ = 0xffffffff;
+ uint ptr = 0;
+ uint ptrNames = 0;
+
+ for (uint i = 0; i < hits; i++)
+ {
+ names = selectBuffer[ptr];
+ ++ptr;
+ if (selectBuffer[ptr] < minZ)
+ {
+ numNames = names;
+ minZ = selectBuffer[ptr];
+ ptrNames = ptr + 2;
+ }
+
+ ptr += names + 2;
+ }
+
+ ptr = ptrNames;
+
+ for (uint i = 0; i < numNames; i++, ptr++)
+ {
+ LastHit = selectBuffer[ptr];
+ }
+
+ if (LastHit >= TERRAIN_START)
+ {
+ // Terrain was clicked on, turn off the context menu
+ glControl.ContextMenu = ExportTerrainMenu;
+ }
+ else
+ {
+ RenderablePrim render;
+ if (RenderPrimList.TryGetValue(LastHit, out render))
+ {
+ if (render.Prim.ParentID == 0)
+ {
+ Camera.FocalPoint = render.Prim.Position;
+ UpdateCamera();
+ }
+ else
+ {
+ // See if we have the parent
+ RenderablePrim renderParent;
+ if (RenderPrimList.TryGetValue(render.Prim.ParentID, out renderParent))
+ {
+ // Turn on the context menu
+ glControl.ContextMenu = ExportPrimMenu;
+
+ // Change the clicked on prim to the parent. Camera position stays on the
+ // clicked child but the highlighting is applied to all the children
+ LastHit = renderParent.Prim.LocalID;
+
+ Camera.FocalPoint = renderParent.Prim.Position + render.Prim.Position;
+ UpdateCamera();
+ }
+ else
+ {
+ Console.WriteLine("Clicked on a child prim with no parent!");
+ LastHit = 0;
+ }
+ }
+ }
+ }
+ }
+
+ private void Objects_OnNewPrim(Simulator simulator, Primitive prim, ulong regionHandle, ushort timeDilation)
+ {
+ RenderablePrim render = new RenderablePrim();
+ render.Prim = prim;
+ render.Mesh = Render.GenerateMeshWithFaces(prim, 2.5f);
+
+ // Create a FaceData struct for each face that stores the 3D data
+ // in a Tao.OpenGL friendly format
+ for (int j = 0; j < render.Mesh.Faces.Count; j++)
+ {
+ Face face = render.Mesh.Faces[j];
+ FaceData data = new FaceData();
+
+ // Vertices for this face
+ data.Vertices = new float[face.Vertices.Count * 3];
+ for (int k = 0; k < face.Vertices.Count; k++)
+ {
+ data.Vertices[k * 3 + 0] = face.Vertices[k].Position.X;
+ data.Vertices[k * 3 + 1] = face.Vertices[k].Position.Y;
+ data.Vertices[k * 3 + 2] = face.Vertices[k].Position.Z;
+ }
+
+ // Indices for this face
+ data.Indices = face.Indices.ToArray();
+
+ // Texture transform for this face
+ LLObject.TextureEntryFace teFace = prim.Textures.GetFace((uint)j);
+ Render.TransformTexCoords(face.Vertices, face.Center, teFace);
+
+ // Texcoords for this face
+ data.TexCoords = new float[face.Vertices.Count * 2];
+ for (int k = 0; k < face.Vertices.Count; k++)
+ {
+ data.TexCoords[k * 2 + 0] = face.Vertices[k].TexCoord.X;
+ data.TexCoords[k * 2 + 1] = face.Vertices[k].TexCoord.Y;
+ }
+
+ // Texture for this face
+ if (teFace.TextureID != LLUUID.Zero &&
+ teFace.TextureID != LLObject.TextureEntry.WHITE_TEXTURE)
+ {
+ lock (Textures)
+ {
+ if (!Textures.ContainsKey(teFace.TextureID))
+ {
+ // We haven't constructed this image in OpenGL yet, get ahold of it
+ TextureDownloader.RequestTexture(teFace.TextureID);
+ }
+ }
+ }
+
+ // Set the UserData for this face to our FaceData struct
+ face.UserData = data;
+ render.Mesh.Faces[j] = face;
+ }
+
+ lock (RenderPrimList) RenderPrimList[prim.LocalID] = render;
+ }
+
+ private void Objects_OnNewFoliage(Simulator simulator, Primitive foliage, ulong regionHandle, ushort timeDilation)
+ {
+ lock (RenderFoliageList)
+ RenderFoliageList[foliage.LocalID] = foliage;
+ }
+
+ private void Objects_OnObjectKilled(Simulator simulator, uint objectID)
+ {
+ //
+ }
+
+ private void Parcels_OnSimParcelsDownloaded(Simulator simulator, InternalDictionary simParcels, int[,] parcelMap)
+ {
+ TotalPrims = 0;
+
+ simParcels.ForEach(
+ delegate(Parcel parcel)
+ {
+ TotalPrims += parcel.TotalPrims;
+ });
+
+ UpdatePrimProgress();
+ }
+
+ private void Terrain_OnLandPatch(Simulator simulator, int x, int y, int width, float[] data)
+ {
+ if (Client != null && Client.Network.CurrentSim == simulator)
+ {
+ Heightmap[y, x].Heightmap = data;
+ }
+
+ // Find the new max height
+ for (int i = 0; i < data.Length; i++)
+ {
+ if (data[i] > MaxHeight)
+ MaxHeight = data[i];
+ }
+ }
+
+ private void Network_OnLogin(LoginStatus login, string message)
+ {
+ if (login == LoginStatus.Success)
+ {
+ // Success!
+ }
+ else if (login == LoginStatus.Failed)
+ {
+ BeginInvoke(
+ (MethodInvoker)delegate()
+ {
+ MessageBox.Show(this, String.Format("Error logging in ({0}): {1}",
+ Client.Network.LoginErrorKey, Client.Network.LoginMessage));
+ cmdLogin.Text = "Login";
+ txtFirst.Enabled = txtLast.Enabled = txtPass.Enabled = true;
+ });
+ }
+ }
+
+ private void Network_OnDisconnected(NetworkManager.DisconnectType reason, string message)
+ {
+ BeginInvoke(
+ (MethodInvoker)delegate()
+ {
+ cmdTeleport.Enabled = false;
+ DoLogout();
+ });
+ }
+
+ private void Network_OnCurrentSimChanged(Simulator PreviousSimulator)
+ {
+ Console.WriteLine("CurrentSim set to " + Client.Network.CurrentSim + ", downloading parcel information");
+ Client.Parcels.RequestAllSimParcels(Client.Network.CurrentSim);
+
+ BeginInvoke((MethodInvoker)delegate() { txtSim.Text = Client.Network.CurrentSim.Name; });
+
+ InitHeightmap();
+ InitLists();
+
+ // Disable teleports until the new event queue comes online
+ if (!Client.Network.CurrentSim.Caps.IsEventQueueRunning)
+ BeginInvoke((MethodInvoker)delegate() { cmdTeleport.Enabled = false; });
+ }
+
+ private void Network_OnEventQueueRunning(Simulator simulator)
+ {
+ if (simulator == Client.Network.CurrentSim)
+ BeginInvoke((MethodInvoker)delegate() { cmdTeleport.Enabled = true; });
+ }
+
+ private void RenderScene()
+ {
+ try
+ {
+ Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);
+ Gl.glLoadIdentity();
+ Gl.glEnableClientState(Gl.GL_VERTEX_ARRAY);
+ Gl.glEnableClientState(Gl.GL_TEXTURE_COORD_ARRAY);
+
+ if (Clicked)
+ StartPicking(ClickX, ClickY);
+
+ // Setup wireframe or solid fill drawing mode
+ Gl.glPolygonMode(Gl.GL_FRONT, Gl.GL_LINE);
+
+ // Position the camera
+ Glu.gluLookAt(
+ Camera.Position.X, Camera.Position.Y, Camera.Position.Z,
+ Camera.FocalPoint.X, Camera.FocalPoint.Y, Camera.FocalPoint.Z,
+ 0f, 0f, 1f);
+
+ RenderSkybox();
+
+ // Push the world matrix
+ Gl.glPushMatrix();
+
+ RenderTerrain();
+ RenderPrims();
+ RenderAvatars();
+
+ Gl.glDisableClientState(Gl.GL_TEXTURE_COORD_ARRAY);
+ Gl.glDisableClientState(Gl.GL_VERTEX_ARRAY);
+
+ if (Clicked)
+ {
+ Clicked = false;
+ StopPicking();
+ }
+
+ // Pop the world matrix
+ Gl.glPopMatrix();
+ Gl.glFlush();
+
+ glControl.Invalidate();
+ }
+ catch (Exception)
+ {
+ }
+ }
+
+ static readonly LLVector3[] SkyboxVerts = new LLVector3[]
+ {
+ // Right side
+ new LLVector3( 10.0f, 10.0f, -10.0f ), //Top left
+ new LLVector3( 10.0f, 10.0f, 10.0f ), //Top right
+ new LLVector3( 10.0f, -10.0f, 10.0f ), //Bottom right
+ new LLVector3( 10.0f, -10.0f, -10.0f ), //Bottom left
+ // Left side
+ new LLVector3( -10.0f, 10.0f, 10.0f ), //Top left
+ new LLVector3( -10.0f, 10.0f, -10.0f ), //Top right
+ new LLVector3( -10.0f, -10.0f, -10.0f ), //Bottom right
+ new LLVector3( -10.0f, -10.0f, 10.0f ), //Bottom left
+ // Top side
+ new LLVector3( -10.0f, 10.0f, 10.0f ), //Top left
+ new LLVector3( 10.0f, 10.0f, 10.0f ), //Top right
+ new LLVector3( 10.0f, 10.0f, -10.0f ), //Bottom right
+ new LLVector3( -10.0f, 10.0f, -10.0f ), //Bottom left
+ // Bottom side
+ new LLVector3( -10.0f, -10.0f, -10.0f ), //Top left
+ new LLVector3( 10.0f, -10.0f, -10.0f ), //Top right
+ new LLVector3( 10.0f, -10.0f, 10.0f ), //Bottom right
+ new LLVector3( -10.0f, -10.0f, 10.0f ), //Bottom left
+ // Front side
+ new LLVector3( -10.0f, 10.0f, -10.0f ), //Top left
+ new LLVector3( 10.0f, 10.0f, -10.0f ), //Top right
+ new LLVector3( 10.0f, -10.0f, -10.0f ), //Bottom right
+ new LLVector3( -10.0f, -10.0f, -10.0f ), //Bottom left
+ // Back side
+ new LLVector3( 10.0f, 10.0f, 10.0f ), //Top left
+ new LLVector3( -10.0f, 10.0f, 10.0f ), //Top right
+ new LLVector3( -10.0f, -10.0f, 10.0f ), //Bottom right
+ new LLVector3( 10.0f, -10.0f, 10.0f ), //Bottom left
+ };
+
+ private void RenderSkybox()
+ {
+ //Gl.glTranslatef(0f, 0f, 0f);
+ }
+
+ private void RenderTerrain()
+ {
+ if (Heightmap != null)
+ {
+ int i = 0;
+
+ // No texture
+ Gl.glBindTexture(Gl.GL_TEXTURE_2D, 0);
+
+ for (int hy = 0; hy < 16; hy++)
+ {
+ for (int hx = 0; hx < 16; hx++)
+ {
+ uint patchName = (uint)(TERRAIN_START + i);
+ Gl.glPushName(patchName);
+ ++i;
+
+ // Check if this patch is currently selected
+ bool selected = (LastHit == patchName);
+
+ for (int y = 0; y < 15; y++)
+ {
+ Gl.glBegin(Gl.GL_TRIANGLE_STRIP);
+
+ for (int x = 0; x < 15; x++)
+ {
+ // Vertex 0
+ float height = Heightmap[hy, hx].Heightmap[y * 16 + x];
+ float color = height / MaxHeight;
+ float red = (selected) ? 1f : color;
+
+ Gl.glColor3f(red, color, color);
+ Gl.glTexCoord2f(0f, 0f);
+ Gl.glVertex3f(hx * 16 + x, hy * 16 + y, height);
+
+ // Vertex 1
+ height = Heightmap[hy, hx].Heightmap[y * 16 + (x + 1)];
+ color = height / MaxHeight;
+ red = (selected) ? 1f : color;
+
+ Gl.glColor3f(red, color, color);
+ Gl.glTexCoord2f(1f, 0f);
+ Gl.glVertex3f(hx * 16 + x + 1, hy * 16 + y, height);
+
+ // Vertex 2
+ height = Heightmap[hy, hx].Heightmap[(y + 1) * 16 + x];
+ color = height / MaxHeight;
+ red = (selected) ? 1f : color;
+
+ Gl.glColor3f(red, color, color);
+ Gl.glTexCoord2f(0f, 1f);
+ Gl.glVertex3f(hx * 16 + x, hy * 16 + y + 1, height);
+
+ // Vertex 3
+ height = Heightmap[hy, hx].Heightmap[(y + 1) * 16 + (x + 1)];
+ color = height / MaxHeight;
+ red = (selected) ? 1f : color;
+
+ Gl.glColor3f(red, color, color);
+ Gl.glTexCoord2f(1f, 1f);
+ Gl.glVertex3f(hx * 16 + x + 1, hy * 16 + y + 1, height);
+ }
+
+ Gl.glEnd();
+ }
+
+ Gl.glPopName();
+ }
+ }
+ }
+ }
+
+ int[] CubeMapDefines = new int[]
+ {
+ Gl.GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
+ Gl.GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
+ Gl.GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
+ Gl.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
+ Gl.GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
+ Gl.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB
+ };
+
+ private void RenderPrims()
+ {
+ if (RenderPrimList != null && RenderPrimList.Count > 0)
+ {
+ Gl.glEnable(Gl.GL_TEXTURE_2D);
+
+ lock (RenderPrimList)
+ {
+ bool firstPass = true;
+ Gl.glDisable(Gl.GL_BLEND);
+ Gl.glEnable(Gl.GL_DEPTH_TEST);
+
+StartRender:
+
+ foreach (RenderablePrim render in RenderPrimList.Values)
+ {
+ RenderablePrim parentRender = RenderablePrim.Empty;
+ Primitive prim = render.Prim;
+
+ if (prim.ParentID != 0)
+ {
+ // Get the parent reference
+ if (!RenderPrimList.TryGetValue(prim.ParentID, out parentRender))
+ {
+ // Can't render a child with no parent prim, skip it
+ continue;
+ }
+ }
+
+ Gl.glPushName(prim.LocalID);
+ Gl.glPushMatrix();
+
+ if (prim.ParentID != 0)
+ {
+ // Child prim
+ Primitive parent = parentRender.Prim;
+
+ // Apply parent translation and rotation
+ Gl.glMultMatrixf(Math3D.CreateTranslationMatrix(parent.Position));
+ Gl.glMultMatrixf(Math3D.CreateRotationMatrix(parent.Rotation));
+ }
+
+ // Apply prim translation and rotation
+ Gl.glMultMatrixf(Math3D.CreateTranslationMatrix(prim.Position));
+ Gl.glMultMatrixf(Math3D.CreateRotationMatrix(prim.Rotation));
+
+ // Scale the prim
+ Gl.glScalef(prim.Scale.X, prim.Scale.Y, prim.Scale.Z);
+
+ // Draw the prim faces
+ for (int j = 0; j < render.Mesh.Faces.Count; j++)
+ {
+ Face face = render.Mesh.Faces[j];
+ FaceData data = (FaceData)face.UserData;
+ LLColor color = face.TextureFace.RGBA;
+ bool alpha = false;
+ int textureID = 0;
+
+ if (color.A < 1.0f)
+ alpha = true;
+
+ #region Texturing
+
+ TextureInfo info;
+ if (Textures.TryGetValue(face.TextureFace.TextureID, out info))
+ {
+ if (info.Alpha)
+ alpha = true;
+
+ textureID = info.ID;
+
+ // Enable texturing for this face
+ Gl.glPolygonMode(Gl.GL_FRONT_AND_BACK, Gl.GL_FILL);
+ }
+ else
+ {
+ if (face.TextureFace.TextureID == LLObject.TextureEntry.WHITE_TEXTURE ||
+ face.TextureFace.TextureID == LLUUID.Zero)
+ {
+ Gl.glPolygonMode(Gl.GL_FRONT, Gl.GL_FILL);
+ }
+ else
+ {
+ Gl.glPolygonMode(Gl.GL_FRONT, Gl.GL_LINE);
+ }
+ }
+
+ if (firstPass && !alpha || !firstPass && alpha)
+ {
+ // Color this prim differently based on whether it is selected or not
+ if (LastHit == prim.LocalID || (LastHit != 0 && LastHit == prim.ParentID))
+ {
+ Gl.glColor4f(1f, color.G * 0.3f, color.B * 0.3f, color.A);
+ }
+ else
+ {
+ Gl.glColor4f(color.R, color.G, color.B, color.A);
+ }
+
+ // Bind the texture
+ Gl.glBindTexture(Gl.GL_TEXTURE_2D, textureID);
+
+ Gl.glTexCoordPointer(2, Gl.GL_FLOAT, 0, data.TexCoords);
+ Gl.glVertexPointer(3, Gl.GL_FLOAT, 0, data.Vertices);
+ Gl.glDrawElements(Gl.GL_TRIANGLES, data.Indices.Length, Gl.GL_UNSIGNED_SHORT, data.Indices);
+ }
+
+ #endregion Texturing
+ }
+
+ Gl.glPopMatrix();
+ Gl.glPopName();
+ }
+
+ if (firstPass)
+ {
+ firstPass = false;
+ Gl.glEnable(Gl.GL_BLEND);
+ Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA);
+ //Gl.glDisable(Gl.GL_DEPTH_TEST);
+
+ goto StartRender;
+ }
+ }
+
+ Gl.glEnable(Gl.GL_DEPTH_TEST);
+ Gl.glDisable(Gl.GL_TEXTURE_2D);
+ }
+ }
+
+ private void RenderAvatars()
+ {
+ if (Client != null && Client.Network.CurrentSim != null)
+ {
+ Gl.glColor3f(0f, 1f, 0f);
+
+ Client.Network.CurrentSim.ObjectsAvatars.ForEach(
+ delegate(Avatar avatar)
+ {
+ Gl.glPushMatrix();
+ Gl.glTranslatef(avatar.Position.X, avatar.Position.Y, avatar.Position.Z);
+
+ Glu.GLUquadric quad = Glu.gluNewQuadric();
+ Glu.gluSphere(quad, 1.0d, 10, 10);
+ Glu.gluDeleteQuadric(quad);
+
+ Gl.glPopMatrix();
+ }
+ );
+
+ Gl.glColor3f(1f, 1f, 1f);
+ }
+ }
+
+ #region Texture Downloading
+
+ private void TextureDownloader_OnDownloadFinished(LLUUID id, bool success)
+ {
+ bool alpha = false;
+ ManagedImage imgData = null;
+ byte[] raw = null;
+
+ try
+ {
+ // Load the image off the disk
+ if (success)
+ {
+ ImageDownload download = TextureDownloader.GetTextureToRender(id);
+ if (OpenJPEG.DecodeToImage(download.AssetData, out imgData))
+ {
+ raw = imgData.ExportRaw();
+
+ if ((imgData.Channels & ManagedImage.ImageChannels.Alpha) != 0)
+ alpha = true;
+ }
+ else
+ {
+ success = false;
+ Console.WriteLine("Failed to decode texture");
+ }
+ }
+
+ // Make sure the OpenGL commands run on the main thread
+ BeginInvoke(
+ (MethodInvoker)delegate()
+ {
+ if (success)
+ {
+ int textureID = 0;
+
+ try
+ {
+ Gl.glGenTextures(1, out textureID);
+ Gl.glBindTexture(Gl.GL_TEXTURE_2D, textureID);
+
+ Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_LINEAR_MIPMAP_NEAREST); //Gl.GL_NEAREST);
+ Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR);
+ Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_REPEAT);
+ Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_REPEAT);
+ Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_GENERATE_MIPMAP, Gl.GL_TRUE); //Gl.GL_FALSE);
+
+ //Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGBA, bitmap.Width, bitmap.Height, 0, Gl.GL_BGRA, Gl.GL_UNSIGNED_BYTE,
+ // bitmapData.Scan0);
+ //int error = Gl.glGetError();
+
+ int error = Glu.gluBuild2DMipmaps(Gl.GL_TEXTURE_2D, Gl.GL_RGBA, imgData.Width, imgData.Height, Gl.GL_BGRA,
+ Gl.GL_UNSIGNED_BYTE, raw);
+
+ if (error == 0)
+ {
+ Textures[id] = new TextureInfo(textureID, alpha);
+ Console.WriteLine("Created OpenGL texture for " + id.ToString());
+ }
+ else
+ {
+ Textures[id] = new TextureInfo(0, false);
+ Console.WriteLine("Error creating OpenGL texture: " + error);
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine(ex);
+ }
+ }
+
+ // Remove this image from the download listbox
+ lock (DownloadList)
+ {
+ GlacialComponents.Controls.GLItem item;
+ if (DownloadList.TryGetValue(id, out item))
+ {
+ DownloadList.Remove(id);
+ try { lstDownloads.Items.Remove(item); }
+ catch (Exception) { }
+ lstDownloads.Invalidate();
+ }
+ }
+ });
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine(ex);
+ }
+ }
+
+ private void TextureDownloader_OnDownloadProgress(LLUUID image, int recieved, int total)
+ {
+ lock (DownloadList)
+ {
+ GlacialComponents.Controls.GLItem item;
+ if (DownloadList.TryGetValue(image, out item))
+ {
+ // Update an existing item
+ BeginInvoke(
+ (MethodInvoker)delegate()
+ {
+ ProgressBar prog = (ProgressBar)item.SubItems[1].Control;
+ if (total >= recieved)
+ prog.Value = (int)Math.Round((((double)recieved / (double)total) * 100.0d));
+ });
+ }
+ else
+ {
+ // Progress bar
+ ProgressBar prog = new ProgressBar();
+ prog.Minimum = 0;
+ prog.Maximum = 100;
+ if (total >= recieved)
+ prog.Value = (int)Math.Round((((double)recieved / (double)total) * 100.0d));
+ else
+ prog.Value = 0;
+
+ // List item
+ item = new GlacialComponents.Controls.GLItem();
+ item.SubItems[0].Text = image.ToString();
+ item.SubItems[1].Control = prog;
+
+ DownloadList[image] = item;
+
+ BeginInvoke(
+ (MethodInvoker)delegate()
+ {
+ lstDownloads.Items.Add(item);
+ lstDownloads.Invalidate();
+ });
+ }
+ }
+ }
+
+ #endregion Texture Downloading
+
+ private void frmBrowser_FormClosing(object sender, FormClosingEventArgs e)
+ {
+ DoLogout();
+
+ Application.Idle -= IdleEvent;
+ }
+
+ private void Application_Idle(object sender, EventArgs e)
+ {
+ while (AppStillIdle)
+ {
+ RenderScene();
+ }
+ }
+
+ private void cmdLogin_Click(object sender, EventArgs e)
+ {
+ if (cmdLogin.Text == "Login")
+ {
+ // Check that all the input boxes are filled in
+ if (txtFirst.Text.Length == 0)
+ {
+ txtFirst.Select();
+ return;
+ }
+ if (txtLast.Text.Length == 0)
+ {
+ txtLast.Select();
+ return;
+ }
+ if (txtPass.Text.Length == 0)
+ {
+ txtPass.Select();
+ return;
+ }
+
+ // Disable input controls
+ txtFirst.Enabled = txtLast.Enabled = txtPass.Enabled = false;
+ cmdLogin.Text = "Logout";
+
+ // Sanity check that we aren't already logged in
+ if (Client != null && Client.Network.Connected)
+ {
+ Client.Network.Logout();
+ }
+
+ // Re-initialize everything
+ InitializeObjects();
+
+ // Start the login
+ LoginParams loginParams = Client.Network.DefaultLoginParams(txtFirst.Text, txtLast.Text,
+ txtPass.Text, "Prim Preview", "0.0.1");
+ if (!String.IsNullOrEmpty((string)cboServer.Items[cboServer.SelectedIndex]))
+ loginParams.URI = (string)cboServer.Items[cboServer.SelectedIndex];
+ Client.Network.BeginLogin(loginParams);
+ }
+ else
+ {
+ DoLogout();
+ }
+ }
+
+ private void DoLogout()
+ {
+ if (Client != null && Client.Network.Connected)
+ {
+ Client.Network.Logout();
+ return;
+ }
+
+ // Clear the download list
+ lstDownloads.Items.Clear();
+
+ // Set the login button back to login state
+ cmdLogin.Text = "Login";
+
+ // Shutdown the texture downloader
+ if (TextureDownloader != null)
+ TextureDownloader.Shutdown();
+
+ // Enable input controls
+ txtFirst.Enabled = txtLast.Enabled = txtPass.Enabled = true;
+ }
+
+ private void glControl_Resize(object sender, EventArgs e)
+ {
+ Gl.glClearColor(0.39f, 0.58f, 0.93f, 1.0f);
+
+ Gl.glViewport(0, 0, glControl.Width, glControl.Height);
+
+ Gl.glPushMatrix();
+ Gl.glMatrixMode(Gl.GL_PROJECTION);
+ Gl.glLoadIdentity();
+
+ SetPerspective();
+
+ Gl.glMatrixMode(Gl.GL_MODELVIEW);
+ Gl.glPopMatrix();
+
+ // Set the center of the glControl as the default pivot point
+ LastPivot = glControl.PointToScreen(new Point(glControl.Width / 2, glControl.Height / 2));
+ }
+
+ private void glControl_MouseClick(object sender, MouseEventArgs e)
+ {
+ if ((Control.ModifierKeys & Keys.Alt) == 0 && e.Button == MouseButtons.Left)
+ {
+ // Only allow clicking if alt is not being held down
+ ClickX = e.X;
+ ClickY = e.Y;
+ Clicked = true;
+ }
+ }
+
+ private void glControl_MouseDown(object sender, MouseEventArgs e)
+ {
+ if ((Control.ModifierKeys & Keys.Alt) != 0 && LastHit > 0)
+ {
+ // Alt is held down and we have a valid target
+ Pivoting = true;
+ PivotPosition = Camera.FocalPoint;
+
+ Control control = (Control)sender;
+ LastPivot = control.PointToScreen(new Point(e.X, e.Y));
+ }
+ }
+
+ private void glControl_MouseMove(object sender, MouseEventArgs e)
+ {
+ if (Pivoting)
+ {
+ float a,x,y,z;
+
+ Control control = (Control)sender;
+ Point mouse = control.PointToScreen(new Point(e.X, e.Y));
+
+ // Calculate the deltas from the center of the control to the current position
+ int deltaX = (int)((mouse.X - LastPivot.X) * -0.5d);
+ int deltaY = (int)((mouse.Y - LastPivot.Y) * -0.5d);
+
+ // Translate so the focal point is the origin
+ LLVector3 altered = Camera.Position - Camera.FocalPoint;
+
+ // Rotate the translated point by deltaX
+ a = (float)deltaX * DEG_TO_RAD;
+ x = (float)((altered.X * Math.Cos(a)) - (altered.Y * Math.Sin(a)));
+ y = (float)((altered.X * Math.Sin(a)) + (altered.Y * Math.Cos(a)));
+
+ altered.X = x;
+ altered.Y = y;
+
+ // Rotate the translated point by deltaY
+ a = (float)deltaY * DEG_TO_RAD;
+ y = (float)((altered.Y * Math.Cos(a)) - (altered.Z * Math.Sin(a)));
+ z = (float)((altered.Y * Math.Sin(a)) + (altered.Z * Math.Cos(a)));
+
+ altered.Y = y;
+ altered.Z = z;
+
+ // Translate back to world space
+ altered += Camera.FocalPoint;
+
+ // Update the camera
+ Camera.Position = altered;
+ UpdateCamera();
+
+ // Update the pivot point
+ LastPivot = mouse;
+ }
+ }
+
+ private void glControl_MouseWheel(object sender, MouseEventArgs e)
+ {
+ /*if (e.Delta != 0)
+ {
+ Camera.Zoom = Camera.Zoom + (double)(e.Delta / 120) * -0.1d;
+ if (Camera.Zoom < 0.05d) Camera.Zoom = 0.05d;
+ UpdateCamera();
+ }*/
+
+ if (e.Delta != 0)
+ {
+ // Calculate the distance to move to/away
+ float dist = (float)(e.Delta / 120) * 10.0f;
+
+ if (LLVector3.Dist(Camera.Position, Camera.FocalPoint) > dist)
+ {
+ // Move closer or further away from the focal point
+ LLVector3 toFocal = Camera.FocalPoint - Camera.Position;
+ toFocal = LLVector3.Norm(toFocal);
+
+ toFocal = toFocal * dist;
+
+ Camera.Position += toFocal;
+ UpdateCamera();
+ }
+ }
+ }
+
+ private void glControl_MouseUp(object sender, MouseEventArgs e)
+ {
+ // Stop pivoting if we were previously
+ Pivoting = false;
+ }
+
+ private void txtLogin_Enter(object sender, EventArgs e)
+ {
+ TextBox input = (TextBox)sender;
+ input.SelectAll();
+ }
+
+ private void cmdTeleport_Click(object sender, EventArgs e)
+ {
+ if (!String.IsNullOrEmpty(txtSim.Text))
+ {
+ // Parse X/Y/Z
+ int x, y, z;
+ if (!Int32.TryParse(txtX.Text, out x))
+ {
+ txtX.SelectAll();
+ return;
+ }
+ if (!Int32.TryParse(txtY.Text, out y))
+ {
+ txtY.SelectAll();
+ return;
+ }
+ if (!Int32.TryParse(txtZ.Text, out z))
+ {
+ txtZ.SelectAll();
+ return;
+ }
+
+ string simName = txtSim.Text.Trim().ToLower();
+ LLVector3 position = new LLVector3(x, y, z);
+
+ if (Client != null && Client.Network.CurrentSim != null)
+ {
+ // Check for a local teleport to shortcut the process
+ if (simName == Client.Network.CurrentSim.Name.ToLower())
+ {
+ // Local teleport
+ Client.Self.RequestTeleport(Client.Network.CurrentSim.Handle, position);
+ }
+ else
+ {
+ // Cross-sim teleport
+ bool success = false;
+
+ BackgroundWorker worker = new BackgroundWorker();
+ worker.DoWork += delegate(object s, DoWorkEventArgs ea) { success = Client.Self.Teleport(simName, position); };
+ worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs ea)
+ {
+ BeginInvoke((MethodInvoker)
+ delegate()
+ {
+ if (!success)
+ System.Windows.Forms.MessageBox.Show("Teleport failed");
+ });
+ };
+
+ worker.RunWorkerAsync();
+ }
+ }
+ else
+ {
+ // Oops! How did the user click this...
+ cmdTeleport.Enabled = false;
+ }
+ }
+ }
+ }
+
+ public struct TextureInfo
+ {
+ /// OpenGL Texture ID
+ public int ID;
+ /// True if this texture has an alpha component
+ public bool Alpha;
+
+ public TextureInfo(int id, bool alpha)
+ {
+ ID = id;
+ Alpha = alpha;
+ }
+ }
+
+ public struct HeightmapLookupValue : IComparable
+ {
+ public int Index;
+ public float Value;
+
+ public HeightmapLookupValue(int index, float value)
+ {
+ Index = index;
+ Value = value;
+ }
+
+ public int CompareTo(HeightmapLookupValue val)
+ {
+ return Value.CompareTo(val.Value);
+ }
+ }
+
+ public struct RenderablePrim
+ {
+ public Primitive Prim;
+ public PrimMeshMultiFace Mesh;
+
+ public readonly static RenderablePrim Empty = new RenderablePrim();
+ }
+
+ public struct Camera
+ {
+ public LLVector3 Position;
+ public LLVector3 FocalPoint;
+ public double Zoom;
+ public double Far;
+ }
+
+ public struct NativeMethods
+ {
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Message
+ {
+ public IntPtr HWnd;
+ public uint Msg;
+ public IntPtr WParam;
+ public IntPtr LParam;
+ public uint Time;
+ public System.Drawing.Point Point;
+ }
+
+ //[System.Security.SuppressUnmanagedCodeSecurity]
+ [DllImport("User32.dll", CharSet = CharSet.Auto)]
+ public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);
+ }
+
+ public static class Math3D
+ {
+ // Column-major:
+ // | 0 4 8 12 |
+ // | 1 5 9 13 |
+ // | 2 6 10 14 |
+ // | 3 7 11 15 |
+
+ public static float[] CreateTranslationMatrix(LLVector3 v)
+ {
+ float[] mat = new float[16];
+
+ mat[12] = v.X;
+ mat[13] = v.Y;
+ mat[14] = v.Z;
+ mat[0] = mat[5] = mat[10] = mat[15] = 1;
+
+ return mat;
+ }
+
+ public static float[] CreateRotationMatrix(LLQuaternion q)
+ {
+ float[] mat = new float[16];
+
+ // Transpose the quaternion (don't ask me why)
+ q.X = q.X * -1f;
+ q.Y = q.Y * -1f;
+ q.Z = q.Z * -1f;
+
+ float x2 = q.X + q.X;
+ float y2 = q.Y + q.Y;
+ float z2 = q.Z + q.Z;
+ float xx = q.X * x2;
+ float xy = q.X * y2;
+ float xz = q.X * z2;
+ float yy = q.Y * y2;
+ float yz = q.Y * z2;
+ float zz = q.Z * z2;
+ float wx = q.W * x2;
+ float wy = q.W * y2;
+ float wz = q.W * z2;
+
+ mat[0] = 1.0f - (yy + zz);
+ mat[1] = xy - wz;
+ mat[2] = xz + wy;
+ mat[3] = 0.0f;
+
+ mat[4] = xy + wz;
+ mat[5] = 1.0f - (xx + zz);
+ mat[6] = yz - wx;
+ mat[7] = 0.0f;
+
+ mat[8] = xz - wy;
+ mat[9] = yz + wx;
+ mat[10] = 1.0f - (xx + yy);
+ mat[11] = 0.0f;
+
+ mat[12] = 0.0f;
+ mat[13] = 0.0f;
+ mat[14] = 0.0f;
+ mat[15] = 1.0f;
+
+ return mat;
+ }
+
+ public static float[] CreateScaleMatrix(LLVector3 v)
+ {
+ float[] mat = new float[16];
+
+ mat[0] = v.X;
+ mat[5] = v.Y;
+ mat[10] = v.Z;
+ mat[15] = 1;
+
+ return mat;
+ }
+ }
+}
diff --git a/Programs/PrimWorkshop/frmBrowser.resx b/Programs/PrimWorkshop/frmBrowser.resx
new file mode 100644
index 00000000..ff31a6db
--- /dev/null
+++ b/Programs/PrimWorkshop/frmBrowser.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/Programs/PrimWorkshop/frmPrimPreview.Designer.cs b/Programs/PrimWorkshop/frmPrimPreview.Designer.cs
new file mode 100644
index 00000000..02f886c9
--- /dev/null
+++ b/Programs/PrimWorkshop/frmPrimPreview.Designer.cs
@@ -0,0 +1,465 @@
+namespace primpreview
+{
+ partial class frmPrimPreview
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ glControl.DestroyContexts();
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.menu = new System.Windows.Forms.MenuStrip();
+ this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.toolStripMenuItem2 = new System.Windows.Forms.ToolStripSeparator();
+ this.savePrimXMLToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.saveTextureToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.importToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.worldBrowserToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.exportToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.oBJToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator();
+ this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.viewToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.wireframeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.aboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.splitContainer = new System.Windows.Forms.SplitContainer();
+ this.glControl = new Tao.Platform.Windows.SimpleOpenGlControl();
+ this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
+ this.panel2 = new System.Windows.Forms.Panel();
+ this.label2 = new System.Windows.Forms.Label();
+ this.cboFace = new System.Windows.Forms.ComboBox();
+ this.panel1 = new System.Windows.Forms.Panel();
+ this.label1 = new System.Windows.Forms.Label();
+ this.cboPrim = new System.Windows.Forms.ComboBox();
+ this.scrollRoll = new System.Windows.Forms.HScrollBar();
+ this.scrollPitch = new System.Windows.Forms.HScrollBar();
+ this.scrollYaw = new System.Windows.Forms.HScrollBar();
+ this.picTexture = new System.Windows.Forms.PictureBox();
+ this.scrollZoom = new System.Windows.Forms.HScrollBar();
+ this.openPrimXMLToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
+ this.textureToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.opToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+ this.menu.SuspendLayout();
+ this.splitContainer.Panel1.SuspendLayout();
+ this.splitContainer.Panel2.SuspendLayout();
+ this.splitContainer.SuspendLayout();
+ this.tableLayoutPanel1.SuspendLayout();
+ this.panel2.SuspendLayout();
+ this.panel1.SuspendLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.picTexture)).BeginInit();
+ this.SuspendLayout();
+ //
+ // menu
+ //
+ this.menu.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.fileToolStripMenuItem,
+ this.viewToolStripMenuItem,
+ this.helpToolStripMenuItem});
+ this.menu.Location = new System.Drawing.Point(0, 0);
+ this.menu.Name = "menu";
+ this.menu.Size = new System.Drawing.Size(996, 24);
+ this.menu.TabIndex = 0;
+ this.menu.Text = "menu";
+ //
+ // fileToolStripMenuItem
+ //
+ this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.opToolStripMenuItem,
+ this.toolStripMenuItem2,
+ this.savePrimXMLToolStripMenuItem,
+ this.saveTextureToolStripMenuItem,
+ this.importToolStripMenuItem,
+ this.exportToolStripMenuItem,
+ this.toolStripMenuItem1,
+ this.exitToolStripMenuItem});
+ this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
+ this.fileToolStripMenuItem.Size = new System.Drawing.Size(35, 20);
+ this.fileToolStripMenuItem.Text = "File";
+ //
+ // toolStripMenuItem2
+ //
+ this.toolStripMenuItem2.Name = "toolStripMenuItem2";
+ this.toolStripMenuItem2.Size = new System.Drawing.Size(151, 6);
+ //
+ // savePrimXMLToolStripMenuItem
+ //
+ this.savePrimXMLToolStripMenuItem.Name = "savePrimXMLToolStripMenuItem";
+ this.savePrimXMLToolStripMenuItem.Size = new System.Drawing.Size(154, 22);
+ this.savePrimXMLToolStripMenuItem.Text = "Save Prim XML";
+ this.savePrimXMLToolStripMenuItem.Click += new System.EventHandler(this.savePrimXMLToolStripMenuItem_Click);
+ //
+ // saveTextureToolStripMenuItem
+ //
+ this.saveTextureToolStripMenuItem.Name = "saveTextureToolStripMenuItem";
+ this.saveTextureToolStripMenuItem.Size = new System.Drawing.Size(154, 22);
+ this.saveTextureToolStripMenuItem.Text = "Save Texture";
+ this.saveTextureToolStripMenuItem.Click += new System.EventHandler(this.saveTextureToolStripMenuItem_Click);
+ //
+ // importToolStripMenuItem
+ //
+ this.importToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.worldBrowserToolStripMenuItem});
+ this.importToolStripMenuItem.Name = "importToolStripMenuItem";
+ this.importToolStripMenuItem.Size = new System.Drawing.Size(154, 22);
+ this.importToolStripMenuItem.Text = "Import";
+ //
+ // worldBrowserToolStripMenuItem
+ //
+ this.worldBrowserToolStripMenuItem.Name = "worldBrowserToolStripMenuItem";
+ this.worldBrowserToolStripMenuItem.Size = new System.Drawing.Size(155, 22);
+ this.worldBrowserToolStripMenuItem.Text = "World Browser";
+ this.worldBrowserToolStripMenuItem.Click += new System.EventHandler(this.worldBrowserToolStripMenuItem_Click);
+ //
+ // exportToolStripMenuItem
+ //
+ this.exportToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.oBJToolStripMenuItem});
+ this.exportToolStripMenuItem.Name = "exportToolStripMenuItem";
+ this.exportToolStripMenuItem.Size = new System.Drawing.Size(154, 22);
+ this.exportToolStripMenuItem.Text = "Export";
+ //
+ // oBJToolStripMenuItem
+ //
+ this.oBJToolStripMenuItem.Name = "oBJToolStripMenuItem";
+ this.oBJToolStripMenuItem.Size = new System.Drawing.Size(141, 22);
+ this.oBJToolStripMenuItem.Text = "OBJ Format";
+ this.oBJToolStripMenuItem.Click += new System.EventHandler(this.oBJToolStripMenuItem_Click);
+ //
+ // toolStripMenuItem1
+ //
+ this.toolStripMenuItem1.Name = "toolStripMenuItem1";
+ this.toolStripMenuItem1.Size = new System.Drawing.Size(151, 6);
+ //
+ // exitToolStripMenuItem
+ //
+ this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
+ this.exitToolStripMenuItem.Size = new System.Drawing.Size(154, 22);
+ this.exitToolStripMenuItem.Text = "Exit";
+ this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click);
+ //
+ // viewToolStripMenuItem
+ //
+ this.viewToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.wireframeToolStripMenuItem});
+ this.viewToolStripMenuItem.Name = "viewToolStripMenuItem";
+ this.viewToolStripMenuItem.Size = new System.Drawing.Size(41, 20);
+ this.viewToolStripMenuItem.Text = "View";
+ //
+ // wireframeToolStripMenuItem
+ //
+ this.wireframeToolStripMenuItem.Checked = true;
+ this.wireframeToolStripMenuItem.CheckState = System.Windows.Forms.CheckState.Checked;
+ this.wireframeToolStripMenuItem.Name = "wireframeToolStripMenuItem";
+ this.wireframeToolStripMenuItem.Size = new System.Drawing.Size(135, 22);
+ this.wireframeToolStripMenuItem.Text = "Wireframe";
+ this.wireframeToolStripMenuItem.Click += new System.EventHandler(this.wireframeToolStripMenuItem_Click);
+ //
+ // helpToolStripMenuItem
+ //
+ this.helpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.aboutToolStripMenuItem});
+ this.helpToolStripMenuItem.Name = "helpToolStripMenuItem";
+ this.helpToolStripMenuItem.Size = new System.Drawing.Size(40, 20);
+ this.helpToolStripMenuItem.Text = "Help";
+ //
+ // aboutToolStripMenuItem
+ //
+ this.aboutToolStripMenuItem.Name = "aboutToolStripMenuItem";
+ this.aboutToolStripMenuItem.Size = new System.Drawing.Size(114, 22);
+ this.aboutToolStripMenuItem.Text = "About";
+ this.aboutToolStripMenuItem.Click += new System.EventHandler(this.aboutToolStripMenuItem_Click);
+ //
+ // splitContainer
+ //
+ this.splitContainer.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.splitContainer.Location = new System.Drawing.Point(0, 24);
+ this.splitContainer.Name = "splitContainer";
+ //
+ // splitContainer.Panel1
+ //
+ this.splitContainer.Panel1.Controls.Add(this.glControl);
+ //
+ // splitContainer.Panel2
+ //
+ this.splitContainer.Panel2.Controls.Add(this.tableLayoutPanel1);
+ this.splitContainer.Size = new System.Drawing.Size(996, 512);
+ this.splitContainer.SplitterDistance = 550;
+ this.splitContainer.TabIndex = 6;
+ //
+ // glControl
+ //
+ this.glControl.AccumBits = ((byte)(0));
+ this.glControl.AutoCheckErrors = false;
+ this.glControl.AutoFinish = false;
+ this.glControl.AutoMakeCurrent = true;
+ this.glControl.AutoSwapBuffers = true;
+ this.glControl.BackColor = System.Drawing.Color.Black;
+ this.glControl.ColorBits = ((byte)(32));
+ this.glControl.DepthBits = ((byte)(16));
+ this.glControl.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.glControl.Location = new System.Drawing.Point(0, 0);
+ this.glControl.Name = "glControl";
+ this.glControl.Size = new System.Drawing.Size(550, 512);
+ this.glControl.StencilBits = ((byte)(0));
+ this.glControl.TabIndex = 5;
+ this.glControl.Paint += new System.Windows.Forms.PaintEventHandler(this.glControl_Paint);
+ this.glControl.Resize += new System.EventHandler(this.glControl_Resize);
+ //
+ // tableLayoutPanel1
+ //
+ this.tableLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
+ this.tableLayoutPanel1.ColumnCount = 1;
+ this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
+ this.tableLayoutPanel1.Controls.Add(this.panel2, 0, 5);
+ this.tableLayoutPanel1.Controls.Add(this.panel1, 0, 4);
+ this.tableLayoutPanel1.Controls.Add(this.scrollRoll, 0, 0);
+ this.tableLayoutPanel1.Controls.Add(this.scrollPitch, 0, 1);
+ this.tableLayoutPanel1.Controls.Add(this.scrollYaw, 0, 2);
+ this.tableLayoutPanel1.Controls.Add(this.picTexture, 0, 6);
+ this.tableLayoutPanel1.Controls.Add(this.scrollZoom, 0, 3);
+ this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
+ this.tableLayoutPanel1.Name = "tableLayoutPanel1";
+ this.tableLayoutPanel1.RowCount = 7;
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 24F));
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30F));
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 30F));
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
+ this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
+ this.tableLayoutPanel1.Size = new System.Drawing.Size(442, 512);
+ this.tableLayoutPanel1.TabIndex = 9;
+ //
+ // panel2
+ //
+ this.panel2.Controls.Add(this.label2);
+ this.panel2.Controls.Add(this.cboFace);
+ this.panel2.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.panel2.Location = new System.Drawing.Point(3, 117);
+ this.panel2.Name = "panel2";
+ this.panel2.Size = new System.Drawing.Size(436, 24);
+ this.panel2.TabIndex = 16;
+ //
+ // label2
+ //
+ this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)));
+ this.label2.AutoSize = true;
+ this.label2.Location = new System.Drawing.Point(4, 5);
+ this.label2.Name = "label2";
+ this.label2.Size = new System.Drawing.Size(34, 13);
+ this.label2.TabIndex = 16;
+ this.label2.Text = "Face:";
+ this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // cboFace
+ //
+ this.cboFace.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.cboFace.FormattingEnabled = true;
+ this.cboFace.Location = new System.Drawing.Point(40, 2);
+ this.cboFace.Name = "cboFace";
+ this.cboFace.Size = new System.Drawing.Size(174, 21);
+ this.cboFace.TabIndex = 15;
+ this.cboFace.SelectedIndexChanged += new System.EventHandler(this.cboFace_SelectedIndexChanged);
+ //
+ // panel1
+ //
+ this.panel1.Controls.Add(this.label1);
+ this.panel1.Controls.Add(this.cboPrim);
+ this.panel1.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.panel1.Location = new System.Drawing.Point(3, 87);
+ this.panel1.Name = "panel1";
+ this.panel1.Size = new System.Drawing.Size(436, 24);
+ this.panel1.TabIndex = 15;
+ //
+ // label1
+ //
+ this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)));
+ this.label1.AutoSize = true;
+ this.label1.Location = new System.Drawing.Point(4, 5);
+ this.label1.Name = "label1";
+ this.label1.Size = new System.Drawing.Size(30, 13);
+ this.label1.TabIndex = 16;
+ this.label1.Text = "Prim:";
+ this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleLeft;
+ //
+ // cboPrim
+ //
+ this.cboPrim.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.cboPrim.FormattingEnabled = true;
+ this.cboPrim.Location = new System.Drawing.Point(40, 2);
+ this.cboPrim.Name = "cboPrim";
+ this.cboPrim.Size = new System.Drawing.Size(174, 21);
+ this.cboPrim.TabIndex = 15;
+ this.cboPrim.SelectedIndexChanged += new System.EventHandler(this.cboPrim_SelectedIndexChanged);
+ //
+ // scrollRoll
+ //
+ this.scrollRoll.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
+ this.scrollRoll.Location = new System.Drawing.Point(0, 2);
+ this.scrollRoll.Maximum = 360;
+ this.scrollRoll.Name = "scrollRoll";
+ this.scrollRoll.Size = new System.Drawing.Size(442, 16);
+ this.scrollRoll.TabIndex = 9;
+ this.scrollRoll.ValueChanged += new System.EventHandler(this.scroll_ValueChanged);
+ //
+ // scrollPitch
+ //
+ this.scrollPitch.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
+ this.scrollPitch.Location = new System.Drawing.Point(0, 22);
+ this.scrollPitch.Maximum = 360;
+ this.scrollPitch.Name = "scrollPitch";
+ this.scrollPitch.Size = new System.Drawing.Size(442, 16);
+ this.scrollPitch.TabIndex = 10;
+ this.scrollPitch.ValueChanged += new System.EventHandler(this.scroll_ValueChanged);
+ //
+ // scrollYaw
+ //
+ this.scrollYaw.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
+ this.scrollYaw.Location = new System.Drawing.Point(0, 42);
+ this.scrollYaw.Maximum = 360;
+ this.scrollYaw.Name = "scrollYaw";
+ this.scrollYaw.Size = new System.Drawing.Size(442, 16);
+ this.scrollYaw.TabIndex = 11;
+ this.scrollYaw.ValueChanged += new System.EventHandler(this.scroll_ValueChanged);
+ //
+ // picTexture
+ //
+ this.picTexture.BackColor = System.Drawing.Color.Black;
+ this.picTexture.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.picTexture.Location = new System.Drawing.Point(3, 147);
+ this.picTexture.Name = "picTexture";
+ this.picTexture.Size = new System.Drawing.Size(436, 362);
+ this.picTexture.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
+ this.picTexture.TabIndex = 17;
+ this.picTexture.TabStop = false;
+ this.picTexture.MouseLeave += new System.EventHandler(this.picTexture_MouseLeave);
+ this.picTexture.MouseMove += new System.Windows.Forms.MouseEventHandler(this.picTexture_MouseMove);
+ this.picTexture.MouseDown += new System.Windows.Forms.MouseEventHandler(this.picTexture_MouseDown);
+ this.picTexture.Paint += new System.Windows.Forms.PaintEventHandler(this.picTexture_Paint);
+ this.picTexture.MouseUp += new System.Windows.Forms.MouseEventHandler(this.picTexture_MouseUp);
+ //
+ // scrollZoom
+ //
+ this.scrollZoom.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.scrollZoom.LargeChange = 1;
+ this.scrollZoom.Location = new System.Drawing.Point(0, 60);
+ this.scrollZoom.Maximum = 0;
+ this.scrollZoom.Minimum = -200;
+ this.scrollZoom.Name = "scrollZoom";
+ this.scrollZoom.Size = new System.Drawing.Size(442, 24);
+ this.scrollZoom.TabIndex = 19;
+ this.scrollZoom.Value = -50;
+ this.scrollZoom.ValueChanged += new System.EventHandler(this.scrollZoom_ValueChanged);
+ //
+ // openPrimXMLToolStripMenuItem1
+ //
+ this.openPrimXMLToolStripMenuItem1.Name = "openPrimXMLToolStripMenuItem1";
+ this.openPrimXMLToolStripMenuItem1.Size = new System.Drawing.Size(152, 22);
+ this.openPrimXMLToolStripMenuItem1.Text = "Prim XML";
+ this.openPrimXMLToolStripMenuItem1.Click += new System.EventHandler(this.openPrimXMLToolStripMenuItem1_Click);
+ //
+ // textureToolStripMenuItem
+ //
+ this.textureToolStripMenuItem.Name = "textureToolStripMenuItem";
+ this.textureToolStripMenuItem.Size = new System.Drawing.Size(152, 22);
+ this.textureToolStripMenuItem.Text = "Texture";
+ this.textureToolStripMenuItem.Click += new System.EventHandler(this.textureToolStripMenuItem_Click);
+ //
+ // opToolStripMenuItem
+ //
+ this.opToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.openPrimXMLToolStripMenuItem1,
+ this.textureToolStripMenuItem});
+ this.opToolStripMenuItem.Name = "opToolStripMenuItem";
+ this.opToolStripMenuItem.Size = new System.Drawing.Size(154, 22);
+ this.opToolStripMenuItem.Text = "Open";
+ //
+ // frmPrimPreview
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+ this.ClientSize = new System.Drawing.Size(996, 536);
+ this.Controls.Add(this.splitContainer);
+ this.Controls.Add(this.menu);
+ this.MainMenuStrip = this.menu;
+ this.Name = "frmPrimPreview";
+ this.Text = "Prim Preview";
+ this.menu.ResumeLayout(false);
+ this.menu.PerformLayout();
+ this.splitContainer.Panel1.ResumeLayout(false);
+ this.splitContainer.Panel2.ResumeLayout(false);
+ this.splitContainer.ResumeLayout(false);
+ this.tableLayoutPanel1.ResumeLayout(false);
+ this.panel2.ResumeLayout(false);
+ this.panel2.PerformLayout();
+ this.panel1.ResumeLayout(false);
+ this.panel1.PerformLayout();
+ ((System.ComponentModel.ISupportInitialize)(this.picTexture)).EndInit();
+ this.ResumeLayout(false);
+ this.PerformLayout();
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.MenuStrip menu;
+ private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem;
+ private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1;
+ private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem helpToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem aboutToolStripMenuItem;
+ private System.Windows.Forms.SplitContainer splitContainer;
+ private Tao.Platform.Windows.SimpleOpenGlControl glControl;
+ private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
+ private System.Windows.Forms.HScrollBar scrollRoll;
+ private System.Windows.Forms.HScrollBar scrollPitch;
+ private System.Windows.Forms.HScrollBar scrollYaw;
+ private System.Windows.Forms.Panel panel2;
+ private System.Windows.Forms.Label label2;
+ private System.Windows.Forms.ComboBox cboFace;
+ private System.Windows.Forms.Panel panel1;
+ private System.Windows.Forms.Label label1;
+ private System.Windows.Forms.ComboBox cboPrim;
+ private System.Windows.Forms.PictureBox picTexture;
+ private System.Windows.Forms.ToolStripMenuItem savePrimXMLToolStripMenuItem;
+ private System.Windows.Forms.ToolStripSeparator toolStripMenuItem2;
+ private System.Windows.Forms.ToolStripMenuItem saveTextureToolStripMenuItem;
+ private System.Windows.Forms.HScrollBar scrollZoom;
+ private System.Windows.Forms.ToolStripMenuItem exportToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem oBJToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem viewToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem wireframeToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem importToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem worldBrowserToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem opToolStripMenuItem;
+ private System.Windows.Forms.ToolStripMenuItem openPrimXMLToolStripMenuItem1;
+ private System.Windows.Forms.ToolStripMenuItem textureToolStripMenuItem;
+ }
+}
+
diff --git a/Programs/PrimWorkshop/frmPrimPreview.cs b/Programs/PrimWorkshop/frmPrimPreview.cs
new file mode 100644
index 00000000..f21cb947
--- /dev/null
+++ b/Programs/PrimWorkshop/frmPrimPreview.cs
@@ -0,0 +1,542 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+using System.Windows.Forms;
+using Tao.OpenGl;
+using Tao.Platform.Windows;
+using libsecondlife;
+using libsecondlife.StructuredData;
+using libsecondlife.Imaging;
+using libsecondlife.Rendering;
+
+// NOTE: Batches are divided by texture, fullbright, shiny, transparent, and glow
+
+namespace primpreview
+{
+ public struct FaceData
+ {
+ public float[] Vertices;
+ public ushort[] Indices;
+ public float[] TexCoords;
+ public int TexturePointer;
+ public System.Drawing.Image Texture;
+ // TODO: Normals / binormals?
+ }
+
+ public partial class frmPrimPreview : Form
+ {
+ #region Form Globals
+
+ List Prims = null;
+ PrimMeshMultiFace CurrentPrim = null;
+ ProfileFace? CurrentFace = null;
+
+ bool DraggingTexture = false;
+ bool Wireframe = true;
+ int[] TexturePointers = new int[1];
+
+ #endregion Form Globals
+
+ public frmPrimPreview()
+ {
+ InitializeComponent();
+ glControl.InitializeContexts();
+
+ Gl.glShadeModel(Gl.GL_SMOOTH);
+ Gl.glClearColor(0f, 0f, 0f, 0f);
+
+ Gl.glClearDepth(1.0f);
+ Gl.glEnable(Gl.GL_DEPTH_TEST);
+ Gl.glDepthMask(Gl.GL_TRUE);
+ Gl.glDepthFunc(Gl.GL_LEQUAL);
+ Gl.glHint(Gl.GL_PERSPECTIVE_CORRECTION_HINT, Gl.GL_NICEST);
+
+ TexturePointers[0] = 0;
+
+ // Call the resizing function which sets up the GL drawing window
+ // and will also invalidate the GL control
+ glControl_Resize(null, null);
+ }
+
+ #region GLControl Callbacks
+
+ private void glControl_Paint(object sender, PaintEventArgs e)
+ {
+ Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);
+ Gl.glLoadIdentity();
+
+ // Setup wireframe or solid fill drawing mode
+ if (Wireframe)
+ Gl.glPolygonMode(Gl.GL_FRONT_AND_BACK, Gl.GL_LINE);
+ else
+ Gl.glPolygonMode(Gl.GL_FRONT, Gl.GL_FILL);
+
+ LLVector3 center = LLVector3.Zero;
+
+ Glu.gluLookAt(
+ center.X, (double)scrollZoom.Value * 0.1d + center.Y, center.Z,
+ center.X, center.Y, center.Z,
+ 0d, 0d, 1d);
+
+ // Push the world matrix
+ Gl.glPushMatrix();
+
+ Gl.glEnableClientState(Gl.GL_VERTEX_ARRAY);
+ Gl.glEnableClientState(Gl.GL_TEXTURE_COORD_ARRAY);
+
+ // World rotations
+ Gl.glRotatef((float)scrollRoll.Value, 1f, 0f, 0f);
+ Gl.glRotatef((float)scrollPitch.Value, 0f, 1f, 0f);
+ Gl.glRotatef((float)scrollYaw.Value, 0f, 0f, 1f);
+
+ if (Prims != null)
+ {
+ for (int i = 0; i < Prims.Count; i++)
+ {
+ Primitive prim = Prims[i].Prim;
+
+ if (i == cboPrim.SelectedIndex)
+ Gl.glColor3f(1f, 0f, 0f);
+ else
+ Gl.glColor3f(1f, 1f, 1f);
+
+ // Individual prim matrix
+ Gl.glPushMatrix();
+
+ // The root prim position is sim-relative, while child prim positions are
+ // parent-relative. We want to apply parent-relative translations but not
+ // sim-relative ones
+ if (Prims[i].Prim.ParentID != 0)
+ {
+ Gl.glTranslatef(prim.Position.X, prim.Position.Y, prim.Position.Z);
+
+ // Prim rotation
+ // Using euler angles because I have no clue what I'm doing
+ float roll, pitch, yaw;
+
+ LLMatrix3 rotation = prim.Rotation.ToMatrix();
+ rotation.GetEulerAngles(out roll, out pitch, out yaw);
+
+ Gl.glRotatef(roll * 57.2957795f, 1f, 0f, 0f);
+ Gl.glRotatef(pitch * 57.2957795f, 0f, 1f, 0f);
+ Gl.glRotatef(yaw * 57.2957795f, 0f, 0f, 1f);
+ }
+
+ // Prim scaling
+ Gl.glScalef(prim.Scale.X, prim.Scale.Y, prim.Scale.Z);
+
+ // Draw the prim faces
+ for (int j = 0; j < Prims[i].Faces.Count; j++)
+ {
+ if (i == cboPrim.SelectedIndex)
+ {
+ // This prim is currently selected in the dropdown
+ //Gl.glColor3f(0f, 1f, 0f);
+ Gl.glColor3f(1f, 1f, 1f);
+
+ if (j == cboFace.SelectedIndex)
+ {
+ // This face is currently selected in the dropdown
+ }
+ else
+ {
+ // This face is not currently selected in the dropdown
+ }
+ }
+ else
+ {
+ // This prim is not currently selected in the dropdown
+ Gl.glColor3f(1f, 1f, 1f);
+ }
+
+ #region Texturing
+
+ Face face = Prims[i].Faces[j];
+ FaceData data = (FaceData)face.UserData;
+
+ if (data.TexturePointer != 0)
+ {
+ // Set the color to solid white so the texture is not altered
+ //Gl.glColor3f(1f, 1f, 1f);
+ // Enable texturing for this face
+ Gl.glEnable(Gl.GL_TEXTURE_2D);
+ }
+ else
+ {
+ Gl.glDisable(Gl.GL_TEXTURE_2D);
+ }
+
+ // Bind the texture
+ Gl.glBindTexture(Gl.GL_TEXTURE_2D, data.TexturePointer);
+
+ #endregion Texturing
+
+ Gl.glTexCoordPointer(2, Gl.GL_FLOAT, 0, data.TexCoords);
+ Gl.glVertexPointer(3, Gl.GL_FLOAT, 0, data.Vertices);
+ Gl.glDrawElements(Gl.GL_TRIANGLES, data.Indices.Length, Gl.GL_UNSIGNED_SHORT, data.Indices);
+ }
+
+ // Pop the prim matrix
+ Gl.glPopMatrix();
+ }
+ }
+ /*else if (CurrentMesh != null)
+ {
+ Gl.glColor3f(1f, 1f, 1f);
+
+ GLMesh glmesh = CurrentMesh.Value;
+ LLMesh llmesh = glmesh.Mesh;
+
+ Gl.glRotatef(llmesh.RotationAngles.X, 1f, 0f, 0f);
+ Gl.glRotatef(llmesh.RotationAngles.Y, 0f, 1f, 0f);
+ Gl.glRotatef(llmesh.RotationAngles.Z, 0f, 0f, 1f);
+
+ Gl.glScalef(llmesh.Scale.X, llmesh.Scale.Y, llmesh.Scale.Z);
+
+ // Push the mesh data
+ Gl.glTexCoordPointer(2, Gl.GL_FLOAT, 0, glmesh.TexCoords);
+ Gl.glVertexPointer(3, Gl.GL_FLOAT, 0, glmesh.Vertices);
+ Gl.glDrawElements(Gl.GL_TRIANGLES, glmesh.Indices.Length, Gl.GL_UNSIGNED_SHORT, glmesh.Indices);
+ }*/
+
+ // Pop the world matrix
+ Gl.glPopMatrix();
+
+ Gl.glDisableClientState(Gl.GL_TEXTURE_COORD_ARRAY);
+ Gl.glDisableClientState(Gl.GL_VERTEX_ARRAY);
+
+ Gl.glFlush();
+ }
+
+ private void glControl_Resize(object sender, EventArgs e)
+ {
+ Gl.glClearColor(0.39f, 0.58f, 0.93f, 1.0f);
+
+ Gl.glViewport(0, 0, glControl.Width, glControl.Height);
+
+ Gl.glPushMatrix();
+ Gl.glMatrixMode(Gl.GL_PROJECTION);
+ Gl.glLoadIdentity();
+
+ Glu.gluPerspective(50.0d, 1.0d, 0.1d, 50d);
+
+ Gl.glMatrixMode(Gl.GL_MODELVIEW);
+ Gl.glPopMatrix();
+ }
+
+ #endregion GLControl Callbacks
+
+ #region Menu Callbacks
+
+ private void openPrimXMLToolStripMenuItem1_Click(object sender, EventArgs e)
+ {
+ Prims = null;
+ OpenFileDialog dialog = new OpenFileDialog();
+
+ if (dialog.ShowDialog() == DialogResult.OK)
+ {
+ LLSD llsd = null;
+
+ try { llsd = LLSDParser.DeserializeXml(File.ReadAllText(dialog.FileName)); }
+ catch (Exception ex) { MessageBox.Show(ex.Message); }
+
+ if (llsd != null && llsd.Type == LLSDType.Map)
+ {
+ List primList = Helpers.LLSDToPrimList(llsd);
+ Prims = new List(primList.Count);
+
+ for (int i = 0; i < primList.Count; i++)
+ {
+ // TODO: Can't render sculpted prims without the textures
+ if (primList[i].Sculpt.SculptTexture != LLUUID.Zero)
+ continue;
+
+ Primitive prim = primList[i];
+ PrimMeshMultiFace mesh = Render.GenerateMeshWithFaces(prim, 4f);
+
+ // Create a FaceData struct for each face that stores the 3D data
+ // in a Tao.OpenGL friendly format
+ for (int j = 0; j < mesh.Faces.Count; j++)
+ {
+ Face face = mesh.Faces[j];
+ FaceData data = new FaceData();
+
+ // Vertices for this face
+ data.Vertices = new float[face.Vertices.Count * 3];
+ for (int k = 0; k < face.Vertices.Count; k++)
+ {
+ data.Vertices[k * 3 + 0] = face.Vertices[k].Position.X;
+ data.Vertices[k * 3 + 1] = face.Vertices[k].Position.Y;
+ data.Vertices[k * 3 + 2] = face.Vertices[k].Position.Z;
+ }
+
+ // Indices for this face
+ data.Indices = face.Indices.ToArray();
+
+ // Texture transform for this face
+ LLObject.TextureEntryFace teFace = prim.Textures.GetFace((uint)j);
+ Render.TransformTexCoords(face.Vertices, face.Center, teFace);
+
+ // Texcoords for this face
+ data.TexCoords = new float[face.Vertices.Count * 2];
+ for (int k = 0; k < face.Vertices.Count; k++)
+ {
+ data.TexCoords[k * 2 + 0] = face.Vertices[k].TexCoord.X;
+ data.TexCoords[k * 2 + 1] = face.Vertices[k].TexCoord.Y;
+ }
+
+ // Texture for this face
+ if (LoadTexture(System.IO.Path.GetDirectoryName(dialog.FileName), teFace.TextureID, ref data.Texture))
+ {
+ Bitmap bitmap = new Bitmap(data.Texture);
+ bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
+ Rectangle rectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
+ BitmapData bitmapData = bitmap.LockBits(rectangle, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
+
+ Gl.glGenTextures(1, out data.TexturePointer);
+ Gl.glBindTexture(Gl.GL_TEXTURE_2D, data.TexturePointer);
+
+ Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_LINEAR_MIPMAP_LINEAR);
+ Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR);
+ Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_REPEAT);
+ Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_REPEAT);
+ Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_GENERATE_MIPMAP, Gl.GL_TRUE);
+
+ Glu.gluBuild2DMipmaps(Gl.GL_TEXTURE_2D, Gl.GL_RGB8, bitmap.Width, bitmap.Height, Gl.GL_BGR, Gl.GL_UNSIGNED_BYTE, bitmapData.Scan0);
+
+ bitmap.UnlockBits(bitmapData);
+ bitmap.Dispose();
+ }
+
+ // Set the UserData for this face to our FaceData struct
+ face.UserData = data;
+ mesh.Faces[j] = face;
+ }
+
+ Prims.Add(mesh);
+ }
+
+ // Setup the dropdown list of prims
+ PopulatePrimCombobox();
+
+ glControl.Invalidate();
+ }
+ else
+ {
+ MessageBox.Show("Failed to load LLSD formatted primitive data from " + dialog.FileName);
+ }
+ }
+ }
+
+ private bool LoadTexture(string basePath, LLUUID textureID, ref System.Drawing.Image texture)
+ {
+ if (File.Exists(textureID.ToString() + ".tga"))
+ {
+ try
+ {
+ texture = (Image)LoadTGAClass.LoadTGA(
+ basePath + System.IO.Path.DirectorySeparatorChar + textureID.ToString() + ".tga");
+ return true;
+ }
+ catch (Exception)
+ {
+ }
+ }
+
+ return false;
+ }
+
+ private void textureToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ picTexture.Image = null;
+ TexturePointers[0] = 0;
+
+ OpenFileDialog dialog = new OpenFileDialog();
+
+ if (dialog.ShowDialog() == DialogResult.OK)
+ {
+ try
+ {
+ picTexture.Image = System.Drawing.Image.FromFile(dialog.FileName);
+ Bitmap bitmap = new Bitmap(picTexture.Image);
+ bitmap.RotateFlip(RotateFlipType.RotateNoneFlipY);
+
+ // Create the GL texture space
+ Gl.glGenTextures(1, TexturePointers);
+ Rectangle rectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
+ BitmapData bitmapData = bitmap.LockBits(rectangle, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
+
+ Gl.glBindTexture(Gl.GL_TEXTURE_2D, TexturePointers[0]);
+
+ Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_LINEAR_MIPMAP_LINEAR);
+ Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR);
+ Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_CLAMP_TO_EDGE);
+ Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_CLAMP_TO_EDGE);
+ Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_GENERATE_MIPMAP, Gl.GL_TRUE);
+
+ Glu.gluBuild2DMipmaps(Gl.GL_TEXTURE_2D, Gl.GL_RGB8, bitmap.Width, bitmap.Height, Gl.GL_BGR, Gl.GL_UNSIGNED_BYTE, bitmapData.Scan0);
+
+ bitmap.UnlockBits(bitmapData);
+ bitmap.Dispose();
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show("Failed to load image from file " + dialog.FileName + ": " + ex.Message);
+ }
+ }
+ }
+
+ private void savePrimXMLToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+
+ }
+
+ private void saveTextureToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+
+ }
+
+ private void oBJToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ SaveFileDialog dialog = new SaveFileDialog();
+ dialog.Filter = "OBJ files (*.obj)|*.obj";
+
+ if (dialog.ShowDialog() == DialogResult.OK)
+ {
+ if (!MeshToOBJ.MeshesToOBJ(Prims, dialog.FileName))
+ {
+ MessageBox.Show("Failed to save file " + dialog.FileName +
+ ". Ensure that you have permission to write to that file and it is currently not in use");
+ }
+ }
+ }
+
+ private void exitToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ Close();
+ }
+
+ private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ MessageBox.Show(
+ "Written by John Hurliman (http://www.jhurliman.org/)");
+ }
+
+ #endregion Menu Callbacks
+
+ #region Scrollbar Callbacks
+
+ private void scroll_ValueChanged(object sender, EventArgs e)
+ {
+ glControl.Invalidate();
+ }
+
+ private void scrollZoom_ValueChanged(object sender, EventArgs e)
+ {
+ glControl_Resize(null, null);
+ glControl.Invalidate();
+ }
+
+ #endregion Scrollbar Callbacks
+
+ #region PictureBox Callbacks
+
+ private void picTexture_MouseDown(object sender, MouseEventArgs e)
+ {
+ DraggingTexture = true;
+ }
+
+ private void picTexture_MouseUp(object sender, MouseEventArgs e)
+ {
+ DraggingTexture = false;
+ }
+
+ private void picTexture_MouseLeave(object sender, EventArgs e)
+ {
+ DraggingTexture = false;
+ }
+
+ private void picTexture_MouseMove(object sender, MouseEventArgs e)
+ {
+ if (DraggingTexture)
+ {
+ // What is the current action?
+ // None, DraggingEdge, DraggingCorner, DraggingWhole
+ }
+ else
+ {
+ // Check if the mouse is close to the edge or corner of a selection
+ // rectangle
+
+ // If so, change the cursor accordingly
+ }
+ }
+
+ private void picTexture_Paint(object sender, PaintEventArgs e)
+ {
+ // Draw the current selection rectangles
+ }
+
+ #endregion PictureBox Callbacks
+
+ private void cboPrim_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ CurrentPrim = (PrimMeshMultiFace)cboPrim.Items[cboPrim.SelectedIndex];
+ PopulateFaceCombobox();
+
+ glControl.Invalidate();
+ }
+
+ private void cboFace_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ CurrentFace = (ProfileFace)cboFace.Items[cboFace.SelectedIndex];
+
+ glControl.Invalidate();
+ }
+
+ private void PopulatePrimCombobox()
+ {
+ cboPrim.Items.Clear();
+
+ if (Prims != null)
+ {
+ for (int i = 0; i < Prims.Count; i++)
+ cboPrim.Items.Add(Prims[i]);
+ }
+
+ if (cboPrim.Items.Count > 0)
+ cboPrim.SelectedIndex = 0;
+ }
+
+ private void PopulateFaceCombobox()
+ {
+ cboFace.Items.Clear();
+
+ if (CurrentPrim != null)
+ {
+ for (int i = 0; i < CurrentPrim.Profile.Faces.Count; i++)
+ cboFace.Items.Add(CurrentPrim.Profile.Faces[i]);
+ }
+
+ if (cboFace.Items.Count > 0)
+ cboFace.SelectedIndex = 0;
+ }
+
+ private void wireframeToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ wireframeToolStripMenuItem.Checked = !wireframeToolStripMenuItem.Checked;
+ Wireframe = wireframeToolStripMenuItem.Checked;
+
+ glControl.Invalidate();
+ }
+
+ private void worldBrowserToolStripMenuItem_Click(object sender, EventArgs e)
+ {
+ frmBrowser browser = new frmBrowser();
+ browser.ShowDialog();
+ }
+ }
+}
diff --git a/Programs/PrimWorkshop/frmPrimPreview.resx b/Programs/PrimWorkshop/frmPrimPreview.resx
new file mode 100644
index 00000000..01073880
--- /dev/null
+++ b/Programs/PrimWorkshop/frmPrimPreview.resx
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 17, 17
+
+
\ No newline at end of file
diff --git a/Programs/PrimWorkshop/meshtoobj.cs b/Programs/PrimWorkshop/meshtoobj.cs
new file mode 100644
index 00000000..c3c2fe05
--- /dev/null
+++ b/Programs/PrimWorkshop/meshtoobj.cs
@@ -0,0 +1,153 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.IO;
+using libsecondlife;
+using libsecondlife.Rendering;
+
+namespace primpreview
+{
+ public static class MeshToOBJ
+ {
+ public static bool MeshesToOBJ(List meshes, string filename)
+ {
+ StringBuilder obj = new StringBuilder();
+ StringBuilder mtl = new StringBuilder();
+
+ FileInfo objFileInfo = new FileInfo(filename);
+
+ string mtlFilename = objFileInfo.FullName.Substring(objFileInfo.DirectoryName.Length + 1,
+ objFileInfo.FullName.Length - (objFileInfo.DirectoryName.Length + 1) - 4) + ".mtl";
+
+ obj.AppendLine("# Created by libprimrender");
+ obj.AppendLine("mtllib ./" + mtlFilename);
+ obj.AppendLine();
+
+ mtl.AppendLine("# Created by libprimrender");
+ mtl.AppendLine();
+
+ for (int i = 0; i < meshes.Count; i++)
+ {
+ PrimMeshMultiFace mesh = meshes[i];
+
+ for (int j = 0; j < mesh.Faces.Count; j++)
+ {
+ Face face = mesh.Faces[j];
+
+ if (face.Vertices.Count > 2)
+ {
+ string mtlName = String.Format("material{0}-{1}", i, face.ID);
+ LLObject.TextureEntryFace tex = face.TextureFace;
+ string texName = tex.TextureID.ToString() + ".tga";
+
+ // FIXME: Convert the source to TGA (if needed) and copy to the destination
+
+ float shiny = 0.00f;
+ switch (tex.Shiny)
+ {
+ case Shininess.High:
+ shiny = 1.00f;
+ break;
+ case Shininess.Medium:
+ shiny = 0.66f;
+ break;
+ case Shininess.Low:
+ shiny = 0.33f;
+ break;
+ }
+
+ obj.AppendFormat("g face{0}-{1}{2}", i, face.ID, Environment.NewLine);
+
+ mtl.AppendLine("newmtl " + mtlName);
+ mtl.AppendFormat("Ka {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);
+ mtl.AppendFormat("Kd {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);
+ //mtl.AppendFormat("Ks {0} {1} {2}{3}");
+ mtl.AppendLine("Tr " + tex.RGBA.A);
+ mtl.AppendLine("Ns " + shiny);
+ mtl.AppendLine("illum 1");
+ if (tex.TextureID != LLUUID.Zero && tex.TextureID != LLObject.TextureEntry.WHITE_TEXTURE)
+ mtl.AppendLine("map_Kd ./" + texName);
+ mtl.AppendLine();
+
+ // Write the vertices, texture coordinates, and vertex normals for this side
+ for (int k = 0; k < face.Vertices.Count; k++)
+ {
+ Vertex vertex = face.Vertices[k];
+
+ #region Vertex
+
+ LLVector3 pos = vertex.Position;
+
+ // Apply scaling
+ pos *= mesh.Prim.Scale;
+
+ // Apply rotation
+ pos *= mesh.Prim.Rotation;
+
+ // The root prim position is sim-relative, while child prim positions are
+ // parent-relative. We want to apply parent-relative translations but not
+ // sim-relative ones
+ if (mesh.Prim.ParentID != 0)
+ pos += mesh.Prim.Position;
+
+ obj.AppendFormat("v {0} {1} {2}{3}", pos.X, pos.Y, pos.Z, Environment.NewLine);
+
+ #endregion Vertex
+
+ #region Texture Coord
+
+ obj.AppendFormat("vt {0} {1}{2}", vertex.TexCoord.X, vertex.TexCoord.Y,
+ Environment.NewLine);
+
+ #endregion Texture Coord
+
+ #region Vertex Normal
+
+ // HACK: Sometimes normals are getting set to
+ if (!Single.IsNaN(vertex.Normal.X) && !Single.IsNaN(vertex.Normal.Y) && !Single.IsNaN(vertex.Normal.Z))
+ obj.AppendFormat("vn {0} {1} {2}{3}", vertex.Normal.X, vertex.Normal.Y, vertex.Normal.Z,
+ Environment.NewLine);
+ else
+ obj.AppendLine("vn 0.0 1.0 0.0");
+
+ #endregion Vertex Normal
+ }
+
+ obj.AppendFormat("# {0} vertices{1}", face.Vertices.Count, Environment.NewLine);
+ obj.AppendLine();
+ obj.AppendLine("usemtl " + mtlName);
+
+ #region Elements
+
+ // Write all of the faces (triangles) for this side
+ for (int k = 0; k < face.Indices.Count / 3; k++)
+ {
+ obj.AppendFormat("f -{0}/-{0}/-{0} -{1}/-{1}/-{1} -{2}/-{2}/-{2}{3}",
+ face.Vertices.Count - face.Indices[k * 3 + 0],
+ face.Vertices.Count - face.Indices[k * 3 + 1],
+ face.Vertices.Count - face.Indices[k * 3 + 2],
+ Environment.NewLine);
+ }
+
+ obj.AppendFormat("# {0} elements{1}", face.Indices.Count / 3, Environment.NewLine);
+ obj.AppendLine();
+
+ #endregion Elements
+ }
+ }
+ }
+
+ try
+ {
+ File.WriteAllText(filename, obj.ToString());
+ File.WriteAllText(mtlFilename, mtl.ToString());
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/bin/GlacialList.dll b/bin/GlacialList.dll
new file mode 100644
index 00000000..ccda7084
Binary files /dev/null and b/bin/GlacialList.dll differ
diff --git a/bin/ICSharpCode.SharpZipLib.dll b/bin/ICSharpCode.SharpZipLib.dll
new file mode 100644
index 00000000..536337c2
Binary files /dev/null and b/bin/ICSharpCode.SharpZipLib.dll differ
diff --git a/bin/Tao.OpenGl.dll b/bin/Tao.OpenGl.dll
new file mode 100644
index 00000000..22085c67
Binary files /dev/null and b/bin/Tao.OpenGl.dll differ
diff --git a/bin/Tao.OpenGl.dll.config b/bin/Tao.OpenGl.dll.config
new file mode 100644
index 00000000..a788d0f0
--- /dev/null
+++ b/bin/Tao.OpenGl.dll.config
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/bin/Tao.Platform.Windows.dll b/bin/Tao.Platform.Windows.dll
new file mode 100644
index 00000000..6612ff2f
Binary files /dev/null and b/bin/Tao.Platform.Windows.dll differ
diff --git a/prebuild.xml b/prebuild.xml
index 59295661..9464e384 100644
--- a/prebuild.xml
+++ b/prebuild.xml
@@ -31,7 +31,7 @@
false
-
+
@@ -128,7 +128,27 @@
+
+
+
+
+ ../bin/
+
+
+
+
+ ../bin/
+
+
+ ../bin/
+
+
+
+
+
+
+
@@ -199,6 +219,34 @@
+
+
+
+ ../../bin/
+
+
+
+
+ ../../bin/
+
+
+
+ ../../bin/
+
+
+
+
+
+
+
+
+
+
+ frmPrimPreview.resx
+
+
+
+
@@ -463,6 +511,60 @@
+
+
+
+
+
+
+ TRACE;DEBUG
+ false
+ false
+ true
+ 4
+ false
+
+ bin
+ true
+ true
+ false
+
+
+
+
+ TRACE
+ true
+ false
+ true
+ 4
+ false
+
+ bin
+ false
+ true
+ false
+
+
+
+
+
+
+ ../bin/
+
+
+
+
+ ../bin/
+
+
+ ../bin/
+
+
+
+
+
+
+