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/ + + + + + + +