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 bool IsInvisible;
\r
77 public UUID TextureID;
\r
78 public bool FetchFailed;
\r
81 public class TextureLoadItem
\r
83 public FaceData Data;
\r
84 public Primitive Prim;
\r
85 public Primitive.TextureEntryFace TeFace;
\r
86 public byte[] TextureData = null;
\r
87 public byte[] TGAData = null;
\r
88 public bool LoadAssetFromCache = false;
\r
91 public enum RenderPass
\r
99 public enum SceneObjectType
\r
107 /// Base class for all scene objects
\r
109 public abstract class SceneObject : IComparable, IDisposable
\r
111 #region Public fields
\r
112 /// <summary>Interpolated local position of the object</summary>
\r
113 public Vector3 InterpolatedPosition;
\r
114 /// <summary>Interpolated local rotation of the object/summary>
\r
115 public Quaternion InterpolatedRotation;
\r
116 /// <summary>Rendered position of the object in the region</summary>
\r
117 public Vector3 RenderPosition;
\r
118 /// <summary>Rendered rotationm of the object in the region</summary>
\r
119 public Quaternion RenderRotation;
\r
120 /// <summary>Per frame calculated square of the distance from camera</summary>
\r
121 public float DistanceSquared;
\r
122 /// <summary>Bounding volume of the object</summary>
\r
123 public BoundingVolume BoundingVolume;
\r
124 /// <summary>Was the sim position and distance from camera calculated during this frame</summary>
\r
125 public bool PositionCalculated;
\r
126 /// <summary>Scene object type</summary>
\r
127 public SceneObjectType Type = SceneObjectType.None;
\r
128 /// <summary>Libomv primitive</summary>
\r
129 public virtual Primitive BasePrim { get; set; }
\r
130 /// <summary>Were initial initialization tasks done</summary>
\r
131 public bool Initialized;
\r
132 /// <summary>Is this object disposed</summary>
\r
133 public bool IsDisposed = false;
\r
134 public int AlphaQueryID = -1;
\r
135 public int SimpleQueryID = -1;
\r
136 public bool HasAlphaFaces;
\r
137 public bool HasSimpleFaces;
\r
138 public bool HasInvisibleFaces;
\r
140 #endregion Public fields
\r
142 uint previousParent = uint.MaxValue;
\r
145 /// Cleanup resources used
\r
147 public virtual void Dispose()
\r
153 /// Task performed the fist time object is set for rendering
\r
155 public virtual void Initialize()
\r
157 RenderPosition = InterpolatedPosition = BasePrim.Position;
\r
158 RenderRotation = InterpolatedRotation = BasePrim.Rotation;
\r
159 Initialized = true;
\r
163 /// Perform per frame tasks
\r
165 /// <param name="time">Time since the last call (last frame time in seconds)</param>
\r
166 public virtual void Step(float time)
\r
168 // Don't interpolate when parent changes (sit/stand link/unlink)
\r
169 if (previousParent != BasePrim.ParentID)
\r
171 previousParent = BasePrim.ParentID;
\r
172 InterpolatedPosition = BasePrim.Position;
\r
173 InterpolatedRotation = BasePrim.Rotation;
\r
177 // Linear velocity and acceleration
\r
178 if (BasePrim.Velocity != Vector3.Zero)
\r
180 BasePrim.Position = InterpolatedPosition = BasePrim.Position + BasePrim.Velocity * time
\r
181 * 0.98f * RadegastInstance.GlobalInstance.Client.Network.CurrentSim.Stats.Dilation;
\r
182 BasePrim.Velocity += BasePrim.Acceleration * time;
\r
184 else if (InterpolatedPosition != BasePrim.Position)
\r
186 InterpolatedPosition = RHelp.Smoothed1stOrder(InterpolatedPosition, BasePrim.Position, time);
\r
189 // Angular velocity (target omega)
\r
190 if (BasePrim.AngularVelocity != Vector3.Zero)
\r
192 Vector3 angVel = BasePrim.AngularVelocity;
\r
193 float angle = time * angVel.Length();
\r
194 Quaternion dQ = Quaternion.CreateFromAxisAngle(angVel, angle);
\r
195 InterpolatedRotation = dQ * InterpolatedRotation;
\r
197 else if (InterpolatedRotation != BasePrim.Rotation && !(this is RenderAvatar))
\r
199 InterpolatedRotation = Quaternion.Slerp(InterpolatedRotation, BasePrim.Rotation, time * 10f);
\r
200 if (1f - Math.Abs(Quaternion.Dot(InterpolatedRotation, BasePrim.Rotation)) < 0.0001)
\r
201 InterpolatedRotation = BasePrim.Rotation;
\r
205 InterpolatedRotation = BasePrim.Rotation;
\r
210 /// Render scene object
\r
212 /// <param name="pass">Which pass are we currently in</param>
\r
213 /// <param name="pickingID">ID used to identify which object was picked</param>
\r
214 /// <param name="scene">Main scene renderer</param>
\r
215 /// <param name="time">Time it took to render the last frame</param>
\r
216 public virtual void Render(RenderPass pass, int pickingID, SceneWindow scene, float time)
\r
221 /// Implementation of the IComparable interface
\r
222 /// used for sorting by distance
\r
224 /// <param name="other">Object we are comparing to</param>
\r
225 /// <returns>Result of the comparison</returns>
\r
226 public virtual int CompareTo(object other)
\r
228 SceneObject o = (SceneObject)other;
\r
229 if (this.DistanceSquared < o.DistanceSquared)
\r
231 else if (this.DistanceSquared > o.DistanceSquared)
\r
237 #region Occlusion queries
\r
238 public void StartQuery(RenderPass pass)
\r
240 if (!RenderSettings.OcclusionCullingEnabled) return;
\r
242 if (pass == RenderPass.Simple)
\r
244 StartSimpleQuery();
\r
246 else if (pass == RenderPass.Alpha)
\r
252 public void EndQuery(RenderPass pass)
\r
254 if (!RenderSettings.OcclusionCullingEnabled) return;
\r
256 if (pass == RenderPass.Simple)
\r
260 else if (pass == RenderPass.Alpha)
\r
266 public void StartAlphaQuery()
\r
268 if (!RenderSettings.OcclusionCullingEnabled) return;
\r
270 if (AlphaQueryID == -1)
\r
272 Compat.GenQueries(out AlphaQueryID);
\r
274 if (AlphaQueryID > 0)
\r
276 Compat.BeginQuery(QueryTarget.SamplesPassed, AlphaQueryID);
\r
280 public void EndAlphaQuery()
\r
282 if (!RenderSettings.OcclusionCullingEnabled) return;
\r
284 if (AlphaQueryID > 0)
\r
286 Compat.EndQuery(QueryTarget.SamplesPassed);
\r
290 public void StartSimpleQuery()
\r
292 if (!RenderSettings.OcclusionCullingEnabled) return;
\r
294 if (SimpleQueryID == -1)
\r
296 Compat.GenQueries(out SimpleQueryID);
\r
298 if (SimpleQueryID > 0)
\r
300 Compat.BeginQuery(QueryTarget.SamplesPassed, SimpleQueryID);
\r
304 public void EndSimpleQuery()
\r
306 if (!RenderSettings.OcclusionCullingEnabled) return;
\r
308 if (SimpleQueryID > 0)
\r
310 Compat.EndQuery(QueryTarget.SamplesPassed);
\r
314 public bool Occluded()
\r
316 if (!RenderSettings.OcclusionCullingEnabled) return false;
\r
318 if (HasInvisibleFaces) return false;
\r
320 if ((SimpleQueryID == -1 && AlphaQueryID == -1))
\r
325 if ((!HasAlphaFaces && !HasSimpleFaces)) return true;
\r
328 if (HasSimpleFaces && SimpleQueryID > 0)
\r
330 Compat.GetQueryObject(SimpleQueryID, GetQueryObjectParam.QueryResult, out samples);
\r
332 if (HasSimpleFaces && samples > 0)
\r
338 if (HasAlphaFaces && AlphaQueryID > 0)
\r
340 Compat.GetQueryObject(AlphaQueryID, GetQueryObjectParam.QueryResult, out samples);
\r
342 if (HasAlphaFaces && samples > 0)
\r
349 #endregion Occlusion queries
\r
352 public static class RHelp
\r
354 public static readonly Vector3 InvalidPosition = new Vector3(99999f, 99999f, 99999f);
\r
355 static float t1 = 0.075f;
\r
356 static float t2 = t1 / 5.7f;
\r
358 public static Vector3 Smoothed1stOrder(Vector3 curPos, Vector3 targetPos, float lastFrameTime)
\r
360 int numIterations = (int)(lastFrameTime * 100);
\r
363 curPos += (targetPos - curPos) * t1;
\r
366 while (numIterations > 0);
\r
367 if (Vector3.DistanceSquared(curPos, targetPos) < 0.00001f)
\r
369 curPos = targetPos;
\r
374 public static Vector3 Smoothed2ndOrder(Vector3 curPos, Vector3 targetPos, ref Vector3 accel, float lastFrameTime)
\r
376 int numIterations = (int)(lastFrameTime * 100);
\r
379 accel += (targetPos - accel - curPos) * t1;
\r
380 curPos += accel * t2;
\r
383 while (numIterations > 0);
\r
384 if (Vector3.DistanceSquared(curPos, targetPos) < 0.00001f)
\r
386 curPos = targetPos;
\r
391 public static OpenTK.Vector2 TKVector3(Vector2 v)
\r
393 return new OpenTK.Vector2(v.X, v.Y);
\r
396 public static OpenTK.Vector3 TKVector3(Vector3 v)
\r
398 return new OpenTK.Vector3(v.X, v.Y, v.Z);
\r
401 public static OpenTK.Vector4 TKVector3(Vector4 v)
\r
403 return new OpenTK.Vector4(v.X, v.Y, v.Z, v.W);
\r
406 public static Vector2 OMVVector2(OpenTK.Vector2 v)
\r
408 return new Vector2(v.X, v.Y);
\r
411 public static Vector3 OMVVector3(OpenTK.Vector3 v)
\r
413 return new Vector3(v.X, v.Y, v.Z);
\r
416 public static Vector4 OMVVector4(OpenTK.Vector4 v)
\r
418 return new Vector4(v.X, v.Y, v.Z, v.W);
\r
421 public static Color WinColor(OpenTK.Graphics.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 Color WinColor(Color4 color)
\r
428 return Color.FromArgb((int)(color.A * 255), (int)(color.R * 255), (int)(color.G * 255), (int)(color.B * 255));
\r
431 public static int NextPow2(int start)
\r
434 while (pow < start) pow *= 2;
\r
438 #region Cached image save and load
\r
439 public static readonly string RAD_IMG_MAGIC = "radegast_img";
\r
441 public static bool LoadCachedImage(UUID textureID, out byte[] tgaData, out bool hasAlpha, out bool fullAlpha, out bool isMask)
\r
444 hasAlpha = fullAlpha = isMask = false;
\r
448 string fname = System.IO.Path.Combine(RadegastInstance.GlobalInstance.Client.Settings.ASSET_CACHE_DIR, string.Format("{0}.rzi", textureID));
\r
449 //string fname = System.IO.Path.Combine(".", string.Format("{0}.rzi", textureID));
\r
451 using (var f = File.Open(fname, FileMode.Open, FileAccess.Read, FileShare.Read))
\r
453 byte[] header = new byte[36];
\r
455 f.Read(header, 0, header.Length);
\r
457 // check if the file is starting with magic string
\r
458 if (RAD_IMG_MAGIC != Utils.BytesToString(header, 0, RAD_IMG_MAGIC.Length))
\r
460 i += RAD_IMG_MAGIC.Length;
\r
462 if (header[i++] != 1) // check version
\r
465 hasAlpha = header[i++] == 1;
\r
466 fullAlpha = header[i++] == 1;
\r
467 isMask = header[i++] == 1;
\r
469 int uncompressedSize = Utils.BytesToInt(header, i);
\r
472 textureID = new UUID(header, i);
\r
475 tgaData = new byte[uncompressedSize];
\r
476 using (var compressed = new DeflateStream(f, CompressionMode.Decompress))
\r
479 while ((read = compressed.Read(tgaData, read, uncompressedSize - read)) > 0) ;
\r
485 catch (FileNotFoundException) { }
\r
486 catch (Exception ex)
\r
488 Logger.DebugLog(string.Format("Failed to load radegast cache file {0}: {1}", textureID, ex.Message));
\r
493 public static bool SaveCachedImage(byte[] tgaData, UUID textureID, bool hasAlpha, bool fullAlpha, bool isMask)
\r
497 string fname = System.IO.Path.Combine(RadegastInstance.GlobalInstance.Client.Settings.ASSET_CACHE_DIR, string.Format("{0}.rzi", textureID));
\r
498 //string fname = System.IO.Path.Combine(".", string.Format("{0}.rzi", textureID));
\r
500 using (var f = File.Open(fname, FileMode.Create, FileAccess.Write, FileShare.None))
\r
504 f.Write(Utils.StringToBytes(RAD_IMG_MAGIC), 0, RAD_IMG_MAGIC.Length);
\r
505 i += RAD_IMG_MAGIC.Length;
\r
508 f.WriteByte((byte)1);
\r
512 f.WriteByte(hasAlpha ? (byte)1 : (byte)0);
\r
513 f.WriteByte(fullAlpha ? (byte)1 : (byte)0);
\r
514 f.WriteByte(isMask ? (byte)1 : (byte)0);
\r
518 byte[] uncompressedSize = Utils.IntToBytes(tgaData.Length);
\r
519 f.Write(uncompressedSize, 0, uncompressedSize.Length);
\r
520 i += uncompressedSize.Length;
\r
523 byte[] id = new byte[16];
\r
524 textureID.ToBytes(id, 0);
\r
525 f.Write(id, 0, 16);
\r
528 // compressed texture data
\r
529 using (var compressed = new DeflateStream(f, CompressionMode.Compress))
\r
531 compressed.Write(tgaData, 0, tgaData.Length);
\r
536 catch (Exception ex)
\r
538 Logger.DebugLog(string.Format("Failed to save radegast cache file {0}: {1}", textureID, ex.Message));
\r
542 #endregion Cached image save and load
\r
544 #region Static vertices and indices for a cube (used for bounding box drawing)
\r
545 /**********************************************
\r
552 ***********************************************/
\r
553 public static readonly float[] CubeVertices = new float[]
\r
555 0.5f, 0.5f, 0.5f, // 0
\r
556 -0.5f, 0.5f, 0.5f, // 1
\r
557 -0.5f, -0.5f, 0.5f, // 2
\r
558 0.5f, -0.5f, 0.5f, // 3
\r
559 0.5f, 0.5f, -0.5f, // 4
\r
560 -0.5f, 0.5f, -0.5f, // 5
\r
561 -0.5f, -0.5f, -0.5f, // 6
\r
562 0.5f, -0.5f, -0.5f // 7
\r
565 public static readonly ushort[] CubeIndices = new ushort[]
\r
567 0, 1, 2, 3, // Front Face
\r
568 4, 5, 6, 7, // Back Face
\r
569 1, 2, 6, 5, // Left Face
\r
570 0, 3, 7, 4, // Right Face
\r
571 0, 1, 5, 4, // Top Face
\r
572 2, 3, 7, 6 // Bottom Face
\r
574 #endregion Static vertices and indices for a cube (used for bounding box drawing)
\r
576 public static int GLLoadImage(Bitmap bitmap, bool hasAlpha)
\r
578 return GLLoadImage(bitmap, hasAlpha, true);
\r
581 public static int GLLoadImage(Bitmap bitmap, bool hasAlpha, bool useMipmap)
\r
583 useMipmap = useMipmap && RenderSettings.HasMipmap;
\r
585 GL.GenTextures(1, out ret);
\r
586 GL.BindTexture(TextureTarget.Texture2D, ret);
\r
588 Rectangle rectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
\r
590 BitmapData bitmapData =
\r
593 ImageLockMode.ReadOnly,
\r
594 hasAlpha ? System.Drawing.Imaging.PixelFormat.Format32bppArgb : System.Drawing.Imaging.PixelFormat.Format24bppRgb);
\r
597 TextureTarget.Texture2D,
\r
599 hasAlpha ? PixelInternalFormat.Rgba : PixelInternalFormat.Rgb8,
\r
603 hasAlpha ? OpenTK.Graphics.OpenGL.PixelFormat.Bgra : OpenTK.Graphics.OpenGL.PixelFormat.Bgr,
\r
604 PixelType.UnsignedByte,
\r
607 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
\r
608 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Repeat);
\r
609 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Repeat);
\r
612 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.LinearMipmapLinear);
\r
613 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.GenerateMipmap, 1);
\r
614 GL.GenerateMipmap(GenerateMipmapTarget.Texture2D);
\r
618 GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
\r
621 bitmap.UnlockBits(bitmapData);
\r
625 public static void Draw2DBox(float x, float y, float width, float height, float depth)
\r
627 GL.Begin(BeginMode.Quads);
\r
629 GL.TexCoord2(0, 1);
\r
630 GL.Vertex3(x, y, depth);
\r
631 GL.TexCoord2(1, 1);
\r
632 GL.Vertex3(x + width, y, depth);
\r
633 GL.TexCoord2(1, 0);
\r
634 GL.Vertex3(x + width, y + height, depth);
\r
635 GL.TexCoord2(0, 0);
\r
636 GL.Vertex3(x, y + height, depth);
\r
641 public static void ResetMaterial()
\r
643 GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Ambient, new float[] { 0.2f, 0.2f, 0.2f, 1.0f });
\r
644 GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Diffuse, new float[] { 0.8f, 0.8f, 0.8f, 1.0f });
\r
645 GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Specular, new float[] { 0f, 0f, 0f, 1.0f });
\r
646 GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Emission, new float[] { 0f, 0f, 0f, 1.0f });
\r
647 GL.Material(MaterialFace.FrontAndBack, MaterialParameter.Shininess, 0f);
\r
648 ShaderProgram.Stop();
\r
653 /// Represents camera object
\r
655 public class Camera
\r
658 /// Indicates that there was manual camera movement, stop tracking objects
\r
660 public bool Manual;
\r
662 Vector3 mFocalPoint;
\r
665 /// <summary>Camera position</summary>
\r
666 public Vector3 Position { get { return mPosition; } set { mPosition = value; Modify(); } }
\r
667 /// <summary>Camera target</summary>
\r
668 public Vector3 FocalPoint { get { return mFocalPoint; } set { mFocalPoint = value; Modify(); } }
\r
669 /// <summary>Zoom level</summary>
\r
671 /// <summary>Draw distance</summary>
\r
673 /// <summary>Has camera been modified</summary>
\r
674 public bool Modified { get { return mModified; } set { mModified = value; } }
\r
676 public float TimeToTarget = 0f;
\r
678 public Vector3 RenderPosition;
\r
679 public Vector3 RenderFocalPoint;
\r
686 public void Step(float time)
\r
688 if (RenderPosition != Position)
\r
690 RenderPosition = RHelp.Smoothed1stOrder(RenderPosition, Position, time);
\r
693 if (RenderFocalPoint != FocalPoint)
\r
695 RenderFocalPoint = RHelp.Smoothed1stOrder(RenderFocalPoint, FocalPoint, time);
\r
700 [Obsolete("Use Step(), left in here for reference")]
\r
701 public void Step2(float time)
\r
703 TimeToTarget -= time;
\r
704 if (TimeToTarget <= time)
\r
712 float pctElapsed = time / TimeToTarget;
\r
714 if (RenderPosition != Position)
\r
716 float distance = Vector3.Distance(RenderPosition, Position);
\r
717 RenderPosition = Vector3.Lerp(RenderPosition, Position, distance * pctElapsed);
\r
720 if (RenderFocalPoint != FocalPoint)
\r
722 RenderFocalPoint = Interpolate(RenderFocalPoint, FocalPoint, pctElapsed);
\r
726 Vector3 Interpolate(Vector3 start, Vector3 end, float fraction)
\r
728 float distance = Vector3.Distance(start, end);
\r
729 Vector3 direction = end - start;
\r
730 return start + direction * fraction;
\r
733 public void EndMove()
\r
737 RenderPosition = Position;
\r
738 RenderFocalPoint = FocalPoint;
\r
741 public void Pan(float deltaX, float deltaY)
\r
744 Vector3 direction = Position - FocalPoint;
\r
745 direction.Normalize();
\r
746 Vector3 vy = direction % Vector3.UnitZ;
\r
747 Vector3 vx = vy % direction;
\r
748 Vector3 vxy = vx * deltaY + vy * deltaX;
\r
753 public void Rotate(float delta, bool horizontal)
\r
756 Vector3 direction = Position - FocalPoint;
\r
759 Position = FocalPoint + direction * new Quaternion(0f, 0f, (float)Math.Sin(delta), (float)Math.Cos(delta));
\r
763 Position = FocalPoint + direction * Quaternion.CreateFromAxisAngle(direction % Vector3.UnitZ, delta);
\r
767 public void MoveToTarget(float delta)
\r
770 Position += (Position - FocalPoint) * delta;
\r
774 /// Sets the world in perspective of the camera
\r
776 public void LookAt()
\r
778 OpenTK.Matrix4 lookAt = OpenTK.Matrix4.LookAt(
\r
779 RenderPosition.X, RenderPosition.Y, RenderPosition.Z,
\r
780 RenderFocalPoint.X, RenderFocalPoint.Y, RenderFocalPoint.Z,
\r
782 GL.MultMatrix(ref lookAt);
\r
786 public static class MeshToOBJ
\r
788 public static bool MeshesToOBJ(Dictionary<uint, FacetedMesh> meshes, string filename)
\r
790 StringBuilder obj = new StringBuilder();
\r
791 StringBuilder mtl = new StringBuilder();
\r
793 FileInfo objFileInfo = new FileInfo(filename);
\r
795 string mtlFilename = objFileInfo.FullName.Substring(objFileInfo.DirectoryName.Length + 1,
\r
796 objFileInfo.FullName.Length - (objFileInfo.DirectoryName.Length + 1) - 4) + ".mtl";
\r
798 obj.AppendLine("# Created by libprimrender");
\r
799 obj.AppendLine("mtllib ./" + mtlFilename);
\r
802 mtl.AppendLine("# Created by libprimrender");
\r
806 foreach (FacetedMesh mesh in meshes.Values)
\r
808 for (int j = 0; j < mesh.Faces.Count; j++)
\r
810 Face face = mesh.Faces[j];
\r
812 if (face.Vertices.Count > 2)
\r
814 string mtlName = String.Format("material{0}-{1}", primNr, face.ID);
\r
815 Primitive.TextureEntryFace tex = face.TextureFace;
\r
816 string texName = tex.TextureID.ToString() + ".tga";
\r
818 // FIXME: Convert the source to TGA (if needed) and copy to the destination
\r
820 float shiny = 0.00f;
\r
823 case Shininess.High:
\r
826 case Shininess.Medium:
\r
829 case Shininess.Low:
\r
834 obj.AppendFormat("g face{0}-{1}{2}", primNr, face.ID, Environment.NewLine);
\r
836 mtl.AppendLine("newmtl " + mtlName);
\r
837 mtl.AppendFormat("Ka {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);
\r
838 mtl.AppendFormat("Kd {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);
\r
839 //mtl.AppendFormat("Ks {0} {1} {2}{3}");
\r
840 mtl.AppendLine("Tr " + tex.RGBA.A);
\r
841 mtl.AppendLine("Ns " + shiny);
\r
842 mtl.AppendLine("illum 1");
\r
843 if (tex.TextureID != UUID.Zero && tex.TextureID != Primitive.TextureEntry.WHITE_TEXTURE)
\r
844 mtl.AppendLine("map_Kd ./" + texName);
\r
847 // Write the vertices, texture coordinates, and vertex normals for this side
\r
848 for (int k = 0; k < face.Vertices.Count; k++)
\r
850 Vertex vertex = face.Vertices[k];
\r
854 Vector3 pos = vertex.Position;
\r
857 pos *= mesh.Prim.Scale;
\r
860 pos *= mesh.Prim.Rotation;
\r
862 // The root prim position is sim-relative, while child prim positions are
\r
863 // parent-relative. We want to apply parent-relative translations but not
\r
864 // sim-relative ones
\r
865 if (mesh.Prim.ParentID != 0)
\r
866 pos += mesh.Prim.Position;
\r
868 obj.AppendFormat("v {0} {1} {2}{3}", pos.X, pos.Y, pos.Z, Environment.NewLine);
\r
872 #region Texture Coord
\r
874 obj.AppendFormat("vt {0} {1}{2}", vertex.TexCoord.X, vertex.TexCoord.Y,
\r
875 Environment.NewLine);
\r
877 #endregion Texture Coord
\r
879 #region Vertex Normal
\r
881 // HACK: Sometimes normals are getting set to <NaN,NaN,NaN>
\r
882 if (!Single.IsNaN(vertex.Normal.X) && !Single.IsNaN(vertex.Normal.Y) && !Single.IsNaN(vertex.Normal.Z))
\r
883 obj.AppendFormat("vn {0} {1} {2}{3}", vertex.Normal.X, vertex.Normal.Y, vertex.Normal.Z,
\r
884 Environment.NewLine);
\r
886 obj.AppendLine("vn 0.0 1.0 0.0");
\r
888 #endregion Vertex Normal
\r
891 obj.AppendFormat("# {0} vertices{1}", face.Vertices.Count, Environment.NewLine);
\r
893 obj.AppendLine("usemtl " + mtlName);
\r
897 // Write all of the faces (triangles) for this side
\r
898 for (int k = 0; k < face.Indices.Count / 3; k++)
\r
900 obj.AppendFormat("f -{0}/-{0}/-{0} -{1}/-{1}/-{1} -{2}/-{2}/-{2}{3}",
\r
901 face.Vertices.Count - face.Indices[k * 3 + 0],
\r
902 face.Vertices.Count - face.Indices[k * 3 + 1],
\r
903 face.Vertices.Count - face.Indices[k * 3 + 2],
\r
904 Environment.NewLine);
\r
907 obj.AppendFormat("# {0} elements{1}", face.Indices.Count / 3, Environment.NewLine);
\r
910 #endregion Elements
\r
918 File.WriteAllText(filename, obj.ToString());
\r
919 File.WriteAllText(mtlFilename, mtl.ToString());
\r
930 public static class Math3D
\r
938 public static float[] CreateTranslationMatrix(Vector3 v)
\r
940 float[] mat = new float[16];
\r
945 mat[0] = mat[5] = mat[10] = mat[15] = 1;
\r
950 public static float[] CreateRotationMatrix(Quaternion q)
\r
952 float[] mat = new float[16];
\r
954 // Transpose the quaternion (don't ask me why)
\r
959 float x2 = q.X + q.X;
\r
960 float y2 = q.Y + q.Y;
\r
961 float z2 = q.Z + q.Z;
\r
962 float xx = q.X * x2;
\r
963 float xy = q.X * y2;
\r
964 float xz = q.X * z2;
\r
965 float yy = q.Y * y2;
\r
966 float yz = q.Y * z2;
\r
967 float zz = q.Z * z2;
\r
968 float wx = q.W * x2;
\r
969 float wy = q.W * y2;
\r
970 float wz = q.W * z2;
\r
972 mat[0] = 1.0f - (yy + zz);
\r
978 mat[5] = 1.0f - (xx + zz);
\r
984 mat[10] = 1.0f - (xx + yy);
\r
995 public static float[] CreateSRTMatrix(Vector3 scale, Quaternion q, Vector3 pos)
\r
997 float[] mat = new float[16];
\r
999 // Transpose the quaternion (don't ask me why)
\r
1004 float x2 = q.X + q.X;
\r
1005 float y2 = q.Y + q.Y;
\r
1006 float z2 = q.Z + q.Z;
\r
1007 float xx = q.X * x2;
\r
1008 float xy = q.X * y2;
\r
1009 float xz = q.X * z2;
\r
1010 float yy = q.Y * y2;
\r
1011 float yz = q.Y * z2;
\r
1012 float zz = q.Z * z2;
\r
1013 float wx = q.W * x2;
\r
1014 float wy = q.W * y2;
\r
1015 float wz = q.W * z2;
\r
1017 mat[0] = (1.0f - (yy + zz)) * scale.X;
\r
1018 mat[1] = (xy - wz) * scale.X;
\r
1019 mat[2] = (xz + wy) * scale.X;
\r
1022 mat[4] = (xy + wz) * scale.Y;
\r
1023 mat[5] = (1.0f - (xx + zz)) * scale.Y;
\r
1024 mat[6] = (yz - wx) * scale.Y;
\r
1027 mat[8] = (xz - wy) * scale.Z;
\r
1028 mat[9] = (yz + wx) * scale.Z;
\r
1029 mat[10] = (1.0f - (xx + yy)) * scale.Z;
\r
1032 //Positional parts
\r
1042 public static float[] CreateScaleMatrix(Vector3 v)
\r
1044 float[] mat = new float[16];
\r
1054 public static float[] Lerp(float[] matrix1, float[] matrix2, float amount)
\r
1057 float[] lerp = new float[16];
\r
1058 //Probably not doing this as a loop is cheaper(unrolling)
\r
1059 //also for performance we probably should not create new objects
\r
1061 for (int x = 0; x < 16; x++)
\r
1063 lerp[x] = matrix1[x] + ((matrix2[x] - matrix1[x]) * amount);
\r
1070 public static bool GluProject(OpenTK.Vector3 objPos, OpenTK.Matrix4 modelMatrix, OpenTK.Matrix4 projMatrix, int[] viewport, out OpenTK.Vector3 screenPos)
\r
1072 OpenTK.Vector4 _in;
\r
1073 OpenTK.Vector4 _out;
\r
1080 _out = OpenTK.Vector4.Transform(_in, modelMatrix);
\r
1081 _in = OpenTK.Vector4.Transform(_out, projMatrix);
\r
1085 screenPos = OpenTK.Vector3.Zero;
\r
1092 /* Map x, y and z to range 0-1 */
\r
1093 _in.X = _in.X * 0.5f + 0.5f;
\r
1094 _in.Y = _in.Y * 0.5f + 0.5f;
\r
1095 _in.Z = _in.Z * 0.5f + 0.5f;
\r
1097 /* Map x,y to viewport */
\r
1098 _in.X = _in.X * viewport[2] + viewport[0];
\r
1099 _in.Y = _in.Y * viewport[3] + viewport[1];
\r
1101 screenPos.X = _in.X;
\r
1102 screenPos.Y = _in.Y;
\r
1103 screenPos.Z = _in.Z;
\r
1108 public static bool GluUnProject(float winx, float winy, float winz, OpenTK.Matrix4 modelMatrix, OpenTK.Matrix4 projMatrix, int[] viewport, out OpenTK.Vector3 pos)
\r
1110 OpenTK.Matrix4 finalMatrix;
\r
1111 OpenTK.Vector4 _in;
\r
1112 OpenTK.Vector4 _out;
\r
1114 finalMatrix = OpenTK.Matrix4.Mult(modelMatrix, projMatrix);
\r
1116 finalMatrix.Invert();
\r
1123 /* Map x and y from window coordinates */
\r
1124 _in.X = (_in.X - viewport[0]) / viewport[2];
\r
1125 _in.Y = (_in.Y - viewport[1]) / viewport[3];
\r
1127 pos = OpenTK.Vector3.Zero;
\r
1129 /* Map to range -1 to 1 */
\r
1130 _in.X = _in.X * 2 - 1;
\r
1131 _in.Y = _in.Y * 2 - 1;
\r
1132 _in.Z = _in.Z * 2 - 1;
\r
1134 //__gluMultMatrixVecd(finalMatrix, _in, _out);
\r
1135 // check if this works:
\r
1136 _out = OpenTK.Vector4.Transform(_in, finalMatrix);
\r
1138 if (_out.W == 0.0f)
\r
1149 public static double[] AbovePlane(double height)
\r
1151 return new double[] { 0, 0, 1, -height };
\r
1154 public static double[] BelowPlane(double height)
\r
1156 return new double[] { 0, 0, -1, height };
\r
1161 * Helper classs for reading the static VFS file, call
\r
1162 * staticVFS.readVFSheaders() with the path to the static_data.db2 and static_index.db2 files
\r
1163 * and it will pass and dump in to openmetaverse_data for you
\r
1164 * This should only be needed to be used if LL update the static VFS in order to refresh our data
\r
1169 public int mLocation;
\r
1170 public int mLength;
\r
1171 public int mAccessTime;
\r
1172 public UUID mFileID;
\r
1174 public AssetType mAssetType;
\r
1176 public int readblock(byte[] blockdata, int offset)
\r
1179 BitPack input = new BitPack(blockdata, offset);
\r
1180 mLocation = input.UnpackInt();
\r
1181 mLength = input.UnpackInt();
\r
1182 mAccessTime = input.UnpackInt();
\r
1183 mFileID = input.UnpackUUID();
\r
1184 int filetype = input.UnpackShort();
\r
1185 mAssetType = (AssetType)filetype;
\r
1186 mSize = input.UnpackInt();
\r
1189 Logger.Log(String.Format("Found header for {0} type {1} length {2} at {3}", mFileID, mAssetType, mSize, mLocation), Helpers.LogLevel.Info);
\r
1196 public class staticVFS
\r
1198 public static void readVFSheaders(string datafile, string indexfile)
\r
1200 FileStream datastream;
\r
1201 FileStream indexstream;
\r
1203 datastream = File.Open(datafile, FileMode.Open);
\r
1204 indexstream = File.Open(indexfile, FileMode.Open);
\r
1208 byte[] blockdata = new byte[indexstream.Length];
\r
1209 indexstream.Read(blockdata, 0, (int)indexstream.Length);
\r
1211 while (offset < indexstream.Length)
\r
1213 VFSblock block = new VFSblock();
\r
1214 offset = block.readblock(blockdata, offset);
\r
1216 FileStream writer = File.Open(OpenMetaverse.Settings.RESOURCE_DIR + System.IO.Path.DirectorySeparatorChar + block.mFileID.ToString(), FileMode.Create);
\r
1217 byte[] data = new byte[block.mSize];
\r
1218 datastream.Seek(block.mLocation, SeekOrigin.Begin);
\r
1219 datastream.Read(data, 0, block.mSize);
\r
1220 writer.Write(data, 0, block.mSize);
\r