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 ICSharpCode.SharpZipLib.Zip; using OpenMetaverse; using OpenMetaverse.StructuredData; using OpenMetaverse.Imaging; using OpenMetaverse.Rendering; // NOTE: Batches are divided by texture, fullbright, shiny, transparent, and glow namespace PrimWorkshop { public partial class frmPrimWorkshop : Form { #region Form Globals List Prims = null; FacetedMesh CurrentPrim = null; ProfileFace? CurrentFace = null; bool DraggingTexture = false; bool Wireframe = true; int[] TexturePointers = new int[1]; Dictionary Textures = new Dictionary(); #endregion Form Globals public frmPrimWorkshop() { 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); } private void frmPrimWorkshop_Shown(object sender, EventArgs e) { // Get a list of rendering plugins List renderers = RenderingLoader.ListRenderers("."); foreach (string r in renderers) { DialogResult result = MessageBox.Show( String.Format("Use renderer {0}?", r), "Select Rendering Plugin", MessageBoxButtons.YesNo); if (result == DialogResult.Yes) { Render.Plugin = RenderingLoader.LoadRenderer(r); break; } } if (Render.Plugin == null) { MessageBox.Show("No valid rendering plugin loaded, exiting..."); Application.Exit(); } } #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); Vector3 center = Vector3.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) { // Apply prim translation and rotation Gl.glMultMatrixf(Math3D.CreateTranslationMatrix(prim.Position)); Gl.glMultMatrixf(Math3D.CreateRotationMatrix(prim.Rotation)); } // 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(); } } // 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, 256d); 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(); dialog.Filter = "Prim Package (*.zip)|*.zip|Sculpt Map (*.png)|*.png|OAR XML (*.xml)|*.xml"; if (dialog.ShowDialog() == DialogResult.OK) { if (dialog.FileName.ToLowerInvariant().EndsWith(".zip")) { LoadPrimPackage(dialog.FileName); } else if (dialog.FileName.ToLowerInvariant().EndsWith(".xml")) { LoadXmlPrim(dialog.FileName); } else { LoadSculpt(dialog.FileName); } } } private void LoadDebugPrim() { Prims = new List(); Primitive prim = new Primitive(); prim.Textures = new Primitive.TextureEntry(UUID.Zero); prim.Scale = Vector3.One; prim.PrimData = ObjectManager.BuildBasicShape(PrimType.Cylinder); prim.PrimData.ProfileHollow = 0.95f; SimpleMesh simpleMesh = Render.Plugin.GenerateSimpleMesh(prim, DetailLevel.High); FacetedMesh facetedMesh = new FacetedMesh(); facetedMesh.Faces = new List { new Face { Vertices = simpleMesh.Vertices, Indices = simpleMesh.Indices } }; facetedMesh.Path = simpleMesh.Path; facetedMesh.Profile = simpleMesh.Profile; facetedMesh.Prim = prim; LoadMesh(facetedMesh, "."); PopulatePrimCombobox(); glControl.Invalidate(); } private void LoadXmlPrim(string filename) { byte[] data = File.ReadAllBytes(filename); OpenMetaverse.Assets.OarFile.LoadObjects(data, XmlObjectLoadedHandler, 0, data.Length); } private void XmlObjectLoadedHandler(OpenMetaverse.Assets.AssetPrim linkset, long bytesRead, long totalBytes) { Prims = new List(linkset.Children.Count + 1); Primitive parent = linkset.Parent.ToPrimitive(); { FacetedMesh mesh = null; if (parent.Sculpt == null || parent.Sculpt.SculptTexture == UUID.Zero) mesh = Render.Plugin.GenerateFacetedMesh(parent, DetailLevel.Highest); if (mesh != null) LoadMesh(mesh, null); } for (int i = 0; i < linkset.Children.Count; i++) { Primitive child = linkset.Children[i].ToPrimitive(); FacetedMesh mesh = null; if (parent.Sculpt == null || child.Sculpt.SculptTexture == UUID.Zero) mesh = Render.Plugin.GenerateFacetedMesh(child, DetailLevel.Highest); if (mesh != null) LoadMesh(mesh, null); } PopulatePrimCombobox(); glControl.Invalidate(); } private void LoadSculpt(string filename) { // Try to parse this as an image file Image sculptTexture = Image.FromFile(filename); Primitive prim = new Primitive(); prim.PrimData = ObjectManager.BuildBasicShape(PrimType.Sculpt); prim.Sculpt = new Primitive.SculptData { SculptTexture = UUID.Random(), Type = SculptType.Sphere }; prim.Textures = new Primitive.TextureEntry(UUID.Zero); prim.Scale = Vector3.One * 3f; FacetedMesh mesh = Render.Plugin.GenerateFacetedSculptMesh(prim, (Bitmap)sculptTexture, DetailLevel.Highest); if (mesh != null) { Prims = new List(1); LoadMesh(mesh, null); } glControl.Invalidate(); } private void LoadPrimPackage(string filename) { string tempPath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), System.IO.Path.GetRandomFileName()); try { // Create a temporary directory Directory.CreateDirectory(tempPath); FastZip fastzip = new FastZip(); fastzip.ExtractZip(filename, tempPath, String.Empty); } catch (Exception ex) { MessageBox.Show(ex.Message); return; } // Check for the prims.xml file string primsFile = System.IO.Path.Combine(tempPath, "prims.xml"); if (!File.Exists(primsFile)) { MessageBox.Show("prims.xml not found in the archive"); return; } OSD osd = null; try { osd = OSDParser.DeserializeLLSDXml(File.ReadAllText(primsFile)); } catch (Exception ex) { MessageBox.Show(ex.Message); } if (osd != null && osd.Type == OSDType.Map) { List primList = Helpers.OSDToPrimList(osd); Prims = new List(primList.Count); for (int i = 0; i < primList.Count; i++) { Primitive prim = primList[i]; FacetedMesh mesh = null; if (prim.Sculpt.SculptTexture != UUID.Zero) { Image sculptTexture = null; if (LoadTexture(tempPath, prim.Sculpt.SculptTexture, ref sculptTexture)) mesh = Render.Plugin.GenerateFacetedSculptMesh(prim, (Bitmap)sculptTexture, DetailLevel.Highest); } else { mesh = Render.Plugin.GenerateFacetedMesh(prim, DetailLevel.Highest); } if (mesh != null) LoadMesh(mesh, tempPath); } // Setup the dropdown list of prims PopulatePrimCombobox(); glControl.Invalidate(); } else { MessageBox.Show("Failed to load LLSD formatted primitive data from " + filename); } Directory.Delete(tempPath); } private void LoadMesh(FacetedMesh mesh, string basePath) { // 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 Primitive.TextureEntryFace teFace = mesh.Prim.Textures.GetFace((uint)j); Render.Plugin.TransformTexCoords(face.Vertices, face.Center, teFace, mesh.Prim.Scale); // 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 (!String.IsNullOrEmpty(basePath) && LoadTexture(basePath, 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); } private bool LoadTexture(string basePath, UUID textureID, ref System.Drawing.Image texture) { if (Textures.ContainsKey(textureID)) { texture = Textures[textureID]; return true; } string texturePath = System.IO.Path.Combine(basePath, textureID.ToString()); if (File.Exists(texturePath + ".tga")) { try { texture = (Image)LoadTGAClass.LoadTGA(texturePath + ".tga"); Textures[textureID] = texture; return true; } catch (Exception) { } } else if (File.Exists(texturePath + ".jp2")) { try { ManagedImage managedImage; if (OpenJPEG.DecodeToImage(File.ReadAllBytes(texturePath + ".jp2"), out managedImage, out texture)) { Textures[textureID] = texture; 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 = (FacetedMesh)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 && CurrentPrim.Profile.Faces != 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(); } } public struct FaceData { public float[] Vertices; public ushort[] Indices; public float[] TexCoords; public int TexturePointer; public System.Drawing.Image Texture; // TODO: Normals } public static class Render { public static IRendering Plugin; } }