2 using System.Collections.Generic;
\r
3 using System.Collections;
\r
7 using System.IO.Compression;
\r
9 using System.Threading;
\r
10 using OpenTK.Graphics.OpenGL;
\r
11 using System.Runtime.InteropServices;
\r
12 using OpenMetaverse;
\r
13 using OpenMetaverse.Rendering;
\r
15 namespace Radegast.Rendering
\r
17 public class FaceData
\r
19 public float[] Vertices;
\r
20 public ushort[] Indices;
\r
21 public float[] TexCoords;
\r
22 public float[] Normals;
\r
23 public int PickingID = -1;
\r
24 public int VertexVBO = -1;
\r
25 public int IndexVBO = -1;
\r
26 public TextureInfo TextureInfo = new TextureInfo();
\r
27 public BoundingVolume BoundingVolume = new BoundingVolume();
\r
28 public static int VertexSize = 32; // sizeof (vertex), 2 x vector3 + 1 x vector2 = 8 floats x 4 bytes = 32 bytes
\r
29 public TextureAnimationInfo AnimInfo;
\r
30 public int QueryID = 0;
\r
32 public void CheckVBO(Face face)
\r
34 if (VertexVBO == -1)
\r
36 Vertex[] vArray = face.Vertices.ToArray();
\r
37 GL.GenBuffers(1, out VertexVBO);
\r
38 GL.BindBuffer(BufferTarget.ArrayBuffer, VertexVBO);
\r
39 GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vArray.Length * VertexSize), vArray, BufferUsageHint.StaticDraw);
\r
44 ushort[] iArray = face.Indices.ToArray();
\r
45 GL.GenBuffers(1, out IndexVBO);
\r
46 GL.BindBuffer(BufferTarget.ElementArrayBuffer, IndexVBO);
\r
47 GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(iArray.Length * sizeof(ushort)), iArray, BufferUsageHint.StaticDraw);
\r
52 public class TextureAnimationInfo
\r
54 public Primitive.TextureAnimation PrimAnimInfo;
\r
55 public float CurrentFrame;
\r
56 public float CurrentTime;
\r
57 public bool PingPong;
\r
58 float LastTime = 0f;
\r
59 float TotalTime = 0f;
\r
61 public void Step(float lastFrameTime)
\r
63 float numFrames = 1f;
\r
64 float fullLength = 1f;
\r
66 if (PrimAnimInfo.Length > 0)
\r
68 numFrames = PrimAnimInfo.Length;
\r
72 numFrames = Math.Max(1f, (float)(PrimAnimInfo.SizeX * PrimAnimInfo.SizeY));
\r
75 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.PING_PONG) != 0)
\r
77 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) != 0)
\r
79 fullLength = 2f * numFrames;
\r
81 else if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.LOOP) != 0)
\r
83 fullLength = 2f * numFrames - 2f;
\r
84 fullLength = Math.Max(1f, fullLength);
\r
88 fullLength = 2f * numFrames - 1f;
\r
89 fullLength = Math.Max(1f, fullLength);
\r
94 fullLength = numFrames;
\r
98 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) != 0)
\r
100 frameCounter = lastFrameTime * PrimAnimInfo.Rate + LastTime;
\r
104 TotalTime += lastFrameTime;
\r
105 frameCounter = TotalTime * PrimAnimInfo.Rate;
\r
107 LastTime = frameCounter;
\r
109 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.LOOP) != 0)
\r
111 frameCounter %= fullLength;
\r
115 frameCounter = Math.Min(fullLength - 1f, frameCounter);
\r
118 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) == 0)
\r
120 frameCounter = (float)Math.Floor(frameCounter + 0.01f);
\r
123 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.PING_PONG) != 0)
\r
125 if (frameCounter > numFrames)
\r
127 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) != 0)
\r
129 frameCounter = numFrames - (frameCounter - numFrames);
\r
133 frameCounter = (numFrames - 1.99f) - (frameCounter - numFrames);
\r
138 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.REVERSE) != 0)
\r
140 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) != 0)
\r
142 frameCounter = numFrames - frameCounter;
\r
146 frameCounter = (numFrames - 0.99f) - frameCounter;
\r
150 frameCounter += PrimAnimInfo.Start;
\r
152 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) == 0)
\r
154 frameCounter = (float)Math.Round(frameCounter);
\r
158 GL.MatrixMode(MatrixMode.Texture);
\r
161 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.ROTATE) != 0)
\r
163 GL.Translate(0.5f, 0.5f, 0f);
\r
164 GL.Rotate(Utils.RAD_TO_DEG * frameCounter, OpenTK.Vector3d.UnitZ);
\r
165 GL.Translate(-0.5f, -0.5f, 0f);
\r
167 else if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SCALE) != 0)
\r
169 GL.Scale(frameCounter, frameCounter, 0);
\r
173 float sizeX = Math.Max(1f, (float)PrimAnimInfo.SizeX);
\r
174 float sizeY = Math.Max(1f, (float)PrimAnimInfo.SizeY);
\r
176 GL.Scale(1f / sizeX, 1f / sizeY, 0);
\r
177 GL.Translate(frameCounter % sizeX, Math.Floor(frameCounter / sizeY), 0);
\r
180 GL.MatrixMode(MatrixMode.Modelview);
\r
183 [Obsolete("Use Step() instead")]
\r
184 public void ExperimentalStep(float time)
\r
186 int reverseFactor = 1;
\r
187 float rate = PrimAnimInfo.Rate;
\r
192 reverseFactor = -reverseFactor;
\r
195 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.REVERSE) != 0)
\r
197 reverseFactor = -reverseFactor;
\r
200 CurrentTime += time;
\r
201 float totalTime = 1 / rate;
\r
203 uint x = Math.Max(1, PrimAnimInfo.SizeX);
\r
204 uint y = Math.Max(1, PrimAnimInfo.SizeY);
\r
205 uint nrFrames = x * y;
\r
207 if (PrimAnimInfo.Length > 0 && PrimAnimInfo.Length < nrFrames)
\r
209 nrFrames = (uint)PrimAnimInfo.Length;
\r
212 GL.MatrixMode(MatrixMode.Texture);
\r
215 if (CurrentTime >= totalTime)
\r
219 if (CurrentFrame > nrFrames) CurrentFrame = (uint)PrimAnimInfo.Start;
\r
220 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.PING_PONG) != 0)
\r
222 PingPong = !PingPong;
\r
226 float smoothOffset = 0f;
\r
228 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) != 0)
\r
230 smoothOffset = (CurrentTime / totalTime) * reverseFactor;
\r
233 float f = CurrentFrame;
\r
234 if (reverseFactor < 0)
\r
236 f = nrFrames - CurrentFrame;
\r
239 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.ROTATE) == 0) // not rotating
\r
241 GL.Scale(1f / x, 1f / y, 0f);
\r
242 GL.Translate((f % x) + smoothOffset, f / y, 0);
\r
246 smoothOffset = (CurrentTime * PrimAnimInfo.Rate);
\r
247 float startAngle = PrimAnimInfo.Start;
\r
248 float endAngle = PrimAnimInfo.Length;
\r
249 float angle = startAngle + (endAngle - startAngle) * smoothOffset;
\r
250 GL.Translate(0.5f, 0.5f, 0f);
\r
251 GL.Rotate(Utils.RAD_TO_DEG * angle, OpenTK.Vector3d.UnitZ);
\r
252 GL.Translate(-0.5f, -0.5f, 0f);
\r
255 GL.MatrixMode(MatrixMode.Modelview);
\r
261 public class TextureInfo
\r
263 public System.Drawing.Image Texture;
\r
264 public int TexturePointer;
\r
265 public bool HasAlpha;
\r
266 public bool FullAlpha;
\r
267 public bool IsMask;
\r
268 public UUID TextureID;
\r
269 public bool FetchFailed;
\r
272 public class TextureLoadItem
\r
274 public FaceData Data;
\r
275 public Primitive Prim;
\r
276 public Primitive.TextureEntryFace TeFace;
\r
277 public byte[] TextureData = null;
\r
278 public byte[] TGAData = null;
\r
279 public bool LoadAssetFromCache = false;
\r
282 public enum RenderPass
\r
289 public enum SceneObjectType
\r
297 /// Base class for all scene objects
\r
299 public abstract class SceneObject : IComparable, IDisposable
\r
301 #region Public fields
\r
302 /// <summary>Interpolated local position of the object</summary>
\r
303 public Vector3 InterpolatedPosition;
\r
304 /// <summary>Interpolated local rotation of the object/summary>
\r
305 public Quaternion InterpolatedRotation;
\r
306 /// <summary>Rendered position of the object in the region</summary>
\r
307 public Vector3 RenderPosition;
\r
308 /// <summary>Rendered rotationm of the object in the region</summary>
\r
309 public Quaternion RenderRotation;
\r
310 /// <summary>Per frame calculated square of the distance from camera</summary>
\r
311 public float DistanceSquared;
\r
312 /// <summary>Bounding volume of the object</summary>
\r
313 public BoundingVolume BoundingVolume;
\r
314 /// <summary>Was the sim position and distance from camera calculated during this frame</summary>
\r
315 public bool PositionCalculated;
\r
316 /// <summary>Scene object type</summary>
\r
317 public SceneObjectType Type = SceneObjectType.None;
\r
318 /// <summary>Libomv primitive</summary>
\r
319 public virtual Primitive BasePrim { get; set; }
\r
320 /// <summary>Were initial initialization tasks done</summary>
\r
321 public bool Initialized;
\r
322 public int AlphaQueryID = -1;
\r
323 public int SimpleQueryID = -1;
\r
324 public bool HasAlphaFaces;
\r
325 public bool HasSimpleFaces;
\r
327 #endregion Public fields
\r
330 /// Cleanup resources used
\r
332 public virtual void Dispose()
\r
337 /// Task performed the fist time object is set for rendering
\r
339 public virtual void Initialize()
\r
341 RenderPosition = InterpolatedPosition = BasePrim.Position;
\r
342 RenderRotation = InterpolatedRotation = BasePrim.Rotation;
\r
343 Initialized = true;
\r
347 /// Perform per frame tasks
\r
349 /// <param name="time">Time since the last call (last frame time in seconds)</param>
\r
350 public virtual void Step(float time)
\r
352 // Linear velocity and acceleration
\r
353 if (BasePrim.Velocity != Vector3.Zero)
\r
355 BasePrim.Position = InterpolatedPosition = BasePrim.Position + BasePrim.Velocity * time
\r
356 * 0.98f * RadegastInstance.GlobalInstance.Client.Network.CurrentSim.Stats.Dilation;
\r
357 BasePrim.Velocity += BasePrim.Acceleration * time;
\r
359 else if (InterpolatedPosition != BasePrim.Position)
\r
361 InterpolatedPosition = RHelp.Smoothed1stOrder(InterpolatedPosition, BasePrim.Position, time);
\r
364 // Angular velocity (target omega)
\r
365 if (BasePrim.AngularVelocity != Vector3.Zero)
\r
367 Vector3 angVel = BasePrim.AngularVelocity;
\r
368 float angle = time * angVel.Length();
\r
369 Quaternion dQ = Quaternion.CreateFromAxisAngle(angVel, angle);
\r
370 InterpolatedRotation = dQ * InterpolatedRotation;
\r
372 else if (InterpolatedRotation != BasePrim.Rotation)
\r
374 InterpolatedRotation = Quaternion.Slerp(InterpolatedRotation, BasePrim.Rotation, time * 10f);
\r
375 if (Math.Abs(1f - Quaternion.Dot(InterpolatedRotation, BasePrim.Rotation)) < 0.0001)
\r
376 InterpolatedRotation = BasePrim.Rotation;
\r
381 /// Implementation of the IComparable interface
\r
382 /// used for sorting by distance
\r
384 /// <param name="other">Object we are comparing to</param>
\r
385 /// <returns>Result of the comparison</returns>
\r
386 public virtual int CompareTo(object other)
\r
388 SceneObject o = (SceneObject)other;
\r
389 if (this.DistanceSquared < o.DistanceSquared)
\r
391 else if (this.DistanceSquared > o.DistanceSquared)
\r
397 public void StartQuery(RenderPass pass)
\r
399 if (pass == RenderPass.Simple)
\r
401 StartSimpleQuery();
\r
403 else if (pass == RenderPass.Alpha)
\r
409 public void EndQuery(RenderPass pass)
\r
411 if (pass == RenderPass.Simple)
\r
415 else if (pass == RenderPass.Alpha)
\r
421 public void StartAlphaQuery()
\r
423 if (AlphaQueryID == -1)
\r
425 GL.GenQueries(1, out AlphaQueryID);
\r
427 if (AlphaQueryID > 0)
\r
429 GL.BeginQuery(QueryTarget.SamplesPassed, AlphaQueryID);
\r
433 public void EndAlphaQuery()
\r
435 if (AlphaQueryID > 0)
\r
437 GL.EndQuery(QueryTarget.SamplesPassed);
\r
441 public void StartSimpleQuery()
\r
443 if (SimpleQueryID == -1)
\r
445 GL.GenQueries(1, out SimpleQueryID);
\r
447 if (SimpleQueryID > 0)
\r
449 GL.BeginQuery(QueryTarget.SamplesPassed, SimpleQueryID);
\r
453 public void EndSimpleQuery()
\r
455 if (SimpleQueryID > 0)
\r
457 GL.EndQuery(QueryTarget.SamplesPassed);
\r
461 public bool Occluded()
\r
463 if ((SimpleQueryID == -1 && AlphaQueryID == -1))
\r
468 if ((!HasAlphaFaces && !HasSimpleFaces)) return true;
\r
471 if (HasSimpleFaces && SimpleQueryID > 0)
\r
473 GL.GetQueryObject(SimpleQueryID, GetQueryObjectParam.QueryResult, out samples);
\r
475 if (HasSimpleFaces && samples > 0)
\r
481 if (HasAlphaFaces && AlphaQueryID > 0)
\r
483 GL.GetQueryObject(AlphaQueryID, GetQueryObjectParam.QueryResult, out samples);
\r
485 if (HasAlphaFaces && samples > 0)
\r
494 public class RenderPrimitive : SceneObject
\r
496 public Primitive Prim;
\r
497 public List<Face> Faces;
\r
498 /// <summary>Is this object attached to an avatar</summary>
\r
499 public bool Attached;
\r
500 /// <summary>Do we know if object is attached</summary>
\r
501 public bool AttachedStateKnown;
\r
502 /// <summary>Are meshes constructed and ready for this prim</summary>
\r
503 public bool Meshed;
\r
504 /// <summary>Process of creating a mesh is underway</summary>
\r
505 public bool Meshing;
\r
506 /// <summary>Hash code for mesh to detect when mesh is regenerated</summary>
\r
507 public int LastMeshHash;
\r
509 public RenderPrimitive()
\r
511 Type = SceneObjectType.Primitive;
\r
514 public int GetMeshHash()
\r
516 if (Prim.Type == PrimType.Sculpt || Prim.Type == PrimType.Mesh)
\r
518 return Prim.Sculpt.GetHashCode();
\r
522 return Prim.PrimData.GetHashCode();
\r
526 public override Primitive BasePrim
\r
528 get { return Prim; }
\r
529 set { Prim = value; }
\r
532 public override void Initialize()
\r
534 AttachedStateKnown = false;
\r
538 public override string ToString()
\r
540 uint id = Prim == null ? 0 : Prim.LocalID;
\r
541 float distance = (float)Math.Sqrt(DistanceSquared);
\r
542 return string.Format("LocalID: {0}, distance {0.00}", id, distance);
\r
546 public static class Render
\r
548 public static IRendering Plugin;
\r
551 public static class RHelp
\r
553 public static readonly Vector3 InvalidPosition = new Vector3(99999f, 99999f, 99999f);
\r
554 static float t1 = 0.075f;
\r
555 static float t2 = t1 / 5.7f;
\r
557 public static Vector3 Smoothed1stOrder(Vector3 curPos, Vector3 targetPos, float lastFrameTime)
\r
559 int numIterations = (int)(lastFrameTime * 100);
\r
562 curPos += (targetPos - curPos) * t1;
\r
565 while (numIterations > 0);
\r
566 if (Vector3.DistanceSquared(curPos, targetPos) < 0.00001f)
\r
568 curPos = targetPos;
\r
573 public static Vector3 Smoothed2ndOrder(Vector3 curPos, Vector3 targetPos, ref Vector3 accel, float lastFrameTime)
\r
575 int numIterations = (int)(lastFrameTime * 100);
\r
578 accel += (targetPos - accel - curPos) * t1;
\r
579 curPos += accel * t2;
\r
582 while (numIterations > 0);
\r
583 if (Vector3.DistanceSquared(curPos, targetPos) < 0.00001f)
\r
585 curPos = targetPos;
\r
590 public static OpenTK.Vector2 TKVector3(Vector2 v)
\r
592 return new OpenTK.Vector2(v.X, v.Y);
\r
595 public static OpenTK.Vector3 TKVector3(Vector3 v)
\r
597 return new OpenTK.Vector3(v.X, v.Y, v.Z);
\r
600 public static OpenTK.Vector4 TKVector3(Vector4 v)
\r
602 return new OpenTK.Vector4(v.X, v.Y, v.Z, v.W);
\r
605 #region Cached image save and load
\r
606 public static readonly string RAD_IMG_MAGIC = "radegast_img";
\r
608 public static bool LoadCachedImage(UUID textureID, out byte[] tgaData, out bool hasAlpha, out bool fullAlpha, out bool isMask)
\r
611 hasAlpha = fullAlpha = isMask = false;
\r
615 string fname = System.IO.Path.Combine(RadegastInstance.GlobalInstance.Client.Settings.ASSET_CACHE_DIR, string.Format("{0}.rzi", textureID));
\r
616 //string fname = System.IO.Path.Combine(".", string.Format("{0}.rzi", textureID));
\r
618 using (var f = File.Open(fname, FileMode.Open, FileAccess.Read, FileShare.Read))
\r
620 byte[] header = new byte[36];
\r
622 f.Read(header, 0, header.Length);
\r
624 // check if the file is starting with magic string
\r
625 if (RAD_IMG_MAGIC != Utils.BytesToString(header, 0, RAD_IMG_MAGIC.Length))
\r
627 i += RAD_IMG_MAGIC.Length;
\r
629 if (header[i++] != 1) // check version
\r
632 hasAlpha = header[i++] == 1;
\r
633 fullAlpha = header[i++] == 1;
\r
634 isMask = header[i++] == 1;
\r
636 int uncompressedSize = Utils.BytesToInt(header, i);
\r
639 textureID = new UUID(header, i);
\r
642 tgaData = new byte[uncompressedSize];
\r
643 using (var compressed = new DeflateStream(f, CompressionMode.Decompress))
\r
646 while ((read = compressed.Read(tgaData, read, uncompressedSize - read)) > 0) ;
\r
652 catch (FileNotFoundException) { }
\r
653 catch (Exception ex)
\r
655 Logger.DebugLog(string.Format("Failed to load radegast cache file {0}: {1}", textureID, ex.Message));
\r
660 public static bool SaveCachedImage(byte[] tgaData, UUID textureID, bool hasAlpha, bool fullAlpha, bool isMask)
\r
664 string fname = System.IO.Path.Combine(RadegastInstance.GlobalInstance.Client.Settings.ASSET_CACHE_DIR, string.Format("{0}.rzi", textureID));
\r
665 //string fname = System.IO.Path.Combine(".", string.Format("{0}.rzi", textureID));
\r
667 using (var f = File.Open(fname, FileMode.Create, FileAccess.Write, FileShare.None))
\r
671 f.Write(Utils.StringToBytes(RAD_IMG_MAGIC), 0, RAD_IMG_MAGIC.Length);
\r
672 i += RAD_IMG_MAGIC.Length;
\r
675 f.WriteByte((byte)1);
\r
679 f.WriteByte(hasAlpha ? (byte)1 : (byte)0);
\r
680 f.WriteByte(fullAlpha ? (byte)1 : (byte)0);
\r
681 f.WriteByte(isMask ? (byte)1 : (byte)0);
\r
685 byte[] uncompressedSize = Utils.IntToBytes(tgaData.Length);
\r
686 f.Write(uncompressedSize, 0, uncompressedSize.Length);
\r
687 i += uncompressedSize.Length;
\r
690 byte[] id = new byte[16];
\r
691 textureID.ToBytes(id, 0);
\r
692 f.Write(id, 0, 16);
\r
695 // compressed texture data
\r
696 using (var compressed = new DeflateStream(f, CompressionMode.Compress))
\r
698 compressed.Write(tgaData, 0, tgaData.Length);
\r
703 catch (Exception ex)
\r
705 Logger.DebugLog(string.Format("Failed to save radegast cache file {0}: {1}", textureID, ex.Message));
\r
709 #endregion Cached image save and load
\r
711 #region Static vertices and indices for a cube (used for bounding box drawing)
\r
712 /**********************************************
\r
719 ***********************************************/
\r
720 public static readonly float[] CubeVertices = new float[]
\r
722 0.5f, 0.5f, 0.5f, // 0
\r
723 -0.5f, 0.5f, 0.5f, // 1
\r
724 -0.5f, -0.5f, 0.5f, // 2
\r
725 0.5f, -0.5f, 0.5f, // 3
\r
726 0.5f, 0.5f, -0.5f, // 4
\r
727 -0.5f, 0.5f, -0.5f, // 5
\r
728 -0.5f, -0.5f, -0.5f, // 6
\r
729 0.5f, -0.5f, -0.5f // 7
\r
732 public static readonly ushort[] CubeIndices = new ushort[]
\r
734 0, 1, 2, 3, // Front Face
\r
735 4, 5, 6, 7, // Back Face
\r
736 1, 2, 6, 5, // Left Face
\r
737 0, 3, 7, 4, // Right Face
\r
738 0, 1, 5, 4, // Top Face
\r
739 2, 3, 7, 6 // Bottom Face
\r
741 #endregion Static vertices and indices for a cube (used for bounding box drawing)
\r
745 /// Represents camera object
\r
747 public class Camera
\r
750 Vector3 mFocalPoint;
\r
753 /// <summary>Camera position</summary>
\r
754 public Vector3 Position { get { return mPosition; } set { mPosition = value; Modify(); } }
\r
755 /// <summary>Camera target</summary>
\r
756 public Vector3 FocalPoint { get { return mFocalPoint; } set { mFocalPoint = value; Modify(); } }
\r
757 /// <summary>Zoom level</summary>
\r
759 /// <summary>Draw distance</summary>
\r
761 /// <summary>Has camera been modified</summary>
\r
762 public bool Modified { get { return mModified; } set { mModified = value; } }
\r
764 public float TimeToTarget = 0f;
\r
766 public Vector3 RenderPosition;
\r
767 public Vector3 RenderFocalPoint;
\r
774 public void Step(float time)
\r
776 if (RenderPosition != Position)
\r
778 RenderPosition = RHelp.Smoothed1stOrder(RenderPosition, Position, time);
\r
781 if (RenderFocalPoint != FocalPoint)
\r
783 RenderFocalPoint = RHelp.Smoothed1stOrder(RenderFocalPoint, FocalPoint, time);
\r
788 [Obsolete("Use Step(), left in here for reference")]
\r
789 public void Step2(float time)
\r
791 TimeToTarget -= time;
\r
792 if (TimeToTarget <= time)
\r
800 float pctElapsed = time / TimeToTarget;
\r
802 if (RenderPosition != Position)
\r
804 float distance = Vector3.Distance(RenderPosition, Position);
\r
805 RenderPosition = Vector3.Lerp(RenderPosition, Position, distance * pctElapsed);
\r
808 if (RenderFocalPoint != FocalPoint)
\r
810 RenderFocalPoint = Interpolate(RenderFocalPoint, FocalPoint, pctElapsed);
\r
814 Vector3 Interpolate(Vector3 start, Vector3 end, float fraction)
\r
816 float distance = Vector3.Distance(start, end);
\r
817 Vector3 direction = end - start;
\r
818 return start + direction * fraction;
\r
821 public void EndMove()
\r
825 RenderPosition = Position;
\r
826 RenderFocalPoint = FocalPoint;
\r
830 public static class MeshToOBJ
\r
832 public static bool MeshesToOBJ(Dictionary<uint, FacetedMesh> meshes, string filename)
\r
834 StringBuilder obj = new StringBuilder();
\r
835 StringBuilder mtl = new StringBuilder();
\r
837 FileInfo objFileInfo = new FileInfo(filename);
\r
839 string mtlFilename = objFileInfo.FullName.Substring(objFileInfo.DirectoryName.Length + 1,
\r
840 objFileInfo.FullName.Length - (objFileInfo.DirectoryName.Length + 1) - 4) + ".mtl";
\r
842 obj.AppendLine("# Created by libprimrender");
\r
843 obj.AppendLine("mtllib ./" + mtlFilename);
\r
846 mtl.AppendLine("# Created by libprimrender");
\r
850 foreach (FacetedMesh mesh in meshes.Values)
\r
852 for (int j = 0; j < mesh.Faces.Count; j++)
\r
854 Face face = mesh.Faces[j];
\r
856 if (face.Vertices.Count > 2)
\r
858 string mtlName = String.Format("material{0}-{1}", primNr, face.ID);
\r
859 Primitive.TextureEntryFace tex = face.TextureFace;
\r
860 string texName = tex.TextureID.ToString() + ".tga";
\r
862 // FIXME: Convert the source to TGA (if needed) and copy to the destination
\r
864 float shiny = 0.00f;
\r
867 case Shininess.High:
\r
870 case Shininess.Medium:
\r
873 case Shininess.Low:
\r
878 obj.AppendFormat("g face{0}-{1}{2}", primNr, face.ID, Environment.NewLine);
\r
880 mtl.AppendLine("newmtl " + mtlName);
\r
881 mtl.AppendFormat("Ka {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);
\r
882 mtl.AppendFormat("Kd {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);
\r
883 //mtl.AppendFormat("Ks {0} {1} {2}{3}");
\r
884 mtl.AppendLine("Tr " + tex.RGBA.A);
\r
885 mtl.AppendLine("Ns " + shiny);
\r
886 mtl.AppendLine("illum 1");
\r
887 if (tex.TextureID != UUID.Zero && tex.TextureID != Primitive.TextureEntry.WHITE_TEXTURE)
\r
888 mtl.AppendLine("map_Kd ./" + texName);
\r
891 // Write the vertices, texture coordinates, and vertex normals for this side
\r
892 for (int k = 0; k < face.Vertices.Count; k++)
\r
894 Vertex vertex = face.Vertices[k];
\r
898 Vector3 pos = vertex.Position;
\r
901 pos *= mesh.Prim.Scale;
\r
904 pos *= mesh.Prim.Rotation;
\r
906 // The root prim position is sim-relative, while child prim positions are
\r
907 // parent-relative. We want to apply parent-relative translations but not
\r
908 // sim-relative ones
\r
909 if (mesh.Prim.ParentID != 0)
\r
910 pos += mesh.Prim.Position;
\r
912 obj.AppendFormat("v {0} {1} {2}{3}", pos.X, pos.Y, pos.Z, Environment.NewLine);
\r
916 #region Texture Coord
\r
918 obj.AppendFormat("vt {0} {1}{2}", vertex.TexCoord.X, vertex.TexCoord.Y,
\r
919 Environment.NewLine);
\r
921 #endregion Texture Coord
\r
923 #region Vertex Normal
\r
925 // HACK: Sometimes normals are getting set to <NaN,NaN,NaN>
\r
926 if (!Single.IsNaN(vertex.Normal.X) && !Single.IsNaN(vertex.Normal.Y) && !Single.IsNaN(vertex.Normal.Z))
\r
927 obj.AppendFormat("vn {0} {1} {2}{3}", vertex.Normal.X, vertex.Normal.Y, vertex.Normal.Z,
\r
928 Environment.NewLine);
\r
930 obj.AppendLine("vn 0.0 1.0 0.0");
\r
932 #endregion Vertex Normal
\r
935 obj.AppendFormat("# {0} vertices{1}", face.Vertices.Count, Environment.NewLine);
\r
937 obj.AppendLine("usemtl " + mtlName);
\r
941 // Write all of the faces (triangles) for this side
\r
942 for (int k = 0; k < face.Indices.Count / 3; k++)
\r
944 obj.AppendFormat("f -{0}/-{0}/-{0} -{1}/-{1}/-{1} -{2}/-{2}/-{2}{3}",
\r
945 face.Vertices.Count - face.Indices[k * 3 + 0],
\r
946 face.Vertices.Count - face.Indices[k * 3 + 1],
\r
947 face.Vertices.Count - face.Indices[k * 3 + 2],
\r
948 Environment.NewLine);
\r
951 obj.AppendFormat("# {0} elements{1}", face.Indices.Count / 3, Environment.NewLine);
\r
954 #endregion Elements
\r
962 File.WriteAllText(filename, obj.ToString());
\r
963 File.WriteAllText(mtlFilename, mtl.ToString());
\r
974 public static class Math3D
\r
982 public static float[] CreateTranslationMatrix(Vector3 v)
\r
984 float[] mat = new float[16];
\r
989 mat[0] = mat[5] = mat[10] = mat[15] = 1;
\r
994 public static float[] CreateRotationMatrix(Quaternion q)
\r
996 float[] mat = new float[16];
\r
998 // Transpose the quaternion (don't ask me why)
\r
1003 float x2 = q.X + q.X;
\r
1004 float y2 = q.Y + q.Y;
\r
1005 float z2 = q.Z + q.Z;
\r
1006 float xx = q.X * x2;
\r
1007 float xy = q.X * y2;
\r
1008 float xz = q.X * z2;
\r
1009 float yy = q.Y * y2;
\r
1010 float yz = q.Y * z2;
\r
1011 float zz = q.Z * z2;
\r
1012 float wx = q.W * x2;
\r
1013 float wy = q.W * y2;
\r
1014 float wz = q.W * z2;
\r
1016 mat[0] = 1.0f - (yy + zz);
\r
1022 mat[5] = 1.0f - (xx + zz);
\r
1028 mat[10] = 1.0f - (xx + yy);
\r
1039 public static float[] CreateSRTMatrix(Vector3 scale, Quaternion q, Vector3 pos)
\r
1041 float[] mat = new float[16];
\r
1043 // Transpose the quaternion (don't ask me why)
\r
1048 float x2 = q.X + q.X;
\r
1049 float y2 = q.Y + q.Y;
\r
1050 float z2 = q.Z + q.Z;
\r
1051 float xx = q.X * x2;
\r
1052 float xy = q.X * y2;
\r
1053 float xz = q.X * z2;
\r
1054 float yy = q.Y * y2;
\r
1055 float yz = q.Y * z2;
\r
1056 float zz = q.Z * z2;
\r
1057 float wx = q.W * x2;
\r
1058 float wy = q.W * y2;
\r
1059 float wz = q.W * z2;
\r
1061 mat[0] = (1.0f - (yy + zz)) * scale.X;
\r
1062 mat[1] = (xy - wz) * scale.X;
\r
1063 mat[2] = (xz + wy) * scale.X;
\r
1066 mat[4] = (xy + wz) * scale.Y;
\r
1067 mat[5] = (1.0f - (xx + zz)) * scale.Y;
\r
1068 mat[6] = (yz - wx) * scale.Y;
\r
1071 mat[8] = (xz - wy) * scale.Z;
\r
1072 mat[9] = (yz + wx) * scale.Z;
\r
1073 mat[10] = (1.0f - (xx + yy)) * scale.Z;
\r
1076 //Positional parts
\r
1086 public static float[] CreateScaleMatrix(Vector3 v)
\r
1088 float[] mat = new float[16];
\r
1098 public static float[] Lerp(float[] matrix1, float[] matrix2, float amount)
\r
1101 float[] lerp = new float[16];
\r
1102 //Probably not doing this as a loop is cheaper(unrolling)
\r
1103 //also for performance we probably should not create new objects
\r
1105 for (int x = 0; x < 16; x++)
\r
1107 lerp[x] = matrix1[x] + ((matrix2[x] - matrix1[x]) * amount);
\r
1114 public static bool GluProject(OpenTK.Vector3 objPos, OpenTK.Matrix4 modelMatrix, OpenTK.Matrix4 projMatrix, int[] viewport, out OpenTK.Vector3 screenPos)
\r
1116 OpenTK.Vector4 _in;
\r
1117 OpenTK.Vector4 _out;
\r
1124 _out = OpenTK.Vector4.Transform(_in, modelMatrix);
\r
1125 _in = OpenTK.Vector4.Transform(_out, projMatrix);
\r
1129 screenPos = OpenTK.Vector3.Zero;
\r
1136 /* Map x, y and z to range 0-1 */
\r
1137 _in.X = _in.X * 0.5f + 0.5f;
\r
1138 _in.Y = _in.Y * 0.5f + 0.5f;
\r
1139 _in.Z = _in.Z * 0.5f + 0.5f;
\r
1141 /* Map x,y to viewport */
\r
1142 _in.X = _in.X * viewport[2] + viewport[0];
\r
1143 _in.Y = _in.Y * viewport[3] + viewport[1];
\r
1145 screenPos.X = _in.X;
\r
1146 screenPos.Y = _in.Y;
\r
1147 screenPos.Z = _in.Z;
\r
1153 public class attachment_point
\r
1155 public string name;
\r
1156 public string joint;
\r
1157 public Vector3 position;
\r
1158 public Quaternion rotation;
\r
1162 public GLMesh jointmesh;
\r
1163 public int jointmeshindex;
\r
1165 public attachment_point(XmlNode node)
\r
1167 name = node.Attributes.GetNamedItem("name").Value;
\r
1168 joint = node.Attributes.GetNamedItem("joint").Value;
\r
1169 position = VisualParamEx.XmlParseVector(node.Attributes.GetNamedItem("position").Value);
\r
1170 rotation = VisualParamEx.XmlParseRotation(node.Attributes.GetNamedItem("rotation").Value);
\r
1171 id = Int32.Parse(node.Attributes.GetNamedItem("id").Value);
\r
1172 group = Int32.Parse(node.Attributes.GetNamedItem("group").Value);
\r
1178 /// Subclass of LindenMesh that adds vertex, index, and texture coordinate
\r
1179 /// arrays suitable for pushing direct to OpenGL
\r
1181 public class GLMesh : LindenMesh
\r
1184 /// Subclass of LODMesh that adds an index array suitable for pushing
\r
1185 /// direct to OpenGL
\r
1189 public int teFaceID;
\r
1190 public Dictionary<int, VisualParamEx> _evp = new Dictionary<int, VisualParamEx>();
\r
1192 new public class LODMesh : LindenMesh.LODMesh
\r
1194 public ushort[] Indices;
\r
1196 public override void LoadMesh(string filename)
\r
1198 base.LoadMesh(filename);
\r
1200 // Generate the index array
\r
1201 Indices = new ushort[_numFaces * 3];
\r
1203 for (int i = 0; i < _numFaces; i++)
\r
1205 Indices[current++] = (ushort)_faces[i].Indices[0];
\r
1206 Indices[current++] = (ushort)_faces[i].Indices[1];
\r
1207 Indices[current++] = (ushort)_faces[i].Indices[2];
\r
1215 public struct GLData
\r
1217 public float[] Vertices;
\r
1218 public float[] Normals;
\r
1219 public ushort[] Indices;
\r
1220 public float[] TexCoords;
\r
1221 public Vector3 Center;
\r
1222 public float[] weights; //strictly these are constant and don't need instancing with the GLMesh
\r
1223 public string[] skinJoints; //strictly these are constant and don't need instancing with the GLMesh
\r
1226 public static GLData baseRenderData;
\r
1227 public GLData RenderData;
\r
1228 public GLData OrigRenderData;
\r
1229 public GLData MorphRenderData;
\r
1231 public GLAvatar av;
\r
1233 public GLMesh(string name)
\r
1238 public GLMesh(GLMesh source, GLAvatar av)
\r
1239 : base(source.Name)
\r
1242 // Make a new GLMesh copy from the supplied source
\r
1244 RenderData.Vertices = new float[source.RenderData.Vertices.Length];
\r
1245 RenderData.Normals = new float[source.RenderData.Normals.Length];
\r
1246 RenderData.TexCoords = new float[source.RenderData.TexCoords.Length];
\r
1247 RenderData.Indices = new ushort[source.RenderData.Indices.Length];
\r
1249 RenderData.weights = new float[source.RenderData.weights.Length];
\r
1250 RenderData.skinJoints = new string[source.RenderData.skinJoints.Length];
\r
1252 Array.Copy(source.RenderData.Vertices, RenderData.Vertices, source.RenderData.Vertices.Length);
\r
1253 Array.Copy(source.RenderData.Normals, RenderData.Normals, source.RenderData.Normals.Length);
\r
1255 Array.Copy(source.RenderData.TexCoords, RenderData.TexCoords, source.RenderData.TexCoords.Length);
\r
1256 Array.Copy(source.RenderData.Indices, RenderData.Indices, source.RenderData.Indices.Length);
\r
1257 Array.Copy(source.RenderData.weights, RenderData.weights, source.RenderData.weights.Length);
\r
1258 Array.Copy(source.RenderData.skinJoints, RenderData.skinJoints, source.RenderData.skinJoints.Length);
\r
1260 RenderData.Center = new Vector3(source.RenderData.Center);
\r
1262 teFaceID = source.teFaceID;
\r
1264 _rotationAngles = new Vector3(source.RotationAngles);
\r
1265 _scale = new Vector3(source.Scale);
\r
1266 _position = new Vector3(source.Position);
\r
1268 // We should not need to instance these the reference from the top should be constant
\r
1269 _evp = source._evp;
\r
1270 _morphs = source._morphs;
\r
1272 OrigRenderData.Indices = new ushort[source.RenderData.Indices.Length];
\r
1273 OrigRenderData.TexCoords = new float[source.RenderData.TexCoords.Length];
\r
1274 OrigRenderData.Vertices = new float[source.RenderData.Vertices.Length];
\r
1276 MorphRenderData.Vertices = new float[source.RenderData.Vertices.Length];
\r
1278 Array.Copy(source.RenderData.Vertices, OrigRenderData.Vertices, source.RenderData.Vertices.Length);
\r
1279 Array.Copy(source.RenderData.Vertices, MorphRenderData.Vertices, source.RenderData.Vertices.Length);
\r
1281 Array.Copy(source.RenderData.TexCoords, OrigRenderData.TexCoords, source.RenderData.TexCoords.Length);
\r
1282 Array.Copy(source.RenderData.Indices, OrigRenderData.Indices, source.RenderData.Indices.Length);
\r
1288 public void setMeshPos(Vector3 pos)
\r
1293 public void setMeshRot(Vector3 rot)
\r
1295 _rotationAngles = rot;
\r
1298 public override void LoadMesh(string filename)
\r
1300 base.LoadMesh(filename);
\r
1302 float minX, minY, minZ;
\r
1303 minX = minY = minZ = Single.MaxValue;
\r
1304 float maxX, maxY, maxZ;
\r
1305 maxX = maxY = maxZ = Single.MinValue;
\r
1307 // Generate the vertex array
\r
1308 RenderData.Vertices = new float[_numVertices * 3];
\r
1309 RenderData.Normals = new float[_numVertices * 3];
\r
1311 Quaternion quat = Quaternion.CreateFromEulers(0, 0, (float)(Math.PI / 4.0));
\r
1314 for (int i = 0; i < _numVertices; i++)
\r
1317 RenderData.Normals[current] = _vertices[i].Normal.X;
\r
1318 RenderData.Vertices[current++] = _vertices[i].Coord.X;
\r
1319 RenderData.Normals[current] = _vertices[i].Normal.Y;
\r
1320 RenderData.Vertices[current++] = _vertices[i].Coord.Y;
\r
1321 RenderData.Normals[current] = _vertices[i].Normal.Z;
\r
1322 RenderData.Vertices[current++] = _vertices[i].Coord.Z;
\r
1324 if (_vertices[i].Coord.X < minX)
\r
1325 minX = _vertices[i].Coord.X;
\r
1326 else if (_vertices[i].Coord.X > maxX)
\r
1327 maxX = _vertices[i].Coord.X;
\r
1329 if (_vertices[i].Coord.Y < minY)
\r
1330 minY = _vertices[i].Coord.Y;
\r
1331 else if (_vertices[i].Coord.Y > maxY)
\r
1332 maxY = _vertices[i].Coord.Y;
\r
1334 if (_vertices[i].Coord.Z < minZ)
\r
1335 minZ = _vertices[i].Coord.Z;
\r
1336 else if (_vertices[i].Coord.Z > maxZ)
\r
1337 maxZ = _vertices[i].Coord.Z;
\r
1340 // Calculate the center-point from the bounding box edges
\r
1341 RenderData.Center = new Vector3((minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2);
\r
1343 // Generate the index array
\r
1344 RenderData.Indices = new ushort[_numFaces * 3];
\r
1346 for (int i = 0; i < _numFaces; i++)
\r
1348 RenderData.Indices[current++] = (ushort)_faces[i].Indices[0];
\r
1349 RenderData.Indices[current++] = (ushort)_faces[i].Indices[1];
\r
1350 RenderData.Indices[current++] = (ushort)_faces[i].Indices[2];
\r
1353 // Generate the texcoord array
\r
1354 RenderData.TexCoords = new float[_numVertices * 2];
\r
1356 for (int i = 0; i < _numVertices; i++)
\r
1358 RenderData.TexCoords[current++] = _vertices[i].TexCoord.X;
\r
1359 RenderData.TexCoords[current++] = _vertices[i].TexCoord.Y;
\r
1362 RenderData.weights = new float[_numVertices];
\r
1363 for (int i = 0; i < _numVertices; i++)
\r
1365 RenderData.weights[i] = _vertices[i].Weight;
\r
1368 RenderData.skinJoints = new string[_skinJoints.Length + 3];
\r
1369 for (int i = 1; i < _skinJoints.Length; i++)
\r
1371 RenderData.skinJoints[i] = _skinJoints[i];
\r
1377 public override void LoadLODMesh(int level, string filename)
\r
1379 LODMesh lod = new LODMesh();
\r
1380 lod.LoadMesh(filename);
\r
1381 _lodMeshes[level] = lod;
\r
1384 public void applyjointweights()
\r
1387 /*Each weight actually contains two pieces of information.
\r
1388 * The number to the left of the decimal point is the index of the joint and also
\r
1389 * implicitly indexes to the following joint. The actual weight is to the right of
\r
1390 * the decimal point and interpolates between these two joints. The index is into an
\r
1391 * "expanded" list of joints, not just a linear array of the joints as defined in the
\r
1392 * skeleton file. In particular, any joint that has more than one child will be repeated
\r
1393 * in the list for each of its children.
\r
1396 float weight = -9999;
\r
1397 int jointindex = 0;
\r
1403 for (int v = 0, x = 0; v < RenderData.Vertices.Length; v = v + 3, x++)
\r
1405 if (weight != RenderData.weights[x])
\r
1408 jointindex = (int)Math.Floor(weight = RenderData.weights[x]);
\r
1409 factor = RenderData.weights[x] - jointindex;
\r
1410 weight = weight - jointindex;
\r
1412 string jointname = "", jointname2 = "";
\r
1414 if (this.Name == "upperBodyMesh")
\r
1416 jointname = skeleton.mUpperMeshMapping[jointindex];
\r
1418 jointname2 = skeleton.mUpperMeshMapping[jointindex];
\r
1420 else if (Name == "lowerBodyMesh")
\r
1422 jointname = skeleton.mLowerMeshMapping[jointindex];
\r
1424 jointname2 = skeleton.mLowerMeshMapping[jointindex];
\r
1426 else if (Name == "headMesh")
\r
1428 jointname = skeleton.mHeadMeshMapping[jointindex];
\r
1430 jointname2 = skeleton.mHeadMeshMapping[jointindex];
\r
1434 return; // not interested in this mesh
\r
1438 if (jointname == "")
\r
1440 //Don't yet handle this, its a split joint to two children
\r
1441 ba = av.skel.mBones[jointname2];
\r
1449 ba = av.skel.mBones[jointname];
\r
1452 if (jointname2 == "")
\r
1458 bb = av.skel.mBones[jointname2];
\r
1462 //Special cases 0 is not used
\r
1463 // ON upper torso 5 and 10 are not used
\r
1464 // 4 is neck and 6 and 11 are the left and right collar bones
\r
1468 Quaternion rot = ba.getTotalRotation();
\r
1472 lerp = Vector3.Lerp(ba.getDeltaOffset(), bb.getDeltaOffset(), weight);
\r
1473 offset = Vector3.Lerp(ba.getTotalOffset(), bb.getTotalOffset(), weight);
\r
1477 lerp = ba.getDeltaOffset();
\r
1478 offset = ba.getTotalOffset();
\r
1479 rot = ba.getTotalRotation();
\r
1482 Vector3 pos = new Vector3(MorphRenderData.Vertices[v], MorphRenderData.Vertices[v + 1], MorphRenderData.Vertices[v + 2]);
\r
1484 //move back to mesh local coords
\r
1485 pos = pos - offset;
\r
1486 // apply LERPd offset
\r
1488 // rotate our offset by total rotation
\r
1490 //move back to avatar local coords
\r
1491 pos = pos + offset;
\r
1493 RenderData.Vertices[v] = pos.X;
\r
1494 RenderData.Vertices[v + 1] = pos.Y;
\r
1495 RenderData.Vertices[v + 2] = pos.Z;
\r
1499 public void resetallmorphs()
\r
1501 for (int i = 0; i < OrigRenderData.Vertices.Length / 3; i++)
\r
1504 MorphRenderData.Vertices[i * 3] = OrigRenderData.Vertices[i * 3];
\r
1505 MorphRenderData.Vertices[(i * 3) + 1] = OrigRenderData.Vertices[i * 3 + 1];
\r
1506 MorphRenderData.Vertices[(i * 3) + 2] = OrigRenderData.Vertices[i * 3 + 2];
\r
1508 //MorphRenderData.Normals[i * 3] = OrigRenderData.Normals[i * 3];
\r
1509 //MorphRenderData.Normals[(i * 3) + 1] = OrigRenderData.Normals[i * 3 + 1];
\r
1510 //MorphRenderData.Normals[(i * 3) + 2] = OrigRenderData.Normals[i * 3 + 2];
\r
1512 RenderData.TexCoords[i * 2] = OrigRenderData.TexCoords[i * 2];
\r
1513 RenderData.TexCoords[(i * 2) + 1] = OrigRenderData.TexCoords[i * 2 + 1];
\r
1519 public void morphmesh(Morph morph, float weight)
\r
1521 for (int v = 0; v < morph.NumVertices; v++)
\r
1523 MorphVertex mvx = morph.Vertices[v];
\r
1525 uint i = mvx.VertexIndex;
\r
1527 MorphRenderData.Vertices[i * 3] = MorphRenderData.Vertices[i * 3] + mvx.Coord.X * weight;
\r
1528 MorphRenderData.Vertices[(i * 3) + 1] = MorphRenderData.Vertices[i * 3 + 1] + mvx.Coord.Y * weight;
\r
1529 MorphRenderData.Vertices[(i * 3) + 2] = MorphRenderData.Vertices[i * 3 + 2] + mvx.Coord.Z * weight;
\r
1531 //MorphRenderData.Normals[i * 3] = MorphRenderData.Normals[i * 3] + mvx.Normal.X * weight;
\r
1532 //MorphRenderData.Normals[(i * 3)+1] = MorphRenderData.Normals[(i * 3)+1] + mvx.Normal.Y * weight;
\r
1533 //MorphRenderData.Normals[(i * 3)+2] = MorphRenderData.Normals[(i * 3)+2] + mvx.Normal.Z * weight;
\r
1535 RenderData.TexCoords[i * 2] = OrigRenderData.TexCoords[i * 2] + mvx.TexCoord.X * weight;
\r
1536 RenderData.TexCoords[(i * 2) + 1] = OrigRenderData.TexCoords[i * 2 + 1] + mvx.TexCoord.Y * weight;
\r
1542 public class GLAvatar
\r
1544 private static Dictionary<string, GLMesh> _defaultmeshes = new Dictionary<string, GLMesh>();
\r
1545 public Dictionary<string, GLMesh> _meshes = new Dictionary<string, GLMesh>();
\r
1547 public skeleton skel = new skeleton();
\r
1548 public static Dictionary<int, attachment_point> attachment_points = new Dictionary<int, attachment_point>();
\r
1550 public bool _wireframe = true;
\r
1551 public bool _showSkirt = false;
\r
1553 public VisualParamEx.EparamSex msex;
\r
1555 public byte[] VisualAppearanceParameters = new byte[1024];
\r
1556 bool vpsent = false;
\r
1557 static bool lindenMeshesLoaded = false;
\r
1561 foreach (KeyValuePair<string, GLMesh> kvp in _defaultmeshes)
\r
1563 GLMesh mesh = new GLMesh(kvp.Value, this); // Instance our meshes
\r
1564 _meshes.Add(kvp.Key, mesh);
\r
1569 public static void dumptweaks()
\r
1572 for (int x = 0; x < VisualParamEx.tweakable_params.Count; x++)
\r
1574 VisualParamEx vpe = (VisualParamEx)VisualParamEx.tweakable_params.GetByIndex(x);
\r
1575 Console.WriteLine(string.Format("{0} is {1}", x, vpe.Name));
\r
1581 public static void loadlindenmeshes2(string LODfilename)
\r
1583 // Already have mashes loaded?
\r
1584 if (lindenMeshesLoaded) return;
\r
1586 attachment_points.Clear();
\r
1589 string basedir = Directory.GetCurrentDirectory() + System.IO.Path.DirectorySeparatorChar + "character" + System.IO.Path.DirectorySeparatorChar;
\r
1591 XmlDocument lad = new XmlDocument();
\r
1592 lad.Load(basedir + LODfilename);
\r
1594 //Firstly read the skeleton section this contains attachment point info and the bone deform info for visual params
\r
1595 // And load the skeleton file in to the bones class
\r
1597 XmlNodeList skeleton = lad.GetElementsByTagName("skeleton");
\r
1598 string skeletonfilename = skeleton[0].Attributes.GetNamedItem("file_name").Value;
\r
1599 Bone.loadbones(skeletonfilename);
\r
1601 // Next read all the skeleton child nodes, we have attachment points and bone deform params
\r
1602 // attachment points are an offset and rotation from a bone location
\r
1603 // the name of the bone they reference is the joint paramater
\r
1604 // params in the skeleton nodes are bone deforms, eg leg length changes the scale of the leg bones
\r
1606 foreach (XmlNode skeletonnode in skeleton[0].ChildNodes)
\r
1608 if (skeletonnode.Name == "attachment_point")
\r
1610 attachment_point point = new attachment_point(skeletonnode);
\r
1611 attachment_points.Add(point.id, point);
\r
1614 if (skeletonnode.Name == "param")
\r
1616 //Bone deform param
\r
1617 VisualParamEx vp = new VisualParamEx(skeletonnode, VisualParamEx.ParamType.TYPE_BONEDEFORM);
\r
1621 //Now we parse the mesh nodes, mesh nodes reference a particular LLM file with a LOD
\r
1622 //and also list VisualParams for the various mesh morphs that can be applied
\r
1624 XmlNodeList meshes = lad.GetElementsByTagName("mesh");
\r
1625 foreach (XmlNode meshNode in meshes)
\r
1627 string type = meshNode.Attributes.GetNamedItem("type").Value;
\r
1628 int lod = Int32.Parse(meshNode.Attributes.GetNamedItem("lod").Value);
\r
1629 string fileName = meshNode.Attributes.GetNamedItem("file_name").Value;
\r
1631 GLMesh mesh = (_defaultmeshes.ContainsKey(type) ? _defaultmeshes[type] : new GLMesh(type));
\r
1633 if (meshNode.HasChildNodes)
\r
1635 foreach (XmlNode paramnode in meshNode.ChildNodes)
\r
1637 if (paramnode.Name == "param")
\r
1639 VisualParamEx vp = new VisualParamEx(paramnode, VisualParamEx.ParamType.TYPE_MORPH);
\r
1641 mesh._evp.Add(vp.ParamID, vp); //Not sure we really need this may optimise out later
\r
1642 vp.morphmesh = mesh.Name;
\r
1647 // Set up the texture elemenets for each mesh
\r
1648 // And hack the eyeball position
\r
1649 switch (mesh.Name)
\r
1651 case "lowerBodyMesh":
\r
1652 mesh.teFaceID = (int)AvatarTextureIndex.LowerBaked;
\r
1655 case "upperBodyMesh":
\r
1656 mesh.teFaceID = (int)AvatarTextureIndex.UpperBaked;
\r
1660 mesh.teFaceID = (int)AvatarTextureIndex.HeadBaked;
\r
1664 mesh.teFaceID = (int)AvatarTextureIndex.HairBaked;
\r
1667 case "eyelashMesh":
\r
1668 mesh.teFaceID = (int)AvatarTextureIndex.HeadBaked;
\r
1671 case "eyeBallRightMesh":
\r
1672 mesh.setMeshPos(Bone.mBones["mEyeLeft"].getTotalOffset());
\r
1673 //mesh.setMeshRot(Bone.getRotation("mEyeLeft"));
\r
1674 mesh.teFaceID = (int)AvatarTextureIndex.EyesBaked;
\r
1677 case "eyeBallLeftMesh":
\r
1678 mesh.setMeshPos(Bone.mBones["mEyeRight"].getTotalOffset());
\r
1679 //mesh.setMeshRot(Bone.getRotation("mEyeRight"));
\r
1680 mesh.teFaceID = (int)AvatarTextureIndex.EyesBaked;
\r
1684 mesh.teFaceID = (int)AvatarTextureIndex.SkirtBaked;
\r
1688 mesh.teFaceID = 0;
\r
1693 mesh.LoadMesh(basedir + fileName);
\r
1695 mesh.LoadLODMesh(lod, basedir + fileName);
\r
1697 _defaultmeshes[type] = mesh;
\r
1701 // Next are the textureing params, skipping for the moment
\r
1703 XmlNodeList colors = lad.GetElementsByTagName("global_color");
\r
1705 foreach (XmlNode globalcolornode in colors)
\r
1707 foreach (XmlNode node in globalcolornode.ChildNodes)
\r
1709 if (node.Name == "param")
\r
1711 VisualParamEx vp = new VisualParamEx(node, VisualParamEx.ParamType.TYPE_COLOR);
\r
1717 // Get layer paramaters, a bit of a verbose way to do it but we probably want to get access
\r
1718 // to some of the other data not just the <param> tag
\r
1720 XmlNodeList layer_sets = lad.GetElementsByTagName("layer_set");
\r
1722 foreach (XmlNode layer_set in layer_sets)
\r
1724 foreach (XmlNode layer in layer_set.ChildNodes)
\r
1726 foreach (XmlNode layernode in layer.ChildNodes)
\r
1728 if (layernode.Name == "param")
\r
1730 VisualParamEx vp = new VisualParamEx(layernode, VisualParamEx.ParamType.TYPE_COLOR);
\r
1737 // Next are the driver parameters, these are parameters that change multiple real parameters
\r
1739 XmlNodeList drivers = lad.GetElementsByTagName("driver_parameters");
\r
1741 foreach (XmlNode node in drivers[0].ChildNodes) //lazy
\r
1743 if (node.Name == "param")
\r
1745 VisualParamEx vp = new VisualParamEx(node, VisualParamEx.ParamType.TYPE_DRIVER);
\r
1749 lindenMeshesLoaded = true;
\r
1752 public void morphtest(Avatar av, int param, float weight)
\r
1754 VisualParamEx vpx;
\r
1755 if (VisualParamEx.allParams.TryGetValue(param, out vpx))
\r
1758 //Logger.Log(string.Format("Applying visual parameter {0} id {1} value {2}", vpx.Name, vpx.ParamID, weight), Helpers.LogLevel.Info);
\r
1760 //weight = weight * 2.0f;
\r
1761 //weight=weight-1.0f;
\r
1769 float value = vpx.MinValue + ((vpx.MaxValue - vpx.MinValue) * weight);
\r
1771 if (vpx.pType == VisualParamEx.ParamType.TYPE_MORPH)
\r
1775 if (_meshes.TryGetValue(vpx.morphmesh, out mesh))
\r
1777 foreach (LindenMesh.Morph morph in mesh.Morphs) //optimise me to a dictionary
\r
1779 if (morph.Name == vpx.Name)
\r
1781 if (mesh.Name == "skirtMesh" && _showSkirt == false)
\r
1784 mesh.morphmesh(morph, value);
\r
1792 // Not a mesh morph
\r
1794 // Its a volume deform, these appear to be related to collision volumes
\r
1796 if (vpx.VolumeDeforms == null)
\r
1798 Logger.Log(String.Format("paramater {0} has invalid mesh {1}", param, vpx.morphmesh), Helpers.LogLevel.Warning);
\r
1802 foreach (KeyValuePair<string, VisualParamEx.VolumeDeform> kvp in vpx.VolumeDeforms)
\r
1804 skel.deformbone(kvp.Key, kvp.Value.pos, kvp.Value.scale);
\r
1814 // Its not a morph, it might be a driver though
\r
1815 if (vpx.pType == VisualParamEx.ParamType.TYPE_DRIVER)
\r
1817 foreach (VisualParamEx.driven child in vpx.childparams)
\r
1819 morphtest(av, child.id, weight); //TO DO use minmax if they are present
\r
1824 //Is this a bone deform?
\r
1825 if (vpx.pType == VisualParamEx.ParamType.TYPE_BONEDEFORM)
\r
1827 foreach (KeyValuePair<string, Vector3> kvp in vpx.BoneDeforms)
\r
1829 skel.deformbone(kvp.Key, new Vector3(0, 0, 0), kvp.Value * value, Quaternion.Identity);
\r
1835 //Logger.Log(String.Format("paramater {0} is not a morph and not a driver", param), Helpers.LogLevel.Warning);
\r
1842 Logger.Log("Invalid paramater " + param.ToString(), Helpers.LogLevel.Warning);
\r
1846 public void morph(Avatar av)
\r
1849 if (av.VisualParameters == null)
\r
1852 ThreadPool.QueueUserWorkItem(sync =>
\r
1856 if (av.VisualParameters.Length > 123)
\r
1858 if (av.VisualParameters[31] > 127)
\r
1860 msex = VisualParamEx.EparamSex.SEX_MALE;
\r
1864 msex = VisualParamEx.EparamSex.SEX_FEMALE;
\r
1868 foreach (GLMesh mesh in _meshes.Values)
\r
1870 mesh.resetallmorphs();
\r
1873 foreach (byte vpvalue in av.VisualParameters)
\r
1876 if (vpsent == true && VisualAppearanceParameters[x] == vpvalue)
\r
1884 VisualAppearanceParameters[x] = vpvalue;
\r
1886 if (x >= VisualParamEx.tweakable_params.Count)
\r
1888 //Logger.Log("Two many visual paramaters in Avatar appearance", Helpers.LogLevel.Warning);
\r
1892 VisualParamEx vpe = (VisualParamEx)VisualParamEx.tweakable_params.GetByIndex(x);
\r
1894 if (vpe.sex != VisualParamEx.EparamSex.SEX_BOTH && vpe.sex != msex)
\r
1900 float value = (vpvalue / 255.0f);
\r
1901 this.morphtest(av, vpe.ParamID, value);
\r
1910 foreach (GLMesh mesh in _meshes.Values)
\r
1912 mesh.applyjointweights();
\r
1918 public class RenderAvatar : SceneObject
\r
1920 public static readonly BoundingVolume AvatarBoundingVolume;
\r
1922 // Static constructor
\r
1923 static RenderAvatar()
\r
1925 AvatarBoundingVolume = new BoundingVolume();
\r
1926 // Bounding sphere for avatar is 1m in diametar
\r
1927 // Bounding box 1m cube
\r
1928 // These values get scaled with Avatar.Scale by the time we perform culling
\r
1929 AvatarBoundingVolume.R = 1f;
\r
1930 AvatarBoundingVolume.Min = new Vector3(-0.5f, -0.5f, -0.5f);
\r
1931 AvatarBoundingVolume.Max = new Vector3(0.5f, 0.5f, 0.5f);
\r
1934 // Default constructor
\r
1935 public RenderAvatar()
\r
1937 BoundingVolume = AvatarBoundingVolume;
\r
1938 Type = SceneObjectType.Avatar;
\r
1941 public override Primitive BasePrim
\r
1943 get { return avatar; }
\r
1944 set { if (value is Avatar) avatar = (Avatar)value; }
\r
1947 public override void Step(float time)
\r
1952 public GLAvatar glavatar = new GLAvatar();
\r
1953 public Avatar avatar;
\r
1954 public FaceData[] data = new FaceData[32];
\r
1955 public Dictionary<UUID, Animation> animlist = new Dictionary<UUID, Animation>();
\r
1956 public Dictionary<WearableType, AppearanceManager.WearableData> Wearables = new Dictionary<WearableType, AppearanceManager.WearableData>();
\r
1960 public class skeleton
\r
1962 public Dictionary<string, Bone> mBones;
\r
1963 public Dictionary<string, int> mPriority = new Dictionary<string, int>();
\r
1964 public static Dictionary<int, string> mUpperMeshMapping = new Dictionary<int, string>();
\r
1965 public static Dictionary<int, string> mLowerMeshMapping = new Dictionary<int, string>();
\r
1966 public static Dictionary<int, string> mHeadMeshMapping = new Dictionary<int, string>();
\r
1968 public List<BinBVHAnimationReader> mAnimations = new List<BinBVHAnimationReader>();
\r
1970 public static Dictionary<UUID, RenderAvatar> mAnimationTransactions = new Dictionary<UUID, RenderAvatar>();
\r
1972 public static Dictionary<UUID, BinBVHAnimationReader> mAnimationCache = new Dictionary<UUID, BinBVHAnimationReader>();
\r
1974 public bool mNeedsUpdate = false;
\r
1975 public bool mNeedsMeshRebuild = false;
\r
1977 public Bone mLeftEye = null;
\r
1978 public Bone mRightEye = null;
\r
1980 public struct binBVHJointState
\r
1982 public float currenttime_rot;
\r
1983 public int lastkeyframe_rot;
\r
1984 public int nextkeyframe_rot;
\r
1986 public float currenttime_pos;
\r
1987 public int lastkeyframe_pos;
\r
1988 public int nextkeyframe_pos;
\r
1990 public int loopinframe;
\r
1991 public int loopoutframe;
\r
2000 mBones = new Dictionary<string, Bone>();
\r
2002 foreach (Bone src in Bone.mBones.Values)
\r
2004 Bone newbone = new Bone(src);
\r
2005 mBones.Add(newbone.name, newbone);
\r
2008 //rebuild the skeleton structure on the new copy
\r
2009 foreach (Bone src in mBones.Values)
\r
2011 if (src.mParentBone != null)
\r
2013 src.parent = mBones[src.mParentBone];
\r
2014 src.parent.children.Add(src);
\r
2019 if (mUpperMeshMapping.Count == 0)
\r
2021 mUpperMeshMapping.Add(1, "mPelvis");
\r
2022 mUpperMeshMapping.Add(2, "mTorso");
\r
2023 mUpperMeshMapping.Add(3, "mChest");
\r
2024 mUpperMeshMapping.Add(4, "mNeck");
\r
2025 mUpperMeshMapping.Add(5, "");
\r
2026 mUpperMeshMapping.Add(6, "mCollarLeft");
\r
2027 mUpperMeshMapping.Add(7, "mShoulderLeft");
\r
2028 mUpperMeshMapping.Add(8, "mElbowLeft");
\r
2029 mUpperMeshMapping.Add(9, "mWristLeft");
\r
2030 mUpperMeshMapping.Add(10, "");
\r
2031 mUpperMeshMapping.Add(11, "mCollarRight");
\r
2032 mUpperMeshMapping.Add(12, "mShoulderRight");
\r
2033 mUpperMeshMapping.Add(13, "mElbowRight");
\r
2034 mUpperMeshMapping.Add(14, "mWristRight");
\r
2035 mUpperMeshMapping.Add(15, "");
\r
2037 mLowerMeshMapping.Add(1, "mPelvis");
\r
2038 mLowerMeshMapping.Add(2, "mHipRight");
\r
2039 mLowerMeshMapping.Add(3, "mKneeRight");
\r
2040 mLowerMeshMapping.Add(4, "mAnkleRight");
\r
2041 mLowerMeshMapping.Add(5, "");
\r
2042 mLowerMeshMapping.Add(6, "mHipLeft");
\r
2043 mLowerMeshMapping.Add(7, "mKneeLeft");
\r
2044 mLowerMeshMapping.Add(8, "mAnkleLeft");
\r
2045 mLowerMeshMapping.Add(9, "");
\r
2047 mHeadMeshMapping.Add(1, "mNeck");
\r
2048 mHeadMeshMapping.Add(2, "mHead");
\r
2049 mHeadMeshMapping.Add(3, "");
\r
2053 mLeftEye = mBones["mEyeLeft"];
\r
2054 mRightEye = mBones["mEyeRight"];
\r
2058 public void deformbone(string name, Vector3 pos, Vector3 scale, Quaternion rotation)
\r
2061 if (mBones.TryGetValue(name, out bone))
\r
2063 bone.deformbone(pos, scale, rotation);
\r
2067 //TODO check offset and rot calcuations should each offset be multiplied by its parent rotation in
\r
2068 // a standard child/parent rot/offset way?
\r
2069 public Vector3 getOffset(string bonename)
\r
2072 if (mBones.TryGetValue(bonename, out b))
\r
2074 return (b.getTotalOffset());
\r
2078 return Vector3.Zero;
\r
2082 public Quaternion getRotation(string bonename)
\r
2085 if (mBones.TryGetValue(bonename, out b))
\r
2087 return (b.getTotalRotation());
\r
2091 return Quaternion.Identity;
\r
2096 public void flushanimations()
\r
2098 lock (mAnimations)
\r
2100 mAnimations.Clear();
\r
2104 // Add animations to the global decoded list
\r
2105 // TODO garbage collect unused animations somehow
\r
2106 public static void addanimation(OpenMetaverse.Assets.Asset asset,UUID tid, BinBVHAnimationReader b)
\r
2109 mAnimationTransactions.TryGetValue(tid, out av);
\r
2113 mAnimationTransactions.Remove(tid);
\r
2115 if (asset != null)
\r
2117 b = new BinBVHAnimationReader(asset.AssetData);
\r
2118 mAnimationCache[asset.AssetID] = b;
\r
2119 Logger.Log("Adding new decoded animaton known animations " + asset.AssetID.ToString(), Helpers.LogLevel.Info);
\r
2123 foreach (binBVHJoint joint in b.joints)
\r
2125 binBVHJointState state;
\r
2127 state.lastkeyframe_rot = 0;
\r
2128 state.nextkeyframe_rot = 1;
\r
2130 state.lastkeyframe_pos = 0;
\r
2131 state.nextkeyframe_pos = 1;
\r
2133 state.currenttime_rot = 0;
\r
2134 state.currenttime_pos = 0;
\r
2136 state.loopinframe = 0;
\r
2137 state.loopoutframe = joint.rotationkeys.Length - 1;
\r
2139 if (b.Loop == true)
\r
2142 foreach( binBVHJointKey key in joint.rotationkeys)
\r
2144 if (key.time == b.InPoint)
\r
2146 state.loopinframe = frame;
\r
2149 if (key.time == b.OutPoint)
\r
2151 state.loopoutframe = frame;
\r
2160 b.joints[pos].Tag = state;
\r
2164 lock (av.glavatar.skel.mAnimations)
\r
2166 av.glavatar.skel.mAnimations.Add(b);
\r
2170 public void animate(float lastframetime)
\r
2172 mPriority.Clear();
\r
2175 foreach (BinBVHAnimationReader b in mAnimations)
\r
2181 foreach (binBVHJoint joint in b.joints)
\r
2185 //Quick hack to stack animations in the correct order
\r
2186 //TODO we need to do this per joint as they all have their own priorities as well ;-(
\r
2187 if (mPriority.TryGetValue(joint.Name, out prio))
\r
2189 if (prio > b.Priority)
\r
2193 mPriority[joint.Name] = b.Priority;
\r
2195 binBVHJointState state = (binBVHJointState) b.joints[jpos].Tag;
\r
2197 state.currenttime_rot += lastframetime;
\r
2198 state.currenttime_pos += lastframetime;
\r
2201 if (b.joints[jpos].rotationkeys.Length == 1)
\r
2203 state.nextkeyframe_rot = 0;
\r
2206 Vector3 poslerp = Vector3.Zero;
\r
2208 if (b.joints[jpos].positionkeys.Length > 2)
\r
2210 binBVHJointKey pos2 = b.joints[jpos].positionkeys[state.nextkeyframe_pos];
\r
2213 if (state.currenttime_pos > pos2.time)
\r
2215 state.lastkeyframe_pos++;
\r
2216 state.nextkeyframe_pos++;
\r
2218 if (state.nextkeyframe_pos >= b.joints[jpos].positionkeys.Length || (state.nextkeyframe_pos >=state.loopoutframe && b.Loop==true))
\r
2220 if (b.Loop == true)
\r
2222 state.nextkeyframe_pos = state.loopinframe;
\r
2223 state.currenttime_pos = b.InPoint;
\r
2225 if (state.lastkeyframe_pos >= b.joints[jpos].positionkeys.Length)
\r
2227 state.lastkeyframe_pos = state.loopinframe;
\r
2232 state.nextkeyframe_pos = joint.positionkeys.Length - 1;
\r
2238 if (state.lastkeyframe_pos >= b.joints[jpos].positionkeys.Length)
\r
2240 if (b.Loop == true)
\r
2242 state.lastkeyframe_pos = 0;
\r
2243 state.currenttime_pos = 0;
\r
2248 state.lastkeyframe_pos = joint.positionkeys.Length - 1;
\r
2249 if (state.lastkeyframe_pos < 0)//eeww
\r
2250 state.lastkeyframe_pos = 0;
\r
2255 binBVHJointKey pos = b.joints[jpos].positionkeys[state.lastkeyframe_pos];
\r
2258 float delta = (pos2.time - pos.time) / ((state.currenttime_pos) - (pos.time - b.joints[jpos].positionkeys[0].time));
\r
2266 poslerp = Vector3.Lerp(pos.key_element, pos2.key_element, delta) *-1;
\r
2271 Vector3 rotlerp = Vector3.Zero;
\r
2272 if (b.joints[jpos].rotationkeys.Length > 0)
\r
2274 binBVHJointKey rot2 = b.joints[jpos].rotationkeys[state.nextkeyframe_rot];
\r
2276 if (state.currenttime_rot > rot2.time)
\r
2278 state.lastkeyframe_rot++;
\r
2279 state.nextkeyframe_rot++;
\r
2281 if (state.nextkeyframe_rot >= b.joints[jpos].rotationkeys.Length || (state.nextkeyframe_rot >= state.loopoutframe && b.Loop == true))
\r
2283 if (b.Loop == true)
\r
2285 state.nextkeyframe_rot = state.loopinframe;
\r
2286 state.currenttime_rot = b.InPoint;
\r
2288 if (state.lastkeyframe_rot >= b.joints[jpos].rotationkeys.Length)
\r
2290 state.lastkeyframe_rot = state.loopinframe;
\r
2298 state.nextkeyframe_rot = joint.rotationkeys.Length - 1;
\r
2302 if (state.lastkeyframe_rot >= b.joints[jpos].rotationkeys.Length)
\r
2304 if (b.Loop == true)
\r
2306 state.lastkeyframe_rot = 0;
\r
2307 state.currenttime_rot = 0;
\r
2312 state.lastkeyframe_rot = joint.rotationkeys.Length - 1;
\r
2317 binBVHJointKey rot = b.joints[jpos].rotationkeys[state.lastkeyframe_rot];
\r
2318 rot2 = b.joints[jpos].rotationkeys[state.nextkeyframe_rot];
\r
2320 float deltarot = (rot2.time - rot.time) / ((state.currenttime_rot) - (rot.time - b.joints[jpos].rotationkeys[0].time));
\r
2329 rotlerp = Vector3.Lerp(rot.key_element, rot2.key_element, deltarot);
\r
2333 b.joints[jpos].Tag = (object)state;
\r
2335 deformbone(joint.Name, poslerp, new Vector3(0, 0, 0), new Quaternion(rotlerp.X, rotlerp.Y, rotlerp.Z));
\r
2340 mNeedsMeshRebuild = true;
\r
2343 mNeedsUpdate = false;
\r
2349 public string name;
\r
2350 public Vector3 pos;
\r
2351 public Quaternion rot;
\r
2352 public Vector3 scale;
\r
2353 public Vector3 piviot;
\r
2355 public Vector3 orig_pos;
\r
2356 public Quaternion orig_rot;
\r
2357 public Vector3 orig_scale;
\r
2358 public Vector3 orig_piviot;
\r
2360 Matrix4 mDeformMatrix = Matrix4.Identity;
\r
2362 public Bone parent;
\r
2364 public List<Bone> children = new List<Bone>();
\r
2366 public static Dictionary<string, Bone> mBones = new Dictionary<string, Bone>();
\r
2367 public static Dictionary<int, Bone> mIndexedBones = new Dictionary<int, Bone>();
\r
2368 static int boneaddindex = 0;
\r
2370 private bool rotdirty = true;
\r
2371 private bool posdirty = true;
\r
2373 private Vector3 mTotalPos;
\r
2374 private Quaternion mTotalRot;
\r
2376 private Vector3 mDeltaPos;
\r
2377 private Quaternion mDeltaRot;
\r
2379 public string mParentBone = null;
\r
2385 public Bone(Bone source)
\r
2387 name = String.Copy(source.name);
\r
2388 pos = new Vector3(source.pos);
\r
2389 rot = new Quaternion(source.rot);
\r
2390 scale = new Vector3(source.scale);
\r
2391 piviot = new Vector3(source.piviot);
\r
2393 orig_piviot = source.orig_piviot;
\r
2394 orig_pos = source.orig_pos;
\r
2395 orig_rot = source.orig_rot;
\r
2396 orig_scale = source.orig_scale;
\r
2398 mParentBone = source.mParentBone;
\r
2400 mDeformMatrix = new Matrix4(source.mDeformMatrix);
\r
2403 public static void loadbones(string skeletonfilename)
\r
2406 string basedir = Directory.GetCurrentDirectory() + System.IO.Path.DirectorySeparatorChar + "character" + System.IO.Path.DirectorySeparatorChar;
\r
2407 XmlDocument skeleton = new XmlDocument();
\r
2408 skeleton.Load(basedir + skeletonfilename);
\r
2409 XmlNode boneslist = skeleton.GetElementsByTagName("linden_skeleton")[0];
\r
2410 addbone(boneslist.ChildNodes[0], null);
\r
2413 public static void addbone(XmlNode bone, Bone parent)
\r
2416 if (bone.Name != "bone")
\r
2419 Bone b = new Bone();
\r
2420 b.name = bone.Attributes.GetNamedItem("name").Value;
\r
2422 string pos = bone.Attributes.GetNamedItem("pos").Value;
\r
2423 string[] posparts = pos.Split(' ');
\r
2424 b.pos = new Vector3(float.Parse(posparts[0]), float.Parse(posparts[1]), float.Parse(posparts[2]));
\r
2425 b.orig_pos = new Vector3(b.pos);
\r
2427 string rot = bone.Attributes.GetNamedItem("rot").Value;
\r
2428 string[] rotparts = rot.Split(' ');
\r
2429 b.rot = 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));
\r
2430 b.orig_rot = new Quaternion(b.rot);
\r
2432 string scale = bone.Attributes.GetNamedItem("scale").Value;
\r
2433 string[] scaleparts = scale.Split(' ');
\r
2434 b.scale = new Vector3(float.Parse(scaleparts[0]), float.Parse(scaleparts[1]), float.Parse(scaleparts[2]));
\r
2435 b.orig_scale = new Vector3(b.scale);
\r
2437 float[] deform = Math3D.CreateSRTMatrix(new Vector3(1, 1, 1), b.rot, b.orig_pos);
\r
2438 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]);
\r
2442 b.parent = parent;
\r
2443 if (parent != null)
\r
2445 b.mParentBone = parent.name;
\r
2446 parent.children.Add(b);
\r
2449 mBones.Add(b.name, b);
\r
2450 mIndexedBones.Add(boneaddindex++, b);
\r
2452 Logger.Log("Found bone " + b.name, Helpers.LogLevel.Info);
\r
2454 foreach (XmlNode childbone in bone.ChildNodes)
\r
2456 addbone(childbone, b);
\r
2461 public void deformbone(Vector3 pos, Vector3 scale, Quaternion rot)
\r
2463 //float[] deform = Math3D.CreateSRTMatrix(scale, rot, this.orig_pos);
\r
2464 //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]);
\r
2465 this.pos = Bone.mBones[name].orig_pos + pos;
\r
2466 this.scale = Bone.mBones[name].orig_scale + scale;
\r
2467 this.rot = Bone.mBones[name].orig_rot * rot;
\r
2472 // If we deform a bone mark this bone and all its children as dirty.
\r
2473 public void markdirty()
\r
2477 foreach (Bone childbone in children)
\r
2479 childbone.markdirty();
\r
2483 public Matrix4 getdeform()
\r
2485 if (this.parent != null)
\r
2487 return mDeformMatrix * parent.getdeform();
\r
2491 return mDeformMatrix;
\r
2495 private Vector3 getOffset()
\r
2497 if (parent != null)
\r
2499 Quaternion totalrot = getParentRot(); // we don't want this joints rotation included
\r
2500 Vector3 parento = parent.getOffset();
\r
2501 Vector3 mepre = pos * scale;
\r
2502 mepre = mepre * totalrot;
\r
2503 mTotalPos = parento + mepre;
\r
2505 Vector3 orig = getOrigOffset();
\r
2506 mDeltaPos = mTotalPos - orig;
\r
2514 Vector3 orig = getOrigOffset();
\r
2515 mTotalPos = (pos * scale);
\r
2516 mDeltaPos = mTotalPos - orig;
\r
2523 public Vector3 getMyOffset()
\r
2525 return pos * scale;
\r
2528 // Try to save some cycles by not recalculating positions and rotations every time
\r
2529 public Vector3 getTotalOffset()
\r
2531 if (posdirty == false)
\r
2537 return getOffset();
\r
2541 public Vector3 getDeltaOffset()
\r
2543 if (posdirty == false)
\r
2554 private Vector3 getOrigOffset()
\r
2556 if (parent != null)
\r
2558 return (parent.getOrigOffset() + orig_pos);
\r
2566 private static Quaternion getRotation(string bonename)
\r
2569 if (mBones.TryGetValue(bonename, out b))
\r
2571 return (b.getRotation());
\r
2575 return Quaternion.Identity;
\r
2580 private Quaternion getParentRot()
\r
2582 Quaternion totalrot = Quaternion.Identity;
\r
2584 if (parent != null)
\r
2586 totalrot = parent.getRotation();
\r
2593 private Quaternion getRotation()
\r
2595 Quaternion totalrot = rot;
\r
2597 if (parent != null)
\r
2599 totalrot = rot * parent.getRotation();
\r
2602 mTotalRot = totalrot;
\r
2608 public Quaternion getTotalRotation()
\r
2610 if (rotdirty == false)
\r
2616 return getRotation();
\r
2621 public class VisualParamEx
\r
2624 static public Dictionary<int, VisualParamEx> allParams = new Dictionary<int, VisualParamEx>();
\r
2625 static public Dictionary<int, VisualParamEx> deformParams = new Dictionary<int, VisualParamEx>();
\r
2626 static public Dictionary<int, VisualParamEx> morphParams = new Dictionary<int, VisualParamEx>();
\r
2627 static public Dictionary<int, VisualParamEx> drivenParams = new Dictionary<int, VisualParamEx>();
\r
2628 static public SortedList tweakable_params = new SortedList();
\r
2630 public Dictionary<string, Vector3> BoneDeforms = null;
\r
2631 public Dictionary<string, VolumeDeform> VolumeDeforms = null;
\r
2632 public List<driven> childparams = null;
\r
2634 public string morphmesh = null;
\r
2638 VISUAL_PARAM_GROUP_TWEAKABLE = 0,
\r
2639 VISUAL_PARAM_GROUP_ANIMATABLE,
\r
2640 VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT,
\r
2643 public struct VolumeDeform
\r
2645 public string name;
\r
2646 public Vector3 scale;
\r
2647 public Vector3 pos;
\r
2650 public enum EparamSex
\r
2657 public enum ParamType
\r
2666 public struct driven
\r
2669 public float max1;
\r
2670 public float max2;
\r
2671 public float min1;
\r
2672 public float min2;
\r
2673 public bool hasMinMax;
\r
2676 public string meshname;
\r
2678 /// <summary>Index of this visual param</summary>
\r
2679 public int ParamID;
\r
2680 /// <summary>Internal name</summary>
\r
2681 public string Name;
\r
2682 /// <summary>Group ID this parameter belongs to</summary>
\r
2684 /// <summary>Name of the wearable this parameter belongs to</summary>
\r
2685 public string Wearable;
\r
2686 /// <summary>Displayable label of this characteristic</summary>
\r
2687 public string Label;
\r
2688 /// <summary>Displayable label for the minimum value of this characteristic</summary>
\r
2689 public string LabelMin;
\r
2690 /// <summary>Displayable label for the maximum value of this characteristic</summary>
\r
2691 public string LabelMax;
\r
2692 /// <summary>Default value</summary>
\r
2693 public float DefaultValue;
\r
2694 /// <summary>Minimum value</summary>
\r
2695 public float MinValue;
\r
2696 /// <summary>Maximum value</summary>
\r
2697 public float MaxValue;
\r
2698 /// <summary>Is this param used for creation of bump layer?</summary>
\r
2699 public bool IsBumpAttribute;
\r
2700 /// <summary>Alpha blending/bump info</summary>
\r
2701 public VisualAlphaParam? AlphaParams;
\r
2702 /// <summary>Color information</summary>
\r
2703 public VisualColorParam? ColorParams;
\r
2704 /// <summary>Array of param IDs that are drivers for this parameter</summary>
\r
2705 public int[] Drivers;
\r
2706 /// <summary>The Avatar Sex that this parameter applies to</summary>
\r
2707 public EparamSex sex;
\r
2709 public ParamType pType;
\r
2711 public static int count = 0;
\r
2714 /// Set all the values through the constructor
\r
2716 /// <param name="paramID">Index of this visual param</param>
\r
2717 /// <param name="name">Internal name</param>
\r
2718 /// <param name="group"></param>
\r
2719 /// <param name="wearable"></param>
\r
2720 /// <param name="label">Displayable label of this characteristic</param>
\r
2721 /// <param name="labelMin">Displayable label for the minimum value of this characteristic</param>
\r
2722 /// <param name="labelMax">Displayable label for the maximum value of this characteristic</param>
\r
2723 /// <param name="def">Default value</param>
\r
2724 /// <param name="min">Minimum value</param>
\r
2725 /// <param name="max">Maximum value</param>
\r
2726 /// <param name="isBumpAttribute">Is this param used for creation of bump layer?</param>
\r
2727 /// <param name="drivers">Array of param IDs that are drivers for this parameter</param>
\r
2728 /// <param name="alpha">Alpha blending/bump info</param>
\r
2729 /// <param name="colorParams">Color information</param>
\r
2730 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)
\r
2732 ParamID = paramID;
\r
2735 Wearable = wearable;
\r
2737 LabelMin = labelMin;
\r
2738 LabelMax = labelMax;
\r
2739 DefaultValue = def;
\r
2742 IsBumpAttribute = isBumpAttribute;
\r
2743 Drivers = drivers;
\r
2744 AlphaParams = alpha;
\r
2745 ColorParams = colorParams;
\r
2746 sex = EparamSex.SEX_BOTH;
\r
2749 public VisualParamEx(XmlNode node, ParamType pt)
\r
2753 ParamID = Int32.Parse(node.Attributes.GetNamedItem("id").Value);
\r
2754 Name = node.Attributes.GetNamedItem("name").Value;
\r
2755 Group = Int32.Parse(node.Attributes.GetNamedItem("group").Value);
\r
2757 //These dont exist for facal expresion morphs
\r
2758 if (node.Attributes.GetNamedItem("wearable") != null)
\r
2759 Wearable = node.Attributes.GetNamedItem("wearable").Value;
\r
2761 MinValue = float.Parse(node.Attributes.GetNamedItem("value_min").Value);
\r
2762 MaxValue = float.Parse(node.Attributes.GetNamedItem("value_max").Value);
\r
2764 // These do not exists for driven parameters
\r
2765 if (node.Attributes.GetNamedItem("label_min") != null)
\r
2767 LabelMin = node.Attributes.GetNamedItem("label_min").Value;
\r
2770 if (node.Attributes.GetNamedItem("label_max") != null)
\r
2772 LabelMax = node.Attributes.GetNamedItem("label_max").Value;
\r
2775 XmlNode sexnode = node.Attributes.GetNamedItem("sex");
\r
2777 if (sexnode != null)
\r
2779 if (sexnode.Value == "male")
\r
2781 sex = EparamSex.SEX_MALE;
\r
2785 sex = EparamSex.SEX_FEMALE;
\r
2790 Group = int.Parse(node.Attributes.GetNamedItem("group").Value);
\r
2792 if (Group == (int)GroupType.VISUAL_PARAM_GROUP_TWEAKABLE)
\r
2794 if (!tweakable_params.ContainsKey(ParamID)) //stupid duplicate shared params
\r
2796 tweakable_params.Add(this.ParamID, this);
\r
2798 //Logger.Log(String.Format("Adding tweakable paramater ID {0} {1}", count, this.Name), Helpers.LogLevel.Info);
\r
2802 //TODO other paramaters but these arew concerned with editing the GUI display so not too fussed at the moment
\r
2806 allParams.Add(ParamID, this);
\r
2810 Logger.Log("Duplicate VisualParam in allParams id " + ParamID.ToString(), Helpers.LogLevel.Info);
\r
2813 if (pt == ParamType.TYPE_BONEDEFORM)
\r
2815 // If we are in the skeleton section then we also have bone deforms to parse
\r
2816 BoneDeforms = new Dictionary<string, Vector3>();
\r
2817 if (node.HasChildNodes && node.ChildNodes[0].HasChildNodes)
\r
2819 ParseBoneDeforms(node.ChildNodes[0].ChildNodes);
\r
2821 deformParams.Add(ParamID, this);
\r
2824 if (pt == ParamType.TYPE_MORPH)
\r
2826 VolumeDeforms = new Dictionary<string, VolumeDeform>();
\r
2827 if (node.HasChildNodes && node.ChildNodes[0].HasChildNodes)
\r
2829 ParseVolumeDeforms(node.ChildNodes[0].ChildNodes);
\r
2834 morphParams.Add(ParamID, this);
\r
2838 Logger.Log("Duplicate VisualParam in morphParams id " + ParamID.ToString(), Helpers.LogLevel.Info);
\r
2843 if (pt == ParamType.TYPE_DRIVER)
\r
2845 childparams = new List<driven>();
\r
2846 if (node.HasChildNodes && node.ChildNodes[0].HasChildNodes) //LAZY
\r
2848 ParseDrivers(node.ChildNodes[0].ChildNodes);
\r
2851 drivenParams.Add(ParamID, this);
\r
2855 if (pt == ParamType.TYPE_COLOR)
\r
2857 if (node.HasChildNodes)
\r
2859 foreach (XmlNode colorchild in node.ChildNodes)
\r
2861 if (colorchild.Name == "param_color")
\r
2863 //TODO extract <value color="50, 25, 5, 255" />
\r
2872 void ParseBoneDeforms(XmlNodeList deforms)
\r
2874 foreach (XmlNode node in deforms)
\r
2876 if (node.Name == "bone")
\r
2878 string name = node.Attributes.GetNamedItem("name").Value;
\r
2879 Vector3 scale = XmlParseVector(node.Attributes.GetNamedItem("scale").Value);
\r
2880 BoneDeforms.Add(name, scale);
\r
2885 void ParseVolumeDeforms(XmlNodeList deforms)
\r
2887 foreach (XmlNode node in deforms)
\r
2889 if (node.Name == "volume_morph")
\r
2891 VolumeDeform vd = new VolumeDeform();
\r
2892 vd.name = node.Attributes.GetNamedItem("name").Value;
\r
2893 vd.name = vd.name.ToLower();
\r
2895 if (node.Attributes.GetNamedItem("scale") != null)
\r
2897 vd.scale = XmlParseVector(node.Attributes.GetNamedItem("scale").Value);
\r
2901 vd.scale = new Vector3(0, 0, 0);
\r
2904 if (node.Attributes.GetNamedItem("pos") != null)
\r
2906 vd.pos = XmlParseVector(node.Attributes.GetNamedItem("pos").Value);
\r
2910 vd.pos = new Vector3(0f, 0f, 0f);
\r
2913 VolumeDeforms.Add(vd.name, vd);
\r
2918 void ParseDrivers(XmlNodeList drivennodes)
\r
2920 foreach (XmlNode node in drivennodes)
\r
2922 if (node.Name == "driven")
\r
2924 driven d = new driven();
\r
2926 d.id = Int32.Parse(node.Attributes.GetNamedItem("id").Value);
\r
2927 XmlNode param = node.Attributes.GetNamedItem("max1");
\r
2928 if (param != null)
\r
2930 d.max1 = float.Parse(param.Value);
\r
2931 d.max2 = float.Parse(node.Attributes.GetNamedItem("max2").Value);
\r
2932 d.min1 = float.Parse(node.Attributes.GetNamedItem("min1").Value);
\r
2933 d.max2 = float.Parse(node.Attributes.GetNamedItem("min2").Value);
\r
2934 d.hasMinMax = true;
\r
2938 d.hasMinMax = false;
\r
2941 childparams.Add(d);
\r
2947 public static Vector3 XmlParseVector(string data)
\r
2949 string[] posparts = data.Split(' ');
\r
2950 return new Vector3(float.Parse(posparts[0]), float.Parse(posparts[1]), float.Parse(posparts[2]));
\r
2953 public static Quaternion XmlParseRotation(string data)
\r
2955 string[] rotparts = data.Split(' ');
\r
2956 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));
\r
2961 * Helper classs for reading the static VFS file, call
\r
2962 * staticVFS.readVFSheaders() with the path to the static_data.db2 and static_index.db2 files
\r
2963 * and it will pass and dump in to openmetaverse_data for you
\r
2964 * This should only be needed to be used if LL update the static VFS in order to refresh our data
\r
2969 public int mLocation;
\r
2970 public int mLength;
\r
2971 public int mAccessTime;
\r
2972 public UUID mFileID;
\r
2974 public AssetType mAssetType;
\r
2976 public int readblock(byte[] blockdata, int offset)
\r
2979 BitPack input = new BitPack(blockdata, offset);
\r
2980 mLocation = input.UnpackInt();
\r
2981 mLength = input.UnpackInt();
\r
2982 mAccessTime = input.UnpackInt();
\r
2983 mFileID = input.UnpackUUID();
\r
2984 int filetype = input.UnpackShort();
\r
2985 mAssetType = (AssetType)filetype;
\r
2986 mSize = input.UnpackInt();
\r
2989 Logger.Log(String.Format("Found header for {0} type {1} length {2} at {3}", mFileID, mAssetType, mSize, mLocation),Helpers.LogLevel.Info);
\r
2996 public class staticVFS
\r
2998 public static void readVFSheaders(string datafile, string indexfile)
\r
3000 FileStream datastream;
\r
3001 FileStream indexstream;
\r
3003 datastream = File.Open(datafile, FileMode.Open);
\r
3004 indexstream = File.Open(indexfile, FileMode.Open);
\r
3008 byte[] blockdata = new byte[indexstream.Length];
\r
3009 indexstream.Read(blockdata, 0, (int)indexstream.Length);
\r
3011 while (offset < indexstream.Length)
\r
3013 VFSblock block = new VFSblock();
\r
3014 offset = block.readblock(blockdata, offset);
\r
3016 FileStream writer = File.Open(OpenMetaverse.Settings.RESOURCE_DIR+System.IO.Path.DirectorySeparatorChar+block.mFileID.ToString(),FileMode.Create);
\r
3017 byte[] data = new byte[block.mSize];
\r
3018 datastream.Seek(block.mLocation, SeekOrigin.Begin);
\r
3019 datastream.Read(data, 0, block.mSize);
\r
3020 writer.Write(data, 0, block.mSize);
\r