corrade-vassal – Rev 16
?pathlinks?
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<FacetedMesh> Prims = null;
FacetedMesh CurrentPrim = null;
ProfileFace? CurrentFace = null;
bool DraggingTexture = false;
bool Wireframe = true;
int[] TexturePointers = new int[1];
Dictionary<UUID, Image> Textures = new Dictionary<UUID, Image>();
#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<string> 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<FacetedMesh>();
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<Face> { 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<FacetedMesh>(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<FacetedMesh>(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<Primitive> primList = Helpers.OSDToPrimList(osd);
Prims = new List<FacetedMesh>(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 <jhurliman@jhurliman.org> (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;
}
}