corrade-vassal – Rev 16
?pathlinks?
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 OpenMetaverse;
using OpenMetaverse.StructuredData;
using OpenMetaverse.Imaging;
using OpenMetaverse.Rendering;
using OpenMetaverse.Assets;
namespace PrimWorkshop
{
public partial class frmBrowser : Form
{
const float DEG_TO_RAD = 0.0174532925f;
const uint TERRAIN_START = (uint)Int32.MaxValue + 1;
ContextMenu ExportPrimMenu;
ContextMenu ExportTerrainMenu;
GridClient Client;
Camera Camera;
Dictionary<uint, Primitive> RenderFoliageList = new Dictionary<uint, Primitive>();
Dictionary<uint, RenderablePrim> RenderPrimList = new Dictionary<uint, RenderablePrim>();
Dictionary<UUID, GlacialComponents.Controls.GLItem> DownloadList = new Dictionary<UUID, GlacialComponents.Controls.GLItem>();
EventHandler IdleEvent;
System.Timers.Timer ProgressTimer;
int TotalPrims;
// Textures
Dictionary<UUID, TextureInfo> Textures = new Dictionary<UUID, TextureInfo>();
// Terrain
float MaxHeight = 0.1f;
TerrainPatch[,] Heightmap;
HeightmapLookupValue[] LookupHeightTable;
// Picking globals
bool Clicked = false;
int ClickX = 0;
int ClickY = 0;
uint LastHit = 0;
//warning CS0414: The private field `PrimWorkshop.frmBrowser.PivotPosition' is assigned but its value is never used
Vector3 PivotPosition = Vector3.Zero;
private bool Pivoting;
Point LastPivot;
//
const int SELECT_BUFSIZE = 512;
uint[] SelectBuffer = new uint[SELECT_BUFSIZE];
//warning CS0414: The private field `PrimWorkshop.frmBrowser.msg' is assigned but its value is never used
NativeMethods.Message msg;
private bool AppStillIdle
{
get { return !NativeMethods.PeekMessage(out msg, IntPtr.Zero, 0, 0, 0); }
}
/// <summary>
/// Default constructor
/// </summary>
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
InitHeightmap();
InitOpenGL();
InitCamera();
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()
{
InitLists();
if (DownloadList != null)
lock (DownloadList)
DownloadList.Clear();
// Initialize the SL client
Client = new GridClient();
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.USE_ASSET_CACHE = true;
//Client.Settings.ASSET_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.LoginProgress += Network_OnLogin;
Client.Network.Disconnected += Network_OnDisconnected;
Client.Network.SimChanged += Network_OnCurrentSimChanged;
Client.Network.EventQueueRunning += Network_OnEventQueueRunning;
Client.Objects.ObjectUpdate += Objects_OnNewPrim;
Client.Terrain.LandPatchReceived += new EventHandler<LandPatchReceivedEventArgs>(Terrain_LandPatchReceived);
Client.Parcels.SimParcelsDownloaded += new EventHandler<SimParcelsDownloadedEventArgs>(Parcels_SimParcelsDownloaded);
Client.Assets.ImageReceiveProgress += new EventHandler<ImageReceiveProgressEventArgs>(Assets_ImageReceiveProgress);
// 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);
*/
}
void Objects_NewPrim(object sender, PrimEventArgs e)
{
throw new NotImplementedException();
}
void Parcels_SimParcelsDownloaded(object sender, SimParcelsDownloadedEventArgs e)
{
TotalPrims = 0;
e.Parcels.ForEach(
delegate(Parcel parcel)
{
TotalPrims += parcel.TotalPrims;
});
UpdatePrimProgress(); TotalPrims = 0;
e.Parcels.ForEach(
delegate(Parcel parcel)
{
TotalPrims += parcel.TotalPrims;
});
UpdatePrimProgress();
}
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 TerrainPatch[16, 16];
for (int y = 0; y < 16; y++)
{
for (int x = 0; x < 16; x++)
{
Heightmap[y, x] = new TerrainPatch();
Heightmap[y, x].Data = 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((ushort)(i + (j * 256)), ((float)i * ((float)j / 127.0f)));
}
}
Array.Sort<HeightmapLookupValue>(LookupHeightTable);
}
private void InitCamera()
{
Camera = new Camera();
Camera.Position = new Vector3(128f, -192f, 90f);
Camera.FocalPoint = new Vector3(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<Primitive> primList = new List<Primitive>();
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<Primitive> primList = new List<Primitive>();
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<Primitive> primList, string fileName, out int prims, out int textures, out string error)
{
List<UUID> textureList = new List<UUID>();
prims = 0;
textures = 0;
// Write the LLSD to the hard drive in XML format
string output = OSDParser.SerializeLLSDXmlString(Helpers.PrimListToOSD(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++)
{
Primitive.TextureEntryFace face = prim.Textures.FaceTextures[i];
if (face != null && face.TextureID != UUID.Zero && face.TextureID != Primitive.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 (UUID texture in textureList)
{
string tempFileName = Client.Assets.Cache.AssetFileName(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<Primitive> 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);
OSD osd = OSDParser.DeserializeLLSDXml(raw);
return Helpers.OSDToPrimList(osd);
}
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].Data[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<HeightmapLookupValue>(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)));
Vector3 targetPos = new Vector3(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<Primitive> 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(object sender, PrimEventArgs e)
{
Primitive prim = e.Prim;
if (prim.PrimData.PCode == PCode.Grass || prim.PrimData.PCode == PCode.Tree || prim.PrimData.PCode == PCode.NewTree)
{
lock (RenderFoliageList)
RenderFoliageList[prim.LocalID] = prim;
return;
}
RenderablePrim render = new RenderablePrim();
render.Prim = prim;
// FIXME: Handle sculpted prims by calling Render.Plugin.GenerateFacetedSculptMesh() instead
render.Mesh = Render.Plugin.GenerateFacetedMesh(prim, DetailLevel.High);
// 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
Primitive.TextureEntryFace teFace = prim.Textures.GetFace((uint)j);
Render.Plugin.TransformTexCoords(face.Vertices, face.Center, teFace, 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 (teFace.TextureID != UUID.Zero &&
teFace.TextureID != Primitive.TextureEntry.WHITE_TEXTURE)
{
lock (Textures)
{
if (!Textures.ContainsKey(teFace.TextureID))
{
// We haven't constructed this image in OpenGL yet, get ahold of it
Client.Assets.RequestImage(teFace.TextureID, ImageType.Normal, TextureDownloader_OnDownloadFinished);
}
}
}
// 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 Terrain_LandPatchReceived(object sender, LandPatchReceivedEventArgs e)
{
if (Client != null && Client.Network.CurrentSim == e.Simulator)
{
Heightmap[e.Y, e.X].Data = e.HeightMap;
}
// Find the new max height
for (int i = 0; i < e.HeightMap.Length; i++)
{
if (e.HeightMap[i] > MaxHeight)
MaxHeight = e.HeightMap[i];
}
}
private void Network_OnLogin(object sender, LoginProgressEventArgs e)
{
if (e.Status == LoginStatus.Success)
{
// Success!
}
else if (e.Status == 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(object sender, DisconnectedEventArgs e)
{
BeginInvoke(
(MethodInvoker)delegate()
{
cmdTeleport.Enabled = false;
DoLogout();
});
}
private void Network_OnCurrentSimChanged(object sender, SimChangedEventArgs e)
{
Console.WriteLine("CurrentSim set to " + Client.Network.CurrentSim + ", downloading parcel information");
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(object sender, EventQueueRunningEventArgs e)
{
if (e.Simulator == Client.Network.CurrentSim)
BeginInvoke((MethodInvoker)delegate() { cmdTeleport.Enabled = true; });
// Now seems like a good time to start requesting parcel information
Client.Parcels.RequestAllSimParcels(Client.Network.CurrentSim, false, 100);
}
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 Vector3[] SkyboxVerts = new Vector3[]
{
// Right side
new Vector3( 10.0f, 10.0f, -10.0f ), //Top left
new Vector3( 10.0f, 10.0f, 10.0f ), //Top right
new Vector3( 10.0f, -10.0f, 10.0f ), //Bottom right
new Vector3( 10.0f, -10.0f, -10.0f ), //Bottom left
// Left side
new Vector3( -10.0f, 10.0f, 10.0f ), //Top left
new Vector3( -10.0f, 10.0f, -10.0f ), //Top right
new Vector3( -10.0f, -10.0f, -10.0f ), //Bottom right
new Vector3( -10.0f, -10.0f, 10.0f ), //Bottom left
// Top side
new Vector3( -10.0f, 10.0f, 10.0f ), //Top left
new Vector3( 10.0f, 10.0f, 10.0f ), //Top right
new Vector3( 10.0f, 10.0f, -10.0f ), //Bottom right
new Vector3( -10.0f, 10.0f, -10.0f ), //Bottom left
// Bottom side
new Vector3( -10.0f, -10.0f, -10.0f ), //Top left
new Vector3( 10.0f, -10.0f, -10.0f ), //Top right
new Vector3( 10.0f, -10.0f, 10.0f ), //Bottom right
new Vector3( -10.0f, -10.0f, 10.0f ), //Bottom left
// Front side
new Vector3( -10.0f, 10.0f, -10.0f ), //Top left
new Vector3( 10.0f, 10.0f, -10.0f ), //Top right
new Vector3( 10.0f, -10.0f, -10.0f ), //Bottom right
new Vector3( -10.0f, -10.0f, -10.0f ), //Bottom left
// Back side
new Vector3( 10.0f, 10.0f, 10.0f ), //Top left
new Vector3( -10.0f, 10.0f, 10.0f ), //Top right
new Vector3( -10.0f, -10.0f, 10.0f ), //Bottom right
new Vector3( 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].Data[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].Data[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].Data[(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].Data[(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;
Color4 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 == Primitive.TextureEntry.WHITE_TEXTURE ||
face.TextureFace.TextureID == UUID.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(TextureRequestState state, AssetTexture asset)
{
bool alpha = false;
ManagedImage imgData = null;
byte[] raw = null;
bool success = (state == TextureRequestState.Finished);
UUID id = asset.AssetID;
try
{
// Load the image off the disk
if (success)
{
//ImageDownload download = TextureDownloader.GetTextureToRender(id);
if (OpenJPEG.DecodeToImage(asset.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 Assets_ImageReceiveProgress(object sender, ImageReceiveProgressEventArgs e)
{
lock (DownloadList)
{
GlacialComponents.Controls.GLItem item;
if (DownloadList.TryGetValue(e.ImageID, out item))
{
// Update an existing item
BeginInvoke(
(MethodInvoker)delegate()
{
ProgressBar prog = (ProgressBar)item.SubItems[1].Control;
if (e.Total >= e.Received)
prog.Value = (int)Math.Round((((double)e.Received / (double)e.Total) * 100.0d));
});
}
else
{
// Progress bar
ProgressBar prog = new ProgressBar();
prog.Minimum = 0;
prog.Maximum = 100;
if (e.Total >= e.Received)
prog.Value = (int)Math.Round((((double)e.Received / (double)e.Total) * 100.0d));
else
prog.Value = 0;
// List item
item = new GlacialComponents.Controls.GLItem();
item.SubItems[0].Text = e.ImageID.ToString();
item.SubItems[1].Control = prog;
DownloadList[e.ImageID] = 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(cboServer.Text))
loginParams.URI = cboServer.Text;
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";
// 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
Vector3 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 (Vector3.Distance(Camera.Position, Camera.FocalPoint) > dist)
{
// Move closer or further away from the focal point
Vector3 toFocal = Camera.FocalPoint - Camera.Position;
toFocal.Normalize();
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();
Vector3 position = new Vector3(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
{
/// <summary>OpenGL Texture ID</summary>
public int ID;
/// <summary>True if this texture has an alpha component</summary>
public bool Alpha;
public TextureInfo(int id, bool alpha)
{
ID = id;
Alpha = alpha;
}
}
public struct HeightmapLookupValue : IComparable<HeightmapLookupValue>
{
public ushort Index;
public float Value;
public HeightmapLookupValue(ushort index, float value)
{
Index = index;
Value = value;
}
public int CompareTo(HeightmapLookupValue val)
{
return Value.CompareTo(val.Value);
}
}
public struct RenderablePrim
{
public Primitive Prim;
public FacetedMesh Mesh;
public readonly static RenderablePrim Empty = new RenderablePrim();
}
public struct Camera
{
public Vector3 Position;
public Vector3 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(Vector3 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(Quaternion 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(Vector3 v)
{
float[] mat = new float[16];
mat[0] = v.X;
mat[5] = v.Y;
mat[10] = v.Z;
mat[15] = 1;
return mat;
}
}
}