2 // Radegast Metaverse Client
\r
3 // Copyright (c) 2009-2011, Radegast Development Team
\r
4 // All rights reserved.
\r
6 // Redistribution and use in source and binary forms, with or without
\r
7 // modification, are permitted provided that the following conditions are met:
\r
9 // * Redistributions of source code must retain the above copyright notice,
\r
10 // this list of conditions and the following disclaimer.
\r
11 // * Redistributions in binary form must reproduce the above copyright
\r
12 // notice, this list of conditions and the following disclaimer in the
\r
13 // documentation and/or other materials provided with the distribution.
\r
14 // * Neither the name of the application "Radegast", nor the names of its
\r
15 // contributors may be used to endorse or promote products derived from
\r
16 // this software without specific prior CreateReflectionTexture permission.
\r
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
\r
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
\r
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
\r
21 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
\r
22 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
\r
23 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
\r
24 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
\r
25 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
\r
26 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
\r
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\r
33 using System.Collections.Generic;
\r
34 using System.Collections;
\r
38 using System.IO.Compression;
\r
40 using System.Threading;
\r
41 using OpenTK.Graphics.OpenGL;
\r
42 using System.Runtime.InteropServices;
\r
43 using System.Drawing;
\r
44 using System.Drawing.Imaging;
\r
45 using OpenMetaverse;
\r
46 using OpenMetaverse.Rendering;
\r
48 namespace Radegast.Rendering
\r
50 [StructLayout(LayoutKind.Sequential)]
\r
51 public struct Color4b
\r
59 [StructLayout(LayoutKind.Explicit)]
\r
60 public struct ColorVertex
\r
63 public Vertex Vertex;
\r
65 public Color4b Color;
\r
66 public static int Size = 36;
\r
69 public class TextureInfo
\r
71 public System.Drawing.Image Texture;
\r
72 public int TexturePointer;
\r
73 public bool HasAlpha;
\r
74 public bool FullAlpha;
\r
76 public UUID TextureID;
\r
77 public bool FetchFailed;
\r
80 public class TextureLoadItem
\r
82 public FaceData Data;
\r
83 public Primitive Prim;
\r
84 public Primitive.TextureEntryFace TeFace;
\r
85 public byte[] TextureData = null;
\r
86 public byte[] TGAData = null;
\r
87 public bool LoadAssetFromCache = false;
\r
90 public enum RenderPass
\r
97 public enum SceneObjectType
\r
105 /// Base class for all scene objects
\r
107 public abstract class SceneObject : IComparable, IDisposable
\r
109 #region Public fields
\r
110 /// <summary>Interpolated local position of the object</summary>
\r
111 public Vector3 InterpolatedPosition;
\r
112 /// <summary>Interpolated local rotation of the object/summary>
\r
113 public Quaternion InterpolatedRotation;
\r
114 /// <summary>Rendered position of the object in the region</summary>
\r
115 public Vector3 RenderPosition;
\r
116 /// <summary>Rendered rotationm of the object in the region</summary>
\r
117 public Quaternion RenderRotation;
\r
118 /// <summary>Per frame calculated square of the distance from camera</summary>
\r
119 public float DistanceSquared;
\r
120 /// <summary>Bounding volume of the object</summary>
\r
121 public BoundingVolume BoundingVolume;
\r
122 /// <summary>Was the sim position and distance from camera calculated during this frame</summary>
\r
123 public bool PositionCalculated;
\r
124 /// <summary>Scene object type</summary>
\r
125 public SceneObjectType Type = SceneObjectType.None;
\r
126 /// <summary>Libomv primitive</summary>
\r
127 public virtual Primitive BasePrim { get; set; }
\r
128 /// <summary>Were initial initialization tasks done</summary>
\r
129 public bool Initialized;
\r
130 /// <summary>Is this object disposed</summary>
\r
131 public bool IsDisposed = false;
\r
132 public int AlphaQueryID = -1;
\r
133 public int SimpleQueryID = -1;
\r
134 public bool HasAlphaFaces;
\r
135 public bool HasSimpleFaces;
\r
137 #endregion Public fields
\r
139 uint previousParent = uint.MaxValue;
\r
142 /// Cleanup resources used
\r
144 public virtual void Dispose()
\r
150 /// Task performed the fist time object is set for rendering
\r
152 public virtual void Initialize()
\r
154 RenderPosition = InterpolatedPosition = BasePrim.Position;
\r
155 RenderRotation = InterpolatedRotation = BasePrim.Rotation;
\r
156 Initialized = true;
\r
160 /// Perform per frame tasks
\r
162 /// <param name="time">Time since the last call (last frame time in seconds)</param>
\r
163 public virtual void Step(float time)
\r
165 // Don't interpolate when parent changes (sit/stand link/unlink)
\r
166 if (previousParent != BasePrim.ParentID)
\r
168 previousParent = BasePrim.ParentID;
\r
169 InterpolatedPosition = BasePrim.Position;
\r
170 InterpolatedRotation = BasePrim.Rotation;
\r
174 // Linear velocity and acceleration
\r
175 if (BasePrim.Velocity != Vector3.Zero)
\r
177 BasePrim.Position = InterpolatedPosition = BasePrim.Position + BasePrim.Velocity * time
\r
178 * 0.98f * RadegastInstance.GlobalInstance.Client.Network.CurrentSim.Stats.Dilation;
\r
179 BasePrim.Velocity += BasePrim.Acceleration * time;
\r
181 else if (InterpolatedPosition != BasePrim.Position)
\r
183 InterpolatedPosition = RHelp.Smoothed1stOrder(InterpolatedPosition, BasePrim.Position, time);
\r
186 // Angular velocity (target omega)
\r
187 if (BasePrim.AngularVelocity != Vector3.Zero)
\r
189 Vector3 angVel = BasePrim.AngularVelocity;
\r
190 float angle = time * angVel.Length();
\r
191 Quaternion dQ = Quaternion.CreateFromAxisAngle(angVel, angle);
\r
192 InterpolatedRotation = dQ * InterpolatedRotation;
\r
194 else if (InterpolatedRotation != BasePrim.Rotation && !(this is RenderAvatar))
\r
196 InterpolatedRotation = Quaternion.Slerp(InterpolatedRotation, BasePrim.Rotation, time * 10f);
\r
197 if (1f - Math.Abs(Quaternion.Dot(InterpolatedRotation, BasePrim.Rotation)) < 0.0001)
\r
198 InterpolatedRotation = BasePrim.Rotation;
\r
202 InterpolatedRotation = BasePrim.Rotation;
\r
207 /// Render scene object
\r
209 /// <param name="pass">Which pass are we currently in</param>
\r
210 /// <param name="pickingID">ID used to identify which object was picked</param>
\r
211 /// <param name="scene">Main scene renderer</param>
\r
212 /// <param name="time">Time it took to render the last frame</param>
\r
213 public virtual void Render(RenderPass pass, int pickingID, SceneWindow scene, float time)
\r
218 /// Implementation of the IComparable interface
\r
219 /// used for sorting by distance
\r
221 /// <param name="other">Object we are comparing to</param>
\r
222 /// <returns>Result of the comparison</returns>
\r
223 public virtual int CompareTo(object other)
\r
225 SceneObject o = (SceneObject)other;
\r
226 if (this.DistanceSquared < o.DistanceSquared)
\r
228 else if (this.DistanceSquared > o.DistanceSquared)
\r
234 #region Occlusion queries
\r
235 public void StartQuery(RenderPass pass)
\r
237 if (!RenderSettings.OcclusionCullingEnabled) return;
\r
239 if (pass == RenderPass.Simple)
\r
241 StartSimpleQuery();
\r
243 else if (pass == RenderPass.Alpha)
\r
249 public void EndQuery(RenderPass pass)
\r
251 if (!RenderSettings.OcclusionCullingEnabled) return;
\r
253 if (pass == RenderPass.Simple)
\r
257 else if (pass == RenderPass.Alpha)
\r
263 public void StartAlphaQuery()
\r
265 if (!RenderSettings.OcclusionCullingEnabled) return;
\r
267 if (AlphaQueryID == -1)
\r
269 Compat.GenQueries(out AlphaQueryID);
\r
271 if (AlphaQueryID > 0)
\r
273 Compat.BeginQuery(QueryTarget.SamplesPassed, AlphaQueryID);
\r
277 public void EndAlphaQuery()
\r
279 if (!RenderSettings.OcclusionCullingEnabled) return;
\r
281 if (AlphaQueryID > 0)
\r
283 Compat.EndQuery(QueryTarget.SamplesPassed);
\r
287 public void StartSimpleQuery()
\r
289 if (!RenderSettings.OcclusionCullingEnabled) return;
\r
291 if (SimpleQueryID == -1)
\r
293 Compat.GenQueries(out SimpleQueryID);
\r
295 if (SimpleQueryID > 0)
\r
297 Compat.BeginQuery(QueryTarget.SamplesPassed, SimpleQueryID);
\r
301 public void EndSimpleQuery()
\r
303 if (!RenderSettings.OcclusionCullingEnabled) return;
\r
305 if (SimpleQueryID > 0)
\r
307 Compat.EndQuery(QueryTarget.SamplesPassed);
\r
311 public bool Occluded()
\r
313 if (!RenderSettings.OcclusionCullingEnabled) return false;
\r
315 if ((SimpleQueryID == -1 && AlphaQueryID == -1))
\r
320 if ((!HasAlphaFaces && !HasSimpleFaces)) return true;
\r
323 if (HasSimpleFaces && SimpleQueryID > 0)
\r
325 Compat.GetQueryObject(SimpleQueryID, GetQueryObjectParam.QueryResult, out samples);
\r
327 if (HasSimpleFaces && samples > 0)
\r
333 if (HasAlphaFaces && AlphaQueryID > 0)
\r
335 Compat.GetQueryObject(AlphaQueryID, GetQueryObjectParam.QueryResult, out samples);
\r
337 if (HasAlphaFaces && samples > 0)
\r
344 #endregion Occlusion queries
\r
347 public static class RHelp
\r
349 public static readonly Vector3 InvalidPosition = new Vector3(99999f, 99999f, 99999f);
\r
350 static float t1 = 0.075f;
\r
351 static float t2 = t1 / 5.7f;
\r
353 public static Vector3 Smoothed1stOrder(Vector3 curPos, Vector3 targetPos, float lastFrameTime)
\r
355 int numIterations = (int)(lastFrameTime * 100);
\r
358 curPos += (targetPos - curPos) * t1;
\r
361 while (numIterations > 0);
\r
362 if (Vector3.DistanceSquared(curPos, targetPos) < 0.00001f)
\r
364 curPos = targetPos;
\r
369 public static Vector3 Smoothed2ndOrder(Vector3 curPos, Vector3 targetPos, ref Vector3 accel, float lastFrameTime)
\r
371 int numIterations = (int)(lastFrameTime * 100);
\r
374 accel += (targetPos - accel - curPos) * t1;
\r
375 curPos += accel * t2;
\r
378 while (numIterations > 0);
\r
379 if (Vector3.DistanceSquared(curPos, targetPos) < 0.00001f)
\r
381 curPos = targetPos;
\r
386 public static OpenTK.Vector2 TKVector3(Vector2 v)
\r
388 return new OpenTK.Vector2(v.X, v.Y);
\r
391 public static OpenTK.Vector3 TKVector3(Vector3 v)
\r
393 return new OpenTK.Vector3(v.X, v.Y, v.Z);
\r
396 public static OpenTK.Vector4 TKVector3(Vector4 v)
\r
398 return new OpenTK.Vector4(v.X, v.Y, v.Z, v.W);
\r
401 public static Vector2 OMVVector2(OpenTK.Vector2 v)
\r
403 return new Vector2(v.X, v.Y);
\r
406 public static Vector3 OMVVector3(OpenTK.Vector3 v)
\r
408 return new Vector3(v.X, v.Y, v.Z);
\r
411 public static Vector4 OMVVector4(OpenTK.Vector4 v)
\r
413 return new Vector4(v.X, v.Y, v.Z, v.W);
\r
416 public static Color WinColor(OpenTK.Graphics.Color4 color)
\r
418 return Color.FromArgb((int)(color.A * 255), (int)(color.R * 255), (int)(color.G * 255), (int)(color.B * 255));
\r
421 public static Color WinColor(Color4 color)
\r
423 return Color.FromArgb((int)(color.A * 255), (int)(color.R * 255), (int)(color.G * 255), (int)(color.B * 255));
\r
426 public static int NextPow2(int start)
\r
429 while (pow < start) pow *= 2;
\r
433 #region Cached image save and load
\r
434 public static readonly string RAD_IMG_MAGIC = "radegast_img";
\r
436 public static bool LoadCachedImage(UUID textureID, out byte[] tgaData, out bool hasAlpha, out bool fullAlpha, out bool isMask)
\r
439 hasAlpha = fullAlpha = isMask = false;
\r
443 string fname = System.IO.Path.Combine(RadegastInstance.GlobalInstance.Client.Settings.ASSET_CACHE_DIR, string.Format("{0}.rzi", textureID));
\r
444 //string fname = System.IO.Path.Combine(".", string.Format("{0}.rzi", textureID));
\r
446 using (var f = File.Open(fname, FileMode.Open, FileAccess.Read, FileShare.Read))
\r
448 byte[] header = new byte[36];
\r
450 f.Read(header, 0, header.Length);
\r
452 // check if the file is starting with magic string
\r
453 if (RAD_IMG_MAGIC != Utils.BytesToString(header, 0, RAD_IMG_MAGIC.Length))
\r
455 i += RAD_IMG_MAGIC.Length;
\r
457 if (header[i++] != 1) // check version
\r
460 hasAlpha = header[i++] == 1;
\r
461 fullAlpha = header[i++] == 1;
\r
462 isMask = header[i++] == 1;
\r
464 int uncompressedSize = Utils.BytesToInt(header, i);
\r
467 textureID = new UUID(header, i);
\r
470 tgaData = new byte[uncompressedSize];
\r
471 using (var compressed = new DeflateStream(f, CompressionMode.Decompress))
\r
474 while ((read = compressed.Read(tgaData, read, uncompressedSize - read)) > 0) ;
\r
480 catch (FileNotFoundException) { }
\r
481 catch (Exception ex)
\r
483 Logger.DebugLog(string.Format("Failed to load radegast cache file {0}: {1}", textureID, ex.Message));
\r
488 public static bool SaveCachedImage(byte[] tgaData, UUID textureID, bool hasAlpha, bool fullAlpha, bool isMask)
\r
492 string fname = System.IO.Path.Combine(RadegastInstance.GlobalInstance.Client.Settings.ASSET_CACHE_DIR, string.Format("{0}.rzi", textureID));
\r
493 //string fname = System.IO.Path.Combine(".", string.Format("{0}.rzi", textureID));
\r
495 using (var f = File.Open(fname, FileMode.Create, FileAccess.Write, FileShare.None))
\r
499 f.Write(Utils.StringToBytes(RAD_IMG_MAGIC), 0, RAD_IMG_MAGIC.Length);
\r
500 i += RAD_IMG_MAGIC.Length;
\r
503 f.WriteByte((byte)1);
\r
507 f.WriteByte(hasAlpha ? (byte)1 : (byte)0);
\r
508 f.WriteByte(fullAlpha ? (byte)1 : (byte)0);
\r
509 f.WriteByte(isMask ? (byte)1 : (byte)0);
\r
513 byte[] uncompressedSize = Utils.IntToBytes(tgaData.Length);
\r
514 f.Write(uncompressedSize, 0, uncompressedSize.Length);
\r
515 i += uncompressedSize.Length;
\r
518 byte[] id = new byte[16];
\r
519 textureID.ToBytes(id, 0);
\r
520 f.Write(id, 0, 16);
\r
523 // compressed texture data
\r
524 using (var compressed = new DeflateStream(f, CompressionMode.Compress))
\r
526 compressed.Write(tgaData, 0, tgaData.Length);
\r
531 catch (Exception ex)
\r
533 Logger.DebugLog(string.Format("Failed to save radegast cache file {0}: {1}", textureID, ex.Message));
\r
537 #endregion Cached image save and load
\r
539 #region Static vertices and indices for a cube (used for bounding box drawing)
\r
540 /**********************************************
\r
547 ***********************************************/
\r
548 public static readonly float[] CubeVertices = new float[]
\r
550 0.5f, 0.5f, 0.5f, // 0
\r
551 -0.5f, 0.5f, 0.5f, // 1
\r
552 -0.5f, -0.5f, 0.5f, // 2
\r
553 0.5f, -0.5f, 0.5f, // 3
\r
554 0.5f, 0.5f, -0.5f, // 4
\r
555 -0.5f, 0.5f, -0.5f, // 5
\r
556 -0.5f, -0.5f, -0.5f, // 6
\r
557 0.5f, -0.5f, -0.5f // 7
\r
560 public static readonly ushort[] CubeIndices = new ushort[]
\r
562 0, 1, 2, 3, // Front Face
\r
563 4, 5, 6, 7, // Back Face
\r
564 1, 2, 6, 5, // Left Face
\r
565 0, 3, 7, 4, // Right Face
\r
566 0, 1, 5, 4, // Top Face
\r
567 2, 3, 7, 6 // Bottom Face
\r
569 #endregion Static vertices and indices for a cube (used for bounding box drawing)
\r
571 public static int GLLoadImage(Bitmap bitmap, bool hasAlpha)
\r
573 return GLLoadImage(bitmap, hasAlpha, true);
\r
576 public static int GLLoadImage(Bitmap bitmap, bool hasAlpha, bool useMipmap)
\r
578 useMipmap = useMipmap && RenderSettings.HasMipmap;
\r
580 GL.GenTextures(1, out ret);
\r
581 GL.BindTexture(TextureTarget.Texture2D, ret);
\r
583 Rectangle rectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
\r
585 BitmapData bitmapData =
\r
588 ImageLockMode.ReadOnly,
\r
589 hasAlpha ? System.Drawing.Imaging.PixelFormat.Format32bppArgb : System.Drawing.Imaging.PixelFormat.Format24bppRgb);
\r
592 TextureTarget.Texture2D,
\r
594 hasAlpha ? PixelInternalFormat.Rgba : PixelInternalFormat.Rgb8,
\r
598 hasAlpha ? OpenTK.Graphics.OpenGL.PixelFormat.Bgra : OpenTK.Graphics.OpenGL.PixelFormat.Bgr,
\r
599 PixelType.UnsignedByte,
\r
602 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
\r
603 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
\r
604 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
\r
607 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.LinearMipmapLinear);
\r
608 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.GenerateMipmap, 1);
\r
609 GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
\r
613 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
\r
616 bitmap.UnlockBits(bitmapData);
\r
620 public static void Draw2DBox(float x, float y, float width, float height, float depth)
\r
622 GL.Begin(BeginMode.Quads);
\r
624 GL.TexCoord2(0, 1);
\r
625 GL.Vertex3(x, y, depth);
\r
626 GL.TexCoord2(1, 1);
\r
627 GL.Vertex3(x + width, y, depth);
\r
628 GL.TexCoord2(1, 0);
\r
629 GL.Vertex3(x + width, y + height, depth);
\r
630 GL.TexCoord2(0, 0);
\r
631 GL.Vertex3(x, y + height, depth);
\r
636 public static void ResetMaterial()
\r
638 GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Ambient, new float[] { 0.2f, 0.2f, 0.2f, 1.0f });
\r
639 GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Diffuse, new float[] { 0.8f, 0.8f, 0.8f, 1.0f });
\r
640 GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Specular, new float[] { 0f, 0f, 0f, 1.0f });
\r
641 GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Emission, new float[] { 0f, 0f, 0f, 1.0f });
\r
642 GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Shininess, 0f);
\r
643 ShaderProgram.Stop();
\r
648 /// Represents camera object
\r
650 public class Camera
\r
653 /// Indicates that there was manual camera movement, stop tracking objects
\r
655 public bool Manual;
\r
657 Vector3 mFocalPoint;
\r
660 /// <summary>Camera position</summary>
\r
661 public Vector3 Position { get { return mPosition; } set { mPosition = value; Modify(); } }
\r
662 /// <summary>Camera target</summary>
\r
663 public Vector3 FocalPoint { get { return mFocalPoint; } set { mFocalPoint = value; Modify(); } }
\r
664 /// <summary>Zoom level</summary>
\r
666 /// <summary>Draw distance</summary>
\r
668 /// <summary>Has camera been modified</summary>
\r
669 public bool Modified { get { return mModified; } set { mModified = value; } }
\r
671 public float TimeToTarget = 0f;
\r
673 public Vector3 RenderPosition;
\r
674 public Vector3 RenderFocalPoint;
\r
681 public void Step(float time)
\r
683 if (RenderPosition != Position)
\r
685 RenderPosition = RHelp.Smoothed1stOrder(RenderPosition, Position, time);
\r
688 if (RenderFocalPoint != FocalPoint)
\r
690 RenderFocalPoint = RHelp.Smoothed1stOrder(RenderFocalPoint, FocalPoint, time);
\r
695 [Obsolete("Use Step(), left in here for reference")]
\r
696 public void Step2(float time)
\r
698 TimeToTarget -= time;
\r
699 if (TimeToTarget <= time)
\r
707 float pctElapsed = time / TimeToTarget;
\r
709 if (RenderPosition != Position)
\r
711 float distance = Vector3.Distance(RenderPosition, Position);
\r
712 RenderPosition = Vector3.Lerp(RenderPosition, Position, distance * pctElapsed);
\r
715 if (RenderFocalPoint != FocalPoint)
\r
717 RenderFocalPoint = Interpolate(RenderFocalPoint, FocalPoint, pctElapsed);
\r
721 Vector3 Interpolate(Vector3 start, Vector3 end, float fraction)
\r
723 float distance = Vector3.Distance(start, end);
\r
724 Vector3 direction = end - start;
\r
725 return start + direction * fraction;
\r
728 public void EndMove()
\r
732 RenderPosition = Position;
\r
733 RenderFocalPoint = FocalPoint;
\r
736 public void Pan(float deltaX, float deltaY)
\r
739 Vector3 direction = Position - FocalPoint;
\r
740 direction.Normalize();
\r
741 Vector3 vy = direction % Vector3.UnitZ;
\r
742 Vector3 vx = vy % direction;
\r
743 Vector3 vxy = vx * deltaY + vy * deltaX;
\r
748 public void Rotate(float delta, bool horizontal)
\r
751 Vector3 direction = Position - FocalPoint;
\r
754 Position = FocalPoint + direction * new Quaternion(0f, 0f, (float)Math.Sin(delta), (float)Math.Cos(delta));
\r
758 Position = FocalPoint + direction * Quaternion.CreateFromAxisAngle(direction % Vector3.UnitZ, delta);
\r
762 public void MoveToTarget(float delta)
\r
765 Position += (Position - FocalPoint) * delta;
\r
769 /// Sets the world in perspective of the camera
\r
771 public void LookAt()
\r
773 OpenTK.Matrix4 lookAt = OpenTK.Matrix4.LookAt(
\r
774 RenderPosition.X, RenderPosition.Y, RenderPosition.Z,
\r
775 RenderFocalPoint.X, RenderFocalPoint.Y, RenderFocalPoint.Z,
\r
777 GL.MultMatrix(ref lookAt);
\r
781 public static class MeshToOBJ
\r
783 public static bool MeshesToOBJ(Dictionary<uint, FacetedMesh> meshes, string filename)
\r
785 StringBuilder obj = new StringBuilder();
\r
786 StringBuilder mtl = new StringBuilder();
\r
788 FileInfo objFileInfo = new FileInfo(filename);
\r
790 string mtlFilename = objFileInfo.FullName.Substring(objFileInfo.DirectoryName.Length + 1,
\r
791 objFileInfo.FullName.Length - (objFileInfo.DirectoryName.Length + 1) - 4) + ".mtl";
\r
793 obj.AppendLine("# Created by libprimrender");
\r
794 obj.AppendLine("mtllib ./" + mtlFilename);
\r
797 mtl.AppendLine("# Created by libprimrender");
\r
801 foreach (FacetedMesh mesh in meshes.Values)
\r
803 for (int j = 0; j < mesh.Faces.Count; j++)
\r
805 Face face = mesh.Faces[j];
\r
807 if (face.Vertices.Count > 2)
\r
809 string mtlName = String.Format("material{0}-{1}", primNr, face.ID);
\r
810 Primitive.TextureEntryFace tex = face.TextureFace;
\r
811 string texName = tex.TextureID.ToString() + ".tga";
\r
813 // FIXME: Convert the source to TGA (if needed) and copy to the destination
\r
815 float shiny = 0.00f;
\r
818 case Shininess.High:
\r
821 case Shininess.Medium:
\r
824 case Shininess.Low:
\r
829 obj.AppendFormat("g face{0}-{1}{2}", primNr, face.ID, Environment.NewLine);
\r
831 mtl.AppendLine("newmtl " + mtlName);
\r
832 mtl.AppendFormat("Ka {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);
\r
833 mtl.AppendFormat("Kd {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);
\r
834 //mtl.AppendFormat("Ks {0} {1} {2}{3}");
\r
835 mtl.AppendLine("Tr " + tex.RGBA.A);
\r
836 mtl.AppendLine("Ns " + shiny);
\r
837 mtl.AppendLine("illum 1");
\r
838 if (tex.TextureID != UUID.Zero && tex.TextureID != Primitive.TextureEntry.WHITE_TEXTURE)
\r
839 mtl.AppendLine("map_Kd ./" + texName);
\r
842 // Write the vertices, texture coordinates, and vertex normals for this side
\r
843 for (int k = 0; k < face.Vertices.Count; k++)
\r
845 Vertex vertex = face.Vertices[k];
\r
849 Vector3 pos = vertex.Position;
\r
852 pos *= mesh.Prim.Scale;
\r
855 pos *= mesh.Prim.Rotation;
\r
857 // The root prim position is sim-relative, while child prim positions are
\r
858 // parent-relative. We want to apply parent-relative translations but not
\r
859 // sim-relative ones
\r
860 if (mesh.Prim.ParentID != 0)
\r
861 pos += mesh.Prim.Position;
\r
863 obj.AppendFormat("v {0} {1} {2}{3}", pos.X, pos.Y, pos.Z, Environment.NewLine);
\r
867 #region Texture Coord
\r
869 obj.AppendFormat("vt {0} {1}{2}", vertex.TexCoord.X, vertex.TexCoord.Y,
\r
870 Environment.NewLine);
\r
872 #endregion Texture Coord
\r
874 #region Vertex Normal
\r
876 // HACK: Sometimes normals are getting set to <NaN,NaN,NaN>
\r
877 if (!Single.IsNaN(vertex.Normal.X) && !Single.IsNaN(vertex.Normal.Y) && !Single.IsNaN(vertex.Normal.Z))
\r
878 obj.AppendFormat("vn {0} {1} {2}{3}", vertex.Normal.X, vertex.Normal.Y, vertex.Normal.Z,
\r
879 Environment.NewLine);
\r
881 obj.AppendLine("vn 0.0 1.0 0.0");
\r
883 #endregion Vertex Normal
\r
886 obj.AppendFormat("# {0} vertices{1}", face.Vertices.Count, Environment.NewLine);
\r
888 obj.AppendLine("usemtl " + mtlName);
\r
892 // Write all of the faces (triangles) for this side
\r
893 for (int k = 0; k < face.Indices.Count / 3; k++)
\r
895 obj.AppendFormat("f -{0}/-{0}/-{0} -{1}/-{1}/-{1} -{2}/-{2}/-{2}{3}",
\r
896 face.Vertices.Count - face.Indices[k * 3 + 0],
\r
897 face.Vertices.Count - face.Indices[k * 3 + 1],
\r
898 face.Vertices.Count - face.Indices[k * 3 + 2],
\r
899 Environment.NewLine);
\r
902 obj.AppendFormat("# {0} elements{1}", face.Indices.Count / 3, Environment.NewLine);
\r
905 #endregion Elements
\r
913 File.WriteAllText(filename, obj.ToString());
\r
914 File.WriteAllText(mtlFilename, mtl.ToString());
\r
925 public static class Math3D
\r
933 public static float[] CreateTranslationMatrix(Vector3 v)
\r
935 float[] mat = new float[16];
\r
940 mat[0] = mat[5] = mat[10] = mat[15] = 1;
\r
945 public static float[] CreateRotationMatrix(Quaternion q)
\r
947 float[] mat = new float[16];
\r
949 // Transpose the quaternion (don't ask me why)
\r
954 float x2 = q.X + q.X;
\r
955 float y2 = q.Y + q.Y;
\r
956 float z2 = q.Z + q.Z;
\r
957 float xx = q.X * x2;
\r
958 float xy = q.X * y2;
\r
959 float xz = q.X * z2;
\r
960 float yy = q.Y * y2;
\r
961 float yz = q.Y * z2;
\r
962 float zz = q.Z * z2;
\r
963 float wx = q.W * x2;
\r
964 float wy = q.W * y2;
\r
965 float wz = q.W * z2;
\r
967 mat[0] = 1.0f - (yy + zz);
\r
973 mat[5] = 1.0f - (xx + zz);
\r
979 mat[10] = 1.0f - (xx + yy);
\r
990 public static float[] CreateSRTMatrix(Vector3 scale, Quaternion q, Vector3 pos)
\r
992 float[] mat = new float[16];
\r
994 // Transpose the quaternion (don't ask me why)
\r
999 float x2 = q.X + q.X;
\r
1000 float y2 = q.Y + q.Y;
\r
1001 float z2 = q.Z + q.Z;
\r
1002 float xx = q.X * x2;
\r
1003 float xy = q.X * y2;
\r
1004 float xz = q.X * z2;
\r
1005 float yy = q.Y * y2;
\r
1006 float yz = q.Y * z2;
\r
1007 float zz = q.Z * z2;
\r
1008 float wx = q.W * x2;
\r
1009 float wy = q.W * y2;
\r
1010 float wz = q.W * z2;
\r
1012 mat[0] = (1.0f - (yy + zz)) * scale.X;
\r
1013 mat[1] = (xy - wz) * scale.X;
\r
1014 mat[2] = (xz + wy) * scale.X;
\r
1017 mat[4] = (xy + wz) * scale.Y;
\r
1018 mat[5] = (1.0f - (xx + zz)) * scale.Y;
\r
1019 mat[6] = (yz - wx) * scale.Y;
\r
1022 mat[8] = (xz - wy) * scale.Z;
\r
1023 mat[9] = (yz + wx) * scale.Z;
\r
1024 mat[10] = (1.0f - (xx + yy)) * scale.Z;
\r
1027 //Positional parts
\r
1037 public static float[] CreateScaleMatrix(Vector3 v)
\r
1039 float[] mat = new float[16];
\r
1049 public static float[] Lerp(float[] matrix1, float[] matrix2, float amount)
\r
1052 float[] lerp = new float[16];
\r
1053 //Probably not doing this as a loop is cheaper(unrolling)
\r
1054 //also for performance we probably should not create new objects
\r
1056 for (int x = 0; x < 16; x++)
\r
1058 lerp[x] = matrix1[x] + ((matrix2[x] - matrix1[x]) * amount);
\r
1065 public static bool GluProject(OpenTK.Vector3 objPos, OpenTK.Matrix4 modelMatrix, OpenTK.Matrix4 projMatrix, int[] viewport, out OpenTK.Vector3 screenPos)
\r
1067 OpenTK.Vector4 _in;
\r
1068 OpenTK.Vector4 _out;
\r
1075 _out = OpenTK.Vector4.Transform(_in, modelMatrix);
\r
1076 _in = OpenTK.Vector4.Transform(_out, projMatrix);
\r
1080 screenPos = OpenTK.Vector3.Zero;
\r
1087 /* Map x, y and z to range 0-1 */
\r
1088 _in.X = _in.X * 0.5f + 0.5f;
\r
1089 _in.Y = _in.Y * 0.5f + 0.5f;
\r
1090 _in.Z = _in.Z * 0.5f + 0.5f;
\r
1092 /* Map x,y to viewport */
\r
1093 _in.X = _in.X * viewport[2] + viewport[0];
\r
1094 _in.Y = _in.Y * viewport[3] + viewport[1];
\r
1096 screenPos.X = _in.X;
\r
1097 screenPos.Y = _in.Y;
\r
1098 screenPos.Z = _in.Z;
\r
1103 public static bool GluUnProject(float winx, float winy, float winz, OpenTK.Matrix4 modelMatrix, OpenTK.Matrix4 projMatrix, int[] viewport, out OpenTK.Vector3 pos)
\r
1105 OpenTK.Matrix4 finalMatrix;
\r
1106 OpenTK.Vector4 _in;
\r
1107 OpenTK.Vector4 _out;
\r
1109 finalMatrix = OpenTK.Matrix4.Mult(modelMatrix, projMatrix);
\r
1111 finalMatrix.Invert();
\r
1118 /* Map x and y from window coordinates */
\r
1119 _in.X = (_in.X - viewport[0]) / viewport[2];
\r
1120 _in.Y = (_in.Y - viewport[1]) / viewport[3];
\r
1122 pos = OpenTK.Vector3.Zero;
\r
1124 /* Map to range -1 to 1 */
\r
1125 _in.X = _in.X * 2 - 1;
\r
1126 _in.Y = _in.Y * 2 - 1;
\r
1127 _in.Z = _in.Z * 2 - 1;
\r
1129 //__gluMultMatrixVecd(finalMatrix, _in, _out);
\r
1130 // check if this works:
\r
1131 _out = OpenTK.Vector4.Transform(_in, finalMatrix);
\r
1133 if (_out.W == 0.0f)
\r
1144 public static double[] AbovePlane(double height)
\r
1146 return new double[] { 0, 0, 1, -height };
\r
1149 public static double[] BelowPlane(double height)
\r
1151 return new double[] { 0, 0, -1, height };
\r
1156 * Helper classs for reading the static VFS file, call
\r
1157 * staticVFS.readVFSheaders() with the path to the static_data.db2 and static_index.db2 files
\r
1158 * and it will pass and dump in to openmetaverse_data for you
\r
1159 * This should only be needed to be used if LL update the static VFS in order to refresh our data
\r
1164 public int mLocation;
\r
1165 public int mLength;
\r
1166 public int mAccessTime;
\r
1167 public UUID mFileID;
\r
1169 public AssetType mAssetType;
\r
1171 public int readblock(byte[] blockdata, int offset)
\r
1174 BitPack input = new BitPack(blockdata, offset);
\r
1175 mLocation = input.UnpackInt();
\r
1176 mLength = input.UnpackInt();
\r
1177 mAccessTime = input.UnpackInt();
\r
1178 mFileID = input.UnpackUUID();
\r
1179 int filetype = input.UnpackShort();
\r
1180 mAssetType = (AssetType)filetype;
\r
1181 mSize = input.UnpackInt();
\r
1184 Logger.Log(String.Format("Found header for {0} type {1} length {2} at {3}", mFileID, mAssetType, mSize, mLocation), Helpers.LogLevel.Info);
\r
1191 public class staticVFS
\r
1193 public static void readVFSheaders(string datafile, string indexfile)
\r
1195 FileStream datastream;
\r
1196 FileStream indexstream;
\r
1198 datastream = File.Open(datafile, FileMode.Open);
\r
1199 indexstream = File.Open(indexfile, FileMode.Open);
\r
1203 byte[] blockdata = new byte[indexstream.Length];
\r
1204 indexstream.Read(blockdata, 0, (int)indexstream.Length);
\r
1206 while (offset < indexstream.Length)
\r
1208 VFSblock block = new VFSblock();
\r
1209 offset = block.readblock(blockdata, offset);
\r
1211 FileStream writer = File.Open(OpenMetaverse.Settings.RESOURCE_DIR + System.IO.Path.DirectorySeparatorChar + block.mFileID.ToString(), FileMode.Create);
\r
1212 byte[] data = new byte[block.mSize];
\r
1213 datastream.Seek(block.mLocation, SeekOrigin.Begin);
\r
1214 datastream.Read(data, 0, block.mSize);
\r
1215 writer.Write(data, 0, block.mSize);
\r