X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=Radegast%2FGUI%2FRendering%2FRenderingHelpers.cs;h=1bce425d554e3408f6502ca5aeb542b300c944fa;hb=abb53191178e2fe091acaf72baad5c85d69654ad;hp=01292bb649e1ef8076d11c2aaa002c452cea0fd7;hpb=9135e9e2b2a93b0bd8d770f2866ecd7c0d9b3a16;p=radegast%2Fradegast.git
diff --git a/Radegast/GUI/Rendering/RenderingHelpers.cs b/Radegast/GUI/Rendering/RenderingHelpers.cs
index 01292bb..1bce425 100644
--- a/Radegast/GUI/Rendering/RenderingHelpers.cs
+++ b/Radegast/GUI/Rendering/RenderingHelpers.cs
@@ -1,3022 +1,1263 @@
-//
-// Radegast Metaverse Client
-// Copyright (c) 2009-2011, Radegast Development Team
-// 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.
-// * 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.
-// * Neither the name of the application "Radegast", nor the names of its
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior CreateReflectionTexture 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 HOLDER 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.
-//
-// $Id$
-//
-
-using System;
-using System.Collections.Generic;
-using System.Collections;
-using System.Linq;
-using System.Text;
-using System.IO;
-using System.IO.Compression;
-using System.Xml;
-using System.Threading;
-using OpenTK.Graphics.OpenGL;
-using System.Runtime.InteropServices;
-using System.Drawing;
-using System.Drawing.Imaging;
-using OpenMetaverse;
-using OpenMetaverse.Rendering;
-
-namespace Radegast.Rendering
-{
- [StructLayout(LayoutKind.Sequential)]
- public struct Color4b
- {
- public byte R;
- public byte G;
- public byte B;
- public byte A;
- }
-
- [StructLayout(LayoutKind.Explicit)]
- public struct ColorVertex
- {
- [FieldOffset(0)]
- public Vertex Vertex;
- [FieldOffset(32)]
- public Color4b Color;
- public static int Size = 36;
- }
-
- public class TextureInfo
- {
- public System.Drawing.Image Texture;
- public int TexturePointer;
- public bool HasAlpha;
- public bool FullAlpha;
- public bool IsMask;
- public UUID TextureID;
- public bool FetchFailed;
- }
-
- public class TextureLoadItem
- {
- public FaceData Data;
- public Primitive Prim;
- public Primitive.TextureEntryFace TeFace;
- public byte[] TextureData = null;
- public byte[] TGAData = null;
- public bool LoadAssetFromCache = false;
- }
-
- public enum RenderPass
- {
- Picking,
- Simple,
- Alpha
- }
-
- public enum SceneObjectType
- {
- None,
- Primitive,
- Avatar,
- }
-
- ///
- /// Base class for all scene objects
- ///
- public abstract class SceneObject : IComparable, IDisposable
- {
- #region Public fields
- /// Interpolated local position of the object
- public Vector3 InterpolatedPosition;
- /// Interpolated local rotation of the object/summary>
- public Quaternion InterpolatedRotation;
- /// Rendered position of the object in the region
- public Vector3 RenderPosition;
- /// Rendered rotationm of the object in the region
- public Quaternion RenderRotation;
- /// Per frame calculated square of the distance from camera
- public float DistanceSquared;
- /// Bounding volume of the object
- public BoundingVolume BoundingVolume;
- /// Was the sim position and distance from camera calculated during this frame
- public bool PositionCalculated;
- /// Scene object type
- public SceneObjectType Type = SceneObjectType.None;
- /// Libomv primitive
- public virtual Primitive BasePrim { get; set; }
- /// Were initial initialization tasks done
- public bool Initialized;
- /// Is this object disposed
- public bool IsDisposed = false;
- public int AlphaQueryID = -1;
- public int SimpleQueryID = -1;
- public bool HasAlphaFaces;
- public bool HasSimpleFaces;
-
- #endregion Public fields
-
- uint previousParent = uint.MaxValue;
-
- ///
- /// Cleanup resources used
- ///
- public virtual void Dispose()
- {
- IsDisposed = true;
- }
-
- ///
- /// Task performed the fist time object is set for rendering
- ///
- public virtual void Initialize()
- {
- RenderPosition = InterpolatedPosition = BasePrim.Position;
- RenderRotation = InterpolatedRotation = BasePrim.Rotation;
- Initialized = true;
- }
-
- ///
- /// Perform per frame tasks
- ///
- /// Time since the last call (last frame time in seconds)
- public virtual void Step(float time)
- {
- // Don't interpolate when parent changes (sit/stand link/unlink)
- if (previousParent != BasePrim.ParentID)
- {
- previousParent = BasePrim.ParentID;
- InterpolatedPosition = BasePrim.Position;
- InterpolatedRotation = BasePrim.Rotation;
- return;
- }
-
- // Linear velocity and acceleration
- if (BasePrim.Velocity != Vector3.Zero)
- {
- BasePrim.Position = InterpolatedPosition = BasePrim.Position + BasePrim.Velocity * time
- * 0.98f * RadegastInstance.GlobalInstance.Client.Network.CurrentSim.Stats.Dilation;
- BasePrim.Velocity += BasePrim.Acceleration * time;
- }
- else if (InterpolatedPosition != BasePrim.Position)
- {
- InterpolatedPosition = RHelp.Smoothed1stOrder(InterpolatedPosition, BasePrim.Position, time);
- }
-
- // Angular velocity (target omega)
- if (BasePrim.AngularVelocity != Vector3.Zero)
- {
- Vector3 angVel = BasePrim.AngularVelocity;
- float angle = time * angVel.Length();
- Quaternion dQ = Quaternion.CreateFromAxisAngle(angVel, angle);
- InterpolatedRotation = dQ * InterpolatedRotation;
- }
- else if (InterpolatedRotation != BasePrim.Rotation && !(this is RenderAvatar))
- {
- InterpolatedRotation = Quaternion.Slerp(InterpolatedRotation, BasePrim.Rotation, time * 10f);
- if (1f - Math.Abs(Quaternion.Dot(InterpolatedRotation, BasePrim.Rotation)) < 0.0001)
- InterpolatedRotation = BasePrim.Rotation;
- }
- else
- {
- InterpolatedRotation = BasePrim.Rotation;
- }
- }
-
- ///
- /// Render scene object
- ///
- /// Which pass are we currently in
- /// ID used to identify which object was picked
- /// Main scene renderer
- /// Time it took to render the last frame
- public virtual void Render(RenderPass pass, int pickingID, SceneWindow scene, float time)
- {
- }
-
- ///
- /// Implementation of the IComparable interface
- /// used for sorting by distance
- ///
- /// Object we are comparing to
- /// Result of the comparison
- public virtual int CompareTo(object other)
- {
- SceneObject o = (SceneObject)other;
- if (this.DistanceSquared < o.DistanceSquared)
- return -1;
- else if (this.DistanceSquared > o.DistanceSquared)
- return 1;
- else
- return 0;
- }
-
- #region Occlusion queries
- public void StartQuery(RenderPass pass)
- {
- if (!RenderSettings.OcclusionCullingEnabled) return;
-
- if (pass == RenderPass.Simple)
- {
- StartSimpleQuery();
- }
- else if (pass == RenderPass.Alpha)
- {
- StartAlphaQuery();
- }
- }
-
- public void EndQuery(RenderPass pass)
- {
- if (!RenderSettings.OcclusionCullingEnabled) return;
-
- if (pass == RenderPass.Simple)
- {
- EndSimpleQuery();
- }
- else if (pass == RenderPass.Alpha)
- {
- EndAlphaQuery();
- }
- }
-
- public void StartAlphaQuery()
- {
- if (!RenderSettings.OcclusionCullingEnabled) return;
-
- if (AlphaQueryID == -1)
- {
- Compat.GenQueries(out AlphaQueryID);
- }
- if (AlphaQueryID > 0)
- {
- Compat.BeginQuery(QueryTarget.SamplesPassed, AlphaQueryID);
- }
- }
-
- public void EndAlphaQuery()
- {
- if (!RenderSettings.OcclusionCullingEnabled) return;
-
- if (AlphaQueryID > 0)
- {
- Compat.EndQuery(QueryTarget.SamplesPassed);
- }
- }
-
- public void StartSimpleQuery()
- {
- if (!RenderSettings.OcclusionCullingEnabled) return;
-
- if (SimpleQueryID == -1)
- {
- Compat.GenQueries(out SimpleQueryID);
- }
- if (SimpleQueryID > 0)
- {
- Compat.BeginQuery(QueryTarget.SamplesPassed, SimpleQueryID);
- }
- }
-
- public void EndSimpleQuery()
- {
- if (!RenderSettings.OcclusionCullingEnabled) return;
-
- if (SimpleQueryID > 0)
- {
- Compat.EndQuery(QueryTarget.SamplesPassed);
- }
- }
-
- public bool Occluded()
- {
- if (!RenderSettings.OcclusionCullingEnabled) return false;
-
- if ((SimpleQueryID == -1 && AlphaQueryID == -1))
- {
- return false;
- }
-
- if ((!HasAlphaFaces && !HasSimpleFaces)) return true;
-
- int samples = 1;
- if (HasSimpleFaces && SimpleQueryID > 0)
- {
- Compat.GetQueryObject(SimpleQueryID, GetQueryObjectParam.QueryResult, out samples);
- }
- if (HasSimpleFaces && samples > 0)
- {
- return false;
- }
-
- samples = 1;
- if (HasAlphaFaces && AlphaQueryID > 0)
- {
- Compat.GetQueryObject(AlphaQueryID, GetQueryObjectParam.QueryResult, out samples);
- }
- if (HasAlphaFaces && samples > 0)
- {
- return false;
- }
-
- return true;
- }
- #endregion Occlusion queries
- }
-
- public static class RHelp
- {
- public static readonly Vector3 InvalidPosition = new Vector3(99999f, 99999f, 99999f);
- static float t1 = 0.075f;
- static float t2 = t1 / 5.7f;
-
- public static Vector3 Smoothed1stOrder(Vector3 curPos, Vector3 targetPos, float lastFrameTime)
- {
- int numIterations = (int)(lastFrameTime * 100);
- do
- {
- curPos += (targetPos - curPos) * t1;
- numIterations--;
- }
- while (numIterations > 0);
- if (Vector3.DistanceSquared(curPos, targetPos) < 0.00001f)
- {
- curPos = targetPos;
- }
- return curPos;
- }
-
- public static Vector3 Smoothed2ndOrder(Vector3 curPos, Vector3 targetPos, ref Vector3 accel, float lastFrameTime)
- {
- int numIterations = (int)(lastFrameTime * 100);
- do
- {
- accel += (targetPos - accel - curPos) * t1;
- curPos += accel * t2;
- numIterations--;
- }
- while (numIterations > 0);
- if (Vector3.DistanceSquared(curPos, targetPos) < 0.00001f)
- {
- curPos = targetPos;
- }
- return curPos;
- }
-
- public static OpenTK.Vector2 TKVector3(Vector2 v)
- {
- return new OpenTK.Vector2(v.X, v.Y);
- }
-
- public static OpenTK.Vector3 TKVector3(Vector3 v)
- {
- return new OpenTK.Vector3(v.X, v.Y, v.Z);
- }
-
- public static OpenTK.Vector4 TKVector3(Vector4 v)
- {
- return new OpenTK.Vector4(v.X, v.Y, v.Z, v.W);
- }
-
- public static Color WinColor(OpenTK.Graphics.Color4 color)
- {
- return Color.FromArgb((int)(color.A * 255), (int)(color.R * 255), (int)(color.G * 255), (int)(color.B * 255));
- }
-
- public static Color WinColor(Color4 color)
- {
- return Color.FromArgb((int)(color.A * 255), (int)(color.R * 255), (int)(color.G * 255), (int)(color.B * 255));
- }
-
- public static int NextPow2(int start)
- {
- int pow = 1;
- while (pow < start) pow *= 2;
- return pow;
- }
-
- #region Cached image save and load
- public static readonly string RAD_IMG_MAGIC = "radegast_img";
-
- public static bool LoadCachedImage(UUID textureID, out byte[] tgaData, out bool hasAlpha, out bool fullAlpha, out bool isMask)
- {
- tgaData = null;
- hasAlpha = fullAlpha = isMask = false;
-
- try
- {
- string fname = System.IO.Path.Combine(RadegastInstance.GlobalInstance.Client.Settings.ASSET_CACHE_DIR, string.Format("{0}.rzi", textureID));
- //string fname = System.IO.Path.Combine(".", string.Format("{0}.rzi", textureID));
-
- using (var f = File.Open(fname, FileMode.Open, FileAccess.Read, FileShare.Read))
- {
- byte[] header = new byte[36];
- int i = 0;
- f.Read(header, 0, header.Length);
-
- // check if the file is starting with magic string
- if (RAD_IMG_MAGIC != Utils.BytesToString(header, 0, RAD_IMG_MAGIC.Length))
- return false;
- i += RAD_IMG_MAGIC.Length;
-
- if (header[i++] != 1) // check version
- return false;
-
- hasAlpha = header[i++] == 1;
- fullAlpha = header[i++] == 1;
- isMask = header[i++] == 1;
-
- int uncompressedSize = Utils.BytesToInt(header, i);
- i += 4;
-
- textureID = new UUID(header, i);
- i += 16;
-
- tgaData = new byte[uncompressedSize];
- using (var compressed = new DeflateStream(f, CompressionMode.Decompress))
- {
- int read = 0;
- while ((read = compressed.Read(tgaData, read, uncompressedSize - read)) > 0) ;
- }
- }
-
- return true;
- }
- catch (FileNotFoundException) { }
- catch (Exception ex)
- {
- Logger.DebugLog(string.Format("Failed to load radegast cache file {0}: {1}", textureID, ex.Message));
- }
- return false;
- }
-
- public static bool SaveCachedImage(byte[] tgaData, UUID textureID, bool hasAlpha, bool fullAlpha, bool isMask)
- {
- try
- {
- string fname = System.IO.Path.Combine(RadegastInstance.GlobalInstance.Client.Settings.ASSET_CACHE_DIR, string.Format("{0}.rzi", textureID));
- //string fname = System.IO.Path.Combine(".", string.Format("{0}.rzi", textureID));
-
- using (var f = File.Open(fname, FileMode.Create, FileAccess.Write, FileShare.None))
- {
- int i = 0;
- // magic header
- f.Write(Utils.StringToBytes(RAD_IMG_MAGIC), 0, RAD_IMG_MAGIC.Length);
- i += RAD_IMG_MAGIC.Length;
-
- // version
- f.WriteByte((byte)1);
- i++;
-
- // texture info
- f.WriteByte(hasAlpha ? (byte)1 : (byte)0);
- f.WriteByte(fullAlpha ? (byte)1 : (byte)0);
- f.WriteByte(isMask ? (byte)1 : (byte)0);
- i += 3;
-
- // texture size
- byte[] uncompressedSize = Utils.IntToBytes(tgaData.Length);
- f.Write(uncompressedSize, 0, uncompressedSize.Length);
- i += uncompressedSize.Length;
-
- // texture id
- byte[] id = new byte[16];
- textureID.ToBytes(id, 0);
- f.Write(id, 0, 16);
- i += 16;
-
- // compressed texture data
- using (var compressed = new DeflateStream(f, CompressionMode.Compress))
- {
- compressed.Write(tgaData, 0, tgaData.Length);
- }
- }
- return true;
- }
- catch (Exception ex)
- {
- Logger.DebugLog(string.Format("Failed to save radegast cache file {0}: {1}", textureID, ex.Message));
- return false;
- }
- }
- #endregion Cached image save and load
-
- #region Static vertices and indices for a cube (used for bounding box drawing)
- /**********************************************
- 5 --- 4
- /| /|
- 1 --- 0 |
- | 6 --| 7
- |/ |/
- 2 --- 3
- ***********************************************/
- public static readonly float[] CubeVertices = new float[]
- {
- 0.5f, 0.5f, 0.5f, // 0
- -0.5f, 0.5f, 0.5f, // 1
- -0.5f, -0.5f, 0.5f, // 2
- 0.5f, -0.5f, 0.5f, // 3
- 0.5f, 0.5f, -0.5f, // 4
- -0.5f, 0.5f, -0.5f, // 5
- -0.5f, -0.5f, -0.5f, // 6
- 0.5f, -0.5f, -0.5f // 7
- };
-
- public static readonly ushort[] CubeIndices = new ushort[]
- {
- 0, 1, 2, 3, // Front Face
- 4, 5, 6, 7, // Back Face
- 1, 2, 6, 5, // Left Face
- 0, 3, 7, 4, // Right Face
- 0, 1, 5, 4, // Top Face
- 2, 3, 7, 6 // Bottom Face
- };
- #endregion Static vertices and indices for a cube (used for bounding box drawing)
-
- public static int GLLoadImage(Bitmap bitmap, bool hasAlpha)
- {
- return GLLoadImage(bitmap, hasAlpha, true);
- }
-
- public static int GLLoadImage(Bitmap bitmap, bool hasAlpha, bool useMipmap)
- {
- useMipmap = useMipmap && RenderSettings.HasMipmap;
- int ret = -1;
- GL.GenTextures(1, out ret);
- GL.BindTexture(TextureTarget.Texture2D, ret);
-
- Rectangle rectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
-
- BitmapData bitmapData =
- bitmap.LockBits(
- rectangle,
- ImageLockMode.ReadOnly,
- hasAlpha ? System.Drawing.Imaging.PixelFormat.Format32bppArgb : System.Drawing.Imaging.PixelFormat.Format24bppRgb);
-
- GL.TexImage2D(
- TextureTarget.Texture2D,
- 0,
- hasAlpha ? PixelInternalFormat.Rgba : PixelInternalFormat.Rgb8,
- bitmap.Width,
- bitmap.Height,
- 0,
- hasAlpha ? OpenTK.Graphics.OpenGL.PixelFormat.Bgra : OpenTK.Graphics.OpenGL.PixelFormat.Bgr,
- PixelType.UnsignedByte,
- bitmapData.Scan0);
-
- GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
- GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
- GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
- if (useMipmap)
- {
- GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.LinearMipmapLinear);
- GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.GenerateMipmap, 1);
- GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
- }
- else
- {
- GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
- }
-
- bitmap.UnlockBits(bitmapData);
- return ret;
- }
-
- public static void Draw2DBox(float x, float y, float width, float height, float depth)
- {
- GL.Begin(BeginMode.Quads);
- {
- GL.TexCoord2(0, 1);
- GL.Vertex3(x, y, depth);
- GL.TexCoord2(1, 1);
- GL.Vertex3(x + width, y, depth);
- GL.TexCoord2(1, 0);
- GL.Vertex3(x + width, y + height, depth);
- GL.TexCoord2(0, 0);
- GL.Vertex3(x, y + height, depth);
- }
- GL.End();
- }
-
- public static void ResetMaterial()
- {
- GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Ambient, new float[] { 0.2f, 0.2f, 0.2f, 1.0f });
- GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Diffuse, new float[] { 0.8f, 0.8f, 0.8f, 1.0f });
- GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Specular, new float[] { 0f, 0f, 0f, 1.0f });
- GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Emission, new float[] { 0f, 0f, 0f, 1.0f });
- GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Shininess, 0f);
- ShaderProgram.Stop();
- }
- }
-
- ///
- /// Represents camera object
- ///
- public class Camera
- {
- ///
- /// Indicates that there was manual camera movement, stop tracking objects
- ///
- public bool Manual;
- Vector3 mPosition;
- Vector3 mFocalPoint;
- bool mModified;
-
- /// Camera position
- public Vector3 Position { get { return mPosition; } set { mPosition = value; Modify(); } }
- /// Camera target
- public Vector3 FocalPoint { get { return mFocalPoint; } set { mFocalPoint = value; Modify(); } }
- /// Zoom level
- public float Zoom;
- /// Draw distance
- public float Far;
- /// Has camera been modified
- public bool Modified { get { return mModified; } set { mModified = value; } }
-
- public float TimeToTarget = 0f;
-
- public Vector3 RenderPosition;
- public Vector3 RenderFocalPoint;
-
- void Modify()
- {
- mModified = true;
- }
-
- public void Step(float time)
- {
- if (RenderPosition != Position)
- {
- RenderPosition = RHelp.Smoothed1stOrder(RenderPosition, Position, time);
- Modified = true;
- }
- if (RenderFocalPoint != FocalPoint)
- {
- RenderFocalPoint = RHelp.Smoothed1stOrder(RenderFocalPoint, FocalPoint, time);
- Modified = true;
- }
- }
-
- [Obsolete("Use Step(), left in here for reference")]
- public void Step2(float time)
- {
- TimeToTarget -= time;
- if (TimeToTarget <= time)
- {
- EndMove();
- return;
- }
-
- mModified = true;
-
- float pctElapsed = time / TimeToTarget;
-
- if (RenderPosition != Position)
- {
- float distance = Vector3.Distance(RenderPosition, Position);
- RenderPosition = Vector3.Lerp(RenderPosition, Position, distance * pctElapsed);
- }
-
- if (RenderFocalPoint != FocalPoint)
- {
- RenderFocalPoint = Interpolate(RenderFocalPoint, FocalPoint, pctElapsed);
- }
- }
-
- Vector3 Interpolate(Vector3 start, Vector3 end, float fraction)
- {
- float distance = Vector3.Distance(start, end);
- Vector3 direction = end - start;
- return start + direction * fraction;
- }
-
- public void EndMove()
- {
- mModified = true;
- TimeToTarget = 0;
- RenderPosition = Position;
- RenderFocalPoint = FocalPoint;
- }
-
- public void Pan(float deltaX, float deltaY)
- {
- Manual = true;
- Vector3 direction = Position - FocalPoint;
- direction.Normalize();
- Vector3 vy = direction % Vector3.UnitZ;
- Vector3 vx = vy % direction;
- Vector3 vxy = vx * deltaY + vy * deltaX;
- Position += vxy;
- FocalPoint += vxy;
- }
-
- public void Rotate(float delta, bool horizontal)
- {
- Manual = true;
- Vector3 direction = Position - FocalPoint;
- if (horizontal)
- {
- Position = FocalPoint + direction * new Quaternion(0f, 0f, (float)Math.Sin(delta), (float)Math.Cos(delta));
- }
- else
- {
- Position = FocalPoint + direction * Quaternion.CreateFromAxisAngle(direction % Vector3.UnitZ, delta);
- }
- }
-
- public void MoveToTarget(float delta)
- {
- Manual = true;
- Position += (Position - FocalPoint) * delta;
- }
-
- ///
- /// Sets the world in perspective of the camera
- ///
- public void LookAt()
- {
- OpenTK.Matrix4 lookAt = OpenTK.Matrix4.LookAt(
- RenderPosition.X, RenderPosition.Y, RenderPosition.Z,
- RenderFocalPoint.X, RenderFocalPoint.Y, RenderFocalPoint.Z,
- 0f, 0f, 1f);
- GL.MultMatrix(ref lookAt);
- }
- }
-
- public static class MeshToOBJ
- {
- public static bool MeshesToOBJ(Dictionary meshes, string filename)
- {
- StringBuilder obj = new StringBuilder();
- StringBuilder mtl = new StringBuilder();
-
- FileInfo objFileInfo = new FileInfo(filename);
-
- string mtlFilename = objFileInfo.FullName.Substring(objFileInfo.DirectoryName.Length + 1,
- objFileInfo.FullName.Length - (objFileInfo.DirectoryName.Length + 1) - 4) + ".mtl";
-
- obj.AppendLine("# Created by libprimrender");
- obj.AppendLine("mtllib ./" + mtlFilename);
- obj.AppendLine();
-
- mtl.AppendLine("# Created by libprimrender");
- mtl.AppendLine();
-
- int primNr = 0;
- foreach (FacetedMesh mesh in meshes.Values)
- {
- for (int j = 0; j < mesh.Faces.Count; j++)
- {
- Face face = mesh.Faces[j];
-
- if (face.Vertices.Count > 2)
- {
- string mtlName = String.Format("material{0}-{1}", primNr, face.ID);
- Primitive.TextureEntryFace tex = face.TextureFace;
- string texName = tex.TextureID.ToString() + ".tga";
-
- // FIXME: Convert the source to TGA (if needed) and copy to the destination
-
- float shiny = 0.00f;
- switch (tex.Shiny)
- {
- case Shininess.High:
- shiny = 1.00f;
- break;
- case Shininess.Medium:
- shiny = 0.66f;
- break;
- case Shininess.Low:
- shiny = 0.33f;
- break;
- }
-
- obj.AppendFormat("g face{0}-{1}{2}", primNr, face.ID, Environment.NewLine);
-
- mtl.AppendLine("newmtl " + mtlName);
- mtl.AppendFormat("Ka {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);
- mtl.AppendFormat("Kd {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);
- //mtl.AppendFormat("Ks {0} {1} {2}{3}");
- mtl.AppendLine("Tr " + tex.RGBA.A);
- mtl.AppendLine("Ns " + shiny);
- mtl.AppendLine("illum 1");
- if (tex.TextureID != UUID.Zero && tex.TextureID != Primitive.TextureEntry.WHITE_TEXTURE)
- mtl.AppendLine("map_Kd ./" + texName);
- mtl.AppendLine();
-
- // Write the vertices, texture coordinates, and vertex normals for this side
- for (int k = 0; k < face.Vertices.Count; k++)
- {
- Vertex vertex = face.Vertices[k];
-
- #region Vertex
-
- Vector3 pos = vertex.Position;
-
- // Apply scaling
- pos *= mesh.Prim.Scale;
-
- // Apply rotation
- pos *= mesh.Prim.Rotation;
-
- // 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 (mesh.Prim.ParentID != 0)
- pos += mesh.Prim.Position;
-
- obj.AppendFormat("v {0} {1} {2}{3}", pos.X, pos.Y, pos.Z, Environment.NewLine);
-
- #endregion Vertex
-
- #region Texture Coord
-
- obj.AppendFormat("vt {0} {1}{2}", vertex.TexCoord.X, vertex.TexCoord.Y,
- Environment.NewLine);
-
- #endregion Texture Coord
-
- #region Vertex Normal
-
- // HACK: Sometimes normals are getting set to
- if (!Single.IsNaN(vertex.Normal.X) && !Single.IsNaN(vertex.Normal.Y) && !Single.IsNaN(vertex.Normal.Z))
- obj.AppendFormat("vn {0} {1} {2}{3}", vertex.Normal.X, vertex.Normal.Y, vertex.Normal.Z,
- Environment.NewLine);
- else
- obj.AppendLine("vn 0.0 1.0 0.0");
-
- #endregion Vertex Normal
- }
-
- obj.AppendFormat("# {0} vertices{1}", face.Vertices.Count, Environment.NewLine);
- obj.AppendLine();
- obj.AppendLine("usemtl " + mtlName);
-
- #region Elements
-
- // Write all of the faces (triangles) for this side
- for (int k = 0; k < face.Indices.Count / 3; k++)
- {
- obj.AppendFormat("f -{0}/-{0}/-{0} -{1}/-{1}/-{1} -{2}/-{2}/-{2}{3}",
- face.Vertices.Count - face.Indices[k * 3 + 0],
- face.Vertices.Count - face.Indices[k * 3 + 1],
- face.Vertices.Count - face.Indices[k * 3 + 2],
- Environment.NewLine);
- }
-
- obj.AppendFormat("# {0} elements{1}", face.Indices.Count / 3, Environment.NewLine);
- obj.AppendLine();
-
- #endregion Elements
- }
- }
- primNr++;
- }
-
- try
- {
- File.WriteAllText(filename, obj.ToString());
- File.WriteAllText(mtlFilename, mtl.ToString());
- }
- catch (Exception)
- {
- return false;
- }
-
- return true;
- }
- }
-
- 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[] CreateSRTMatrix(Vector3 scale, Quaternion q, Vector3 pos)
- {
- 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)) * scale.X;
- mat[1] = (xy - wz) * scale.X;
- mat[2] = (xz + wy) * scale.X;
- mat[3] = 0.0f;
-
- mat[4] = (xy + wz) * scale.Y;
- mat[5] = (1.0f - (xx + zz)) * scale.Y;
- mat[6] = (yz - wx) * scale.Y;
- mat[7] = 0.0f;
-
- mat[8] = (xz - wy) * scale.Z;
- mat[9] = (yz + wx) * scale.Z;
- mat[10] = (1.0f - (xx + yy)) * scale.Z;
- mat[11] = 0.0f;
-
- //Positional parts
- mat[12] = pos.X;
- mat[13] = pos.Y;
- mat[14] = pos.Z;
- 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;
- }
-
- public static float[] Lerp(float[] matrix1, float[] matrix2, float amount)
- {
-
- float[] lerp = new float[16];
- //Probably not doing this as a loop is cheaper(unrolling)
- //also for performance we probably should not create new objects
- // but meh.
- for (int x = 0; x < 16; x++)
- {
- lerp[x] = matrix1[x] + ((matrix2[x] - matrix1[x]) * amount);
- }
-
- return lerp;
- }
-
-
- public static bool GluProject(OpenTK.Vector3 objPos, OpenTK.Matrix4 modelMatrix, OpenTK.Matrix4 projMatrix, int[] viewport, out OpenTK.Vector3 screenPos)
- {
- OpenTK.Vector4 _in;
- OpenTK.Vector4 _out;
-
- _in.X = objPos.X;
- _in.Y = objPos.Y;
- _in.Z = objPos.Z;
- _in.W = 1.0f;
-
- _out = OpenTK.Vector4.Transform(_in, modelMatrix);
- _in = OpenTK.Vector4.Transform(_out, projMatrix);
-
- if (_in.W <= 0.0)
- {
- screenPos = OpenTK.Vector3.Zero;
- return false;
- }
-
- _in.X /= _in.W;
- _in.Y /= _in.W;
- _in.Z /= _in.W;
- /* Map x, y and z to range 0-1 */
- _in.X = _in.X * 0.5f + 0.5f;
- _in.Y = _in.Y * 0.5f + 0.5f;
- _in.Z = _in.Z * 0.5f + 0.5f;
-
- /* Map x,y to viewport */
- _in.X = _in.X * viewport[2] + viewport[0];
- _in.Y = _in.Y * viewport[3] + viewport[1];
-
- screenPos.X = _in.X;
- screenPos.Y = _in.Y;
- screenPos.Z = _in.Z;
-
- return true;
- }
-
- public static double[] AbovePlane(double height)
- {
- return new double[] { 0, 0, 1, -height};
- }
-
- public static double[] BelowPlane(double height)
- {
- return new double[] { 0, 0, -1, height };
- }
- }
-
- public class attachment_point
- {
- public string name;
- public string joint;
- public Vector3 position;
- public Quaternion rotation;
- public int id;
- public int group;
-
- public GLMesh jointmesh;
- public int jointmeshindex;
-
- public attachment_point(XmlNode node)
- {
- name = node.Attributes.GetNamedItem("name").Value;
- joint = node.Attributes.GetNamedItem("joint").Value;
- position = VisualParamEx.XmlParseVector(node.Attributes.GetNamedItem("position").Value);
- rotation = VisualParamEx.XmlParseRotation(node.Attributes.GetNamedItem("rotation").Value);
- id = Int32.Parse(node.Attributes.GetNamedItem("id").Value);
- group = Int32.Parse(node.Attributes.GetNamedItem("group").Value);
- }
-
- }
-
- ///
- /// Subclass of LindenMesh that adds vertex, index, and texture coordinate
- /// arrays suitable for pushing direct to OpenGL
- ///
- public class GLMesh : LindenMesh
- {
- ///
- /// Subclass of LODMesh that adds an index array suitable for pushing
- /// direct to OpenGL
- ///
- ///
-
- public int teFaceID;
- public Dictionary _evp = new Dictionary();
-
- new public class LODMesh : LindenMesh.LODMesh
- {
- public ushort[] Indices;
-
- public override void LoadMesh(string filename)
- {
- base.LoadMesh(filename);
-
- // Generate the index array
- Indices = new ushort[_numFaces * 3];
- int current = 0;
- for (int i = 0; i < _numFaces; i++)
- {
- Indices[current++] = (ushort)_faces[i].Indices[0];
- Indices[current++] = (ushort)_faces[i].Indices[1];
- Indices[current++] = (ushort)_faces[i].Indices[2];
- }
- }
- }
-
- ///
- ///
- ///
- public struct GLData
- {
- public float[] Vertices;
- public float[] Normals;
- public ushort[] Indices;
- public float[] TexCoords;
- public Vector3 Center;
- public float[] weights; //strictly these are constant and don't need instancing with the GLMesh
- public string[] skinJoints; //strictly these are constant and don't need instancing with the GLMesh
- }
-
- public static GLData baseRenderData;
- public GLData RenderData;
- public GLData OrigRenderData;
- public GLData MorphRenderData;
-
- public GLAvatar av;
-
- public GLMesh(string name)
- : base(name)
- {
- }
-
- public GLMesh(GLMesh source, GLAvatar av)
- : base(source.Name)
- {
- this.av = av;
- // Make a new GLMesh copy from the supplied source
-
- RenderData.Vertices = new float[source.RenderData.Vertices.Length];
- RenderData.Normals = new float[source.RenderData.Normals.Length];
- RenderData.TexCoords = new float[source.RenderData.TexCoords.Length];
- RenderData.Indices = new ushort[source.RenderData.Indices.Length];
-
- RenderData.weights = new float[source.RenderData.weights.Length];
- RenderData.skinJoints = new string[source.RenderData.skinJoints.Length];
-
- Array.Copy(source.RenderData.Vertices, RenderData.Vertices, source.RenderData.Vertices.Length);
- Array.Copy(source.RenderData.Normals, RenderData.Normals, source.RenderData.Normals.Length);
-
- Array.Copy(source.RenderData.TexCoords, RenderData.TexCoords, source.RenderData.TexCoords.Length);
- Array.Copy(source.RenderData.Indices, RenderData.Indices, source.RenderData.Indices.Length);
- Array.Copy(source.RenderData.weights, RenderData.weights, source.RenderData.weights.Length);
- Array.Copy(source.RenderData.skinJoints, RenderData.skinJoints, source.RenderData.skinJoints.Length);
-
- RenderData.Center = new Vector3(source.RenderData.Center);
-
- teFaceID = source.teFaceID;
-
- _rotationAngles = new Vector3(source.RotationAngles);
- _scale = new Vector3(source.Scale);
- _position = new Vector3(source.Position);
-
- // We should not need to instance these the reference from the top should be constant
- _evp = source._evp;
- _morphs = source._morphs;
-
- OrigRenderData.Indices = new ushort[source.RenderData.Indices.Length];
- OrigRenderData.TexCoords = new float[source.RenderData.TexCoords.Length];
- OrigRenderData.Vertices = new float[source.RenderData.Vertices.Length];
-
- MorphRenderData.Vertices = new float[source.RenderData.Vertices.Length];
-
- Array.Copy(source.RenderData.Vertices, OrigRenderData.Vertices, source.RenderData.Vertices.Length);
- Array.Copy(source.RenderData.Vertices, MorphRenderData.Vertices, source.RenderData.Vertices.Length);
-
- Array.Copy(source.RenderData.TexCoords, OrigRenderData.TexCoords, source.RenderData.TexCoords.Length);
- Array.Copy(source.RenderData.Indices, OrigRenderData.Indices, source.RenderData.Indices.Length);
-
-
-
- }
-
- public void setMeshPos(Vector3 pos)
- {
- _position = pos;
- }
-
- public void setMeshRot(Vector3 rot)
- {
- _rotationAngles = rot;
- }
-
- public override void LoadMesh(string filename)
- {
- base.LoadMesh(filename);
-
- float minX, minY, minZ;
- minX = minY = minZ = Single.MaxValue;
- float maxX, maxY, maxZ;
- maxX = maxY = maxZ = Single.MinValue;
-
- // Generate the vertex array
- RenderData.Vertices = new float[_numVertices * 3];
- RenderData.Normals = new float[_numVertices * 3];
-
- Quaternion quat = Quaternion.CreateFromEulers(0, 0, (float)(Math.PI / 4.0));
-
- int current = 0;
- for (int i = 0; i < _numVertices; i++)
- {
-
- RenderData.Normals[current] = _vertices[i].Normal.X;
- RenderData.Vertices[current++] = _vertices[i].Coord.X;
- RenderData.Normals[current] = _vertices[i].Normal.Y;
- RenderData.Vertices[current++] = _vertices[i].Coord.Y;
- RenderData.Normals[current] = _vertices[i].Normal.Z;
- RenderData.Vertices[current++] = _vertices[i].Coord.Z;
-
- if (_vertices[i].Coord.X < minX)
- minX = _vertices[i].Coord.X;
- else if (_vertices[i].Coord.X > maxX)
- maxX = _vertices[i].Coord.X;
-
- if (_vertices[i].Coord.Y < minY)
- minY = _vertices[i].Coord.Y;
- else if (_vertices[i].Coord.Y > maxY)
- maxY = _vertices[i].Coord.Y;
-
- if (_vertices[i].Coord.Z < minZ)
- minZ = _vertices[i].Coord.Z;
- else if (_vertices[i].Coord.Z > maxZ)
- maxZ = _vertices[i].Coord.Z;
- }
-
- // Calculate the center-point from the bounding box edges
- RenderData.Center = new Vector3((minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2);
-
- // Generate the index array
- RenderData.Indices = new ushort[_numFaces * 3];
- current = 0;
- for (int i = 0; i < _numFaces; i++)
- {
- RenderData.Indices[current++] = (ushort)_faces[i].Indices[0];
- RenderData.Indices[current++] = (ushort)_faces[i].Indices[1];
- RenderData.Indices[current++] = (ushort)_faces[i].Indices[2];
- }
-
- // Generate the texcoord array
- RenderData.TexCoords = new float[_numVertices * 2];
- current = 0;
- for (int i = 0; i < _numVertices; i++)
- {
- RenderData.TexCoords[current++] = _vertices[i].TexCoord.X;
- RenderData.TexCoords[current++] = _vertices[i].TexCoord.Y;
- }
-
- RenderData.weights = new float[_numVertices];
- for (int i = 0; i < _numVertices; i++)
- {
- RenderData.weights[i] = _vertices[i].Weight;
- }
-
- RenderData.skinJoints = new string[_skinJoints.Length + 3];
- for (int i = 1; i < _skinJoints.Length; i++)
- {
- RenderData.skinJoints[i] = _skinJoints[i];
- }
-
-
- }
-
- public override void LoadLODMesh(int level, string filename)
- {
- LODMesh lod = new LODMesh();
- lod.LoadMesh(filename);
- _lodMeshes[level] = lod;
- }
-
- public void applyjointweights()
- {
-
- /*Each weight actually contains two pieces of information.
- * The number to the left of the decimal point is the index of the joint and also
- * implicitly indexes to the following joint. The actual weight is to the right of
- * the decimal point and interpolates between these two joints. The index is into an
- * "expanded" list of joints, not just a linear array of the joints as defined in the
- * skeleton file. In particular, any joint that has more than one child will be repeated
- * in the list for each of its children.
- */
-
- float weight = -9999;
- int jointindex = 0;
- float factor;
-
- Bone ba = null;
- Bone bb = null;
-
- for (int v = 0, x = 0; v < RenderData.Vertices.Length; v = v + 3, x++)
- {
- if (weight != RenderData.weights[x])
- {
-
- jointindex = (int)Math.Floor(weight = RenderData.weights[x]);
- factor = RenderData.weights[x] - jointindex;
- weight = weight - jointindex;
-
- string jointname = "", jointname2 = "";
-
- if (this.Name == "upperBodyMesh")
- {
- jointname = skeleton.mUpperMeshMapping[jointindex];
- jointindex++;
- jointname2 = skeleton.mUpperMeshMapping[jointindex];
- }
- else if (Name == "lowerBodyMesh")
- {
- jointname = skeleton.mLowerMeshMapping[jointindex];
- jointindex++;
- jointname2 = skeleton.mLowerMeshMapping[jointindex];
- }
- else if (Name == "headMesh")
- {
- jointname = skeleton.mHeadMeshMapping[jointindex];
- jointindex++;
- jointname2 = skeleton.mHeadMeshMapping[jointindex];
- }
- else
- {
- return; // not interested in this mesh
- }
-
-
- if (jointname == "")
- {
- //Don't yet handle this, its a split joint to two children
- ba = av.skel.mBones[jointname2];
- bb = null;
-
- //continue;
- }
- else
- {
-
- ba = av.skel.mBones[jointname];
- }
-
- if (jointname2 == "")
- {
- bb = null;
- }
- else
- {
- bb = av.skel.mBones[jointname2];
- }
- }
-
- //Special cases 0 is not used
- // ON upper torso 5 and 10 are not used
- // 4 is neck and 6 and 11 are the left and right collar bones
-
-
- //TODO use the SRT matrix to do this!
-
- Vector3 lerpa;
- Vector3 offseta;
- Quaternion rota;
-
- Vector3 lerpb;
- Vector3 offsetb;
- Quaternion rotb;
-
- Vector3 pos;
-
- Vector3 posa = new Vector3(MorphRenderData.Vertices[v], MorphRenderData.Vertices[v + 1], MorphRenderData.Vertices[v + 2]);
-
-
- if (bb != null)
- {
- lerpa = ba.getDeltaOffset();
- offseta = ba.getTotalOffset();
- rota = ba.getTotalRotation();
-
- lerpb = bb.getDeltaOffset();
- offsetb = bb.getTotalOffset();
- rotb = bb.getTotalRotation();
-
- Vector3 posb = new Vector3(MorphRenderData.Vertices[v], MorphRenderData.Vertices[v + 1], MorphRenderData.Vertices[v + 2]);
-
- //move back to mesh local coords
- posa = posa - offseta;
- // apply rotated offset
- posa = (posa + lerpa)*rota;
- //move back to avatar local coords
- posa = posa + offseta;
-
- //move back to mesh local coords
- posb = posb - offsetb;
- // apply rotated offset
- posb = (posb + lerpb)*rotb;
- //move back to avatar local coords
- posb = posb + offsetb;
-
- //LERP the two points to produce smooth skinning ;-)
- pos = Vector3.Lerp(posa, posb, weight);
-
- }
- else
- {
- lerpa = ba.getDeltaOffset();
- offseta = ba.getTotalOffset();
- rota = ba.getTotalRotation();
-
- //move back to mesh local coords
- posa = posa - offseta;
- // apply rotated offset
- posa = (posa + lerpa)*rota;
- //move back to avatar local coords
- posa = posa + offseta;
-
- //Only one bone contributing so its 100% that one.
- pos = posa;
-
- }
-
- RenderData.Vertices[v] = pos.X;
- RenderData.Vertices[v + 1] = pos.Y;
- RenderData.Vertices[v + 2] = pos.Z;
- }
- }
-
- public void resetallmorphs()
- {
- for (int i = 0; i < OrigRenderData.Vertices.Length / 3; i++)
- {
-
- MorphRenderData.Vertices[i * 3] = OrigRenderData.Vertices[i * 3];
- MorphRenderData.Vertices[(i * 3) + 1] = OrigRenderData.Vertices[i * 3 + 1];
- MorphRenderData.Vertices[(i * 3) + 2] = OrigRenderData.Vertices[i * 3 + 2];
-
- //MorphRenderData.Normals[i * 3] = OrigRenderData.Normals[i * 3];
- //MorphRenderData.Normals[(i * 3) + 1] = OrigRenderData.Normals[i * 3 + 1];
- //MorphRenderData.Normals[(i * 3) + 2] = OrigRenderData.Normals[i * 3 + 2];
-
- RenderData.TexCoords[i * 2] = OrigRenderData.TexCoords[i * 2];
- RenderData.TexCoords[(i * 2) + 1] = OrigRenderData.TexCoords[i * 2 + 1];
-
- }
-
- }
-
- public void morphmesh(Morph morph, float weight)
- {
- for (int v = 0; v < morph.NumVertices; v++)
- {
- MorphVertex mvx = morph.Vertices[v];
-
- uint i = mvx.VertexIndex;
-
- MorphRenderData.Vertices[i * 3] = MorphRenderData.Vertices[i * 3] + mvx.Coord.X * weight;
- MorphRenderData.Vertices[(i * 3) + 1] = MorphRenderData.Vertices[i * 3 + 1] + mvx.Coord.Y * weight;
- MorphRenderData.Vertices[(i * 3) + 2] = MorphRenderData.Vertices[i * 3 + 2] + mvx.Coord.Z * weight;
-
- //MorphRenderData.Normals[i * 3] = MorphRenderData.Normals[i * 3] + mvx.Normal.X * weight;
- //MorphRenderData.Normals[(i * 3)+1] = MorphRenderData.Normals[(i * 3)+1] + mvx.Normal.Y * weight;
- //MorphRenderData.Normals[(i * 3)+2] = MorphRenderData.Normals[(i * 3)+2] + mvx.Normal.Z * weight;
-
- RenderData.TexCoords[i * 2] = OrigRenderData.TexCoords[i * 2] + mvx.TexCoord.X * weight;
- RenderData.TexCoords[(i * 2) + 1] = OrigRenderData.TexCoords[i * 2 + 1] + mvx.TexCoord.Y * weight;
-
- }
- }
- }
-
- public class GLAvatar
- {
- private static Dictionary _defaultmeshes = new Dictionary();
- public Dictionary _meshes = new Dictionary();
-
- public skeleton skel = new skeleton();
- public static Dictionary attachment_points = new Dictionary();
-
- public bool _wireframe = true;
- public bool _showSkirt = false;
-
- public VisualParamEx.EparamSex msex;
-
- public byte[] VisualAppearanceParameters = new byte[1024];
- bool vpsent = false;
- static bool lindenMeshesLoaded = false;
-
- public GLAvatar()
- {
- foreach (KeyValuePair kvp in _defaultmeshes)
- {
- GLMesh mesh = new GLMesh(kvp.Value, this); // Instance our meshes
- _meshes.Add(kvp.Key, mesh);
-
- }
- }
-
- public static void dumptweaks()
- {
-
- for (int x = 0; x < VisualParamEx.tweakable_params.Count; x++)
- {
- VisualParamEx vpe = (VisualParamEx)VisualParamEx.tweakable_params.GetByIndex(x);
- Console.WriteLine(string.Format("{0} is {1}", x, vpe.Name));
- }
-
-
- }
-
- public static void loadlindenmeshes2(string LODfilename)
- {
- // Already have mashes loaded?
- if (lindenMeshesLoaded) return;
-
- attachment_points.Clear();
-
-
- string basedir = Directory.GetCurrentDirectory() + System.IO.Path.DirectorySeparatorChar + "character" + System.IO.Path.DirectorySeparatorChar;
-
- XmlDocument lad = new XmlDocument();
- lad.Load(basedir + LODfilename);
-
- //Firstly read the skeleton section this contains attachment point info and the bone deform info for visual params
- // And load the skeleton file in to the bones class
-
- XmlNodeList skeleton = lad.GetElementsByTagName("skeleton");
- string skeletonfilename = skeleton[0].Attributes.GetNamedItem("file_name").Value;
- Bone.loadbones(skeletonfilename);
-
- // Next read all the skeleton child nodes, we have attachment points and bone deform params
- // attachment points are an offset and rotation from a bone location
- // the name of the bone they reference is the joint paramater
- // params in the skeleton nodes are bone deforms, eg leg length changes the scale of the leg bones
-
- foreach (XmlNode skeletonnode in skeleton[0].ChildNodes)
- {
- if (skeletonnode.Name == "attachment_point")
- {
- attachment_point point = new attachment_point(skeletonnode);
- attachment_points.Add(point.id, point);
- }
-
- if (skeletonnode.Name == "param")
- {
- //Bone deform param
- VisualParamEx vp = new VisualParamEx(skeletonnode, VisualParamEx.ParamType.TYPE_BONEDEFORM);
- }
- }
-
- //Now we parse the mesh nodes, mesh nodes reference a particular LLM file with a LOD
- //and also list VisualParams for the various mesh morphs that can be applied
-
- XmlNodeList meshes = lad.GetElementsByTagName("mesh");
- foreach (XmlNode meshNode in meshes)
- {
- string type = meshNode.Attributes.GetNamedItem("type").Value;
- int lod = Int32.Parse(meshNode.Attributes.GetNamedItem("lod").Value);
- string fileName = meshNode.Attributes.GetNamedItem("file_name").Value;
-
- GLMesh mesh = (_defaultmeshes.ContainsKey(type) ? _defaultmeshes[type] : new GLMesh(type));
-
- if (meshNode.HasChildNodes)
- {
- foreach (XmlNode paramnode in meshNode.ChildNodes)
- {
- if (paramnode.Name == "param")
- {
- VisualParamEx vp = new VisualParamEx(paramnode, VisualParamEx.ParamType.TYPE_MORPH);
-
- mesh._evp.Add(vp.ParamID, vp); //Not sure we really need this may optimise out later
- vp.morphmesh = mesh.Name;
- }
- }
- }
-
- // Set up the texture elemenets for each mesh
- // And hack the eyeball position
- switch (mesh.Name)
- {
- case "lowerBodyMesh":
- mesh.teFaceID = (int)AvatarTextureIndex.LowerBaked;
- break;
-
- case "upperBodyMesh":
- mesh.teFaceID = (int)AvatarTextureIndex.UpperBaked;
- break;
-
- case "headMesh":
- mesh.teFaceID = (int)AvatarTextureIndex.HeadBaked;
- break;
-
- case "hairMesh":
- mesh.teFaceID = (int)AvatarTextureIndex.HairBaked;
- break;
-
- case "eyelashMesh":
- mesh.teFaceID = (int)AvatarTextureIndex.HeadBaked;
- break;
-
- case "eyeBallRightMesh":
- mesh.setMeshPos(Bone.mBones["mEyeLeft"].getTotalOffset());
- //mesh.setMeshRot(Bone.getRotation("mEyeLeft"));
- mesh.teFaceID = (int)AvatarTextureIndex.EyesBaked;
- break;
-
- case "eyeBallLeftMesh":
- mesh.setMeshPos(Bone.mBones["mEyeRight"].getTotalOffset());
- //mesh.setMeshRot(Bone.getRotation("mEyeRight"));
- mesh.teFaceID = (int)AvatarTextureIndex.EyesBaked;
- break;
-
- case "skirtMesh":
- mesh.teFaceID = (int)AvatarTextureIndex.SkirtBaked;
- break;
-
- default:
- mesh.teFaceID = 0;
- break;
- }
-
- if (lod == 0)
- mesh.LoadMesh(basedir + fileName);
- else
- mesh.LoadLODMesh(lod, basedir + fileName);
-
- _defaultmeshes[type] = mesh;
-
- }
-
- // Next are the textureing params, skipping for the moment
-
- XmlNodeList colors = lad.GetElementsByTagName("global_color");
- {
- foreach (XmlNode globalcolornode in colors)
- {
- foreach (XmlNode node in globalcolornode.ChildNodes)
- {
- if (node.Name == "param")
- {
- VisualParamEx vp = new VisualParamEx(node, VisualParamEx.ParamType.TYPE_COLOR);
- }
- }
- }
- }
-
- // Get layer paramaters, a bit of a verbose way to do it but we probably want to get access
- // to some of the other data not just the tag
-
- XmlNodeList layer_sets = lad.GetElementsByTagName("layer_set");
- {
- foreach (XmlNode layer_set in layer_sets)
- {
- foreach (XmlNode layer in layer_set.ChildNodes)
- {
- foreach (XmlNode layernode in layer.ChildNodes)
- {
- if (layernode.Name == "param")
- {
- VisualParamEx vp = new VisualParamEx(layernode, VisualParamEx.ParamType.TYPE_COLOR);
- }
- }
- }
- }
- }
-
- // Next are the driver parameters, these are parameters that change multiple real parameters
-
- XmlNodeList drivers = lad.GetElementsByTagName("driver_parameters");
-
- foreach (XmlNode node in drivers[0].ChildNodes) //lazy
- {
- if (node.Name == "param")
- {
- VisualParamEx vp = new VisualParamEx(node, VisualParamEx.ParamType.TYPE_DRIVER);
- }
- }
-
- lindenMeshesLoaded = true;
- }
-
- public void morphtest(Avatar av, int param, float weight)
- {
- VisualParamEx vpx;
- if (VisualParamEx.allParams.TryGetValue(param, out vpx))
- {
-
- //Logger.Log(string.Format("Applying visual parameter {0} id {1} value {2}", vpx.Name, vpx.ParamID, weight), Helpers.LogLevel.Info);
-
- //weight = weight * 2.0f;
- //weight=weight-1.0f;
-
- if (weight < 0)
- weight = 0;
-
- if (weight > 1.0)
- weight = 1;
-
- float value = vpx.MinValue + ((vpx.MaxValue - vpx.MinValue) * weight);
-
- if (vpx.pType == VisualParamEx.ParamType.TYPE_MORPH)
- {
- // Its a morph
- GLMesh mesh;
- if (_meshes.TryGetValue(vpx.morphmesh, out mesh))
- {
- foreach (LindenMesh.Morph morph in mesh.Morphs) //optimise me to a dictionary
- {
- if (morph.Name == vpx.Name)
- {
- if (mesh.Name == "skirtMesh" && _showSkirt == false)
- return;
-
- mesh.morphmesh(morph, value);
-
- return;
- }
- }
- }
- else
- {
- // Not a mesh morph
-
- // Its a volume deform, these appear to be related to collision volumes
- /*
- if (vpx.VolumeDeforms == null)
- {
- Logger.Log(String.Format("paramater {0} has invalid mesh {1}", param, vpx.morphmesh), Helpers.LogLevel.Warning);
- }
- else
- {
- foreach (KeyValuePair kvp in vpx.VolumeDeforms)
- {
- skel.deformbone(kvp.Key, kvp.Value.pos, kvp.Value.scale);
- }
- }
- * */
-
- }
-
- }
- else
- {
- // Its not a morph, it might be a driver though
- if (vpx.pType == VisualParamEx.ParamType.TYPE_DRIVER)
- {
- foreach (VisualParamEx.driven child in vpx.childparams)
- {
- morphtest(av, child.id, weight); //TO DO use minmax if they are present
- }
- return;
- }
-
- //Is this a bone deform?
- if (vpx.pType == VisualParamEx.ParamType.TYPE_BONEDEFORM)
- {
- foreach (KeyValuePair kvp in vpx.BoneDeforms)
- {
- skel.deformbone(kvp.Key, new Vector3(0, 0, 0), kvp.Value * value, Quaternion.Identity);
- }
- return;
- }
- else
- {
- //Logger.Log(String.Format("paramater {0} is not a morph and not a driver", param), Helpers.LogLevel.Warning);
- }
- }
-
- }
- else
- {
- Logger.Log("Invalid paramater " + param.ToString(), Helpers.LogLevel.Warning);
- }
- }
-
- public void morph(Avatar av)
- {
-
- if (av.VisualParameters == null)
- return;
-
- ThreadPool.QueueUserWorkItem(sync =>
- {
- int x = 0;
-
- if (av.VisualParameters.Length > 123)
- {
- if (av.VisualParameters[31] > 127)
- {
- msex = VisualParamEx.EparamSex.SEX_MALE;
- }
- else
- {
- msex = VisualParamEx.EparamSex.SEX_FEMALE;
- }
- }
-
- foreach (GLMesh mesh in _meshes.Values)
- {
- mesh.resetallmorphs();
- }
-
- foreach (byte vpvalue in av.VisualParameters)
- {
- /*
- if (vpsent == true && VisualAppearanceParameters[x] == vpvalue)
- {
-
- x++;
- continue;
- }
- */
-
- VisualAppearanceParameters[x] = vpvalue;
-
- if (x >= VisualParamEx.tweakable_params.Count)
- {
- //Logger.Log("Two many visual paramaters in Avatar appearance", Helpers.LogLevel.Warning);
- break;
- }
-
- VisualParamEx vpe = (VisualParamEx)VisualParamEx.tweakable_params.GetByIndex(x);
-
- if (vpe.sex != VisualParamEx.EparamSex.SEX_BOTH && vpe.sex != msex)
- {
- x++;
- continue;
- }
-
- float value = (vpvalue / 255.0f);
- this.morphtest(av, vpe.ParamID, value);
-
- x++;
- // if (x > 100)
- // break;
- }
-
- vpsent = true;
-
- foreach (GLMesh mesh in _meshes.Values)
- {
- mesh.applyjointweights();
- }
- });
- }
- }
-
- public class RenderAvatar : SceneObject
- {
- public static readonly BoundingVolume AvatarBoundingVolume;
-
- // Static constructor
- static RenderAvatar()
- {
- AvatarBoundingVolume = new BoundingVolume();
- AvatarBoundingVolume.FromScale(Vector3.One);
- }
-
- // Default constructor
- public RenderAvatar()
- {
- BoundingVolume = AvatarBoundingVolume;
- Type = SceneObjectType.Avatar;
- }
-
- public override Primitive BasePrim
- {
- get { return avatar; }
- set
- {
- if (value is Avatar)
- {
- avatar = (Avatar)value;
- AvatarBoundingVolume.CalcScaled(avatar.Scale);
- }
- }
- }
-
- public override void Step(float time)
- {
- glavatar.skel.animate(time);
- base.Step(time);
- }
-
- public GLAvatar glavatar = new GLAvatar();
- public Avatar avatar;
- public FaceData[] data = new FaceData[32];
- public Dictionary animlist = new Dictionary();
- public Dictionary Wearables = new Dictionary();
-
- }
-
- public class skeleton
- {
- public Dictionary mBones;
- public Dictionary mPriority = new Dictionary();
- public static Dictionary mUpperMeshMapping = new Dictionary();
- public static Dictionary mLowerMeshMapping = new Dictionary();
- public static Dictionary mHeadMeshMapping = new Dictionary();
-
- public List mAnimations = new List();
-
- public static Dictionary mAnimationTransactions = new Dictionary();
-
- public static Dictionary mAnimationCache = new Dictionary();
-
- public bool mNeedsUpdate = false;
- public bool mNeedsMeshRebuild = false;
-
- public Bone mLeftEye = null;
- public Bone mRightEye = null;
-
- public struct binBVHJointState
- {
- public float currenttime_rot;
- public int lastkeyframe_rot;
- public int nextkeyframe_rot;
-
- public float currenttime_pos;
- public int lastkeyframe_pos;
- public int nextkeyframe_pos;
-
- public int loopinframe;
- public int loopoutframe;
- }
-
-
- public skeleton()
- {
-
-
-
- mBones = new Dictionary();
-
- foreach (Bone src in Bone.mBones.Values)
- {
- Bone newbone = new Bone(src);
- mBones.Add(newbone.name, newbone);
- }
-
- //rebuild the skeleton structure on the new copy
- foreach (Bone src in mBones.Values)
- {
- if (src.mParentBone != null)
- {
- src.parent = mBones[src.mParentBone];
- src.parent.children.Add(src);
- }
- }
-
- //FUDGE
- if (mUpperMeshMapping.Count == 0)
- {
- mUpperMeshMapping.Add(1, "mPelvis");
- mUpperMeshMapping.Add(2, "mTorso");
- mUpperMeshMapping.Add(3, "mChest");
- mUpperMeshMapping.Add(4, "mNeck");
- mUpperMeshMapping.Add(5, "");
- mUpperMeshMapping.Add(6, "mCollarLeft");
- mUpperMeshMapping.Add(7, "mShoulderLeft");
- mUpperMeshMapping.Add(8, "mElbowLeft");
- mUpperMeshMapping.Add(9, "mWristLeft");
- mUpperMeshMapping.Add(10, "");
- mUpperMeshMapping.Add(11, "mCollarRight");
- mUpperMeshMapping.Add(12, "mShoulderRight");
- mUpperMeshMapping.Add(13, "mElbowRight");
- mUpperMeshMapping.Add(14, "mWristRight");
- mUpperMeshMapping.Add(15, "");
-
- mLowerMeshMapping.Add(1, "mPelvis");
- mLowerMeshMapping.Add(2, "mHipRight");
- mLowerMeshMapping.Add(3, "mKneeRight");
- mLowerMeshMapping.Add(4, "mAnkleRight");
- mLowerMeshMapping.Add(5, "");
- mLowerMeshMapping.Add(6, "mHipLeft");
- mLowerMeshMapping.Add(7, "mKneeLeft");
- mLowerMeshMapping.Add(8, "mAnkleLeft");
- mLowerMeshMapping.Add(9, "");
-
- mHeadMeshMapping.Add(1, "mNeck");
- mHeadMeshMapping.Add(2, "mHead");
- mHeadMeshMapping.Add(3, "");
-
- }
-
- mLeftEye = mBones["mEyeLeft"];
- mRightEye = mBones["mEyeRight"];
-
- }
-
- public void deformbone(string name, Vector3 pos, Vector3 scale, Quaternion rotation)
- {
- Bone bone;
- if (mBones.TryGetValue(name, out bone))
- {
- bone.deformbone(pos, scale, rotation);
- }
- }
-
- //TODO check offset and rot calcuations should each offset be multiplied by its parent rotation in
- // a standard child/parent rot/offset way?
- public Vector3 getOffset(string bonename)
- {
- Bone b;
- if (mBones.TryGetValue(bonename, out b))
- {
- return (b.getTotalOffset());
- }
- else
- {
- return Vector3.Zero;
- }
- }
-
- public Quaternion getRotation(string bonename)
- {
- Bone b;
- if (mBones.TryGetValue(bonename, out b))
- {
- return (b.getTotalRotation());
- }
- else
- {
- return Quaternion.Identity;
- }
- }
-
-
- public void flushanimations()
- {
- lock (mAnimations)
- {
- mAnimations.Clear();
- }
- }
-
- // Add animations to the global decoded list
- // TODO garbage collect unused animations somehow
- public static void addanimation(OpenMetaverse.Assets.Asset asset, UUID tid, BinBVHAnimationReader b)
- {
- RenderAvatar av;
- mAnimationTransactions.TryGetValue(tid, out av);
- if (av == null)
- return;
-
- mAnimationTransactions.Remove(tid);
-
- if (asset != null)
- {
- b = new BinBVHAnimationReader(asset.AssetData);
- mAnimationCache[asset.AssetID] = b;
- Logger.Log("Adding new decoded animaton known animations " + asset.AssetID.ToString(), Helpers.LogLevel.Info);
- }
-
- int pos = 0;
- foreach (binBVHJoint joint in b.joints)
- {
- binBVHJointState state;
-
- state.lastkeyframe_rot = 0;
- state.nextkeyframe_rot = 1;
-
- state.lastkeyframe_pos = 0;
- state.nextkeyframe_pos = 1;
-
- state.currenttime_rot = 0;
- state.currenttime_pos = 0;
-
- state.loopinframe = 0;
- state.loopoutframe = joint.rotationkeys.Length - 1;
-
- if (b.Loop == true)
- {
- int frame = 0;
- foreach (binBVHJointKey key in joint.rotationkeys)
- {
- if (key.time == b.InPoint)
- {
- state.loopinframe = frame;
- }
-
- if (key.time == b.OutPoint)
- {
- state.loopoutframe = frame;
- }
-
- frame++;
-
- }
-
- }
-
- b.joints[pos].Tag = state;
- pos++;
- }
-
- lock (av.glavatar.skel.mAnimations)
- {
- av.glavatar.skel.mAnimations.Add(b);
- }
- }
-
- public void animate(float lastframetime)
- {
- lock (mPriority) mPriority.Clear();
-
- lock (mAnimations)
- foreach (BinBVHAnimationReader b in mAnimations)
- {
- if (b == null)
- continue;
-
- int jpos = 0;
- foreach (binBVHJoint joint in b.joints)
- {
- int prio = 0;
-
- lock (mPriority)
- {
- //Quick hack to stack animations in the correct order
- //TODO we need to do this per joint as they all have their own priorities as well ;-(
- if (mPriority.TryGetValue(joint.Name, out prio))
- {
- if (prio > b.Priority)
- continue;
- }
-
- mPriority[joint.Name] = b.Priority;
- }
-
- binBVHJointState state = (binBVHJointState)b.joints[jpos].Tag;
-
- state.currenttime_rot += lastframetime;
- state.currenttime_pos += lastframetime;
-
- //fudge
- if (b.joints[jpos].rotationkeys.Length == 1)
- {
- state.nextkeyframe_rot = 0;
- }
-
- Vector3 poslerp = Vector3.Zero;
-
- if (b.joints[jpos].positionkeys.Length > 2)
- {
- binBVHJointKey pos2 = b.joints[jpos].positionkeys[state.nextkeyframe_pos];
-
-
- if (state.currenttime_pos > pos2.time)
- {
- state.lastkeyframe_pos++;
- state.nextkeyframe_pos++;
-
- if (state.nextkeyframe_pos >= b.joints[jpos].positionkeys.Length || (state.nextkeyframe_pos >= state.loopoutframe && b.Loop == true))
- {
- if (b.Loop == true)
- {
- state.nextkeyframe_pos = state.loopinframe;
- state.currenttime_pos = b.InPoint;
-
- if (state.lastkeyframe_pos >= b.joints[jpos].positionkeys.Length)
- {
- state.lastkeyframe_pos = state.loopinframe;
- }
- }
- else
- {
- state.nextkeyframe_pos = joint.positionkeys.Length - 1;
- }
- }
-
-
-
- if (state.lastkeyframe_pos >= b.joints[jpos].positionkeys.Length)
- {
- if (b.Loop == true)
- {
- state.lastkeyframe_pos = 0;
- state.currenttime_pos = 0;
-
- }
- else
- {
- state.lastkeyframe_pos = joint.positionkeys.Length - 1;
- if (state.lastkeyframe_pos < 0)//eeww
- state.lastkeyframe_pos = 0;
- }
- }
- }
-
- binBVHJointKey pos = b.joints[jpos].positionkeys[state.lastkeyframe_pos];
-
-
- if (state.currenttime_pos != ((pos.time - b.joints[jpos].positionkeys[0].time)))
- {
-
- float delta = (pos2.time - pos.time) / ((state.currenttime_pos) - (pos.time - b.joints[jpos].positionkeys[0].time));
-
- if (delta < 0)
- delta = 0;
-
- if (delta > 1)
- delta = 1;
-
- poslerp = Vector3.Lerp(pos.key_element, pos2.key_element, delta);
- }
-
- }
-
-
- Vector3 rotlerp = Vector3.Zero;
- if (b.joints[jpos].rotationkeys.Length > 0)
- {
- binBVHJointKey rot2 = b.joints[jpos].rotationkeys[state.nextkeyframe_rot];
-
- if (state.currenttime_rot > rot2.time)
- {
- state.lastkeyframe_rot++;
- state.nextkeyframe_rot++;
-
- if (state.nextkeyframe_rot >= b.joints[jpos].rotationkeys.Length || (state.nextkeyframe_rot >= state.loopoutframe && b.Loop == true))
- {
- if (b.Loop == true)
- {
- state.nextkeyframe_rot = state.loopinframe;
- state.currenttime_rot = b.InPoint;
-
- if (state.lastkeyframe_rot >= b.joints[jpos].rotationkeys.Length)
- {
- state.lastkeyframe_rot = state.loopinframe;
-
-
- }
-
- }
- else
- {
- state.nextkeyframe_rot = joint.rotationkeys.Length - 1;
- }
- }
-
- if (state.lastkeyframe_rot >= b.joints[jpos].rotationkeys.Length)
- {
- if (b.Loop == true)
- {
- state.lastkeyframe_rot = 0;
- state.currenttime_rot = 0;
-
- }
- else
- {
- state.lastkeyframe_rot = joint.rotationkeys.Length - 1;
- }
- }
- }
-
- binBVHJointKey rot = b.joints[jpos].rotationkeys[state.lastkeyframe_rot];
- rot2 = b.joints[jpos].rotationkeys[state.nextkeyframe_rot];
-
- float deltarot = 0;
- if (state.currenttime_rot != (rot.time - b.joints[jpos].rotationkeys[0].time))
- {
- deltarot = (rot2.time - rot.time) / ((state.currenttime_rot) - (rot.time - b.joints[jpos].rotationkeys[0].time));
- }
-
- if (deltarot < 0)
- deltarot = 0;
-
- if (deltarot > 1)
- deltarot = 1;
-
- rotlerp = Vector3.Lerp(rot.key_element, rot2.key_element, deltarot);
-
- }
-
- b.joints[jpos].Tag = (object)state;
-
- deformbone(joint.Name, poslerp, new Vector3(0, 0, 0), new Quaternion(rotlerp.X, rotlerp.Y, rotlerp.Z));
-
- jpos++;
- }
-
- mNeedsMeshRebuild = true;
- }
-
- mNeedsUpdate = false;
- }
- }
-
- public class Bone
- {
- public string name;
- public Vector3 pos;
- public Quaternion rot;
- public Vector3 scale;
- public Vector3 piviot;
-
- public Vector3 orig_pos;
- public Quaternion orig_rot;
- public Vector3 orig_scale;
- public Vector3 orig_piviot;
-
- Matrix4 mDeformMatrix = Matrix4.Identity;
-
- public Bone parent;
-
- public List children = new List();
-
- public static Dictionary mBones = new Dictionary();
- public static Dictionary mIndexedBones = new Dictionary();
- static int boneaddindex = 0;
-
- private bool rotdirty = true;
- private bool posdirty = true;
-
- private Vector3 mTotalPos;
- private Quaternion mTotalRot;
-
- private Vector3 mDeltaPos;
- private Quaternion mDeltaRot;
-
- public string mParentBone = null;
-
- public Bone()
- {
- }
-
- public Bone(Bone source)
- {
- name = String.Copy(source.name);
- pos = new Vector3(source.pos);
- rot = new Quaternion(source.rot);
- scale = new Vector3(source.scale);
- piviot = new Vector3(source.piviot);
-
- orig_piviot = source.orig_piviot;
- orig_pos = source.orig_pos;
- orig_rot = source.orig_rot;
- orig_scale = source.orig_scale;
-
- mParentBone = source.mParentBone;
-
- mDeformMatrix = new Matrix4(source.mDeformMatrix);
- }
-
- public static void loadbones(string skeletonfilename)
- {
- mBones.Clear();
- string basedir = Directory.GetCurrentDirectory() + System.IO.Path.DirectorySeparatorChar + "character" + System.IO.Path.DirectorySeparatorChar;
- XmlDocument skeleton = new XmlDocument();
- skeleton.Load(basedir + skeletonfilename);
- XmlNode boneslist = skeleton.GetElementsByTagName("linden_skeleton")[0];
- addbone(boneslist.ChildNodes[0], null);
- }
-
- public static void addbone(XmlNode bone, Bone parent)
- {
-
- if (bone.Name != "bone")
- return;
-
- Bone b = new Bone();
- b.name = bone.Attributes.GetNamedItem("name").Value;
-
- string pos = bone.Attributes.GetNamedItem("pos").Value;
- string[] posparts = pos.Split(' ');
- b.pos = new Vector3(float.Parse(posparts[0], Utils.EnUsCulture), float.Parse(posparts[1], Utils.EnUsCulture), float.Parse(posparts[2], Utils.EnUsCulture));
- b.orig_pos = new Vector3(b.pos);
-
- string rot = bone.Attributes.GetNamedItem("rot").Value;
- string[] rotparts = rot.Split(' ');
- b.rot = Quaternion.CreateFromEulers((float)(float.Parse(rotparts[0], Utils.EnUsCulture) * Math.PI / 180f), (float)(float.Parse(rotparts[1], Utils.EnUsCulture) * Math.PI / 180f), (float)(float.Parse(rotparts[2], Utils.EnUsCulture) * Math.PI / 180f));
- b.orig_rot = new Quaternion(b.rot);
-
- string scale = bone.Attributes.GetNamedItem("scale").Value;
- string[] scaleparts = scale.Split(' ');
- b.scale = new Vector3(float.Parse(scaleparts[0], Utils.EnUsCulture), float.Parse(scaleparts[1], Utils.EnUsCulture), float.Parse(scaleparts[2], Utils.EnUsCulture));
- b.orig_scale = new Vector3(b.scale);
-
- float[] deform = Math3D.CreateSRTMatrix(new Vector3(1, 1, 1), b.rot, b.orig_pos);
- b.mDeformMatrix = new Matrix4(deform[0], deform[1], deform[2], deform[3], deform[4], deform[5], deform[6], deform[7], deform[8], deform[9], deform[10], deform[11], deform[12], deform[13], deform[14], deform[15]);
-
- //TODO piviot
-
- b.parent = parent;
- if (parent != null)
- {
- b.mParentBone = parent.name;
- parent.children.Add(b);
- }
-
- mBones.Add(b.name, b);
- mIndexedBones.Add(boneaddindex++, b);
-
- Logger.Log("Found bone " + b.name, Helpers.LogLevel.Info);
-
- foreach (XmlNode childbone in bone.ChildNodes)
- {
- addbone(childbone, b);
- }
-
- }
-
- public void deformbone(Vector3 pos, Vector3 scale, Quaternion rot)
- {
- //float[] deform = Math3D.CreateSRTMatrix(scale, rot, this.orig_pos);
- //mDeformMatrix = new Matrix4(deform[0], deform[1], deform[2], deform[3], deform[4], deform[5], deform[6], deform[7], deform[8], deform[9], deform[10], deform[11], deform[12], deform[13], deform[14], deform[15]);
- this.pos = Bone.mBones[name].orig_pos + pos;
- this.scale = Bone.mBones[name].orig_scale + scale;
- this.rot = Bone.mBones[name].orig_rot * rot;
-
- markdirty();
- }
-
- // If we deform a bone mark this bone and all its children as dirty.
- public void markdirty()
- {
- rotdirty = true;
- posdirty = true;
- foreach (Bone childbone in children)
- {
- childbone.markdirty();
- }
- }
-
- public Matrix4 getdeform()
- {
- if (this.parent != null)
- {
- return mDeformMatrix * parent.getdeform();
- }
- else
- {
- return mDeformMatrix;
- }
- }
-
- private Vector3 getOffset()
- {
- if (parent != null)
- {
- Quaternion totalrot = getParentRot(); // we don't want this joints rotation included
- Vector3 parento = parent.getOffset();
- Vector3 mepre = pos * scale;
- mepre = mepre * totalrot;
- mTotalPos = parento + mepre;
-
- Vector3 orig = getOrigOffset();
- mDeltaPos = mTotalPos - orig;
-
- posdirty = false;
-
- return mTotalPos;
- }
- else
- {
- Vector3 orig = getOrigOffset();
- mTotalPos = (pos * scale);
- mDeltaPos = mTotalPos - orig;
- posdirty = false;
- return mTotalPos;
-
- }
- }
-
- public Vector3 getMyOffset()
- {
- return pos * scale;
- }
-
- // Try to save some cycles by not recalculating positions and rotations every time
- public Vector3 getTotalOffset()
- {
- if (posdirty == false)
- {
- return mTotalPos;
- }
- else
- {
- return getOffset();
- }
- }
-
- public Vector3 getDeltaOffset()
- {
- if (posdirty == false)
- {
- return mDeltaPos;
- }
- else
- {
- getOffset();
- return mDeltaPos;
- }
- }
-
- private Vector3 getOrigOffset()
- {
- if (parent != null)
- {
- return (parent.getOrigOffset() + orig_pos);
- }
- else
- {
- return orig_pos;
- }
- }
-
- private static Quaternion getRotation(string bonename)
- {
- Bone b;
- if (mBones.TryGetValue(bonename, out b))
- {
- return (b.getRotation());
- }
- else
- {
- return Quaternion.Identity;
- }
- }
-
-
- private Quaternion getParentRot()
- {
- Quaternion totalrot = Quaternion.Identity;
-
- if (parent != null)
- {
- totalrot = parent.getRotation();
- }
-
- return totalrot;
-
- }
-
- private Quaternion getRotation()
- {
- Quaternion totalrot = rot;
-
- if (parent != null)
- {
- totalrot = parent.getRotation() * rot;
- }
-
- mTotalRot = totalrot;
- rotdirty = false;
-
- return totalrot;
- }
-
- public Quaternion getTotalRotation()
- {
- if (rotdirty == false)
- {
- return mTotalRot;
- }
- else
- {
- return getRotation();
- }
- }
- }
-
- public class VisualParamEx
- {
-
- static public Dictionary allParams = new Dictionary();
- static public Dictionary deformParams = new Dictionary();
- static public Dictionary morphParams = new Dictionary();
- static public Dictionary drivenParams = new Dictionary();
- static public SortedList tweakable_params = new SortedList();
-
- public Dictionary BoneDeforms = null;
- public Dictionary VolumeDeforms = null;
- public List childparams = null;
-
- public string morphmesh = null;
-
- enum GroupType
- {
- VISUAL_PARAM_GROUP_TWEAKABLE = 0,
- VISUAL_PARAM_GROUP_ANIMATABLE,
- VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT,
- }
-
- public struct VolumeDeform
- {
- public string name;
- public Vector3 scale;
- public Vector3 pos;
- }
-
- public enum EparamSex
- {
- SEX_BOTH = 0,
- SEX_FEMALE = 1,
- SEX_MALE = 2
- }
-
- public enum ParamType
- {
- TYPE_BONEDEFORM,
- TYPE_MORPH,
- TYPE_DRIVER,
- TYPE_COLOR,
- TYPE_LAYER
- }
-
- public struct driven
- {
- public int id;
- public float max1;
- public float max2;
- public float min1;
- public float min2;
- public bool hasMinMax;
- }
-
- public string meshname;
-
- /// Index of this visual param
- public int ParamID;
- /// Internal name
- public string Name;
- /// Group ID this parameter belongs to
- public int Group;
- /// Name of the wearable this parameter belongs to
- public string Wearable;
- /// Displayable label of this characteristic
- public string Label;
- /// Displayable label for the minimum value of this characteristic
- public string LabelMin;
- /// Displayable label for the maximum value of this characteristic
- public string LabelMax;
- /// Default value
- public float DefaultValue;
- /// Minimum value
- public float MinValue;
- /// Maximum value
- public float MaxValue;
- /// Is this param used for creation of bump layer?
- public bool IsBumpAttribute;
- /// Alpha blending/bump info
- public VisualAlphaParam? AlphaParams;
- /// Color information
- public VisualColorParam? ColorParams;
- /// Array of param IDs that are drivers for this parameter
- public int[] Drivers;
- /// The Avatar Sex that this parameter applies to
- public EparamSex sex;
-
- public ParamType pType;
-
- public static int count = 0;
-
- ///
- /// Set all the values through the constructor
- ///
- /// Index of this visual param
- /// Internal name
- ///
- ///
- /// Displayable label of this characteristic
- /// Displayable label for the minimum value of this characteristic
- /// Displayable label for the maximum value of this characteristic
- /// Default value
- /// Minimum value
- /// Maximum value
- /// Is this param used for creation of bump layer?
- /// Array of param IDs that are drivers for this parameter
- /// Alpha blending/bump info
- /// Color information
- public VisualParamEx(int paramID, string name, int group, string wearable, string label, string labelMin, string labelMax, float def, float min, float max, bool isBumpAttribute, int[] drivers, VisualAlphaParam? alpha, VisualColorParam? colorParams)
- {
- ParamID = paramID;
- Name = name;
- Group = group;
- Wearable = wearable;
- Label = label;
- LabelMin = labelMin;
- LabelMax = labelMax;
- DefaultValue = def;
- MaxValue = max;
- MinValue = min;
- IsBumpAttribute = isBumpAttribute;
- Drivers = drivers;
- AlphaParams = alpha;
- ColorParams = colorParams;
- sex = EparamSex.SEX_BOTH;
- }
-
- public VisualParamEx(XmlNode node, ParamType pt)
- {
- pType = pt;
-
- ParamID = Int32.Parse(node.Attributes.GetNamedItem("id").Value);
- Name = node.Attributes.GetNamedItem("name").Value;
- Group = Int32.Parse(node.Attributes.GetNamedItem("group").Value);
-
- //These dont exist for facal expresion morphs
- if (node.Attributes.GetNamedItem("wearable") != null)
- Wearable = node.Attributes.GetNamedItem("wearable").Value;
-
- MinValue = float.Parse(node.Attributes.GetNamedItem("value_min").Value, Utils.EnUsCulture);
- MaxValue = float.Parse(node.Attributes.GetNamedItem("value_max").Value, Utils.EnUsCulture);
-
- // These do not exists for driven parameters
- if (node.Attributes.GetNamedItem("label_min") != null)
- {
- LabelMin = node.Attributes.GetNamedItem("label_min").Value;
- }
-
- if (node.Attributes.GetNamedItem("label_max") != null)
- {
- LabelMax = node.Attributes.GetNamedItem("label_max").Value;
- }
-
- XmlNode sexnode = node.Attributes.GetNamedItem("sex");
-
- if (sexnode != null)
- {
- if (sexnode.Value == "male")
- {
- sex = EparamSex.SEX_MALE;
- }
- else
- {
- sex = EparamSex.SEX_FEMALE;
- }
-
- }
-
- Group = int.Parse(node.Attributes.GetNamedItem("group").Value);
-
- if (Group == (int)GroupType.VISUAL_PARAM_GROUP_TWEAKABLE)
- {
- if (!tweakable_params.ContainsKey(ParamID)) //stupid duplicate shared params
- {
- tweakable_params.Add(this.ParamID, this);
- }
- //Logger.Log(String.Format("Adding tweakable paramater ID {0} {1}", count, this.Name), Helpers.LogLevel.Info);
- count++;
- }
-
- //TODO other paramaters but these arew concerned with editing the GUI display so not too fussed at the moment
-
- try
- {
- allParams.Add(ParamID, this);
- }
- catch
- {
- Logger.Log("Duplicate VisualParam in allParams id " + ParamID.ToString(), Helpers.LogLevel.Info);
- }
-
- if (pt == ParamType.TYPE_BONEDEFORM)
- {
- // If we are in the skeleton section then we also have bone deforms to parse
- BoneDeforms = new Dictionary();
- if (node.HasChildNodes && node.ChildNodes[0].HasChildNodes)
- {
- ParseBoneDeforms(node.ChildNodes[0].ChildNodes);
- }
- deformParams.Add(ParamID, this);
- }
-
- if (pt == ParamType.TYPE_MORPH)
- {
- VolumeDeforms = new Dictionary();
- if (node.HasChildNodes && node.ChildNodes[0].HasChildNodes)
- {
- ParseVolumeDeforms(node.ChildNodes[0].ChildNodes);
- }
-
- try
- {
- morphParams.Add(ParamID, this);
- }
- catch
- {
- Logger.Log("Duplicate VisualParam in morphParams id " + ParamID.ToString(), Helpers.LogLevel.Info);
- }
-
- }
-
- if (pt == ParamType.TYPE_DRIVER)
- {
- childparams = new List();
- if (node.HasChildNodes && node.ChildNodes[0].HasChildNodes) //LAZY
- {
- ParseDrivers(node.ChildNodes[0].ChildNodes);
- }
-
- drivenParams.Add(ParamID, this);
-
- }
-
- if (pt == ParamType.TYPE_COLOR)
- {
- if (node.HasChildNodes)
- {
- foreach (XmlNode colorchild in node.ChildNodes)
- {
- if (colorchild.Name == "param_color")
- {
- //TODO extract
- }
- }
-
- }
- }
-
- }
-
- void ParseBoneDeforms(XmlNodeList deforms)
- {
- foreach (XmlNode node in deforms)
- {
- if (node.Name == "bone")
- {
- string name = node.Attributes.GetNamedItem("name").Value;
- Vector3 scale = XmlParseVector(node.Attributes.GetNamedItem("scale").Value);
- BoneDeforms.Add(name, scale);
- }
- }
- }
-
- void ParseVolumeDeforms(XmlNodeList deforms)
- {
- foreach (XmlNode node in deforms)
- {
- if (node.Name == "volume_morph")
- {
- VolumeDeform vd = new VolumeDeform();
- vd.name = node.Attributes.GetNamedItem("name").Value;
- vd.name = vd.name.ToLower();
-
- if (node.Attributes.GetNamedItem("scale") != null)
- {
- vd.scale = XmlParseVector(node.Attributes.GetNamedItem("scale").Value);
- }
- else
- {
- vd.scale = new Vector3(0, 0, 0);
- }
-
- if (node.Attributes.GetNamedItem("pos") != null)
- {
- vd.pos = XmlParseVector(node.Attributes.GetNamedItem("pos").Value);
- }
- else
- {
- vd.pos = new Vector3(0f, 0f, 0f);
- }
-
- VolumeDeforms.Add(vd.name, vd);
- }
- }
- }
-
- void ParseDrivers(XmlNodeList drivennodes)
- {
- foreach (XmlNode node in drivennodes)
- {
- if (node.Name == "driven")
- {
- driven d = new driven();
-
- d.id = Int32.Parse(node.Attributes.GetNamedItem("id").Value);
- XmlNode param = node.Attributes.GetNamedItem("max1");
- if (param != null)
- {
- d.max1 = float.Parse(param.Value, Utils.EnUsCulture);
- d.max2 = float.Parse(node.Attributes.GetNamedItem("max2").Value, Utils.EnUsCulture);
- d.min1 = float.Parse(node.Attributes.GetNamedItem("min1").Value, Utils.EnUsCulture);
- d.max2 = float.Parse(node.Attributes.GetNamedItem("min2").Value, Utils.EnUsCulture);
- d.hasMinMax = true;
- }
- else
- {
- d.hasMinMax = false;
- }
-
- childparams.Add(d);
-
- }
- }
- }
-
- public static Vector3 XmlParseVector(string data)
- {
- string[] posparts = data.Split(' ');
- return new Vector3(float.Parse(posparts[0], Utils.EnUsCulture), float.Parse(posparts[1], Utils.EnUsCulture), float.Parse(posparts[2], Utils.EnUsCulture));
- }
-
- public static Quaternion XmlParseRotation(string data)
- {
- string[] rotparts = data.Split(' ');
- return Quaternion.CreateFromEulers((float)(float.Parse(rotparts[0]) * Math.PI / 180f), (float)(float.Parse(rotparts[1]) * Math.PI / 180f), (float)(float.Parse(rotparts[2]) * Math.PI / 180f));
- }
- }
-
- /*
- * Helper classs for reading the static VFS file, call
- * staticVFS.readVFSheaders() with the path to the static_data.db2 and static_index.db2 files
- * and it will pass and dump in to openmetaverse_data for you
- * This should only be needed to be used if LL update the static VFS in order to refresh our data
- */
-
- class VFSblock
- {
- public int mLocation;
- public int mLength;
- public int mAccessTime;
- public UUID mFileID;
- public int mSize;
- public AssetType mAssetType;
-
- public int readblock(byte[] blockdata, int offset)
- {
-
- BitPack input = new BitPack(blockdata, offset);
- mLocation = input.UnpackInt();
- mLength = input.UnpackInt();
- mAccessTime = input.UnpackInt();
- mFileID = input.UnpackUUID();
- int filetype = input.UnpackShort();
- mAssetType = (AssetType)filetype;
- mSize = input.UnpackInt();
- offset += 34;
-
- Logger.Log(String.Format("Found header for {0} type {1} length {2} at {3}", mFileID, mAssetType, mSize, mLocation), Helpers.LogLevel.Info);
-
- return offset;
- }
-
- }
-
- public class staticVFS
- {
- public static void readVFSheaders(string datafile, string indexfile)
- {
- FileStream datastream;
- FileStream indexstream;
-
- datastream = File.Open(datafile, FileMode.Open);
- indexstream = File.Open(indexfile, FileMode.Open);
-
- int offset = 0;
-
- byte[] blockdata = new byte[indexstream.Length];
- indexstream.Read(blockdata, 0, (int)indexstream.Length);
-
- while (offset < indexstream.Length)
- {
- VFSblock block = new VFSblock();
- offset = block.readblock(blockdata, offset);
-
- FileStream writer = File.Open(OpenMetaverse.Settings.RESOURCE_DIR + System.IO.Path.DirectorySeparatorChar + block.mFileID.ToString(), FileMode.Create);
- byte[] data = new byte[block.mSize];
- datastream.Seek(block.mLocation, SeekOrigin.Begin);
- datastream.Read(data, 0, block.mSize);
- writer.Write(data, 0, block.mSize);
- writer.Close();
- }
-
- }
- }
+//
+// Radegast Metaverse Client
+// Copyright (c) 2009-2014, Radegast Development Team
+// 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.
+// * 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.
+// * Neither the name of the application "Radegast", nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior CreateReflectionTexture 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 HOLDER 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.
+//
+// $Id: RenderingHelpers.cs 1136 2011-09-05 22:45:11Z latifer $
+//
+
+using System;
+using System.Collections.Generic;
+using System.Collections;
+using System.Linq;
+using System.Text;
+using System.IO;
+using System.IO.Compression;
+using System.Xml;
+using System.Threading;
+using OpenTK.Graphics.OpenGL;
+using System.Runtime.InteropServices;
+using System.Drawing;
+using System.Drawing.Imaging;
+using OpenMetaverse;
+using OpenMetaverse.Rendering;
+
+namespace Radegast.Rendering
+{
+ [StructLayout(LayoutKind.Sequential)]
+ public struct Color4b
+ {
+ public byte R;
+ public byte G;
+ public byte B;
+ public byte A;
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ public struct ColorVertex
+ {
+ [FieldOffset(0)]
+ public Vertex Vertex;
+ [FieldOffset(32)]
+ public Color4b Color;
+ public static int Size = 36;
+ }
+
+ public class TextureInfo
+ {
+ public System.Drawing.Image Texture;
+ public int TexturePointer;
+ public bool HasAlpha;
+ public bool FullAlpha;
+ public bool IsMask;
+ public bool IsInvisible;
+ public UUID TextureID;
+ public bool FetchFailed;
+ }
+
+ public class TextureLoadItem
+ {
+ public FaceData Data;
+ public Primitive Prim;
+ public Primitive.TextureEntryFace TeFace;
+ public byte[] TextureData = null;
+ public byte[] TGAData = null;
+ public bool LoadAssetFromCache = false;
+ public OpenMetaverse.ImageType ImageType = OpenMetaverse.ImageType.Normal;
+ public string BakeName = string.Empty;
+ public UUID AvatarID = UUID.Zero;
+ }
+
+ public enum RenderPass
+ {
+ Picking,
+ Simple,
+ Alpha,
+ Invisible
+ }
+
+ public enum SceneObjectType
+ {
+ None,
+ Primitive,
+ Avatar,
+ }
+
+ ///
+ /// Base class for all scene objects
+ ///
+ public abstract class SceneObject : IComparable, IDisposable
+ {
+ #region Public fields
+ /// Interpolated local position of the object
+ public Vector3 InterpolatedPosition;
+ /// Interpolated local rotation of the object/summary>
+ public Quaternion InterpolatedRotation;
+ /// Rendered position of the object in the region
+ public Vector3 RenderPosition;
+ /// Rendered rotationm of the object in the region
+ public Quaternion RenderRotation;
+ /// Per frame calculated square of the distance from camera
+ public float DistanceSquared;
+ /// Bounding volume of the object
+ public BoundingVolume BoundingVolume;
+ /// Was the sim position and distance from camera calculated during this frame
+ public bool PositionCalculated;
+ /// Scene object type
+ public SceneObjectType Type = SceneObjectType.None;
+ /// Libomv primitive
+ public virtual Primitive BasePrim { get; set; }
+ /// Were initial initialization tasks done
+ public bool Initialized;
+ /// Is this object disposed
+ public bool IsDisposed = false;
+ public int AlphaQueryID = -1;
+ public int SimpleQueryID = -1;
+ public bool HasAlphaFaces;
+ public bool HasSimpleFaces;
+ public bool HasInvisibleFaces;
+
+ #endregion Public fields
+
+ uint previousParent = uint.MaxValue;
+
+ ///
+ /// Cleanup resources used
+ ///
+ public virtual void Dispose()
+ {
+ IsDisposed = true;
+ }
+
+ ///
+ /// Task performed the fist time object is set for rendering
+ ///
+ public virtual void Initialize()
+ {
+ RenderPosition = InterpolatedPosition = BasePrim.Position;
+ RenderRotation = InterpolatedRotation = BasePrim.Rotation;
+ Initialized = true;
+ }
+
+ ///
+ /// Perform per frame tasks
+ ///
+ /// Time since the last call (last frame time in seconds)
+ public virtual void Step(float time)
+ {
+ if (BasePrim == null) return;
+
+ // Don't interpolate when parent changes (sit/stand link/unlink)
+ if (previousParent != BasePrim.ParentID)
+ {
+ previousParent = BasePrim.ParentID;
+ InterpolatedPosition = BasePrim.Position;
+ InterpolatedRotation = BasePrim.Rotation;
+ return;
+ }
+
+ // Linear velocity and acceleration
+ if (BasePrim.Velocity != Vector3.Zero)
+ {
+ BasePrim.Position = InterpolatedPosition = BasePrim.Position + BasePrim.Velocity * time
+ * 0.98f * RadegastInstance.GlobalInstance.Client.Network.CurrentSim.Stats.Dilation;
+ BasePrim.Velocity += BasePrim.Acceleration * time;
+ }
+ else if (InterpolatedPosition != BasePrim.Position)
+ {
+ InterpolatedPosition = RHelp.Smoothed1stOrder(InterpolatedPosition, BasePrim.Position, time);
+ }
+
+ // Angular velocity (target omega)
+ if (BasePrim.AngularVelocity != Vector3.Zero)
+ {
+ Vector3 angVel = BasePrim.AngularVelocity;
+ float angle = time * angVel.Length();
+ Quaternion dQ = Quaternion.CreateFromAxisAngle(angVel, angle);
+ InterpolatedRotation = dQ * InterpolatedRotation;
+ }
+ else if (InterpolatedRotation != BasePrim.Rotation && !(this is RenderAvatar))
+ {
+ InterpolatedRotation = Quaternion.Slerp(InterpolatedRotation, BasePrim.Rotation, time * 10f);
+ if (1f - Math.Abs(Quaternion.Dot(InterpolatedRotation, BasePrim.Rotation)) < 0.0001)
+ InterpolatedRotation = BasePrim.Rotation;
+ }
+ else
+ {
+ InterpolatedRotation = BasePrim.Rotation;
+ }
+ }
+
+ ///
+ /// Render scene object
+ ///
+ /// Which pass are we currently in
+ /// ID used to identify which object was picked
+ /// Main scene renderer
+ /// Time it took to render the last frame
+ public virtual void Render(RenderPass pass, int pickingID, SceneWindow scene, float time)
+ {
+ }
+
+ ///
+ /// Implementation of the IComparable interface
+ /// used for sorting by distance
+ ///
+ /// Object we are comparing to
+ /// Result of the comparison
+ public virtual int CompareTo(object other)
+ {
+ SceneObject o = (SceneObject)other;
+ if (this.DistanceSquared < o.DistanceSquared)
+ return -1;
+ else if (this.DistanceSquared > o.DistanceSquared)
+ return 1;
+ else
+ return 0;
+ }
+
+ #region Occlusion queries
+ public void StartQuery(RenderPass pass)
+ {
+ if (!RenderSettings.OcclusionCullingEnabled) return;
+
+ if (pass == RenderPass.Simple)
+ {
+ StartSimpleQuery();
+ }
+ else if (pass == RenderPass.Alpha)
+ {
+ StartAlphaQuery();
+ }
+ }
+
+ public void EndQuery(RenderPass pass)
+ {
+ if (!RenderSettings.OcclusionCullingEnabled) return;
+
+ if (pass == RenderPass.Simple)
+ {
+ EndSimpleQuery();
+ }
+ else if (pass == RenderPass.Alpha)
+ {
+ EndAlphaQuery();
+ }
+ }
+
+ public void StartAlphaQuery()
+ {
+ if (!RenderSettings.OcclusionCullingEnabled) return;
+
+ if (AlphaQueryID == -1)
+ {
+ Compat.GenQueries(out AlphaQueryID);
+ }
+ if (AlphaQueryID > 0)
+ {
+ Compat.BeginQuery(QueryTarget.SamplesPassed, AlphaQueryID);
+ }
+ }
+
+ public void EndAlphaQuery()
+ {
+ if (!RenderSettings.OcclusionCullingEnabled) return;
+
+ if (AlphaQueryID > 0)
+ {
+ Compat.EndQuery(QueryTarget.SamplesPassed);
+ }
+ }
+
+ public void StartSimpleQuery()
+ {
+ if (!RenderSettings.OcclusionCullingEnabled) return;
+
+ if (SimpleQueryID == -1)
+ {
+ Compat.GenQueries(out SimpleQueryID);
+ }
+ if (SimpleQueryID > 0)
+ {
+ Compat.BeginQuery(QueryTarget.SamplesPassed, SimpleQueryID);
+ }
+ }
+
+ public void EndSimpleQuery()
+ {
+ if (!RenderSettings.OcclusionCullingEnabled) return;
+
+ if (SimpleQueryID > 0)
+ {
+ Compat.EndQuery(QueryTarget.SamplesPassed);
+ }
+ }
+
+ public bool Occluded()
+ {
+ if (!RenderSettings.OcclusionCullingEnabled) return false;
+
+ if (HasInvisibleFaces) return false;
+
+ if ((SimpleQueryID == -1 && AlphaQueryID == -1))
+ {
+ return false;
+ }
+
+ if ((!HasAlphaFaces && !HasSimpleFaces)) return true;
+
+ int samples = 1;
+ if (HasSimpleFaces && SimpleQueryID > 0)
+ {
+ Compat.GetQueryObject(SimpleQueryID, GetQueryObjectParam.QueryResult, out samples);
+ }
+ if (HasSimpleFaces && samples > 0)
+ {
+ return false;
+ }
+
+ samples = 1;
+ if (HasAlphaFaces && AlphaQueryID > 0)
+ {
+ Compat.GetQueryObject(AlphaQueryID, GetQueryObjectParam.QueryResult, out samples);
+ }
+ if (HasAlphaFaces && samples > 0)
+ {
+ return false;
+ }
+
+ return true;
+ }
+ #endregion Occlusion queries
+ }
+
+ public static class RHelp
+ {
+ public static readonly Vector3 InvalidPosition = new Vector3(99999f, 99999f, 99999f);
+ static float t1 = 0.075f;
+ static float t2 = t1 / 5.7f;
+
+ public static Vector3 Smoothed1stOrder(Vector3 curPos, Vector3 targetPos, float lastFrameTime)
+ {
+ int numIterations = (int)(lastFrameTime * 100);
+ do
+ {
+ curPos += (targetPos - curPos) * t1;
+ numIterations--;
+ }
+ while (numIterations > 0);
+ if (Vector3.DistanceSquared(curPos, targetPos) < 0.00001f)
+ {
+ curPos = targetPos;
+ }
+ return curPos;
+ }
+
+ public static Vector3 Smoothed2ndOrder(Vector3 curPos, Vector3 targetPos, ref Vector3 accel, float lastFrameTime)
+ {
+ int numIterations = (int)(lastFrameTime * 100);
+ do
+ {
+ accel += (targetPos - accel - curPos) * t1;
+ curPos += accel * t2;
+ numIterations--;
+ }
+ while (numIterations > 0);
+ if (Vector3.DistanceSquared(curPos, targetPos) < 0.00001f)
+ {
+ curPos = targetPos;
+ }
+ return curPos;
+ }
+
+ public static OpenTK.Vector2 TKVector3(Vector2 v)
+ {
+ return new OpenTK.Vector2(v.X, v.Y);
+ }
+
+ public static OpenTK.Vector3 TKVector3(Vector3 v)
+ {
+ return new OpenTK.Vector3(v.X, v.Y, v.Z);
+ }
+
+ public static OpenTK.Vector4 TKVector3(Vector4 v)
+ {
+ return new OpenTK.Vector4(v.X, v.Y, v.Z, v.W);
+ }
+
+ public static Vector2 OMVVector2(OpenTK.Vector2 v)
+ {
+ return new Vector2(v.X, v.Y);
+ }
+
+ public static Vector3 OMVVector3(OpenTK.Vector3 v)
+ {
+ return new Vector3(v.X, v.Y, v.Z);
+ }
+
+ public static Vector4 OMVVector4(OpenTK.Vector4 v)
+ {
+ return new Vector4(v.X, v.Y, v.Z, v.W);
+ }
+
+ public static Color WinColor(OpenTK.Graphics.Color4 color)
+ {
+ return Color.FromArgb((int)(color.A * 255), (int)(color.R * 255), (int)(color.G * 255), (int)(color.B * 255));
+ }
+
+ public static Color WinColor(Color4 color)
+ {
+ return Color.FromArgb((int)(color.A * 255), (int)(color.R * 255), (int)(color.G * 255), (int)(color.B * 255));
+ }
+
+ public static int NextPow2(int start)
+ {
+ int pow = 1;
+ while (pow < start) pow *= 2;
+ return pow;
+ }
+
+ #region Cached image save and load
+ public static readonly string RAD_IMG_MAGIC = "radegast_img";
+
+ public static bool LoadCachedImage(UUID textureID, out byte[] tgaData, out bool hasAlpha, out bool fullAlpha, out bool isMask)
+ {
+ tgaData = null;
+ hasAlpha = fullAlpha = isMask = false;
+
+ try
+ {
+ string fname = RadegastInstance.GlobalInstance.ComputeCacheName(RadegastInstance.GlobalInstance.Client.Settings.ASSET_CACHE_DIR, textureID) + ".rzi";
+
+ using (var f = File.Open(fname, FileMode.Open, FileAccess.Read, FileShare.Read))
+ {
+ byte[] header = new byte[36];
+ int i = 0;
+ f.Read(header, 0, header.Length);
+
+ // check if the file is starting with magic string
+ if (RAD_IMG_MAGIC != Utils.BytesToString(header, 0, RAD_IMG_MAGIC.Length))
+ return false;
+ i += RAD_IMG_MAGIC.Length;
+
+ if (header[i++] != 1) // check version
+ return false;
+
+ hasAlpha = header[i++] == 1;
+ fullAlpha = header[i++] == 1;
+ isMask = header[i++] == 1;
+
+ int uncompressedSize = Utils.BytesToInt(header, i);
+ i += 4;
+
+ textureID = new UUID(header, i);
+ i += 16;
+
+ tgaData = new byte[uncompressedSize];
+ using (var compressed = new DeflateStream(f, CompressionMode.Decompress))
+ {
+ int read = 0;
+ while ((read = compressed.Read(tgaData, read, uncompressedSize - read)) > 0) ;
+ }
+ }
+
+ return true;
+ }
+ catch (FileNotFoundException) { }
+ catch (Exception ex)
+ {
+ Logger.DebugLog(string.Format("Failed to load radegast cache file {0}: {1}", textureID, ex.Message));
+ }
+ return false;
+ }
+
+ public static bool SaveCachedImage(byte[] tgaData, UUID textureID, bool hasAlpha, bool fullAlpha, bool isMask)
+ {
+ try
+ {
+ string fname = RadegastInstance.GlobalInstance.ComputeCacheName(RadegastInstance.GlobalInstance.Client.Settings.ASSET_CACHE_DIR, textureID) + ".rzi";
+
+ using (var f = File.Open(fname, FileMode.Create, FileAccess.Write, FileShare.None))
+ {
+ int i = 0;
+ // magic header
+ f.Write(Utils.StringToBytes(RAD_IMG_MAGIC), 0, RAD_IMG_MAGIC.Length);
+ i += RAD_IMG_MAGIC.Length;
+
+ // version
+ f.WriteByte((byte)1);
+ i++;
+
+ // texture info
+ f.WriteByte(hasAlpha ? (byte)1 : (byte)0);
+ f.WriteByte(fullAlpha ? (byte)1 : (byte)0);
+ f.WriteByte(isMask ? (byte)1 : (byte)0);
+ i += 3;
+
+ // texture size
+ byte[] uncompressedSize = Utils.IntToBytes(tgaData.Length);
+ f.Write(uncompressedSize, 0, uncompressedSize.Length);
+ i += uncompressedSize.Length;
+
+ // texture id
+ byte[] id = new byte[16];
+ textureID.ToBytes(id, 0);
+ f.Write(id, 0, 16);
+ i += 16;
+
+ // compressed texture data
+ using (var compressed = new DeflateStream(f, CompressionMode.Compress))
+ {
+ compressed.Write(tgaData, 0, tgaData.Length);
+ }
+ }
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Logger.DebugLog(string.Format("Failed to save radegast cache file {0}: {1}", textureID, ex.Message));
+ return false;
+ }
+ }
+ #endregion Cached image save and load
+
+ #region Static vertices and indices for a cube (used for bounding box drawing)
+ /**********************************************
+ 5 --- 4
+ /| /|
+ 1 --- 0 |
+ | 6 --| 7
+ |/ |/
+ 2 --- 3
+ ***********************************************/
+ public static readonly float[] CubeVertices = new float[]
+ {
+ 0.5f, 0.5f, 0.5f, // 0
+ -0.5f, 0.5f, 0.5f, // 1
+ -0.5f, -0.5f, 0.5f, // 2
+ 0.5f, -0.5f, 0.5f, // 3
+ 0.5f, 0.5f, -0.5f, // 4
+ -0.5f, 0.5f, -0.5f, // 5
+ -0.5f, -0.5f, -0.5f, // 6
+ 0.5f, -0.5f, -0.5f // 7
+ };
+
+ public static readonly ushort[] CubeIndices = new ushort[]
+ {
+ 0, 1, 2, 3, // Front Face
+ 4, 5, 6, 7, // Back Face
+ 1, 2, 6, 5, // Left Face
+ 0, 3, 7, 4, // Right Face
+ 0, 1, 5, 4, // Top Face
+ 2, 3, 7, 6 // Bottom Face
+ };
+ #endregion Static vertices and indices for a cube (used for bounding box drawing)
+
+ public static int GLLoadImage(Bitmap bitmap, bool hasAlpha)
+ {
+ return GLLoadImage(bitmap, hasAlpha, true);
+ }
+
+ public static int GLLoadImage(Bitmap bitmap, bool hasAlpha, bool useMipmap)
+ {
+ useMipmap = useMipmap && RenderSettings.HasMipmap;
+ int ret = -1;
+ GL.GenTextures(1, out ret);
+ GL.BindTexture(TextureTarget.Texture2D, ret);
+
+ Rectangle rectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
+
+ BitmapData bitmapData =
+ bitmap.LockBits(
+ rectangle,
+ ImageLockMode.ReadOnly,
+ hasAlpha ? System.Drawing.Imaging.PixelFormat.Format32bppArgb : System.Drawing.Imaging.PixelFormat.Format24bppRgb);
+
+ GL.TexImage2D(
+ TextureTarget.Texture2D,
+ 0,
+ hasAlpha ? PixelInternalFormat.Rgba : PixelInternalFormat.Rgb8,
+ bitmap.Width,
+ bitmap.Height,
+ 0,
+ hasAlpha ? OpenTK.Graphics.OpenGL.PixelFormat.Bgra : OpenTK.Graphics.OpenGL.PixelFormat.Bgr,
+ PixelType.UnsignedByte,
+ bitmapData.Scan0);
+
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
+ if (useMipmap)
+ {
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.LinearMipmapLinear);
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.GenerateMipmap, 1);
+ GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
+ }
+ else
+ {
+ GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
+ }
+
+ bitmap.UnlockBits(bitmapData);
+ return ret;
+ }
+
+ public static void Draw2DBox(float x, float y, float width, float height, float depth)
+ {
+ GL.Begin(BeginMode.Quads);
+ {
+ GL.TexCoord2(0, 1);
+ GL.Vertex3(x, y, depth);
+ GL.TexCoord2(1, 1);
+ GL.Vertex3(x + width, y, depth);
+ GL.TexCoord2(1, 0);
+ GL.Vertex3(x + width, y + height, depth);
+ GL.TexCoord2(0, 0);
+ GL.Vertex3(x, y + height, depth);
+ }
+ GL.End();
+ }
+
+ public static void ResetMaterial()
+ {
+ GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Ambient, new float[] { 0.2f, 0.2f, 0.2f, 1.0f });
+ GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Diffuse, new float[] { 0.8f, 0.8f, 0.8f, 1.0f });
+ GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Specular, new float[] { 0f, 0f, 0f, 1.0f });
+ GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Emission, new float[] { 0f, 0f, 0f, 1.0f });
+ GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Shininess, 0f);
+ ShaderProgram.Stop();
+ }
+ }
+
+ ///
+ /// Represents camera object
+ ///
+ public class Camera
+ {
+ ///
+ /// Indicates that there was manual camera movement, stop tracking objects
+ ///
+ public bool Manual;
+ Vector3 mPosition;
+ Vector3 mFocalPoint;
+ bool mModified;
+
+ /// Camera position
+ public Vector3 Position
+ {
+ get
+ {
+ return mPosition;
+ }
+
+ set
+ {
+ if (mPosition != value)
+ {
+ mPosition = value;
+ Modify();
+ }
+ }
+ }
+
+ /// Camera target
+ public Vector3 FocalPoint
+ {
+ get
+ {
+ return mFocalPoint;
+ }
+
+ set
+ {
+ if (mFocalPoint != value)
+ {
+ mFocalPoint = value;
+ Modify();
+ }
+ }
+ }
+
+ /// Zoom level
+ public float Zoom;
+ /// Draw distance
+ public float Far;
+ /// Has camera been modified
+ public bool Modified { get { return mModified; } set { mModified = value; } }
+
+ public float TimeToTarget = 0f;
+
+ public Vector3 RenderPosition;
+ public Vector3 RenderFocalPoint;
+
+ void Modify()
+ {
+ mModified = true;
+ }
+
+ public void Step(float time)
+ {
+ if (RenderPosition != Position)
+ {
+ RenderPosition = RHelp.Smoothed1stOrder(RenderPosition, Position, time);
+ Modified = true;
+ }
+ if (RenderFocalPoint != FocalPoint)
+ {
+ RenderFocalPoint = RHelp.Smoothed1stOrder(RenderFocalPoint, FocalPoint, time);
+ Modified = true;
+ }
+ }
+
+#if OBSOLETE_CODE
+ [Obsolete("Use Step(), left in here for reference")]
+ public void Step2(float time)
+ {
+ TimeToTarget -= time;
+ if (TimeToTarget <= time)
+ {
+ EndMove();
+ return;
+ }
+
+ mModified = true;
+
+ float pctElapsed = time / TimeToTarget;
+
+ if (RenderPosition != Position)
+ {
+ float distance = Vector3.Distance(RenderPosition, Position);
+ RenderPosition = Vector3.Lerp(RenderPosition, Position, distance * pctElapsed);
+ }
+
+ if (RenderFocalPoint != FocalPoint)
+ {
+ RenderFocalPoint = Interpolate(RenderFocalPoint, FocalPoint, pctElapsed);
+ }
+ }
+
+ Vector3 Interpolate(Vector3 start, Vector3 end, float fraction)
+ {
+ float distance = Vector3.Distance(start, end);
+ Vector3 direction = end - start;
+ return start + direction * fraction;
+ }
+
+ public void EndMove()
+ {
+ mModified = true;
+ TimeToTarget = 0;
+ RenderPosition = Position;
+ RenderFocalPoint = FocalPoint;
+ }
+#endif
+
+ public void Pan(float deltaX, float deltaY)
+ {
+ Manual = true;
+ Vector3 direction = Position - FocalPoint;
+ direction.Normalize();
+ Vector3 vy = direction % Vector3.UnitZ;
+ Vector3 vx = vy % direction;
+ Vector3 vxy = vx * deltaY + vy * deltaX;
+ Position += vxy;
+ FocalPoint += vxy;
+ }
+
+ public void Rotate(float delta, bool horizontal)
+ {
+ Manual = true;
+ Vector3 direction = Position - FocalPoint;
+ if (horizontal)
+ {
+ Position = FocalPoint + direction * new Quaternion(0f, 0f, (float)Math.Sin(delta), (float)Math.Cos(delta));
+ }
+ else
+ {
+ Position = FocalPoint + direction * Quaternion.CreateFromAxisAngle(direction % Vector3.UnitZ, delta);
+ }
+ }
+
+ public void MoveToTarget(float delta)
+ {
+ Manual = true;
+ Position += (Position - FocalPoint) * delta;
+ }
+
+ ///
+ /// Sets the world in perspective of the camera
+ ///
+ public void LookAt()
+ {
+ OpenTK.Matrix4 lookAt = OpenTK.Matrix4.LookAt(
+ RenderPosition.X, RenderPosition.Y, RenderPosition.Z,
+ RenderFocalPoint.X, RenderFocalPoint.Y, RenderFocalPoint.Z,
+ 0f, 0f, 1f);
+ GL.MultMatrix(ref lookAt);
+ }
+ }
+
+ public static class MeshToOBJ
+ {
+ public static bool MeshesToOBJ(Dictionary meshes, string filename)
+ {
+ StringBuilder obj = new StringBuilder();
+ StringBuilder mtl = new StringBuilder();
+
+ FileInfo objFileInfo = new FileInfo(filename);
+
+ string mtlFilename = objFileInfo.FullName.Substring(objFileInfo.DirectoryName.Length + 1,
+ objFileInfo.FullName.Length - (objFileInfo.DirectoryName.Length + 1) - 4) + ".mtl";
+
+ obj.AppendLine("# Created by libprimrender");
+ obj.AppendLine("mtllib ./" + mtlFilename);
+ obj.AppendLine();
+
+ mtl.AppendLine("# Created by libprimrender");
+ mtl.AppendLine();
+
+ int primNr = 0;
+ foreach (FacetedMesh mesh in meshes.Values)
+ {
+ for (int j = 0; j < mesh.Faces.Count; j++)
+ {
+ Face face = mesh.Faces[j];
+
+ if (face.Vertices.Count > 2)
+ {
+ string mtlName = String.Format("material{0}-{1}", primNr, face.ID);
+ Primitive.TextureEntryFace tex = face.TextureFace;
+ string texName = tex.TextureID.ToString() + ".tga";
+
+ // FIXME: Convert the source to TGA (if needed) and copy to the destination
+
+ float shiny = 0.00f;
+ switch (tex.Shiny)
+ {
+ case Shininess.High:
+ shiny = 1.00f;
+ break;
+ case Shininess.Medium:
+ shiny = 0.66f;
+ break;
+ case Shininess.Low:
+ shiny = 0.33f;
+ break;
+ }
+
+ obj.AppendFormat("g face{0}-{1}{2}", primNr, face.ID, Environment.NewLine);
+
+ mtl.AppendLine("newmtl " + mtlName);
+ mtl.AppendFormat("Ka {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);
+ mtl.AppendFormat("Kd {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);
+ //mtl.AppendFormat("Ks {0} {1} {2}{3}");
+ mtl.AppendLine("Tr " + tex.RGBA.A);
+ mtl.AppendLine("Ns " + shiny);
+ mtl.AppendLine("illum 1");
+ if (tex.TextureID != UUID.Zero && tex.TextureID != Primitive.TextureEntry.WHITE_TEXTURE)
+ mtl.AppendLine("map_Kd ./" + texName);
+ mtl.AppendLine();
+
+ // Write the vertices, texture coordinates, and vertex normals for this side
+ for (int k = 0; k < face.Vertices.Count; k++)
+ {
+ Vertex vertex = face.Vertices[k];
+
+ #region Vertex
+
+ Vector3 pos = vertex.Position;
+
+ // Apply scaling
+ pos *= mesh.Prim.Scale;
+
+ // Apply rotation
+ pos *= mesh.Prim.Rotation;
+
+ // 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 (mesh.Prim.ParentID != 0)
+ pos += mesh.Prim.Position;
+
+ obj.AppendFormat("v {0} {1} {2}{3}", pos.X, pos.Y, pos.Z, Environment.NewLine);
+
+ #endregion Vertex
+
+ #region Texture Coord
+
+ obj.AppendFormat("vt {0} {1}{2}", vertex.TexCoord.X, vertex.TexCoord.Y,
+ Environment.NewLine);
+
+ #endregion Texture Coord
+
+ #region Vertex Normal
+
+ // HACK: Sometimes normals are getting set to
+ if (!Single.IsNaN(vertex.Normal.X) && !Single.IsNaN(vertex.Normal.Y) && !Single.IsNaN(vertex.Normal.Z))
+ obj.AppendFormat("vn {0} {1} {2}{3}", vertex.Normal.X, vertex.Normal.Y, vertex.Normal.Z,
+ Environment.NewLine);
+ else
+ obj.AppendLine("vn 0.0 1.0 0.0");
+
+ #endregion Vertex Normal
+ }
+
+ obj.AppendFormat("# {0} vertices{1}", face.Vertices.Count, Environment.NewLine);
+ obj.AppendLine();
+ obj.AppendLine("usemtl " + mtlName);
+
+ #region Elements
+
+ // Write all of the faces (triangles) for this side
+ for (int k = 0; k < face.Indices.Count / 3; k++)
+ {
+ obj.AppendFormat("f -{0}/-{0}/-{0} -{1}/-{1}/-{1} -{2}/-{2}/-{2}{3}",
+ face.Vertices.Count - face.Indices[k * 3 + 0],
+ face.Vertices.Count - face.Indices[k * 3 + 1],
+ face.Vertices.Count - face.Indices[k * 3 + 2],
+ Environment.NewLine);
+ }
+
+ obj.AppendFormat("# {0} elements{1}", face.Indices.Count / 3, Environment.NewLine);
+ obj.AppendLine();
+
+ #endregion Elements
+ }
+ }
+ primNr++;
+ }
+
+ try
+ {
+ File.WriteAllText(filename, obj.ToString());
+ File.WriteAllText(mtlFilename, mtl.ToString());
+ }
+ catch (Exception)
+ {
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ 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[] CreateSRTMatrix(Vector3 scale, Quaternion q, Vector3 pos)
+ {
+ 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)) * scale.X;
+ mat[1] = (xy - wz) * scale.X;
+ mat[2] = (xz + wy) * scale.X;
+ mat[3] = 0.0f;
+
+ mat[4] = (xy + wz) * scale.Y;
+ mat[5] = (1.0f - (xx + zz)) * scale.Y;
+ mat[6] = (yz - wx) * scale.Y;
+ mat[7] = 0.0f;
+
+ mat[8] = (xz - wy) * scale.Z;
+ mat[9] = (yz + wx) * scale.Z;
+ mat[10] = (1.0f - (xx + yy)) * scale.Z;
+ mat[11] = 0.0f;
+
+ //Positional parts
+ mat[12] = pos.X;
+ mat[13] = pos.Y;
+ mat[14] = pos.Z;
+ 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;
+ }
+
+ public static float[] Lerp(float[] matrix1, float[] matrix2, float amount)
+ {
+
+ float[] lerp = new float[16];
+ //Probably not doing this as a loop is cheaper(unrolling)
+ //also for performance we probably should not create new objects
+ // but meh.
+ for (int x = 0; x < 16; x++)
+ {
+ lerp[x] = matrix1[x] + ((matrix2[x] - matrix1[x]) * amount);
+ }
+
+ return lerp;
+ }
+
+
+ public static bool GluProject(OpenTK.Vector3 objPos, OpenTK.Matrix4 modelMatrix, OpenTK.Matrix4 projMatrix, int[] viewport, out OpenTK.Vector3 screenPos)
+ {
+ OpenTK.Vector4 _in;
+ OpenTK.Vector4 _out;
+
+ _in.X = objPos.X;
+ _in.Y = objPos.Y;
+ _in.Z = objPos.Z;
+ _in.W = 1.0f;
+
+ _out = OpenTK.Vector4.Transform(_in, modelMatrix);
+ _in = OpenTK.Vector4.Transform(_out, projMatrix);
+
+ if (_in.W <= 0.0)
+ {
+ screenPos = OpenTK.Vector3.Zero;
+ return false;
+ }
+
+ _in.X /= _in.W;
+ _in.Y /= _in.W;
+ _in.Z /= _in.W;
+ /* Map x, y and z to range 0-1 */
+ _in.X = _in.X * 0.5f + 0.5f;
+ _in.Y = _in.Y * 0.5f + 0.5f;
+ _in.Z = _in.Z * 0.5f + 0.5f;
+
+ /* Map x,y to viewport */
+ _in.X = _in.X * viewport[2] + viewport[0];
+ _in.Y = _in.Y * viewport[3] + viewport[1];
+
+ screenPos.X = _in.X;
+ screenPos.Y = _in.Y;
+ screenPos.Z = _in.Z;
+
+ return true;
+ }
+
+ public static bool GluUnProject(float winx, float winy, float winz, OpenTK.Matrix4 modelMatrix, OpenTK.Matrix4 projMatrix, int[] viewport, out OpenTK.Vector3 pos)
+ {
+ OpenTK.Matrix4 finalMatrix;
+ OpenTK.Vector4 _in;
+ OpenTK.Vector4 _out;
+
+ finalMatrix = OpenTK.Matrix4.Mult(modelMatrix, projMatrix);
+
+ finalMatrix.Invert();
+
+ _in.X = winx;
+ _in.Y = winy;
+ _in.Z = winz;
+ _in.W = 1.0f;
+
+ /* Map x and y from window coordinates */
+ _in.X = (_in.X - viewport[0]) / viewport[2];
+ _in.Y = (_in.Y - viewport[1]) / viewport[3];
+
+ pos = OpenTK.Vector3.Zero;
+
+ /* Map to range -1 to 1 */
+ _in.X = _in.X * 2 - 1;
+ _in.Y = _in.Y * 2 - 1;
+ _in.Z = _in.Z * 2 - 1;
+
+ //__gluMultMatrixVecd(finalMatrix, _in, _out);
+ // check if this works:
+ _out = OpenTK.Vector4.Transform(_in, finalMatrix);
+
+ if (_out.W == 0.0f)
+ return false;
+ _out.X /= _out.W;
+ _out.Y /= _out.W;
+ _out.Z /= _out.W;
+ pos.X = _out.X;
+ pos.Y = _out.Y;
+ pos.Z = _out.Z;
+ return true;
+ }
+
+ public static double[] AbovePlane(double height)
+ {
+ return new double[] { 0, 0, 1, -height };
+ }
+
+ public static double[] BelowPlane(double height)
+ {
+ return new double[] { 0, 0, -1, height };
+ }
+ }
+
+ /*
+ * Helper classs for reading the static VFS file, call
+ * staticVFS.readVFSheaders() with the path to the static_data.db2 and static_index.db2 files
+ * and it will pass and dump in to openmetaverse_data for you
+ * This should only be needed to be used if LL update the static VFS in order to refresh our data
+ */
+
+ class VFSblock
+ {
+ public int mLocation;
+ public int mLength;
+ public int mAccessTime;
+ public UUID mFileID;
+ public int mSize;
+ public AssetType mAssetType;
+
+ public int readblock(byte[] blockdata, int offset)
+ {
+
+ BitPack input = new BitPack(blockdata, offset);
+ mLocation = input.UnpackInt();
+ mLength = input.UnpackInt();
+ mAccessTime = input.UnpackInt();
+ mFileID = input.UnpackUUID();
+ int filetype = input.UnpackShort();
+ mAssetType = (AssetType)filetype;
+ mSize = input.UnpackInt();
+ offset += 34;
+
+ Logger.Log(String.Format("Found header for {0} type {1} length {2} at {3}", mFileID, mAssetType, mSize, mLocation), Helpers.LogLevel.Info);
+
+ return offset;
+ }
+
+ }
+
+ public class staticVFS
+ {
+ public static void readVFSheaders(string datafile, string indexfile)
+ {
+ FileStream datastream;
+ FileStream indexstream;
+
+ datastream = File.Open(datafile, FileMode.Open);
+ indexstream = File.Open(indexfile, FileMode.Open);
+
+ int offset = 0;
+
+ byte[] blockdata = new byte[indexstream.Length];
+ indexstream.Read(blockdata, 0, (int)indexstream.Length);
+
+ while (offset < indexstream.Length)
+ {
+ VFSblock block = new VFSblock();
+ offset = block.readblock(blockdata, offset);
+
+ FileStream writer = File.Open(OpenMetaverse.Settings.RESOURCE_DIR + System.IO.Path.DirectorySeparatorChar + block.mFileID.ToString(), FileMode.Create);
+ byte[] data = new byte[block.mSize];
+ datastream.Seek(block.mLocation, SeekOrigin.Begin);
+ datastream.Read(data, 0, block.mSize);
+ writer.Write(data, 0, block.mSize);
+ writer.Close();
+ }
+
+ }
+ }
}
\ No newline at end of file