From 764c8bdb45d2f8e6ba6b8239c6220d41c4f03506 Mon Sep 17 00:00:00 2001 From: Latif Khalifa Date: Thu, 28 Jul 2011 10:17:30 +0000 Subject: [PATCH] [INTERFACE CHANGE] Implement transforming for Planar textures via Meshmerizer Patch by: Revolution Smythe git-svn-id: http://libopenmetaverse.googlecode.com/svn/libopenmetaverse/trunk@3565 52acb1d6-8a22-11de-b505-999d5b087335 --- .../MeshmerizerR.cs | 1054 ++--- .../SimpleRenderer.cs | 316 +- OpenMetaverse/Interfaces/IRendering.cs | 203 +- Programs/PrimWorkshop/frmBrowser.cs | 3922 ++++++++--------- Programs/PrimWorkshop/frmPrimWorkshop.cs | 1404 +++--- 5 files changed, 3459 insertions(+), 3440 deletions(-) diff --git a/OpenMetaverse.Rendering.Meshmerizer/MeshmerizerR.cs b/OpenMetaverse.Rendering.Meshmerizer/MeshmerizerR.cs index 6b76012d..d83a0471 100644 --- a/OpenMetaverse.Rendering.Meshmerizer/MeshmerizerR.cs +++ b/OpenMetaverse.Rendering.Meshmerizer/MeshmerizerR.cs @@ -1,518 +1,536 @@ -/* Copyright (c) 2008 Robert Adams - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * The name of the copyright holder may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * Portions of this code are: - * Copyright (c) Contributors, http://idealistviewer.org - * The basic logic of the extrusion code is based on the Idealist viewer code. - * The Idealist viewer is licensed under the three clause BSD license. - */ -/* - * MeshmerizerR class implments OpenMetaverse.Rendering.IRendering interface - * using PrimMesher (http://forge.opensimulator.org/projects/primmesher). - * There are a few additions/changes: - * TransformTexCoords() does regular transformations but does not do planar - * mapping of textures. - */ - -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Text; -using OMV = OpenMetaverse; -using OMVR = OpenMetaverse.Rendering; - -namespace OpenMetaverse.Rendering -{ - /// - /// Meshing code based on the Idealist Viewer (20081213). - /// - [RendererName("MeshmerizerR")] - public class MeshmerizerR : OMVR.IRendering - { - /// - /// Generates a basic mesh structure from a primitive - /// - /// Primitive to generate the mesh from - /// Level of detail to generate the mesh at - /// The generated mesh or null on failure - public OMVR.SimpleMesh GenerateSimpleMesh(OMV.Primitive prim, OMVR.DetailLevel lod) - { - PrimMesher.PrimMesh newPrim = GeneratePrimMesh(prim, lod, false); - if (newPrim == null) - return null; - - SimpleMesh mesh = new SimpleMesh(); - mesh.Path = new Path(); - mesh.Prim = prim; - mesh.Profile = new Profile(); - mesh.Vertices = new List(newPrim.coords.Count); - for (int i = 0; i < newPrim.coords.Count; i++) - { - PrimMesher.Coord c = newPrim.coords[i]; - mesh.Vertices.Add(new Vertex { Position = new Vector3(c.X, c.Y, c.Z) }); - } - - mesh.Indices = new List(newPrim.faces.Count * 3); - for (int i = 0; i < newPrim.faces.Count; i++) - { - PrimMesher.Face face = newPrim.faces[i]; - mesh.Indices.Add((ushort)face.v1); - mesh.Indices.Add((ushort)face.v2); - mesh.Indices.Add((ushort)face.v3); - } - - return mesh; - } - - /// - /// Generates a basic mesh structure from a sculpted primitive - /// - /// Sculpted primitive to generate the mesh from - /// Sculpt texture - /// Level of detail to generate the mesh at - /// The generated mesh or null on failure - public OMVR.SimpleMesh GenerateSimpleSculptMesh(OMV.Primitive prim, System.Drawing.Bitmap sculptTexture, OMVR.DetailLevel lod) - { - OMVR.FacetedMesh faceted = GenerateFacetedSculptMesh(prim, sculptTexture, lod); - - if (faceted != null && faceted.Faces.Count == 1) - { - Face face = faceted.Faces[0]; - - SimpleMesh mesh = new SimpleMesh(); - mesh.Indices = face.Indices; - mesh.Vertices = face.Vertices; - mesh.Path = faceted.Path; - mesh.Prim = prim; - mesh.Profile = faceted.Profile; - mesh.Vertices = face.Vertices; - - return mesh; - } - - return null; - } - - /// - /// Generates a a series of faces, each face containing a mesh and - /// metadata - /// - /// Primitive to generate the mesh from - /// Level of detail to generate the mesh at - /// The generated mesh - public OMVR.FacetedMesh GenerateFacetedMesh(OMV.Primitive prim, OMVR.DetailLevel lod) - { - bool isSphere = ((OMV.ProfileCurve)(prim.PrimData.profileCurve & 0x07) == OMV.ProfileCurve.HalfCircle); - PrimMesher.PrimMesh newPrim = GeneratePrimMesh(prim, lod, true); - if (newPrim == null) - return null; - - int numViewerFaces = newPrim.viewerFaces.Count; - int numPrimFaces = newPrim.numPrimFaces; - - for (uint i = 0; i < numViewerFaces; i++) - { - PrimMesher.ViewerFace vf = newPrim.viewerFaces[(int)i]; - - if (isSphere) - { - vf.uv1.U = (vf.uv1.U - 0.5f) * 2.0f; - vf.uv2.U = (vf.uv2.U - 0.5f) * 2.0f; - vf.uv3.U = (vf.uv3.U - 0.5f) * 2.0f; - } - } - - // copy the vertex information into OMVR.IRendering structures - OMVR.FacetedMesh omvrmesh = new OMVR.FacetedMesh(); - omvrmesh.Faces = new List(); - omvrmesh.Prim = prim; - omvrmesh.Profile = new OMVR.Profile(); - omvrmesh.Profile.Faces = new List(); - omvrmesh.Profile.Positions = new List(); - omvrmesh.Path = new OMVR.Path(); - omvrmesh.Path.Points = new List(); - - Dictionary vertexAccount = new Dictionary(); - for (int ii = 0; ii < numPrimFaces; ii++) - { - OMVR.Face oface = new OMVR.Face(); - oface.Vertices = new List(); - oface.Indices = new List(); - oface.TextureFace = prim.Textures.GetFace((uint)ii); - int faceVertices = 0; - vertexAccount.Clear(); - OMV.Vector3 pos; - int indx; - OMVR.Vertex vert; - foreach (PrimMesher.ViewerFace vface in newPrim.viewerFaces) - { - if (vface.primFaceNumber == ii) - { - faceVertices++; - pos = new OMV.Vector3(vface.v1.X, vface.v1.Y, vface.v1.Z); - if (vertexAccount.ContainsKey(pos)) - { - // we aleady have this vertex in the list. Just point the index at it - oface.Indices.Add((ushort)vertexAccount[pos]); - } - else - { - // the vertex is not in the list. Add it and the new index. - vert = new OMVR.Vertex(); - vert.Position = pos; - vert.TexCoord = new OMV.Vector2(vface.uv1.U, 1.0f - vface.uv1.V); - vert.Normal = new OMV.Vector3(vface.n1.X, vface.n1.Y, vface.n1.Z); - oface.Vertices.Add(vert); - indx = oface.Vertices.Count - 1; - vertexAccount.Add(pos, indx); - oface.Indices.Add((ushort)indx); - } - - pos = new OMV.Vector3(vface.v2.X, vface.v2.Y, vface.v2.Z); - if (vertexAccount.ContainsKey(pos)) - { - oface.Indices.Add((ushort)vertexAccount[pos]); - } - else - { - vert = new OMVR.Vertex(); - vert.Position = pos; - vert.TexCoord = new OMV.Vector2(vface.uv2.U, 1.0f - vface.uv2.V); - vert.Normal = new OMV.Vector3(vface.n2.X, vface.n2.Y, vface.n2.Z); - oface.Vertices.Add(vert); - indx = oface.Vertices.Count - 1; - vertexAccount.Add(pos, indx); - oface.Indices.Add((ushort)indx); - } - - pos = new OMV.Vector3(vface.v3.X, vface.v3.Y, vface.v3.Z); - if (vertexAccount.ContainsKey(pos)) - { - oface.Indices.Add((ushort)vertexAccount[pos]); - } - else - { - vert = new OMVR.Vertex(); - vert.Position = pos; - vert.TexCoord = new OMV.Vector2(vface.uv3.U, 1.0f - vface.uv3.V); - vert.Normal = new OMV.Vector3(vface.n3.X, vface.n3.Y, vface.n3.Z); - oface.Vertices.Add(vert); - indx = oface.Vertices.Count - 1; - vertexAccount.Add(pos, indx); - oface.Indices.Add((ushort)indx); - } - } - } - if (faceVertices > 0) - { - oface.TextureFace = prim.Textures.FaceTextures[ii]; - if (oface.TextureFace == null) - { - oface.TextureFace = prim.Textures.DefaultTexture; - } - oface.ID = ii; - omvrmesh.Faces.Add(oface); - } - } - - return omvrmesh; - } - - /// - /// Create a sculpty faceted mesh. The actual scuplt texture is fetched and passed to this - /// routine since all the context for finding teh texture is elsewhere. - /// - /// The faceted mesh or null if can't do it - public OMVR.FacetedMesh GenerateFacetedSculptMesh(OMV.Primitive prim, System.Drawing.Bitmap scupltTexture, OMVR.DetailLevel lod) - { - PrimMesher.SculptMesh.SculptType smSculptType; - switch (prim.Sculpt.Type) - { - case OpenMetaverse.SculptType.Cylinder: - smSculptType = PrimMesher.SculptMesh.SculptType.cylinder; - break; - case OpenMetaverse.SculptType.Plane: - smSculptType = PrimMesher.SculptMesh.SculptType.plane; - break; - case OpenMetaverse.SculptType.Sphere: - smSculptType = PrimMesher.SculptMesh.SculptType.sphere; - break; - case OpenMetaverse.SculptType.Torus: - smSculptType = PrimMesher.SculptMesh.SculptType.torus; - break; - default: - smSculptType = PrimMesher.SculptMesh.SculptType.plane; - break; - } - // The lod for sculpties is the resolution of the texture passed. - // The first guess is 1:1 then lower resolutions after that - // int mesherLod = (int)Math.Sqrt(scupltTexture.Width * scupltTexture.Height); - int mesherLod = 32; // number used in Idealist viewer - switch (lod) - { - case OMVR.DetailLevel.Highest: - break; - case OMVR.DetailLevel.High: - break; - case OMVR.DetailLevel.Medium: - mesherLod /= 2; - break; - case OMVR.DetailLevel.Low: - mesherLod /= 4; - break; - } - PrimMesher.SculptMesh newMesh = - new PrimMesher.SculptMesh(scupltTexture, smSculptType, mesherLod, true, prim.Sculpt.Mirror, prim.Sculpt.Invert); - - int numPrimFaces = 1; // a scuplty has only one face - - // copy the vertex information into OMVR.IRendering structures - OMVR.FacetedMesh omvrmesh = new OMVR.FacetedMesh(); - omvrmesh.Faces = new List(); - omvrmesh.Prim = prim; - omvrmesh.Profile = new OMVR.Profile(); - omvrmesh.Profile.Faces = new List(); - omvrmesh.Profile.Positions = new List(); - omvrmesh.Path = new OMVR.Path(); - omvrmesh.Path.Points = new List(); - - Dictionary vertexAccount = new Dictionary(); - - - for (int ii = 0; ii < numPrimFaces; ii++) - { - vertexAccount.Clear(); - OMVR.Face oface = new OMVR.Face(); - oface.Vertices = new List(); - oface.Indices = new List(); - oface.TextureFace = prim.Textures.GetFace((uint)ii); - int faceVertices = newMesh.coords.Count; - OMVR.Vertex vert; - - for (int j = 0; j < faceVertices; j++) - { - vert = new OMVR.Vertex(); - vert.Position = new Vector3(newMesh.coords[j].X, newMesh.coords[j].Y, newMesh.coords[j].Z); - vert.Normal = new Vector3(newMesh.normals[j].X, newMesh.normals[j].Y, newMesh.normals[j].Z); - vert.TexCoord = new Vector2(newMesh.uvs[j].U, newMesh.uvs[j].V); - oface.Vertices.Add(vert); - } - - for (int j = 0; j < newMesh.faces.Count; j++) - { - oface.Indices.Add((ushort)newMesh.faces[j].v1); - oface.Indices.Add((ushort)newMesh.faces[j].v2); - oface.Indices.Add((ushort)newMesh.faces[j].v3); - } - - if (faceVertices > 0) - { - oface.TextureFace = prim.Textures.FaceTextures[ii]; - if (oface.TextureFace == null) - { - oface.TextureFace = prim.Textures.DefaultTexture; - } - oface.ID = ii; - omvrmesh.Faces.Add(oface); - } - } - - return omvrmesh; - } - - /// - /// Apply texture coordinate modifications from a - /// to a list of vertices - /// - /// Vertex list to modify texture coordinates for - /// Center-point of the face - /// Face texture parameters - public void TransformTexCoords(List vertices, OMV.Vector3 center, OMV.Primitive.TextureEntryFace teFace) - { - // compute trig stuff up front - float cosineAngle = (float)Math.Cos(teFace.Rotation); - float sinAngle = (float)Math.Sin(teFace.Rotation); - - // need a check for plainer vs default - // just do default for now (I don't know what planar is) - for (int ii = 0; ii < vertices.Count; ii++) - { - // tex coord comes to us as a number between zero and one - // transform about the center of the texture - OMVR.Vertex vert = vertices[ii]; - float tX = vert.TexCoord.X - 0.5f; - float tY = vert.TexCoord.Y - 0.5f; - // rotate, scale, offset - vert.TexCoord.X = (tX * cosineAngle + tY * sinAngle) * teFace.RepeatU + teFace.OffsetU + 0.5f; - vert.TexCoord.Y = (-tX * sinAngle + tY * cosineAngle) * teFace.RepeatV + teFace.OffsetV + 0.5f; - vertices[ii] = vert; - } - return; - } - - private PrimMesher.PrimMesh GeneratePrimMesh(Primitive prim, DetailLevel lod, bool viewerMode) - { - OMV.Primitive.ConstructionData primData = prim.PrimData; - int sides = 4; - int hollowsides = 4; - - float profileBegin = primData.ProfileBegin; - float profileEnd = primData.ProfileEnd; - - if ((OMV.ProfileCurve)(primData.profileCurve & 0x07) == OMV.ProfileCurve.Circle) - { - switch (lod) - { - case OMVR.DetailLevel.Low: - sides = 6; - break; - case OMVR.DetailLevel.Medium: - sides = 12; - break; - default: - sides = 24; - break; - } - } - else if ((OMV.ProfileCurve)(primData.profileCurve & 0x07) == OMV.ProfileCurve.EqualTriangle) - sides = 3; - else if ((OMV.ProfileCurve)(primData.profileCurve & 0x07) == OMV.ProfileCurve.HalfCircle) - { - // half circle, prim is a sphere - switch (lod) - { - case OMVR.DetailLevel.Low: - sides = 6; - break; - case OMVR.DetailLevel.Medium: - sides = 12; - break; - default: - sides = 24; - break; - } - profileBegin = 0.5f * profileBegin + 0.5f; - profileEnd = 0.5f * profileEnd + 0.5f; - } - - if ((OMV.HoleType)primData.ProfileHole == OMV.HoleType.Same) - hollowsides = sides; - else if ((OMV.HoleType)primData.ProfileHole == OMV.HoleType.Circle) - { - switch (lod) - { - case OMVR.DetailLevel.Low: - hollowsides = 6; - break; - case OMVR.DetailLevel.Medium: - hollowsides = 12; - break; - default: - hollowsides = 24; - break; - } - } - else if ((OMV.HoleType)primData.ProfileHole == OMV.HoleType.Triangle) - hollowsides = 3; - - PrimMesher.PrimMesh newPrim = new PrimMesher.PrimMesh(sides, profileBegin, profileEnd, (float)primData.ProfileHollow, hollowsides); - newPrim.viewerMode = viewerMode; - newPrim.holeSizeX = primData.PathScaleX; - newPrim.holeSizeY = primData.PathScaleY; - newPrim.pathCutBegin = primData.PathBegin; - newPrim.pathCutEnd = primData.PathEnd; - newPrim.topShearX = primData.PathShearX; - newPrim.topShearY = primData.PathShearY; - newPrim.radius = primData.PathRadiusOffset; - newPrim.revolutions = primData.PathRevolutions; - newPrim.skew = primData.PathSkew; - switch (lod) - { - case OMVR.DetailLevel.Low: - newPrim.stepsPerRevolution = 6; - break; - case OMVR.DetailLevel.Medium: - newPrim.stepsPerRevolution = 12; - break; - default: - newPrim.stepsPerRevolution = 24; - break; - } - - if ((primData.PathCurve == OMV.PathCurve.Line) || (primData.PathCurve == OMV.PathCurve.Flexible)) - { - newPrim.taperX = 1.0f - primData.PathScaleX; - newPrim.taperY = 1.0f - primData.PathScaleY; - newPrim.twistBegin = (int)(180 * primData.PathTwistBegin); - newPrim.twistEnd = (int)(180 * primData.PathTwist); - newPrim.ExtrudeLinear(); - } - else - { - newPrim.taperX = primData.PathTaperX; - newPrim.taperY = primData.PathTaperY; - newPrim.twistBegin = (int)(360 * primData.PathTwistBegin); - newPrim.twistEnd = (int)(360 * primData.PathTwist); - newPrim.ExtrudeCircular(); - } - - return newPrim; - } - - /// - /// Method for generating mesh Face from a heightmap - /// - /// Two dimension array of floats containing height information - /// Starting value for X - /// Max value for X - /// Starting value for Y - /// Max value of Y - /// - public OMVR.Face TerrainMesh(float[,] zMap, float xBegin, float xEnd, float yBegin, float yEnd) - { - PrimMesher.SculptMesh newMesh = new PrimMesher.SculptMesh(zMap, xBegin, xEnd, yBegin, yEnd, true); - OMVR.Face terrain = new OMVR.Face(); - int faceVertices = newMesh.coords.Count; - terrain.Vertices = new List(faceVertices); - terrain.Indices = new List(newMesh.faces.Count * 3); - - for (int j = 0; j < faceVertices; j++) - { - var vert = new OMVR.Vertex(); - vert.Position = new Vector3(newMesh.coords[j].X, newMesh.coords[j].Y, newMesh.coords[j].Z); - vert.Normal = new Vector3(newMesh.normals[j].X, newMesh.normals[j].Y, newMesh.normals[j].Z); - vert.TexCoord = new Vector2(newMesh.uvs[j].U, newMesh.uvs[j].V); - terrain.Vertices.Add(vert); - } - - for (int j = 0; j < newMesh.faces.Count; j++) - { - terrain.Indices.Add((ushort)newMesh.faces[j].v1); - terrain.Indices.Add((ushort)newMesh.faces[j].v2); - terrain.Indices.Add((ushort)newMesh.faces[j].v3); - } - - return terrain; - } - } -} +/* Copyright (c) 2008 Robert Adams + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * The name of the copyright holder may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Portions of this code are: + * Copyright (c) Contributors, http://idealistviewer.org + * The basic logic of the extrusion code is based on the Idealist viewer code. + * The Idealist viewer is licensed under the three clause BSD license. + */ +/* + * MeshmerizerR class implments OpenMetaverse.Rendering.IRendering interface + * using PrimMesher (http://forge.opensimulator.org/projects/primmesher). + * There are a few additions/changes: + * TransformTexCoords() does regular transformations but does not do planar + * mapping of textures. + */ + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Text; +using OMV = OpenMetaverse; +using OMVR = OpenMetaverse.Rendering; + +namespace OpenMetaverse.Rendering +{ + /// + /// Meshing code based on the Idealist Viewer (20081213). + /// + [RendererName("MeshmerizerR")] + public class MeshmerizerR : OMVR.IRendering + { + /// + /// Generates a basic mesh structure from a primitive + /// + /// Primitive to generate the mesh from + /// Level of detail to generate the mesh at + /// The generated mesh or null on failure + public OMVR.SimpleMesh GenerateSimpleMesh(OMV.Primitive prim, OMVR.DetailLevel lod) + { + PrimMesher.PrimMesh newPrim = GeneratePrimMesh(prim, lod, false); + if (newPrim == null) + return null; + + SimpleMesh mesh = new SimpleMesh(); + mesh.Path = new Path(); + mesh.Prim = prim; + mesh.Profile = new Profile(); + mesh.Vertices = new List(newPrim.coords.Count); + for (int i = 0; i < newPrim.coords.Count; i++) + { + PrimMesher.Coord c = newPrim.coords[i]; + mesh.Vertices.Add(new Vertex { Position = new Vector3(c.X, c.Y, c.Z) }); + } + + mesh.Indices = new List(newPrim.faces.Count * 3); + for (int i = 0; i < newPrim.faces.Count; i++) + { + PrimMesher.Face face = newPrim.faces[i]; + mesh.Indices.Add((ushort)face.v1); + mesh.Indices.Add((ushort)face.v2); + mesh.Indices.Add((ushort)face.v3); + } + + return mesh; + } + + /// + /// Generates a basic mesh structure from a sculpted primitive + /// + /// Sculpted primitive to generate the mesh from + /// Sculpt texture + /// Level of detail to generate the mesh at + /// The generated mesh or null on failure + public OMVR.SimpleMesh GenerateSimpleSculptMesh(OMV.Primitive prim, System.Drawing.Bitmap sculptTexture, OMVR.DetailLevel lod) + { + OMVR.FacetedMesh faceted = GenerateFacetedSculptMesh(prim, sculptTexture, lod); + + if (faceted != null && faceted.Faces.Count == 1) + { + Face face = faceted.Faces[0]; + + SimpleMesh mesh = new SimpleMesh(); + mesh.Indices = face.Indices; + mesh.Vertices = face.Vertices; + mesh.Path = faceted.Path; + mesh.Prim = prim; + mesh.Profile = faceted.Profile; + mesh.Vertices = face.Vertices; + + return mesh; + } + + return null; + } + + /// + /// Generates a a series of faces, each face containing a mesh and + /// metadata + /// + /// Primitive to generate the mesh from + /// Level of detail to generate the mesh at + /// The generated mesh + public OMVR.FacetedMesh GenerateFacetedMesh(OMV.Primitive prim, OMVR.DetailLevel lod) + { + bool isSphere = ((OMV.ProfileCurve)(prim.PrimData.profileCurve & 0x07) == OMV.ProfileCurve.HalfCircle); + PrimMesher.PrimMesh newPrim = GeneratePrimMesh(prim, lod, true); + if (newPrim == null) + return null; + + int numViewerFaces = newPrim.viewerFaces.Count; + int numPrimFaces = newPrim.numPrimFaces; + + for (uint i = 0; i < numViewerFaces; i++) + { + PrimMesher.ViewerFace vf = newPrim.viewerFaces[(int)i]; + + if (isSphere) + { + vf.uv1.U = (vf.uv1.U - 0.5f) * 2.0f; + vf.uv2.U = (vf.uv2.U - 0.5f) * 2.0f; + vf.uv3.U = (vf.uv3.U - 0.5f) * 2.0f; + } + } + + // copy the vertex information into OMVR.IRendering structures + OMVR.FacetedMesh omvrmesh = new OMVR.FacetedMesh(); + omvrmesh.Faces = new List(); + omvrmesh.Prim = prim; + omvrmesh.Profile = new OMVR.Profile(); + omvrmesh.Profile.Faces = new List(); + omvrmesh.Profile.Positions = new List(); + omvrmesh.Path = new OMVR.Path(); + omvrmesh.Path.Points = new List(); + + Dictionary vertexAccount = new Dictionary(); + for (int ii = 0; ii < numPrimFaces; ii++) + { + OMVR.Face oface = new OMVR.Face(); + oface.Vertices = new List(); + oface.Indices = new List(); + oface.TextureFace = prim.Textures.GetFace((uint)ii); + int faceVertices = 0; + vertexAccount.Clear(); + OMV.Vector3 pos; + int indx; + OMVR.Vertex vert; + foreach (PrimMesher.ViewerFace vface in newPrim.viewerFaces) + { + if (vface.primFaceNumber == ii) + { + faceVertices++; + pos = new OMV.Vector3(vface.v1.X, vface.v1.Y, vface.v1.Z); + if (vertexAccount.ContainsKey(pos)) + { + // we aleady have this vertex in the list. Just point the index at it + oface.Indices.Add((ushort)vertexAccount[pos]); + } + else + { + // the vertex is not in the list. Add it and the new index. + vert = new OMVR.Vertex(); + vert.Position = pos; + vert.TexCoord = new OMV.Vector2(vface.uv1.U, 1.0f - vface.uv1.V); + vert.Normal = new OMV.Vector3(vface.n1.X, vface.n1.Y, vface.n1.Z); + oface.Vertices.Add(vert); + indx = oface.Vertices.Count - 1; + vertexAccount.Add(pos, indx); + oface.Indices.Add((ushort)indx); + } + + pos = new OMV.Vector3(vface.v2.X, vface.v2.Y, vface.v2.Z); + if (vertexAccount.ContainsKey(pos)) + { + oface.Indices.Add((ushort)vertexAccount[pos]); + } + else + { + vert = new OMVR.Vertex(); + vert.Position = pos; + vert.TexCoord = new OMV.Vector2(vface.uv2.U, 1.0f - vface.uv2.V); + vert.Normal = new OMV.Vector3(vface.n2.X, vface.n2.Y, vface.n2.Z); + oface.Vertices.Add(vert); + indx = oface.Vertices.Count - 1; + vertexAccount.Add(pos, indx); + oface.Indices.Add((ushort)indx); + } + + pos = new OMV.Vector3(vface.v3.X, vface.v3.Y, vface.v3.Z); + if (vertexAccount.ContainsKey(pos)) + { + oface.Indices.Add((ushort)vertexAccount[pos]); + } + else + { + vert = new OMVR.Vertex(); + vert.Position = pos; + vert.TexCoord = new OMV.Vector2(vface.uv3.U, 1.0f - vface.uv3.V); + vert.Normal = new OMV.Vector3(vface.n3.X, vface.n3.Y, vface.n3.Z); + oface.Vertices.Add(vert); + indx = oface.Vertices.Count - 1; + vertexAccount.Add(pos, indx); + oface.Indices.Add((ushort)indx); + } + } + } + if (faceVertices > 0) + { + oface.TextureFace = prim.Textures.FaceTextures[ii]; + if (oface.TextureFace == null) + { + oface.TextureFace = prim.Textures.DefaultTexture; + } + oface.ID = ii; + omvrmesh.Faces.Add(oface); + } + } + + return omvrmesh; + } + + /// + /// Create a sculpty faceted mesh. The actual scuplt texture is fetched and passed to this + /// routine since all the context for finding teh texture is elsewhere. + /// + /// The faceted mesh or null if can't do it + public OMVR.FacetedMesh GenerateFacetedSculptMesh(OMV.Primitive prim, System.Drawing.Bitmap scupltTexture, OMVR.DetailLevel lod) + { + PrimMesher.SculptMesh.SculptType smSculptType; + switch (prim.Sculpt.Type) + { + case OpenMetaverse.SculptType.Cylinder: + smSculptType = PrimMesher.SculptMesh.SculptType.cylinder; + break; + case OpenMetaverse.SculptType.Plane: + smSculptType = PrimMesher.SculptMesh.SculptType.plane; + break; + case OpenMetaverse.SculptType.Sphere: + smSculptType = PrimMesher.SculptMesh.SculptType.sphere; + break; + case OpenMetaverse.SculptType.Torus: + smSculptType = PrimMesher.SculptMesh.SculptType.torus; + break; + default: + smSculptType = PrimMesher.SculptMesh.SculptType.plane; + break; + } + // The lod for sculpties is the resolution of the texture passed. + // The first guess is 1:1 then lower resolutions after that + // int mesherLod = (int)Math.Sqrt(scupltTexture.Width * scupltTexture.Height); + int mesherLod = 32; // number used in Idealist viewer + switch (lod) + { + case OMVR.DetailLevel.Highest: + break; + case OMVR.DetailLevel.High: + break; + case OMVR.DetailLevel.Medium: + mesherLod /= 2; + break; + case OMVR.DetailLevel.Low: + mesherLod /= 4; + break; + } + PrimMesher.SculptMesh newMesh = + new PrimMesher.SculptMesh(scupltTexture, smSculptType, mesherLod, true, prim.Sculpt.Mirror, prim.Sculpt.Invert); + + int numPrimFaces = 1; // a scuplty has only one face + + // copy the vertex information into OMVR.IRendering structures + OMVR.FacetedMesh omvrmesh = new OMVR.FacetedMesh(); + omvrmesh.Faces = new List(); + omvrmesh.Prim = prim; + omvrmesh.Profile = new OMVR.Profile(); + omvrmesh.Profile.Faces = new List(); + omvrmesh.Profile.Positions = new List(); + omvrmesh.Path = new OMVR.Path(); + omvrmesh.Path.Points = new List(); + + Dictionary vertexAccount = new Dictionary(); + + + for (int ii = 0; ii < numPrimFaces; ii++) + { + vertexAccount.Clear(); + OMVR.Face oface = new OMVR.Face(); + oface.Vertices = new List(); + oface.Indices = new List(); + oface.TextureFace = prim.Textures.GetFace((uint)ii); + int faceVertices = newMesh.coords.Count; + OMVR.Vertex vert; + + for (int j = 0; j < faceVertices; j++) + { + vert = new OMVR.Vertex(); + vert.Position = new Vector3(newMesh.coords[j].X, newMesh.coords[j].Y, newMesh.coords[j].Z); + vert.Normal = new Vector3(newMesh.normals[j].X, newMesh.normals[j].Y, newMesh.normals[j].Z); + vert.TexCoord = new Vector2(newMesh.uvs[j].U, newMesh.uvs[j].V); + oface.Vertices.Add(vert); + } + + for (int j = 0; j < newMesh.faces.Count; j++) + { + oface.Indices.Add((ushort)newMesh.faces[j].v1); + oface.Indices.Add((ushort)newMesh.faces[j].v2); + oface.Indices.Add((ushort)newMesh.faces[j].v3); + } + + if (faceVertices > 0) + { + oface.TextureFace = prim.Textures.FaceTextures[ii]; + if (oface.TextureFace == null) + { + oface.TextureFace = prim.Textures.DefaultTexture; + } + oface.ID = ii; + omvrmesh.Faces.Add(oface); + } + } + + return omvrmesh; + } + + /// + /// Apply texture coordinate modifications from a + /// to a list of vertices + /// + /// Vertex list to modify texture coordinates for + /// Center-point of the face + /// Face texture parameters + public void TransformTexCoords(List vertices, OMV.Vector3 center, OMV.Primitive.TextureEntryFace teFace, Vector3 primScale) + { + // compute trig stuff up front + float cosineAngle = (float)Math.Cos(teFace.Rotation); + float sinAngle = (float)Math.Sin(teFace.Rotation); + + // need a check for plainer vs default + // just do default for now (I don't know what planar is) + for (int ii = 0; ii < vertices.Count; ii++) + { + // tex coord comes to us as a number between zero and one + // transform about the center of the texture + OMVR.Vertex vert = vertices[ii]; + float tX = vert.TexCoord.X - 0.5f; + float tY = vert.TexCoord.Y - 0.5f; + float repeatU = teFace.RepeatU; + float repeatV = teFace.RepeatV; + if (teFace.TexMapType == MappingType.Planar) + { + Vector3 scale = primScale; + Vector3 normal = vert.Normal; + //Dunno... + if(normal.X < 0) + normal.X *= -1; + if (normal.Y < 0) + normal.Y *= -1; + //Get the diff between the normal and the 'up', then fix the scale + Quaternion rot = Vector3.RotationBetween (new Vector3 (0, 0, 1), normal); + scale *= rot; + //Viewer sends /2 appearently + repeatU = (teFace.RepeatU * 2) * (scale.Y); + repeatV = (teFace.RepeatV * 2) * (scale.X); + } + // rotate, scale, offset + vert.TexCoord.X = (tX * cosineAngle + tY * sinAngle) * repeatU + teFace.OffsetU + 0.5f; + vert.TexCoord.Y = (-tX * sinAngle + tY * cosineAngle) * repeatV + teFace.OffsetV + 0.5f; + vertices[ii] = vert; + } + return; + } + + private PrimMesher.PrimMesh GeneratePrimMesh(Primitive prim, DetailLevel lod, bool viewerMode) + { + OMV.Primitive.ConstructionData primData = prim.PrimData; + int sides = 4; + int hollowsides = 4; + + float profileBegin = primData.ProfileBegin; + float profileEnd = primData.ProfileEnd; + + if ((OMV.ProfileCurve)(primData.profileCurve & 0x07) == OMV.ProfileCurve.Circle) + { + switch (lod) + { + case OMVR.DetailLevel.Low: + sides = 6; + break; + case OMVR.DetailLevel.Medium: + sides = 12; + break; + default: + sides = 24; + break; + } + } + else if ((OMV.ProfileCurve)(primData.profileCurve & 0x07) == OMV.ProfileCurve.EqualTriangle) + sides = 3; + else if ((OMV.ProfileCurve)(primData.profileCurve & 0x07) == OMV.ProfileCurve.HalfCircle) + { + // half circle, prim is a sphere + switch (lod) + { + case OMVR.DetailLevel.Low: + sides = 6; + break; + case OMVR.DetailLevel.Medium: + sides = 12; + break; + default: + sides = 24; + break; + } + profileBegin = 0.5f * profileBegin + 0.5f; + profileEnd = 0.5f * profileEnd + 0.5f; + } + + if ((OMV.HoleType)primData.ProfileHole == OMV.HoleType.Same) + hollowsides = sides; + else if ((OMV.HoleType)primData.ProfileHole == OMV.HoleType.Circle) + { + switch (lod) + { + case OMVR.DetailLevel.Low: + hollowsides = 6; + break; + case OMVR.DetailLevel.Medium: + hollowsides = 12; + break; + default: + hollowsides = 24; + break; + } + } + else if ((OMV.HoleType)primData.ProfileHole == OMV.HoleType.Triangle) + hollowsides = 3; + + PrimMesher.PrimMesh newPrim = new PrimMesher.PrimMesh(sides, profileBegin, profileEnd, (float)primData.ProfileHollow, hollowsides); + newPrim.viewerMode = viewerMode; + newPrim.holeSizeX = primData.PathScaleX; + newPrim.holeSizeY = primData.PathScaleY; + newPrim.pathCutBegin = primData.PathBegin; + newPrim.pathCutEnd = primData.PathEnd; + newPrim.topShearX = primData.PathShearX; + newPrim.topShearY = primData.PathShearY; + newPrim.radius = primData.PathRadiusOffset; + newPrim.revolutions = primData.PathRevolutions; + newPrim.skew = primData.PathSkew; + switch (lod) + { + case OMVR.DetailLevel.Low: + newPrim.stepsPerRevolution = 6; + break; + case OMVR.DetailLevel.Medium: + newPrim.stepsPerRevolution = 12; + break; + default: + newPrim.stepsPerRevolution = 24; + break; + } + + if ((primData.PathCurve == OMV.PathCurve.Line) || (primData.PathCurve == OMV.PathCurve.Flexible)) + { + newPrim.taperX = 1.0f - primData.PathScaleX; + newPrim.taperY = 1.0f - primData.PathScaleY; + newPrim.twistBegin = (int)(180 * primData.PathTwistBegin); + newPrim.twistEnd = (int)(180 * primData.PathTwist); + newPrim.ExtrudeLinear(); + } + else + { + newPrim.taperX = primData.PathTaperX; + newPrim.taperY = primData.PathTaperY; + newPrim.twistBegin = (int)(360 * primData.PathTwistBegin); + newPrim.twistEnd = (int)(360 * primData.PathTwist); + newPrim.ExtrudeCircular(); + } + + return newPrim; + } + + /// + /// Method for generating mesh Face from a heightmap + /// + /// Two dimension array of floats containing height information + /// Starting value for X + /// Max value for X + /// Starting value for Y + /// Max value of Y + /// + public OMVR.Face TerrainMesh(float[,] zMap, float xBegin, float xEnd, float yBegin, float yEnd) + { + PrimMesher.SculptMesh newMesh = new PrimMesher.SculptMesh(zMap, xBegin, xEnd, yBegin, yEnd, true); + OMVR.Face terrain = new OMVR.Face(); + int faceVertices = newMesh.coords.Count; + terrain.Vertices = new List(faceVertices); + terrain.Indices = new List(newMesh.faces.Count * 3); + + for (int j = 0; j < faceVertices; j++) + { + var vert = new OMVR.Vertex(); + vert.Position = new Vector3(newMesh.coords[j].X, newMesh.coords[j].Y, newMesh.coords[j].Z); + vert.Normal = new Vector3(newMesh.normals[j].X, newMesh.normals[j].Y, newMesh.normals[j].Z); + vert.TexCoord = new Vector2(newMesh.uvs[j].U, newMesh.uvs[j].V); + terrain.Vertices.Add(vert); + } + + for (int j = 0; j < newMesh.faces.Count; j++) + { + terrain.Indices.Add((ushort)newMesh.faces[j].v1); + terrain.Indices.Add((ushort)newMesh.faces[j].v2); + terrain.Indices.Add((ushort)newMesh.faces[j].v3); + } + + return terrain; + } + } +} diff --git a/OpenMetaverse.Rendering.Simple/SimpleRenderer.cs b/OpenMetaverse.Rendering.Simple/SimpleRenderer.cs index 5746f5ec..a5b1470b 100644 --- a/OpenMetaverse.Rendering.Simple/SimpleRenderer.cs +++ b/OpenMetaverse.Rendering.Simple/SimpleRenderer.cs @@ -1,158 +1,158 @@ -/* - * Copyright (c) 2008, openmetaverse.org - * All rights reserved. - * - * - Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - Neither the name of the openmetaverse.org nor the names - * of its contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -using System; -using System.Collections.Generic; - -namespace OpenMetaverse.Rendering -{ - [RendererName("Simple Cube Renderer")] - public class SimpleRenderer : IRendering - { - public SimpleMesh GenerateSimpleMesh(Primitive prim, DetailLevel lod) - { - Path path = GeneratePath(); - Profile profile = GenerateProfile(); - - SimpleMesh mesh = new SimpleMesh(); - mesh.Prim = prim; - mesh.Path = path; - mesh.Profile = profile; - mesh.Vertices = GenerateVertices(); - mesh.Indices = GenerateIndices(); - - return mesh; - } - - public SimpleMesh GenerateSimpleSculptMesh(Primitive prim, System.Drawing.Bitmap sculptTexture, DetailLevel lod) - { - return GenerateSimpleMesh(prim, lod); - } - - public FacetedMesh GenerateFacetedMesh(Primitive prim, DetailLevel lod) - { - Path path = GeneratePath(); - Profile profile = GenerateProfile(); - - FacetedMesh mesh = new FacetedMesh(); - mesh.Prim = prim; - mesh.Path = path; - mesh.Profile = profile; - mesh.Faces = GenerateFaces(prim.Textures); - - return mesh; - } - - public FacetedMesh GenerateFacetedSculptMesh(Primitive prim, System.Drawing.Bitmap sculptTexture, DetailLevel lod) - { - return GenerateFacetedMesh(prim, lod); - } - - public void TransformTexCoords(List vertices, Vector3 center, Primitive.TextureEntryFace teFace) - { - // Lalala... - } - - private Path GeneratePath() - { - Path path = new Path(); - path.Points = new List(); - return path; - } - - private Profile GenerateProfile() - { - Profile profile = new Profile(); - profile.Faces = new List(); - profile.Positions = new List(); - return profile; - } - - private List GenerateVertices() - { - List vertices = new List(8); - - Vertex v = new Vertex(); - - // FIXME: Implement these - v.Normal = Vector3.Zero; - v.TexCoord = Vector2.Zero; - - v.Position = new Vector3(0.5f, 0.5f, -0.5f); - vertices.Add(v); - v.Position = new Vector3(0.5f, -0.5f, -0.5f); - vertices.Add(v); - v.Position = new Vector3(-0.5f, -0.5f, -0.5f); - vertices.Add(v); - v.Position = new Vector3(-0.5f, 0.5f, -0.5f); - vertices.Add(v); - v.Position = new Vector3(0.5f, 0.5f, 0.5f); - vertices.Add(v); - v.Position = new Vector3(0.5f, -0.5f, 0.5f); - vertices.Add(v); - v.Position = new Vector3(-0.5f, -0.5f, 0.5f); - vertices.Add(v); - v.Position = new Vector3(-0.5f, 0.5f, 0.5f); - vertices.Add(v); - - return vertices; - } - - private List GenerateIndices() - { - ushort[] indices = new ushort[] { - 0, 1, 2, - 0, 2, 3, - 4, 7, 6, - 4, 6, 5, - 0, 4, 5, - 0, 5, 1, - 1, 5, 6, - 1, 6, 2, - 2, 6, 7, - 2, 7, 3, - 4, 0, 3, - 4, 3, 7, - }; - - return new List(indices); - } - - private List GenerateFaces(Primitive.TextureEntry te) - { - Face face = new Face(); - face.Edge = new List(); - face.TextureFace = te.DefaultTexture; - face.Vertices = GenerateVertices(); - face.Indices = GenerateIndices(); - - List faces = new List(1); - faces.Add(face); - - return faces; - } - } -} +/* + * Copyright (c) 2008, openmetaverse.org + * All rights reserved. + * + * - Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Neither the name of the openmetaverse.org nor the names + * of its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; + +namespace OpenMetaverse.Rendering +{ + [RendererName("Simple Cube Renderer")] + public class SimpleRenderer : IRendering + { + public SimpleMesh GenerateSimpleMesh(Primitive prim, DetailLevel lod) + { + Path path = GeneratePath(); + Profile profile = GenerateProfile(); + + SimpleMesh mesh = new SimpleMesh(); + mesh.Prim = prim; + mesh.Path = path; + mesh.Profile = profile; + mesh.Vertices = GenerateVertices(); + mesh.Indices = GenerateIndices(); + + return mesh; + } + + public SimpleMesh GenerateSimpleSculptMesh(Primitive prim, System.Drawing.Bitmap sculptTexture, DetailLevel lod) + { + return GenerateSimpleMesh(prim, lod); + } + + public FacetedMesh GenerateFacetedMesh(Primitive prim, DetailLevel lod) + { + Path path = GeneratePath(); + Profile profile = GenerateProfile(); + + FacetedMesh mesh = new FacetedMesh(); + mesh.Prim = prim; + mesh.Path = path; + mesh.Profile = profile; + mesh.Faces = GenerateFaces(prim.Textures); + + return mesh; + } + + public FacetedMesh GenerateFacetedSculptMesh(Primitive prim, System.Drawing.Bitmap sculptTexture, DetailLevel lod) + { + return GenerateFacetedMesh(prim, lod); + } + + public void TransformTexCoords(List vertices, Vector3 center, Primitive.TextureEntryFace teFace, Vector3 primScale) + { + // Lalala... + } + + private Path GeneratePath() + { + Path path = new Path(); + path.Points = new List(); + return path; + } + + private Profile GenerateProfile() + { + Profile profile = new Profile(); + profile.Faces = new List(); + profile.Positions = new List(); + return profile; + } + + private List GenerateVertices() + { + List vertices = new List(8); + + Vertex v = new Vertex(); + + // FIXME: Implement these + v.Normal = Vector3.Zero; + v.TexCoord = Vector2.Zero; + + v.Position = new Vector3(0.5f, 0.5f, -0.5f); + vertices.Add(v); + v.Position = new Vector3(0.5f, -0.5f, -0.5f); + vertices.Add(v); + v.Position = new Vector3(-0.5f, -0.5f, -0.5f); + vertices.Add(v); + v.Position = new Vector3(-0.5f, 0.5f, -0.5f); + vertices.Add(v); + v.Position = new Vector3(0.5f, 0.5f, 0.5f); + vertices.Add(v); + v.Position = new Vector3(0.5f, -0.5f, 0.5f); + vertices.Add(v); + v.Position = new Vector3(-0.5f, -0.5f, 0.5f); + vertices.Add(v); + v.Position = new Vector3(-0.5f, 0.5f, 0.5f); + vertices.Add(v); + + return vertices; + } + + private List GenerateIndices() + { + ushort[] indices = new ushort[] { + 0, 1, 2, + 0, 2, 3, + 4, 7, 6, + 4, 6, 5, + 0, 4, 5, + 0, 5, 1, + 1, 5, 6, + 1, 6, 2, + 2, 6, 7, + 2, 7, 3, + 4, 0, 3, + 4, 3, 7, + }; + + return new List(indices); + } + + private List GenerateFaces(Primitive.TextureEntry te) + { + Face face = new Face(); + face.Edge = new List(); + face.TextureFace = te.DefaultTexture; + face.Vertices = GenerateVertices(); + face.Indices = GenerateIndices(); + + List faces = new List(1); + faces.Add(face); + + return faces; + } + } +} diff --git a/OpenMetaverse/Interfaces/IRendering.cs b/OpenMetaverse/Interfaces/IRendering.cs index 8bd1eb85..ebcebc81 100644 --- a/OpenMetaverse/Interfaces/IRendering.cs +++ b/OpenMetaverse/Interfaces/IRendering.cs @@ -1,101 +1,102 @@ -/* - * Copyright (c) 2008, openmetaverse.org - * All rights reserved. - * - * - Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - Neither the name of the openmetaverse.org nor the names - * of its contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -using System; -using System.Collections.Generic; -using System.Drawing; - -namespace OpenMetaverse.Rendering -{ - [AttributeUsage(AttributeTargets.Class)] - public class RendererNameAttribute : System.Attribute - { - private string _name; - - public RendererNameAttribute(string name) - : base() - { - _name = name; - } - - public override string ToString() - { - return _name; - } - } - - /// - /// Abstract base for rendering plugins - /// - public interface IRendering - { - /// - /// Generates a basic mesh structure from a primitive - /// - /// Primitive to generate the mesh from - /// Level of detail to generate the mesh at - /// The generated mesh - SimpleMesh GenerateSimpleMesh(Primitive prim, DetailLevel lod); - - /// - /// Generates a basic mesh structure from a sculpted primitive and - /// texture - /// - /// Sculpted primitive to generate the mesh from - /// Sculpt texture - /// Level of detail to generate the mesh at - /// The generated mesh - SimpleMesh GenerateSimpleSculptMesh(Primitive prim, Bitmap sculptTexture, DetailLevel lod); - - /// - /// Generates a series of faces, each face containing a mesh and - /// metadata - /// - /// Primitive to generate the mesh from - /// Level of detail to generate the mesh at - /// The generated mesh - FacetedMesh GenerateFacetedMesh(Primitive prim, DetailLevel lod); - - /// - /// Generates a series of faces for a sculpted prim, each face - /// containing a mesh and metadata - /// - /// Sculpted primitive to generate the mesh from - /// Sculpt texture - /// Level of detail to generate the mesh at - /// The generated mesh - FacetedMesh GenerateFacetedSculptMesh(Primitive prim, Bitmap sculptTexture, DetailLevel lod); - - /// - /// Apply texture coordinate modifications from a - /// to a list of vertices - /// - /// Vertex list to modify texture coordinates for - /// Center-point of the face - /// Face texture parameters - void TransformTexCoords(List vertices, Vector3 center, Primitive.TextureEntryFace teFace); - } -} +/* + * Copyright (c) 2008, openmetaverse.org + * All rights reserved. + * + * - Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Neither the name of the openmetaverse.org nor the names + * of its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Drawing; + +namespace OpenMetaverse.Rendering +{ + [AttributeUsage(AttributeTargets.Class)] + public class RendererNameAttribute : System.Attribute + { + private string _name; + + public RendererNameAttribute(string name) + : base() + { + _name = name; + } + + public override string ToString() + { + return _name; + } + } + + /// + /// Abstract base for rendering plugins + /// + public interface IRendering + { + /// + /// Generates a basic mesh structure from a primitive + /// + /// Primitive to generate the mesh from + /// Level of detail to generate the mesh at + /// The generated mesh + SimpleMesh GenerateSimpleMesh(Primitive prim, DetailLevel lod); + + /// + /// Generates a basic mesh structure from a sculpted primitive and + /// texture + /// + /// Sculpted primitive to generate the mesh from + /// Sculpt texture + /// Level of detail to generate the mesh at + /// The generated mesh + SimpleMesh GenerateSimpleSculptMesh(Primitive prim, Bitmap sculptTexture, DetailLevel lod); + + /// + /// Generates a series of faces, each face containing a mesh and + /// metadata + /// + /// Primitive to generate the mesh from + /// Level of detail to generate the mesh at + /// The generated mesh + FacetedMesh GenerateFacetedMesh(Primitive prim, DetailLevel lod); + + /// + /// Generates a series of faces for a sculpted prim, each face + /// containing a mesh and metadata + /// + /// Sculpted primitive to generate the mesh from + /// Sculpt texture + /// Level of detail to generate the mesh at + /// The generated mesh + FacetedMesh GenerateFacetedSculptMesh(Primitive prim, Bitmap sculptTexture, DetailLevel lod); + + /// + /// Apply texture coordinate modifications from a + /// to a list of vertices + /// + /// Vertex list to modify texture coordinates for + /// Center-point of the face + /// Face texture parameters + /// Scale of the prim + void TransformTexCoords (List vertices, Vector3 center, Primitive.TextureEntryFace teFace, Vector3 primScale); + } +} diff --git a/Programs/PrimWorkshop/frmBrowser.cs b/Programs/PrimWorkshop/frmBrowser.cs index dac41358..5126a186 100644 --- a/Programs/PrimWorkshop/frmBrowser.cs +++ b/Programs/PrimWorkshop/frmBrowser.cs @@ -1,1961 +1,1961 @@ -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 RenderFoliageList = new Dictionary(); - Dictionary RenderPrimList = new Dictionary(); - Dictionary DownloadList = new Dictionary(); - EventHandler IdleEvent; - - System.Timers.Timer ProgressTimer; - int TotalPrims; - - // Textures - Dictionary Textures = new Dictionary(); - - // 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); } - } - - /// - /// Default constructor - /// - 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(Terrain_LandPatchReceived); - Client.Parcels.SimParcelsDownloaded += new EventHandler(Parcels_SimParcelsDownloaded); - Client.Assets.ImageReceiveProgress += new EventHandler(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(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 primList = new List(); - 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 primList = new List(); - - 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 primList, string fileName, out int prims, out int textures, out string error) - { - List textureList = new List(); - 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 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(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 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); - - // 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 - { - /// OpenGL Texture ID - public int ID; - /// True if this texture has an alpha component - public bool Alpha; - - public TextureInfo(int id, bool alpha) - { - ID = id; - Alpha = alpha; - } - } - - public struct HeightmapLookupValue : IComparable - { - 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; - } - } -} +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 RenderFoliageList = new Dictionary(); + Dictionary RenderPrimList = new Dictionary(); + Dictionary DownloadList = new Dictionary(); + EventHandler IdleEvent; + + System.Timers.Timer ProgressTimer; + int TotalPrims; + + // Textures + Dictionary Textures = new Dictionary(); + + // 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); } + } + + /// + /// Default constructor + /// + 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(Terrain_LandPatchReceived); + Client.Parcels.SimParcelsDownloaded += new EventHandler(Parcels_SimParcelsDownloaded); + Client.Assets.ImageReceiveProgress += new EventHandler(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(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 primList = new List(); + 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 primList = new List(); + + 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 primList, string fileName, out int prims, out int textures, out string error) + { + List textureList = new List(); + 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 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(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 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 + { + /// OpenGL Texture ID + public int ID; + /// True if this texture has an alpha component + public bool Alpha; + + public TextureInfo(int id, bool alpha) + { + ID = id; + Alpha = alpha; + } + } + + public struct HeightmapLookupValue : IComparable + { + 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; + } + } +} diff --git a/Programs/PrimWorkshop/frmPrimWorkshop.cs b/Programs/PrimWorkshop/frmPrimWorkshop.cs index 81c0089a..33107d82 100644 --- a/Programs/PrimWorkshop/frmPrimWorkshop.cs +++ b/Programs/PrimWorkshop/frmPrimWorkshop.cs @@ -1,702 +1,702 @@ -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); - - // 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; - } -} +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; + } +}