using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.IO; using System.Xml; using Tao.OpenGl; using Tao.Platform.Windows; using OpenMetaverse; using OpenMetaverse.Imaging; using OpenMetaverse.Rendering; using OpenMetaverse.Assets; namespace AvatarPreview { public partial class frmAvatar : Form { GridClient _client = new GridClient(); Dictionary _meshes = new Dictionary(); bool _wireframe = true; bool _showSkirt = false; public frmAvatar() { 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); glControl_Resize(null, null); } private void lindenLabMeshToolStripMenuItem_Click(object sender, EventArgs e) { OpenFileDialog dialog = new OpenFileDialog(); dialog.Filter = "avatar_lad.xml|avatar_lad.xml"; if (dialog.ShowDialog() == DialogResult.OK) { _meshes.Clear(); try { // Parse through avatar_lad.xml to find all of the mesh references XmlDocument lad = new XmlDocument(); lad.Load(dialog.FileName); XmlNodeList meshes = lad.GetElementsByTagName("mesh"); foreach (XmlNode meshNode in meshes) { string type = meshNode.Attributes.GetNamedItem("type").Value; int lod = Int32.Parse(meshNode.Attributes.GetNamedItem("lod").Value); string fileName = meshNode.Attributes.GetNamedItem("file_name").Value; //string minPixelWidth = meshNode.Attributes.GetNamedItem("min_pixel_width").Value; // Mash up the filename with the current path fileName = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(dialog.FileName), fileName); GLMesh mesh = (_meshes.ContainsKey(type) ? _meshes[type] : new GLMesh(type)); if (lod == 0) { mesh.LoadMesh(fileName); } else { mesh.LoadLODMesh(lod, fileName); } _meshes[type] = mesh; glControl_Resize(null, null); glControl.Invalidate(); } } catch (Exception ex) { MessageBox.Show("Failed to load avatar mesh: " + ex.Message); } } } private void textureToolStripMenuItem_Click(object sender, EventArgs e) { } private void exitToolStripMenuItem_Click(object sender, EventArgs e) { } private void wireframeToolStripMenuItem_Click(object sender, EventArgs e) { wireframeToolStripMenuItem.Checked = !wireframeToolStripMenuItem.Checked; _wireframe = wireframeToolStripMenuItem.Checked; glControl.Invalidate(); } private void skirtToolStripMenuItem_Click(object sender, EventArgs e) { skirtToolStripMenuItem.Checked = !skirtToolStripMenuItem.Checked; _showSkirt = skirtToolStripMenuItem.Checked; glControl.Invalidate(); } private void aboutToolStripMenuItem_Click(object sender, EventArgs e) { MessageBox.Show( "Written by John Hurliman (http://www.jhurliman.org/)"); } 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); // 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 (_meshes.Count > 0) { foreach (GLMesh mesh in _meshes.Values) { if (!_showSkirt && mesh.Name == "skirtMesh") continue; Gl.glColor3f(1f, 1f, 1f); // Individual prim matrix Gl.glPushMatrix(); //Gl.glTranslatef(mesh.Position.X, mesh.Position.Y, mesh.Position.Z); Gl.glRotatef(mesh.RotationAngles.X, 1f, 0f, 0f); Gl.glRotatef(mesh.RotationAngles.Y, 0f, 1f, 0f); Gl.glRotatef(mesh.RotationAngles.Z, 0f, 0f, 1f); Gl.glScalef(mesh.Scale.X, mesh.Scale.Y, mesh.Scale.Z); // TODO: Texturing Gl.glTexCoordPointer(2, Gl.GL_FLOAT, 0, mesh.RenderData.TexCoords); Gl.glVertexPointer(3, Gl.GL_FLOAT, 0, mesh.RenderData.Vertices); Gl.glDrawElements(Gl.GL_TRIANGLES, mesh.RenderData.Indices.Length, Gl.GL_UNSIGNED_SHORT, mesh.RenderData.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); // Cornflower blue anyone? Gl.glClearColor(0f, 0f, 0f, 1f); Gl.glPushMatrix(); Gl.glMatrixMode(Gl.GL_PROJECTION); Gl.glLoadIdentity(); Gl.glViewport(0, 0, glControl.Width, glControl.Height); Glu.gluPerspective(50.0d, 1.0d, 0.001d, 50d); Vector3 center = Vector3.Zero; GLMesh head, lowerBody; if (_meshes.TryGetValue("headMesh", out head) && _meshes.TryGetValue("lowerBodyMesh", out lowerBody)) center = (head.RenderData.Center + lowerBody.RenderData.Center) / 2f; Glu.gluLookAt( center.X, (double)scrollZoom.Value * 0.1d + center.Y, center.Z, center.X, (double)scrollZoom.Value * 0.1d + center.Y + 1d, center.Z, 0d, 0d, 1d); Gl.glMatrixMode(Gl.GL_MODELVIEW); } private void scroll_ValueChanged(object sender, EventArgs e) { glControl_Resize(null, null); glControl.Invalidate(); } private void pic_MouseClick(object sender, MouseEventArgs e) { PictureBox control = (PictureBox)sender; OpenFileDialog dialog = new OpenFileDialog(); // TODO: Setup a dialog.Filter for supported image types if (dialog.ShowDialog() == DialogResult.OK) { try { System.Drawing.Image image = System.Drawing.Image.FromFile(dialog.FileName); #region Dimensions Check if (control == picEyesBake) { // Eyes texture is 128x128 if (Width != 128 || Height != 128) { Bitmap resized = new Bitmap(128, 128, image.PixelFormat); Graphics graphics = Graphics.FromImage(resized); graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; graphics.DrawImage(image, 0, 0, 128, 128); image.Dispose(); image = resized; } } else { // Other textures are 512x512 if (Width != 128 || Height != 128) { Bitmap resized = new Bitmap(512, 512, image.PixelFormat); Graphics graphics = Graphics.FromImage(resized); graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; graphics.DrawImage(image, 0, 0, 512, 512); image.Dispose(); image = resized; } } #endregion Dimensions Check // Set the control image control.Image = image; } catch (Exception ex) { MessageBox.Show("Failed to load image: " + ex.Message); } } else { control.Image = null; } #region Baking Dictionary paramValues = GetParamValues(); Dictionary layers = new Dictionary(); int textureCount = 0; if ((string)control.Tag == "Head") { if (picHair.Image != null) { layers.Add(AvatarTextureIndex.Hair, new AssetTexture(new ManagedImage((Bitmap)picHair.Image))); ++textureCount; } if (picHeadBodypaint.Image != null) { layers.Add(AvatarTextureIndex.HeadBodypaint, new AssetTexture(new ManagedImage((Bitmap)picHeadBodypaint.Image))); ++textureCount; } // Compute the head bake Baker baker = new Baker(BakeType.Head); foreach (KeyValuePair kvp in layers) { AppearanceManager.TextureData tdata = new AppearanceManager.TextureData(); tdata.Texture = kvp.Value; baker.AddTexture(tdata); } baker.Bake(); if (baker.BakedTexture != null) { AssetTexture bakeAsset = baker.BakedTexture; // Baked textures use the alpha layer for other purposes, so we need to not use it bakeAsset.Image.Channels = ManagedImage.ImageChannels.Color; picHeadBake.Image = LoadTGAClass.LoadTGA(new MemoryStream(bakeAsset.Image.ExportTGA())); } else { MessageBox.Show("Failed to create the bake layer, unknown error"); } } else if ((string)control.Tag == "Upper") { if (picUpperBodypaint.Image != null) { layers.Add(AvatarTextureIndex.UpperBodypaint, new AssetTexture(new ManagedImage((Bitmap)picUpperBodypaint.Image))); ++textureCount; } if (picUpperGloves.Image != null) { layers.Add(AvatarTextureIndex.UpperGloves, new AssetTexture(new ManagedImage((Bitmap)picUpperGloves.Image))); ++textureCount; } if (picUpperUndershirt.Image != null) { layers.Add(AvatarTextureIndex.UpperUndershirt, new AssetTexture(new ManagedImage((Bitmap)picUpperUndershirt.Image))); ++textureCount; } if (picUpperShirt.Image != null) { layers.Add(AvatarTextureIndex.UpperShirt, new AssetTexture(new ManagedImage((Bitmap)picUpperShirt.Image))); ++textureCount; } if (picUpperJacket.Image != null) { layers.Add(AvatarTextureIndex.UpperJacket, new AssetTexture(new ManagedImage((Bitmap)picUpperJacket.Image))); ++textureCount; } // Compute the upper body bake Baker baker = new Baker(BakeType.UpperBody); foreach (KeyValuePair kvp in layers) { AppearanceManager.TextureData tdata = new AppearanceManager.TextureData(); tdata.Texture = kvp.Value; baker.AddTexture(tdata); } baker.Bake(); if (baker.BakedTexture != null) { AssetTexture bakeAsset = baker.BakedTexture; // Baked textures use the alpha layer for other purposes, so we need to not use it bakeAsset.Image.Channels = ManagedImage.ImageChannels.Color; picUpperBodyBake.Image = LoadTGAClass.LoadTGA(new MemoryStream(bakeAsset.Image.ExportTGA())); } else { MessageBox.Show("Failed to create the bake layer, unknown error"); } } else if ((string)control.Tag == "Lower") { if (picLowerBodypaint.Image != null) { layers.Add(AvatarTextureIndex.LowerBodypaint, new AssetTexture(new ManagedImage((Bitmap)picLowerBodypaint.Image))); ++textureCount; } if (picLowerUnderpants.Image != null) { layers.Add(AvatarTextureIndex.LowerUnderpants, new AssetTexture(new ManagedImage((Bitmap)picLowerUnderpants.Image))); ++textureCount; } if (picLowerSocks.Image != null) { layers.Add(AvatarTextureIndex.LowerSocks, new AssetTexture(new ManagedImage((Bitmap)picLowerSocks.Image))); ++textureCount; } if (picLowerShoes.Image != null) { layers.Add(AvatarTextureIndex.LowerShoes, new AssetTexture(new ManagedImage((Bitmap)picLowerShoes.Image))); ++textureCount; } if (picLowerPants.Image != null) { layers.Add(AvatarTextureIndex.LowerPants, new AssetTexture(new ManagedImage((Bitmap)picLowerPants.Image))); ++textureCount; } // Compute the lower body bake Baker baker = new Baker(BakeType.LowerBody); foreach (KeyValuePair kvp in layers) { AppearanceManager.TextureData tdata = new AppearanceManager.TextureData(); tdata.Texture = kvp.Value; baker.AddTexture(tdata); } baker.Bake(); if (baker.BakedTexture != null) { AssetTexture bakeAsset = baker.BakedTexture; // Baked textures use the alpha layer for other purposes, so we need to not use it bakeAsset.Image.Channels = ManagedImage.ImageChannels.Color; picLowerBodyBake.Image = LoadTGAClass.LoadTGA(new MemoryStream(bakeAsset.Image.ExportTGA())); } else { MessageBox.Show("Failed to create the bake layer, unknown error"); } } else if ((string)control.Tag == "Bake") { // Bake image has been set manually, no need to manually calculate a bake // FIXME: } #endregion Baking } private Dictionary GetParamValues() { Dictionary paramValues = new Dictionary(VisualParams.Params.Count); foreach (KeyValuePair kvp in VisualParams.Params) { VisualParam vp = kvp.Value; paramValues.Add(vp.ParamID, vp.DefaultValue); } return paramValues; } } }