using System.Windows.Forms;
using System.Text;
using System.Threading;
+using System.Linq;
using OpenTK.Graphics.OpenGL;
using OpenMetaverse;
using OpenMetaverse.Rendering;
public OpenTK.GLControl glControl = null;
- List<FacetedMesh> Prims = null;
+ Dictionary<uint, FacetedMesh> Prims = new Dictionary<uint, FacetedMesh>();
bool Wireframe = false;
public bool RenderingEnabled = false;
MeshmerizerR renderer;
OpenTK.Graphics.GraphicsMode GLMode = null;
AutoResetEvent TextureThreadContextReady = new AutoResetEvent(false);
+ BlockingQueue<TextureLoadItem> PendingTextures = new BlockingQueue<TextureLoadItem>();
+
#endregion Form Globals
Client.Objects.TerseObjectUpdate += new EventHandler<TerseObjectUpdateEventArgs>(Objects_TerseObjectUpdate);
Client.Objects.ObjectUpdate += new EventHandler<PrimEventArgs>(Objects_ObjectUpdate);
+ Client.Objects.ObjectDataBlockUpdate += new EventHandler<ObjectDataBlockUpdateEventArgs>(Objects_ObjectDataBlockUpdate);
}
public void SetupGLControl()
glControl.MouseMove += glControl_MouseMove;
glControl.MouseWheel += glControl_MouseWheel;
glControl.Load += new EventHandler(glControl_Load);
+ glControl.Disposed += new EventHandler(glControl_Disposed);
glControl.Dock = DockStyle.Fill;
Controls.Add(glControl);
+ glControl.BringToFront();
+ }
+
+ void glControl_Disposed(object sender, EventArgs e)
+ {
+ TextureThreadRunning = false;
+ PendingTextures.Close();
}
void glControl_Load(object sender, EventArgs e)
OpenTK.Graphics.IGraphicsContext context = new OpenTK.Graphics.GraphicsContext(GLMode, window.WindowInfo);
context.MakeCurrent(window.WindowInfo);
TextureThreadContextReady.Set();
+ PendingTextures.Open();
Logger.DebugLog("Started Texture Thread");
while (window.Exists && TextureThreadRunning)
GL.BindTexture(TextureTarget.Texture2D, item.Data.TexturePointer);
Bitmap bitmap = new Bitmap(item.Data.Texture);
+
bool hasAlpha;
if (item.Data.Texture.PixelFormat == System.Drawing.Imaging.PixelFormat.Format32bppArgb)
{
bitmap.UnlockBits(bitmapData);
bitmap.Dispose();
+ GL.Flush();
SafeInvalidate();
}
- GL.Flush();
Thread.Sleep(10);
}
Logger.DebugLog("Texture thread exited");
void frmPrimWorkshop_Disposed(object sender, EventArgs e)
{
+ glControl.Dispose();
Client.Objects.TerseObjectUpdate -= new EventHandler<TerseObjectUpdateEventArgs>(Objects_TerseObjectUpdate);
Client.Objects.ObjectUpdate -= new EventHandler<PrimEventArgs>(Objects_ObjectUpdate);
- TextureThreadRunning = false;
- PendingTextures.Close();
}
void Objects_TerseObjectUpdate(object sender, TerseObjectUpdateEventArgs e)
{
- if (Prims != null && null != Prims.Find(fm => fm.Prim.LocalID == e.Update.LocalID))
+ if (Prims.ContainsKey(e.Prim.LocalID))
{
SafeInvalidate();
}
void Objects_ObjectUpdate(object sender, PrimEventArgs e)
{
- if (Prims != null && null != Prims.Find(fm => fm.Prim.LocalID == e.Prim.LocalID))
+ if (Prims.ContainsKey(e.Prim.LocalID) || Prims.ContainsKey(e.Prim.ParentID))
{
- SafeInvalidate();
+ AddPrimBlocking(e.Prim);
+ }
+ }
+
+ void Objects_ObjectDataBlockUpdate(object sender, ObjectDataBlockUpdateEventArgs e)
+ {
+ if (Prims.ContainsKey(e.Prim.LocalID))
+ {
+ AddPrimBlocking(e.Prim);
}
}
+
void SafeInvalidate()
{
if (InvokeRequired)
private void Render(bool picking)
{
+ glControl.MakeCurrent();
if (picking)
{
GL.ClearColor(1f, 1f, 1f, 1f);
Center.X, Center.Y, Center.Z,
0d, 0d, 1d);
GL.MultMatrix(ref mLookAt);
-
+
//OpenTK.Graphics.Glu.LookAt(
// Center.X, (double)scrollZoom.Value * 0.1d + Center.Y, Center.Z,
// Center.X, Center.Y, Center.Z,
// 0d, 0d, 1d);
-
+
//GL.Light(LightName.Light0, LightParameter.Position, lightPos);
// Push the world matrix
GL.Rotate((float)scrollPitch.Value, 0f, 1f, 0f);
GL.Rotate((float)scrollYaw.Value, 0f, 0f, 1f);
- if (Prims != null)
+ lock (Prims)
{
- for (int i = 0; i < Prims.Count; i++)
+ int primNr = 0;
+ foreach (FacetedMesh mesh in Prims.Values)
{
- Primitive prim = Prims[i].Prim;
-
+ primNr++;
+ Primitive prim = mesh.Prim;
// Individual prim matrix
GL.PushMatrix();
- if (Prims[i].Prim.ParentID != 0)
+ if (prim.ParentID != 0)
{
- var parent = Prims.Find(fm => fm.Prim.LocalID == Prims[i].Prim.ParentID);
-
- if (parent != null)
+ FacetedMesh parent = null;
+ if (Prims.TryGetValue(prim.ParentID, out parent))
{
// Apply prim translation and rotation relative to the root prim
GL.MultMatrix(Math3D.CreateRotationMatrix(parent.Prim.Rotation));
GL.Scale(prim.Scale.X, prim.Scale.Y, prim.Scale.Z);
// Draw the prim faces
- for (int j = 0; j < Prims[i].Faces.Count; j++)
+ for (int j = 0; j < mesh.Faces.Count; j++)
{
- Primitive.TextureEntryFace teFace = Prims[i].Prim.Textures.FaceTextures[j];
+ Primitive.TextureEntryFace teFace = mesh.Prim.Textures.FaceTextures[j];
if (teFace == null)
- teFace = Prims[i].Prim.Textures.DefaultTexture;
+ teFace = mesh.Prim.Textures.DefaultTexture;
// Don't render transparent faces
if (teFace.RGBA.A <= 0.01f) continue;
break;
}
- Face face = Prims[i].Faces[j];
+ Face face = mesh.Faces[j];
FaceData data = (FaceData)face.UserData;
if (!picking)
}
else
{
- var primNr = Utils.Int16ToBytes((short)i);
- var faceColor = new byte[] { primNr[0], primNr[1], (byte)j, 255 };
+ data.PickingID = primNr;
+ var primNrBytes = Utils.Int16ToBytes((short)primNr);
+ var faceColor = new byte[] { primNrBytes[0], primNrBytes[1], (byte)j, 255 };
GL.Color4(faceColor);
}
{
// Save old attributes
GL.PushAttrib(AttribMask.AllAttribBits);
-
+
// Disable some attributes to make the objects flat / solid color when they are drawn
GL.Disable(EnableCap.Fog);
GL.Disable(EnableCap.Texture2D);
int primID = Utils.BytesToUInt16(color, 0);
int faceID = color[2];
- if (Prims.Count > primID)
+ FacetedMesh picked = null;
+
+ lock (Prims)
{
- Client.Self.Grab(Prims[primID].Prim.LocalID, Vector3.Zero, Vector3.Zero, Vector3.Zero, faceID, Vector3.Zero, Vector3.Zero, Vector3.Zero);
- Client.Self.DeGrab(Prims[primID].Prim.LocalID);
+ foreach (var mesh in Prims.Values)
+ {
+ foreach (var face in mesh.Faces)
+ {
+ if (((FaceData)face.UserData).PickingID == primID)
+ {
+ picked = mesh;
+ break;
+ }
+ }
+
+ if (picked != null) break;
+ }
+ }
+
+ if (picked != null)
+ {
+ Client.Self.Grab(picked.Prim.LocalID, Vector3.Zero, Vector3.Zero, Vector3.Zero, faceID, Vector3.Zero, Vector3.Zero, Vector3.Zero);
+ Client.Self.DeGrab(picked.Prim.LocalID);
}
}
private void glControl_Resize(object sender, EventArgs e)
{
if (!RenderingEnabled) return;
+ glControl.MakeCurrent();
GL.ClearColor(0.39f, 0.58f, 0.93f, 1.0f);
}
#endregion GLControl Callbacks
- public void loadPrims(List<Primitive> primList)
- {
- if (!RenderingEnabled) return;
-
- ThreadPool.QueueUserWorkItem((object sync) =>
- {
- loadPrimsBlocking(primList);
- }
- );
- }
-
public FacetedMesh GenerateFacetedMesh(Primitive prim, OSDMap MeshData, DetailLevel LOD)
{
FacetedMesh ret = new FacetedMesh();
return ret;
}
- BlockingQueue<TextureLoadItem> PendingTextures = new BlockingQueue<TextureLoadItem>();
-
- private void loadPrimsBlocking(List<Primitive> primList)
+ public void loadPrims(List<Primitive> primList)
{
- Prims = null;
- Prims = new List<FacetedMesh>(primList.Count);
- PendingTextures.Open();
+ if (!RenderingEnabled) return;
- for (int i = 0; i < primList.Count; i++)
+ ThreadPool.QueueUserWorkItem((object sync) =>
{
- FacetedMesh mesh = null;
- Primitive prim = primList[i];
- if (prim.Textures == null)
- continue;
+ primList.ForEach(p => AddPrimBlocking(p));
+ });
+ }
+
+ private void AddPrimBlocking(Primitive prim)
+ {
+
+ FacetedMesh mesh = null;
+ FacetedMesh existingMesh = null;
- try
+ lock (Prims)
+ {
+ if (Prims.ContainsKey(prim.LocalID))
{
- if (prim.Sculpt != null && prim.Sculpt.SculptTexture != UUID.Zero)
- {
- if (prim.Sculpt.Type != SculptType.Mesh)
- { // Regular sculptie
- Image img = null;
- if (!LoadTexture(primList[i].Sculpt.SculptTexture, ref img, true))
- continue;
- mesh = renderer.GenerateFacetedSculptMesh(prim, (Bitmap)img, DetailLevel.Highest);
- }
- else
- { // Mesh
- AutoResetEvent gotMesh = new AutoResetEvent(false);
- bool meshSuccess = false;
+ existingMesh = Prims[prim.LocalID];
+ }
+ }
- Client.Assets.RequestMesh(prim.Sculpt.SculptTexture, (success, meshAsset) =>
- {
- if (!success || !meshAsset.Decode())
- {
- Logger.Log("Failed to fetch or decode the mesh asset", Helpers.LogLevel.Warning, Client);
- }
- else
- {
- mesh = GenerateFacetedMesh(prim, meshAsset.MeshData, DetailLevel.Highest);
- meshSuccess = true;
- }
- gotMesh.Set();
- });
-
- if (!gotMesh.WaitOne(20 * 1000, false)) continue;
- if (!meshSuccess) continue;
- }
+ if (prim.Textures == null)
+ return;
+
+ try
+ {
+ if (prim.Sculpt != null && prim.Sculpt.SculptTexture != UUID.Zero)
+ {
+ if (prim.Sculpt.Type != SculptType.Mesh)
+ { // Regular sculptie
+ Image img = null;
+ if (!LoadTexture(prim.Sculpt.SculptTexture, ref img, true))
+ return;
+ mesh = renderer.GenerateFacetedSculptMesh(prim, (Bitmap)img, DetailLevel.Highest);
}
else
- {
- mesh = renderer.GenerateFacetedMesh(prim, DetailLevel.Highest);
+ { // Mesh
+ AutoResetEvent gotMesh = new AutoResetEvent(false);
+ bool meshSuccess = false;
+
+ Client.Assets.RequestMesh(prim.Sculpt.SculptTexture, (success, meshAsset) =>
+ {
+ if (!success || !meshAsset.Decode())
+ {
+ Logger.Log("Failed to fetch or decode the mesh asset", Helpers.LogLevel.Warning, Client);
+ }
+ else
+ {
+ mesh = GenerateFacetedMesh(prim, meshAsset.MeshData, DetailLevel.Highest);
+ meshSuccess = true;
+ }
+ gotMesh.Set();
+ });
+
+ if (!gotMesh.WaitOne(20 * 1000, false)) return;
+ if (!meshSuccess) return;
}
}
- catch
+ else
{
- continue;
+ mesh = renderer.GenerateFacetedMesh(prim, DetailLevel.Highest);
}
+ }
+ catch
+ {
+ return;
+ }
- // Create a FaceData struct for each face that stores the 3D data
- // in a OpenGL friendly format
- for (int j = 0; j < mesh.Faces.Count; j++)
+ // Create a FaceData struct for each face that stores the 3D data
+ // in a 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];
+ data.Normals = new float[face.Vertices.Count * 3];
+ for (int k = 0; k < face.Vertices.Count; k++)
{
- Face face = mesh.Faces[j];
- FaceData data = new FaceData();
+ 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;
- // Vertices for this face
- data.Vertices = new float[face.Vertices.Count * 3];
- data.Normals = 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;
+ data.Normals[k * 3 + 0] = face.Vertices[k].Normal.X;
+ data.Normals[k * 3 + 1] = face.Vertices[k].Normal.Y;
+ data.Normals[k * 3 + 2] = face.Vertices[k].Normal.Z;
+ }
- data.Normals[k * 3 + 0] = face.Vertices[k].Normal.X;
- data.Normals[k * 3 + 1] = face.Vertices[k].Normal.Y;
- data.Normals[k * 3 + 2] = face.Vertices[k].Normal.Z;
- }
+ // Indices for this face
+ data.Indices = face.Indices.ToArray();
- // Indices for this face
- data.Indices = face.Indices.ToArray();
+ // Texture transform for this face
+ Primitive.TextureEntryFace teFace = prim.Textures.GetFace((uint)j);
+ renderer.TransformTexCoords(face.Vertices, face.Center, teFace);
- // Texture transform for this face
- Primitive.TextureEntryFace teFace = prim.Textures.GetFace((uint)j);
- renderer.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;
+ }
- // 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;
- }
+ // Set the UserData for this face to our FaceData struct
+ face.UserData = data;
+ mesh.Faces[j] = face;
+
+
+ if (existingMesh != null &&
+ existingMesh.Faces[j].TextureFace.TextureID == teFace.TextureID &&
+ ((FaceData)existingMesh.Faces[j].UserData).TexturePointer != 0
+ )
+ {
+ FaceData existingData = (FaceData)existingMesh.Faces[j].UserData;
+ data.TexturePointer = existingData.TexturePointer;
+ }
+ else
+ {
var textureItem = new TextureLoadItem()
{
};
PendingTextures.Enqueue(textureItem);
-
- // Set the UserData for this face to our FaceData struct
- face.UserData = data;
- mesh.Faces[j] = face;
}
- Prims.Add(mesh);
- SafeInvalidate();
}
+
+ lock (Prims)
+ {
+ Prims[prim.LocalID] = mesh;
+ }
+ SafeInvalidate();
}
private bool LoadTexture(UUID textureID, ref Image texture, bool removeAlpha)
private void glControl_MouseUp(object sender, MouseEventArgs e)
{
- if (e.Button == MouseButtons.Left )
+ if (e.Button == MouseButtons.Left)
{
dragging = false;
public float[] TexCoords;
public float[] Normals;
public int TexturePointer;
+ public int PickingID = -1;
public System.Drawing.Image Texture;
}
public static class MeshToOBJ
{
- public static bool MeshesToOBJ(List<FacetedMesh> meshes, string filename)
+ public static bool MeshesToOBJ(Dictionary<uint, FacetedMesh> meshes, string filename)
{
StringBuilder obj = new StringBuilder();
StringBuilder mtl = new StringBuilder();
mtl.AppendLine("# Created by libprimrender");
mtl.AppendLine();
- for (int i = 0; i < meshes.Count; i++)
+ int primNr = 0;
+ foreach (FacetedMesh mesh in meshes.Values)
{
- FacetedMesh 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);
+ string mtlName = String.Format("material{0}-{1}", primNr, face.ID);
Primitive.TextureEntryFace tex = face.TextureFace;
string texName = tex.TextureID.ToString() + ".tga";
break;
}
- obj.AppendFormat("g face{0}-{1}{2}", i, face.ID, Environment.NewLine);
+ obj.AppendFormat("g face{0}-{1}{2}", primNr, 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);
#endregion Elements
}
}
+ primNr++;
}
try