2 using System.Collections.Generic;
\r
3 using System.Collections;
\r
8 using System.Threading;
\r
9 using OpenTK.Graphics.OpenGL;
\r
10 using System.Runtime.InteropServices;
\r
11 using OpenMetaverse;
\r
12 using OpenMetaverse.Rendering;
\r
14 namespace Radegast.Rendering
\r
16 public class FaceData
\r
18 public float[] Vertices;
\r
19 public ushort[] Indices;
\r
20 public float[] TexCoords;
\r
21 public float[] Normals;
\r
22 public int PickingID = -1;
\r
23 public int VertexVBO = -1;
\r
24 public int IndexVBO = -1;
\r
25 public TextureInfo TextureInfo = new TextureInfo();
\r
26 public BoundingVolume BoundingVolume = new BoundingVolume();
\r
27 public static int VertexSize = 32; // sizeof (vertex), 2 x vector3 + 1 x vector2 = 8 floats x 4 bytes = 32 bytes
\r
28 public TextureAnimationInfo AnimInfo;
\r
29 public int QueryID = 0;
\r
31 public void CheckVBO(Face face)
\r
33 if (VertexVBO == -1)
\r
35 Vertex[] vArray = face.Vertices.ToArray();
\r
36 GL.GenBuffers(1, out VertexVBO);
\r
37 GL.BindBuffer(BufferTarget.ArrayBuffer, VertexVBO);
\r
38 GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vArray.Length * VertexSize), vArray, BufferUsageHint.StaticDraw);
\r
43 ushort[] iArray = face.Indices.ToArray();
\r
44 GL.GenBuffers(1, out IndexVBO);
\r
45 GL.BindBuffer(BufferTarget.ElementArrayBuffer, IndexVBO);
\r
46 GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(iArray.Length * sizeof(ushort)), iArray, BufferUsageHint.StaticDraw);
\r
51 public class TextureAnimationInfo
\r
53 public Primitive.TextureAnimation PrimAnimInfo;
\r
54 public float CurrentFrame;
\r
55 public double CurrentTime;
\r
56 public bool PingPong;
\r
57 float LastTime = 0f;
\r
58 float TotalTime = 0f;
\r
60 public void Step(double lastFrameTime)
\r
62 float numFrames = 1f;
\r
63 float fullLength = 1f;
\r
65 if (PrimAnimInfo.Length > 0)
\r
67 numFrames = PrimAnimInfo.Length;
\r
71 numFrames = Math.Max(1f, (float)(PrimAnimInfo.SizeX * PrimAnimInfo.SizeY));
\r
74 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.PING_PONG) != 0)
\r
76 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) != 0)
\r
78 fullLength = 2f * numFrames;
\r
80 else if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.LOOP) != 0)
\r
82 fullLength = 2f * numFrames - 2f;
\r
83 fullLength = Math.Max(1f, fullLength);
\r
87 fullLength = 2f * numFrames - 1f;
\r
88 fullLength = Math.Max(1f, fullLength);
\r
93 fullLength = numFrames;
\r
97 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) != 0)
\r
99 frameCounter = (float)lastFrameTime * PrimAnimInfo.Rate + LastTime;
\r
103 TotalTime += (float)lastFrameTime;
\r
104 frameCounter = TotalTime * PrimAnimInfo.Rate;
\r
106 LastTime = frameCounter;
\r
108 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.LOOP) != 0)
\r
110 frameCounter %= fullLength;
\r
114 frameCounter = Math.Min(fullLength - 1f, frameCounter);
\r
117 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) == 0)
\r
119 frameCounter = (float)Math.Floor(frameCounter + 0.01f);
\r
122 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.PING_PONG) != 0)
\r
124 if (frameCounter > numFrames)
\r
126 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) != 0)
\r
128 frameCounter = numFrames - (frameCounter - numFrames);
\r
132 frameCounter = (numFrames - 1.99f) - (frameCounter - numFrames);
\r
137 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.REVERSE) != 0)
\r
139 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) != 0)
\r
141 frameCounter = numFrames - frameCounter;
\r
145 frameCounter = (numFrames - 0.99f) - frameCounter;
\r
149 frameCounter += PrimAnimInfo.Start;
\r
151 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) == 0)
\r
153 frameCounter = (float)Math.Round(frameCounter);
\r
157 GL.MatrixMode(MatrixMode.Texture);
\r
160 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.ROTATE) != 0)
\r
162 GL.Translate(0.5f, 0.5f, 0f);
\r
163 GL.Rotate(Utils.RAD_TO_DEG * frameCounter, OpenTK.Vector3d.UnitZ);
\r
164 GL.Translate(-0.5f, -0.5f, 0f);
\r
166 else if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SCALE) != 0)
\r
168 GL.Scale(frameCounter, frameCounter, 0);
\r
172 float sizeX = Math.Max(1f, (float)PrimAnimInfo.SizeX);
\r
173 float sizeY = Math.Max(1f, (float)PrimAnimInfo.SizeY);
\r
175 GL.Scale(1f / sizeX, 1f / sizeY, 0);
\r
176 GL.Translate(frameCounter % sizeX, Math.Floor(frameCounter / sizeY), 0);
\r
179 GL.MatrixMode(MatrixMode.Modelview);
\r
182 [Obsolete("Use Step() instead")]
\r
183 public void ExperimentalStep(double time)
\r
185 int reverseFactor = 1;
\r
186 float rate = PrimAnimInfo.Rate;
\r
191 reverseFactor = -reverseFactor;
\r
194 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.REVERSE) != 0)
\r
196 reverseFactor = -reverseFactor;
\r
199 CurrentTime += time;
\r
200 double totalTime = 1 / rate;
\r
202 uint x = Math.Max(1, PrimAnimInfo.SizeX);
\r
203 uint y = Math.Max(1, PrimAnimInfo.SizeY);
\r
204 uint nrFrames = x * y;
\r
206 if (PrimAnimInfo.Length > 0 && PrimAnimInfo.Length < nrFrames)
\r
208 nrFrames = (uint)PrimAnimInfo.Length;
\r
211 GL.MatrixMode(MatrixMode.Texture);
\r
214 if (CurrentTime >= totalTime)
\r
218 if (CurrentFrame > nrFrames) CurrentFrame = (uint)PrimAnimInfo.Start;
\r
219 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.PING_PONG) != 0)
\r
221 PingPong = !PingPong;
\r
225 float smoothOffset = 0f;
\r
227 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.SMOOTH) != 0)
\r
229 smoothOffset = (float)(CurrentTime / totalTime) * reverseFactor;
\r
232 float f = CurrentFrame;
\r
233 if (reverseFactor < 0)
\r
235 f = nrFrames - CurrentFrame;
\r
238 if ((PrimAnimInfo.Flags & Primitive.TextureAnimMode.ROTATE) == 0) // not rotaating
\r
240 GL.Scale(1f / x, 1f / y, 0f);
\r
241 GL.Translate((f % x) + smoothOffset, f / y, 0);
\r
245 smoothOffset = (float)(CurrentTime * PrimAnimInfo.Rate);
\r
246 double startAngle = PrimAnimInfo.Start;
\r
247 double endAngle = PrimAnimInfo.Length;
\r
248 double angle = startAngle + (endAngle - startAngle) * smoothOffset;
\r
249 GL.Translate(0.5f, 0.5f, 0f);
\r
250 GL.Rotate(Utils.RAD_TO_DEG * angle, OpenTK.Vector3d.UnitZ);
\r
251 GL.Translate(-0.5f, -0.5f, 0f);
\r
254 GL.MatrixMode(MatrixMode.Modelview);
\r
260 public class TextureInfo
\r
262 public System.Drawing.Image Texture;
\r
263 public int TexturePointer;
\r
264 public bool HasAlpha;
\r
265 public bool FullAlpha;
\r
266 public bool IsMask;
\r
267 public UUID TextureID;
\r
270 public class TextureLoadItem
\r
272 public FaceData Data;
\r
273 public Primitive Prim;
\r
274 public Primitive.TextureEntryFace TeFace;
\r
275 public byte[] TextureData = null;
\r
278 public enum RenderPass
\r
285 public enum SceneObjectType
\r
293 /// Base class for all scene objects
\r
295 public abstract class SceneObject : IComparable, IDisposable
\r
297 /// <summary>Actual position of the object in the region</summary>
\r
298 public Vector3 SimPosition;
\r
299 /// <summary>Actual rotation of the object in the region</summary>
\r
300 public Quaternion SimRotation;
\r
301 /// <summary>Rendered position of the object in the region</summary>
\r
302 public Vector3 RenderPosition;
\r
303 /// <summary>Rendered rotationm of the object in the region</summary>
\r
304 public Quaternion RenderRotation;
\r
305 /// <summary>Per frame calculated square of the distance from camera</summary>
\r
306 public float DistanceSquared;
\r
307 /// <summary>Bounding volume of the object</summary>
\r
308 public BoundingVolume BoundingVolume;
\r
309 /// <summary>Was the sim position and distance from camera calculated during this frame</summary>
\r
310 public bool PositionCalculated;
\r
311 /// <summary>Scene object type</summary>
\r
312 public SceneObjectType Type = SceneObjectType.None;
\r
313 /// <summary>Libomv primitive</summary>
\r
314 public virtual Primitive BasePrim { get; set; }
\r
315 /// <summary>Were initial initialization tasks done</summary>
\r
316 public bool Initialized;
\r
319 /// Cleanup resources used
\r
321 public virtual void Dispose()
\r
326 /// Task performed the fist time object is set for rendering
\r
328 public virtual void Initialize()
\r
330 RenderPosition = SimPosition;
\r
331 RenderRotation = SimRotation;
\r
332 Initialized = true;
\r
336 /// Perform per frame tasks
\r
338 /// <param name="time">Time since the last call (last frame time in seconds)</param>
\r
339 public virtual void Step(double time)
\r
344 /// Implementation of the IComparable interface
\r
345 /// used for sorting by distance
\r
347 /// <param name="other">Object we are comparing to</param>
\r
348 /// <returns>Result of the comparison</returns>
\r
349 public virtual int CompareTo(object other)
\r
351 SceneObject o = (SceneObject)other;
\r
352 if (this.DistanceSquared < o.DistanceSquared)
\r
354 else if (this.DistanceSquared > o.DistanceSquared)
\r
361 public class RenderPrimitive : SceneObject
\r
363 public Primitive Prim;
\r
364 public List<Face> Faces;
\r
365 /// <summary>Is this object attached to an avatar</summary>
\r
366 public bool Attached;
\r
367 /// <summary>Do we know if object is attached</summary>
\r
368 public bool AttachedStateKnown;
\r
370 public RenderPrimitive()
\r
372 Type = SceneObjectType.Primitive;
\r
375 public override Primitive BasePrim
\r
377 get { return Prim; }
\r
378 set { Prim = value; }
\r
381 public override void Initialize()
\r
383 AttachedStateKnown = false;
\r
387 public override void Step(double time)
\r
389 // Don't interpolate positions of attached objects
\r
392 RenderPosition = SimPosition;
\r
393 RenderRotation = SimRotation;
\r
397 if (RenderPosition != SimPosition)
\r
399 RenderPosition = RHelp.Smoothed1stOrder(RenderPosition, SimPosition, time);
\r
401 if (Prim.AngularVelocity != Vector3.Zero)
\r
403 Vector3 angVel = Prim.AngularVelocity;
\r
404 float angle = (float)time * angVel.Length();
\r
405 Quaternion dQ = Quaternion.CreateFromAxisAngle(angVel, angle);
\r
406 RenderRotation = dQ * RenderRotation;
\r
408 else if (RenderRotation != SimRotation)
\r
410 RenderRotation = SimRotation;
\r
414 public override string ToString()
\r
416 uint id = Prim == null ? 0 : Prim.LocalID;
\r
417 double distance = Math.Sqrt(DistanceSquared);
\r
418 return string.Format("LocalID: {0}, distance {0.00}", id, distance);
\r
422 public static class Render
\r
424 public static IRendering Plugin;
\r
427 public static class RHelp
\r
429 static float t1 = 0.075f;
\r
430 static float t2 = t1 / 5.7f;
\r
432 public static Vector3 Smoothed1stOrder(Vector3 curPos, Vector3 targetPos, double lastFrameTime)
\r
434 int numIterations = (int)(lastFrameTime * 100);
\r
437 curPos += (targetPos - curPos) * t1;
\r
440 while (numIterations > 0);
\r
441 if (Vector3.DistanceSquared(curPos, targetPos) < 0.000001)
\r
443 curPos = targetPos;
\r
448 public static Vector3 Smoothed2ndOrder(Vector3 curPos, Vector3 targetPos, ref Vector3 accel, double lastFrameTime)
\r
450 int numIterations = (int)(lastFrameTime * 100);
\r
453 accel += (targetPos - accel - curPos) * t1;
\r
454 curPos += accel * t2;
\r
457 while (numIterations > 0);
\r
458 if (Vector3.DistanceSquared(curPos, targetPos) < 0.000001)
\r
460 curPos = targetPos;
\r
465 public static OpenTK.Vector2 TKVector3(Vector2 v)
\r
467 return new OpenTK.Vector2(v.X, v.Y);
\r
470 public static OpenTK.Vector3 TKVector3(Vector3 v)
\r
472 return new OpenTK.Vector3(v.X, v.Y, v.Z);
\r
475 public static OpenTK.Vector4 TKVector3(Vector4 v)
\r
477 return new OpenTK.Vector4(v.X, v.Y, v.Z, v.W);
\r
482 /// Represents camera object
\r
484 public class Camera
\r
487 Vector3 mFocalPoint;
\r
490 /// <summary>Camera position</summary>
\r
491 public Vector3 Position { get { return mPosition; } set { mPosition = value; Modify(); } }
\r
492 /// <summary>Camera target</summary>
\r
493 public Vector3 FocalPoint { get { return mFocalPoint; } set { mFocalPoint = value; Modify(); } }
\r
494 /// <summary>Zoom level</summary>
\r
496 /// <summary>Draw distance</summary>
\r
498 /// <summary>Has camera been modified</summary>
\r
499 public bool Modified { get { return mModified; } set { mModified = value; } }
\r
501 public double TimeToTarget = 0d;
\r
503 public Vector3 RenderPosition;
\r
504 public Vector3 RenderFocalPoint;
\r
511 public void Step(double time)
\r
513 if (RenderPosition != Position)
\r
515 RenderPosition = RHelp.Smoothed1stOrder(RenderPosition, Position, time);
\r
518 if (RenderFocalPoint != FocalPoint)
\r
520 RenderFocalPoint = RHelp.Smoothed1stOrder(RenderFocalPoint, FocalPoint, time);
\r
525 [Obsolete("Use Step(), left in here for reference")]
\r
526 public void Step2(double time)
\r
528 TimeToTarget -= time;
\r
529 if (TimeToTarget <= time)
\r
537 float pctElapsed = (float)(time / TimeToTarget);
\r
539 if (RenderPosition != Position)
\r
541 float distance = Vector3.Distance(RenderPosition, Position);
\r
542 RenderPosition = Vector3.Lerp(RenderPosition, Position, (float)(distance * pctElapsed));
\r
545 if (RenderFocalPoint != FocalPoint)
\r
547 RenderFocalPoint = Interpolate(RenderFocalPoint, FocalPoint, pctElapsed);
\r
551 Vector3 Interpolate(Vector3 start, Vector3 end, float fraction)
\r
553 float distance = Vector3.Distance(start, end);
\r
554 Vector3 direction = end - start;
\r
555 return start + direction * fraction;
\r
558 public void EndMove()
\r
562 RenderPosition = Position;
\r
563 RenderFocalPoint = FocalPoint;
\r
567 public static class MeshToOBJ
\r
569 public static bool MeshesToOBJ(Dictionary<uint, FacetedMesh> meshes, string filename)
\r
571 StringBuilder obj = new StringBuilder();
\r
572 StringBuilder mtl = new StringBuilder();
\r
574 FileInfo objFileInfo = new FileInfo(filename);
\r
576 string mtlFilename = objFileInfo.FullName.Substring(objFileInfo.DirectoryName.Length + 1,
\r
577 objFileInfo.FullName.Length - (objFileInfo.DirectoryName.Length + 1) - 4) + ".mtl";
\r
579 obj.AppendLine("# Created by libprimrender");
\r
580 obj.AppendLine("mtllib ./" + mtlFilename);
\r
583 mtl.AppendLine("# Created by libprimrender");
\r
587 foreach (FacetedMesh mesh in meshes.Values)
\r
589 for (int j = 0; j < mesh.Faces.Count; j++)
\r
591 Face face = mesh.Faces[j];
\r
593 if (face.Vertices.Count > 2)
\r
595 string mtlName = String.Format("material{0}-{1}", primNr, face.ID);
\r
596 Primitive.TextureEntryFace tex = face.TextureFace;
\r
597 string texName = tex.TextureID.ToString() + ".tga";
\r
599 // FIXME: Convert the source to TGA (if needed) and copy to the destination
\r
601 float shiny = 0.00f;
\r
604 case Shininess.High:
\r
607 case Shininess.Medium:
\r
610 case Shininess.Low:
\r
615 obj.AppendFormat("g face{0}-{1}{2}", primNr, face.ID, Environment.NewLine);
\r
617 mtl.AppendLine("newmtl " + mtlName);
\r
618 mtl.AppendFormat("Ka {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);
\r
619 mtl.AppendFormat("Kd {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);
\r
620 //mtl.AppendFormat("Ks {0} {1} {2}{3}");
\r
621 mtl.AppendLine("Tr " + tex.RGBA.A);
\r
622 mtl.AppendLine("Ns " + shiny);
\r
623 mtl.AppendLine("illum 1");
\r
624 if (tex.TextureID != UUID.Zero && tex.TextureID != Primitive.TextureEntry.WHITE_TEXTURE)
\r
625 mtl.AppendLine("map_Kd ./" + texName);
\r
628 // Write the vertices, texture coordinates, and vertex normals for this side
\r
629 for (int k = 0; k < face.Vertices.Count; k++)
\r
631 Vertex vertex = face.Vertices[k];
\r
635 Vector3 pos = vertex.Position;
\r
638 pos *= mesh.Prim.Scale;
\r
641 pos *= mesh.Prim.Rotation;
\r
643 // The root prim position is sim-relative, while child prim positions are
\r
644 // parent-relative. We want to apply parent-relative translations but not
\r
645 // sim-relative ones
\r
646 if (mesh.Prim.ParentID != 0)
\r
647 pos += mesh.Prim.Position;
\r
649 obj.AppendFormat("v {0} {1} {2}{3}", pos.X, pos.Y, pos.Z, Environment.NewLine);
\r
653 #region Texture Coord
\r
655 obj.AppendFormat("vt {0} {1}{2}", vertex.TexCoord.X, vertex.TexCoord.Y,
\r
656 Environment.NewLine);
\r
658 #endregion Texture Coord
\r
660 #region Vertex Normal
\r
662 // HACK: Sometimes normals are getting set to <NaN,NaN,NaN>
\r
663 if (!Single.IsNaN(vertex.Normal.X) && !Single.IsNaN(vertex.Normal.Y) && !Single.IsNaN(vertex.Normal.Z))
\r
664 obj.AppendFormat("vn {0} {1} {2}{3}", vertex.Normal.X, vertex.Normal.Y, vertex.Normal.Z,
\r
665 Environment.NewLine);
\r
667 obj.AppendLine("vn 0.0 1.0 0.0");
\r
669 #endregion Vertex Normal
\r
672 obj.AppendFormat("# {0} vertices{1}", face.Vertices.Count, Environment.NewLine);
\r
674 obj.AppendLine("usemtl " + mtlName);
\r
678 // Write all of the faces (triangles) for this side
\r
679 for (int k = 0; k < face.Indices.Count / 3; k++)
\r
681 obj.AppendFormat("f -{0}/-{0}/-{0} -{1}/-{1}/-{1} -{2}/-{2}/-{2}{3}",
\r
682 face.Vertices.Count - face.Indices[k * 3 + 0],
\r
683 face.Vertices.Count - face.Indices[k * 3 + 1],
\r
684 face.Vertices.Count - face.Indices[k * 3 + 2],
\r
685 Environment.NewLine);
\r
688 obj.AppendFormat("# {0} elements{1}", face.Indices.Count / 3, Environment.NewLine);
\r
691 #endregion Elements
\r
699 File.WriteAllText(filename, obj.ToString());
\r
700 File.WriteAllText(mtlFilename, mtl.ToString());
\r
711 public static class Math3D
\r
719 public static float[] CreateTranslationMatrix(Vector3 v)
\r
721 float[] mat = new float[16];
\r
726 mat[0] = mat[5] = mat[10] = mat[15] = 1;
\r
731 public static float[] CreateRotationMatrix(Quaternion q)
\r
733 float[] mat = new float[16];
\r
735 // Transpose the quaternion (don't ask me why)
\r
740 float x2 = q.X + q.X;
\r
741 float y2 = q.Y + q.Y;
\r
742 float z2 = q.Z + q.Z;
\r
743 float xx = q.X * x2;
\r
744 float xy = q.X * y2;
\r
745 float xz = q.X * z2;
\r
746 float yy = q.Y * y2;
\r
747 float yz = q.Y * z2;
\r
748 float zz = q.Z * z2;
\r
749 float wx = q.W * x2;
\r
750 float wy = q.W * y2;
\r
751 float wz = q.W * z2;
\r
753 mat[0] = 1.0f - (yy + zz);
\r
759 mat[5] = 1.0f - (xx + zz);
\r
765 mat[10] = 1.0f - (xx + yy);
\r
776 public static float[] CreateSRTMatrix(Vector3 scale, Quaternion q, Vector3 pos)
\r
778 float[] mat = new float[16];
\r
780 // Transpose the quaternion (don't ask me why)
\r
785 float x2 = q.X + q.X;
\r
786 float y2 = q.Y + q.Y;
\r
787 float z2 = q.Z + q.Z;
\r
788 float xx = q.X * x2;
\r
789 float xy = q.X * y2;
\r
790 float xz = q.X * z2;
\r
791 float yy = q.Y * y2;
\r
792 float yz = q.Y * z2;
\r
793 float zz = q.Z * z2;
\r
794 float wx = q.W * x2;
\r
795 float wy = q.W * y2;
\r
796 float wz = q.W * z2;
\r
798 mat[0] = (1.0f - (yy + zz)) * scale.X;
\r
799 mat[1] = (xy - wz) * scale.X;
\r
800 mat[2] = (xz + wy) * scale.X;
\r
803 mat[4] = (xy + wz) * scale.Y;
\r
804 mat[5] = (1.0f - (xx + zz)) * scale.Y;
\r
805 mat[6] = (yz - wx) * scale.Y;
\r
808 mat[8] = (xz - wy) * scale.Z;
\r
809 mat[9] = (yz + wx) * scale.Z;
\r
810 mat[10] = (1.0f - (xx + yy)) * scale.Z;
\r
823 public static float[] CreateScaleMatrix(Vector3 v)
\r
825 float[] mat = new float[16];
\r
835 public static float[] Lerp(float[] matrix1, float[] matrix2, float amount)
\r
838 float[] lerp = new float[16];
\r
839 //Probably not doing this as a loop is cheaper(unrolling)
\r
840 //also for performance we probably should not create new objects
\r
842 for (int x = 0; x < 16; x++)
\r
844 lerp[x] = matrix1[x] + ((matrix2[x] - matrix1[x]) * amount);
\r
851 public static bool GluProject(OpenTK.Vector3 objPos, OpenTK.Matrix4 modelMatrix, OpenTK.Matrix4 projMatrix, int[] viewport, out OpenTK.Vector3 screenPos)
\r
853 OpenTK.Vector4 _in;
\r
854 OpenTK.Vector4 _out;
\r
861 _out = OpenTK.Vector4.Transform(_in, modelMatrix);
\r
862 _in = OpenTK.Vector4.Transform(_out, projMatrix);
\r
866 screenPos = OpenTK.Vector3.Zero;
\r
873 /* Map x, y and z to range 0-1 */
\r
874 _in.X = _in.X * 0.5f + 0.5f;
\r
875 _in.Y = _in.Y * 0.5f + 0.5f;
\r
876 _in.Z = _in.Z * 0.5f + 0.5f;
\r
878 /* Map x,y to viewport */
\r
879 _in.X = _in.X * viewport[2] + viewport[0];
\r
880 _in.Y = _in.Y * viewport[3] + viewport[1];
\r
882 screenPos.X = _in.X;
\r
883 screenPos.Y = _in.Y;
\r
884 screenPos.Z = _in.Z;
\r
890 public class attachment_point
\r
892 public string name;
\r
893 public string joint;
\r
894 public Vector3 position;
\r
895 public Quaternion rotation;
\r
899 public GLMesh jointmesh;
\r
900 public int jointmeshindex;
\r
902 public attachment_point(XmlNode node)
\r
904 name = node.Attributes.GetNamedItem("name").Value;
\r
905 joint = node.Attributes.GetNamedItem("joint").Value;
\r
906 position = VisualParamEx.XmlParseVector(node.Attributes.GetNamedItem("position").Value);
\r
907 rotation = VisualParamEx.XmlParseRotation(node.Attributes.GetNamedItem("rotation").Value);
\r
908 id = Int32.Parse(node.Attributes.GetNamedItem("id").Value);
\r
909 group = Int32.Parse(node.Attributes.GetNamedItem("group").Value);
\r
915 /// Subclass of LindenMesh that adds vertex, index, and texture coordinate
\r
916 /// arrays suitable for pushing direct to OpenGL
\r
918 public class GLMesh : LindenMesh
\r
921 /// Subclass of LODMesh that adds an index array suitable for pushing
\r
922 /// direct to OpenGL
\r
926 public int teFaceID;
\r
927 public Dictionary<int, VisualParamEx> _evp = new Dictionary<int, VisualParamEx>();
\r
929 new public class LODMesh : LindenMesh.LODMesh
\r
931 public ushort[] Indices;
\r
933 public override void LoadMesh(string filename)
\r
935 base.LoadMesh(filename);
\r
937 // Generate the index array
\r
938 Indices = new ushort[_numFaces * 3];
\r
940 for (int i = 0; i < _numFaces; i++)
\r
942 Indices[current++] = (ushort)_faces[i].Indices[0];
\r
943 Indices[current++] = (ushort)_faces[i].Indices[1];
\r
944 Indices[current++] = (ushort)_faces[i].Indices[2];
\r
952 public struct GLData
\r
954 public float[] Vertices;
\r
955 public float[] Normals;
\r
956 public ushort[] Indices;
\r
957 public float[] TexCoords;
\r
958 public Vector3 Center;
\r
959 public float[] weights; //strictly these are constant and don't need instancing with the GLMesh
\r
960 public string[] skinJoints; //strictly these are constant and don't need instancing with the GLMesh
\r
963 public static GLData baseRenderData;
\r
964 public GLData RenderData;
\r
965 public GLData OrigRenderData;
\r
966 public GLData MorphRenderData;
\r
968 public GLAvatar av;
\r
970 public GLMesh(string name)
\r
975 public GLMesh(GLMesh source, GLAvatar av)
\r
976 : base(source.Name)
\r
979 // Make a new GLMesh copy from the supplied source
\r
981 RenderData.Vertices = new float[source.RenderData.Vertices.Length];
\r
982 RenderData.Normals = new float[source.RenderData.Normals.Length];
\r
983 RenderData.TexCoords = new float[source.RenderData.TexCoords.Length];
\r
984 RenderData.Indices = new ushort[source.RenderData.Indices.Length];
\r
986 RenderData.weights = new float[source.RenderData.weights.Length];
\r
987 RenderData.skinJoints = new string[source.RenderData.skinJoints.Length];
\r
989 Array.Copy(source.RenderData.Vertices, RenderData.Vertices, source.RenderData.Vertices.Length);
\r
990 Array.Copy(source.RenderData.Normals, RenderData.Normals, source.RenderData.Normals.Length);
\r
992 Array.Copy(source.RenderData.TexCoords, RenderData.TexCoords, source.RenderData.TexCoords.Length);
\r
993 Array.Copy(source.RenderData.Indices, RenderData.Indices, source.RenderData.Indices.Length);
\r
994 Array.Copy(source.RenderData.weights, RenderData.weights, source.RenderData.weights.Length);
\r
995 Array.Copy(source.RenderData.skinJoints, RenderData.skinJoints, source.RenderData.skinJoints.Length);
\r
998 RenderData.Center = new Vector3(source.RenderData.Center);
\r
1000 teFaceID = source.teFaceID;
\r
1002 _rotationAngles = new Vector3(source.RotationAngles);
\r
1003 _scale = new Vector3(source.Scale);
\r
1004 _position = new Vector3(source.Position);
\r
1006 // We should not need to instance these the reference from the top should be constant
\r
1007 _evp = source._evp;
\r
1008 _morphs = source._morphs;
\r
1010 OrigRenderData.Indices = new ushort[source.RenderData.Indices.Length];
\r
1011 OrigRenderData.TexCoords = new float[source.RenderData.TexCoords.Length];
\r
1012 OrigRenderData.Vertices = new float[source.RenderData.Vertices.Length];
\r
1014 MorphRenderData.Vertices = new float[source.RenderData.Vertices.Length];
\r
1016 Array.Copy(source.RenderData.Vertices, OrigRenderData.Vertices, source.RenderData.Vertices.Length);
\r
1017 Array.Copy(source.RenderData.Vertices, MorphRenderData.Vertices, source.RenderData.Vertices.Length);
\r
1019 Array.Copy(source.RenderData.TexCoords, OrigRenderData.TexCoords, source.RenderData.TexCoords.Length);
\r
1020 Array.Copy(source.RenderData.Indices, OrigRenderData.Indices, source.RenderData.Indices.Length);
\r
1026 public void setMeshPos(Vector3 pos)
\r
1031 public void setMeshRot(Vector3 rot)
\r
1033 _rotationAngles = rot;
\r
1036 public override void LoadMesh(string filename)
\r
1038 base.LoadMesh(filename);
\r
1040 float minX, minY, minZ;
\r
1041 minX = minY = minZ = Single.MaxValue;
\r
1042 float maxX, maxY, maxZ;
\r
1043 maxX = maxY = maxZ = Single.MinValue;
\r
1045 // Generate the vertex array
\r
1046 RenderData.Vertices = new float[_numVertices * 3];
\r
1047 RenderData.Normals = new float[_numVertices * 3];
\r
1049 Quaternion quat = Quaternion.CreateFromEulers(0, 0, (float)(Math.PI / 4.0));
\r
1052 for (int i = 0; i < _numVertices; i++)
\r
1055 RenderData.Normals[current] = _vertices[i].Normal.X;
\r
1056 RenderData.Vertices[current++] = _vertices[i].Coord.X;
\r
1057 RenderData.Normals[current] = _vertices[i].Normal.Y;
\r
1058 RenderData.Vertices[current++] = _vertices[i].Coord.Y;
\r
1059 RenderData.Normals[current] = _vertices[i].Normal.Z;
\r
1060 RenderData.Vertices[current++] = _vertices[i].Coord.Z;
\r
1062 if (_vertices[i].Coord.X < minX)
\r
1063 minX = _vertices[i].Coord.X;
\r
1064 else if (_vertices[i].Coord.X > maxX)
\r
1065 maxX = _vertices[i].Coord.X;
\r
1067 if (_vertices[i].Coord.Y < minY)
\r
1068 minY = _vertices[i].Coord.Y;
\r
1069 else if (_vertices[i].Coord.Y > maxY)
\r
1070 maxY = _vertices[i].Coord.Y;
\r
1072 if (_vertices[i].Coord.Z < minZ)
\r
1073 minZ = _vertices[i].Coord.Z;
\r
1074 else if (_vertices[i].Coord.Z > maxZ)
\r
1075 maxZ = _vertices[i].Coord.Z;
\r
1078 // Calculate the center-point from the bounding box edges
\r
1079 RenderData.Center = new Vector3((minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2);
\r
1081 // Generate the index array
\r
1082 RenderData.Indices = new ushort[_numFaces * 3];
\r
1084 for (int i = 0; i < _numFaces; i++)
\r
1086 RenderData.Indices[current++] = (ushort)_faces[i].Indices[0];
\r
1087 RenderData.Indices[current++] = (ushort)_faces[i].Indices[1];
\r
1088 RenderData.Indices[current++] = (ushort)_faces[i].Indices[2];
\r
1091 // Generate the texcoord array
\r
1092 RenderData.TexCoords = new float[_numVertices * 2];
\r
1094 for (int i = 0; i < _numVertices; i++)
\r
1096 RenderData.TexCoords[current++] = _vertices[i].TexCoord.X;
\r
1097 RenderData.TexCoords[current++] = _vertices[i].TexCoord.Y;
\r
1100 RenderData.weights = new float[_numVertices];
\r
1101 for (int i = 0; i < _numVertices; i++)
\r
1103 RenderData.weights[i] = _vertices[i].Weight;
\r
1106 RenderData.skinJoints = new string[_skinJoints.Length + 3];
\r
1107 for (int i = 1; i < _skinJoints.Length; i++)
\r
1109 RenderData.skinJoints[i] = _skinJoints[i];
\r
1115 public override void LoadLODMesh(int level, string filename)
\r
1117 LODMesh lod = new LODMesh();
\r
1118 lod.LoadMesh(filename);
\r
1119 _lodMeshes[level] = lod;
\r
1122 public void applyjointweights()
\r
1125 /*Each weight actually contains two pieces of information.
\r
1126 * The number to the left of the decimal point is the index of the joint and also
\r
1127 * implicitly indexes to the following joint. The actual weight is to the right of
\r
1128 * the decimal point and interpolates between these two joints. The index is into an
\r
1129 * "expanded" list of joints, not just a linear array of the joints as defined in the
\r
1130 * skeleton file. In particular, any joint that has more than one child will be repeated
\r
1131 * in the list for each of its children.
\r
1134 float weight = -9999;
\r
1135 int jointindex = 0;
\r
1141 for (int v = 0, x = 0; v < RenderData.Vertices.Length; v = v + 3, x++)
\r
1143 if (weight != RenderData.weights[x])
\r
1146 jointindex = (int)Math.Floor(weight = RenderData.weights[x]);
\r
1147 factor = RenderData.weights[x] - jointindex;
\r
1148 weight = weight - jointindex;
\r
1150 string jointname = "", jointname2 = "";
\r
1152 if (this.Name == "upperBodyMesh")
\r
1154 jointname = skeleton.mUpperMeshMapping[jointindex];
\r
1156 jointname2 = skeleton.mUpperMeshMapping[jointindex];
\r
1158 else if (Name == "lowerBodyMesh")
\r
1160 jointname = skeleton.mLowerMeshMapping[jointindex];
\r
1162 jointname2 = skeleton.mLowerMeshMapping[jointindex];
\r
1164 else if (Name == "headMesh")
\r
1166 jointname = skeleton.mHeadMeshMapping[jointindex];
\r
1168 jointname2 = skeleton.mHeadMeshMapping[jointindex];
\r
1172 return; // not interested in this mesh
\r
1176 if (jointname == "")
\r
1178 //Don't yet handle this, its a split joint to two children
\r
1183 ba = av.skel.mBones[jointname];
\r
1186 if (jointname2 == "")
\r
1192 bb = av.skel.mBones[jointname2];
\r
1196 //Special cases 0 is not used
\r
1197 // ON upper torso 5 and 10 are not used
\r
1198 // 4 is neck and 6 and 11 are the left and right collar bones
\r
1203 Quaternion rot = ba.getRotation();
\r
1207 Vector3 oa = ba.getOffset() - ba.getOrigOffset();
\r
1208 Vector3 ob = bb.getOffset() - bb.getOrigOffset();
\r
1209 lerp = Vector3.Lerp(oa, ob, weight);
\r
1210 offset = Vector3.Lerp(ba.getOffset(), bb.getOffset(), weight);
\r
1214 lerp = ba.getOffset() - ba.getOrigOffset();
\r
1215 offset = ba.getOffset();
\r
1216 rot = ba.getRotation();
\r
1219 Vector3 pos = new Vector3(MorphRenderData.Vertices[v], MorphRenderData.Vertices[v + 1], MorphRenderData.Vertices[v + 2]);
\r
1221 pos = pos - offset;
\r
1223 pos = pos + offset;
\r
1225 RenderData.Vertices[v] = pos.X;
\r
1226 RenderData.Vertices[v + 1] = pos.Y;
\r
1227 RenderData.Vertices[v + 2] = pos.Z;
\r
1231 public void morphmesh(Morph morph, float weight)
\r
1233 for (int v = 0; v < morph.NumVertices; v++)
\r
1235 MorphVertex mvx = morph.Vertices[v];
\r
1237 uint i = mvx.VertexIndex;
\r
1239 MorphRenderData.Vertices[i * 3] = OrigRenderData.Vertices[i * 3] + mvx.Coord.X * weight;
\r
1240 MorphRenderData.Vertices[(i * 3) + 1] = OrigRenderData.Vertices[i * 3 + 1] + mvx.Coord.Y * weight;
\r
1241 MorphRenderData.Vertices[(i * 3) + 2] = OrigRenderData.Vertices[i * 3 + 2] + mvx.Coord.Z * weight;
\r
1243 RenderData.TexCoords[i * 2] = OrigRenderData.TexCoords[i * 2] + mvx.TexCoord.X * weight;
\r
1244 RenderData.TexCoords[(i * 2) + 1] = OrigRenderData.TexCoords[i * 2 + 1] + mvx.TexCoord.Y * weight;
\r
1250 public class GLAvatar
\r
1252 private static Dictionary<string, GLMesh> _defaultmeshes = new Dictionary<string, GLMesh>();
\r
1253 public Dictionary<string, GLMesh> _meshes = new Dictionary<string, GLMesh>();
\r
1255 public skeleton skel = new skeleton();
\r
1256 public static Dictionary<int, attachment_point> attachment_points = new Dictionary<int, attachment_point>();
\r
1258 public bool _wireframe = true;
\r
1259 public bool _showSkirt = false;
\r
1261 public VisualParamEx.EparamSex msex;
\r
1263 public byte[] VisualAppearanceParameters = new byte[1024];
\r
1264 bool vpsent = false;
\r
1265 static bool lindenMeshesLoaded = false;
\r
1269 foreach (KeyValuePair<string, GLMesh> kvp in _defaultmeshes)
\r
1271 GLMesh mesh = new GLMesh(kvp.Value, this); // Instance our meshes
\r
1272 _meshes.Add(kvp.Key, mesh);
\r
1277 public static void dumptweaks()
\r
1280 for (int x = 0; x < VisualParamEx.tweakable_params.Count; x++)
\r
1282 VisualParamEx vpe = (VisualParamEx)VisualParamEx.tweakable_params.GetByIndex(x);
\r
1283 Console.WriteLine(string.Format("{0} is {1}", x, vpe.Name));
\r
1289 public static void loadlindenmeshes2(string LODfilename)
\r
1291 // Already have mashes loaded?
\r
1292 if (lindenMeshesLoaded) return;
\r
1294 attachment_points.Clear();
\r
1297 string basedir = Directory.GetCurrentDirectory() + System.IO.Path.DirectorySeparatorChar + "character" + System.IO.Path.DirectorySeparatorChar;
\r
1299 XmlDocument lad = new XmlDocument();
\r
1300 lad.Load(basedir + LODfilename);
\r
1302 //Firstly read the skeleton section this contains attachment point info and the bone deform info for visual params
\r
1303 // And load the skeleton file in to the bones class
\r
1305 XmlNodeList skeleton = lad.GetElementsByTagName("skeleton");
\r
1306 string skeletonfilename = skeleton[0].Attributes.GetNamedItem("file_name").Value;
\r
1307 Bone.loadbones(skeletonfilename);
\r
1309 // Next read all the skeleton child nodes, we have attachment points and bone deform params
\r
1310 // attachment points are an offset and rotation from a bone location
\r
1311 // the name of the bone they reference is the joint paramater
\r
1312 // params in the skeleton nodes are bone deforms, eg leg length changes the scale of the leg bones
\r
1314 foreach (XmlNode skeletonnode in skeleton[0].ChildNodes)
\r
1316 if (skeletonnode.Name == "attachment_point")
\r
1318 attachment_point point = new attachment_point(skeletonnode);
\r
1319 attachment_points.Add(point.id, point);
\r
1322 if (skeletonnode.Name == "param")
\r
1324 //Bone deform param
\r
1325 VisualParamEx vp = new VisualParamEx(skeletonnode, VisualParamEx.ParamType.TYPE_BONEDEFORM);
\r
1329 //Now we parse the mesh nodes, mesh nodes reference a particular LLM file with a LOD
\r
1330 //and also list VisualParams for the various mesh morphs that can be applied
\r
1332 XmlNodeList meshes = lad.GetElementsByTagName("mesh");
\r
1333 foreach (XmlNode meshNode in meshes)
\r
1335 string type = meshNode.Attributes.GetNamedItem("type").Value;
\r
1336 int lod = Int32.Parse(meshNode.Attributes.GetNamedItem("lod").Value);
\r
1337 string fileName = meshNode.Attributes.GetNamedItem("file_name").Value;
\r
1339 GLMesh mesh = (_defaultmeshes.ContainsKey(type) ? _defaultmeshes[type] : new GLMesh(type));
\r
1341 if (meshNode.HasChildNodes)
\r
1343 foreach (XmlNode paramnode in meshNode.ChildNodes)
\r
1345 if (paramnode.Name == "param")
\r
1347 VisualParamEx vp = new VisualParamEx(paramnode, VisualParamEx.ParamType.TYPE_MORPH);
\r
1349 mesh._evp.Add(vp.ParamID, vp); //Not sure we really need this may optimise out later
\r
1350 vp.morphmesh = mesh.Name;
\r
1355 // Set up the texture elemenets for each mesh
\r
1356 // And hack the eyeball position
\r
1357 switch (mesh.Name)
\r
1359 case "lowerBodyMesh":
\r
1360 mesh.teFaceID = (int)AvatarTextureIndex.LowerBaked;
\r
1363 case "upperBodyMesh":
\r
1364 mesh.teFaceID = (int)AvatarTextureIndex.UpperBaked;
\r
1368 mesh.teFaceID = (int)AvatarTextureIndex.HeadBaked;
\r
1372 mesh.teFaceID = (int)AvatarTextureIndex.HairBaked;
\r
1375 case "eyelashMesh":
\r
1376 mesh.teFaceID = (int)AvatarTextureIndex.HeadBaked;
\r
1379 case "eyeBallRightMesh":
\r
1380 mesh.setMeshPos(Bone.mBones["mEyeLeft"].getOffset());
\r
1381 //mesh.setMeshRot(Bone.getRotation("mEyeLeft"));
\r
1382 mesh.teFaceID = (int)AvatarTextureIndex.EyesBaked;
\r
1385 case "eyeBallLeftMesh":
\r
1386 mesh.setMeshPos(Bone.mBones["mEyeRight"].getOffset());
\r
1387 //mesh.setMeshRot(Bone.getRotation("mEyeRight"));
\r
1388 mesh.teFaceID = (int)AvatarTextureIndex.EyesBaked;
\r
1392 mesh.teFaceID = (int)AvatarTextureIndex.SkirtBaked;
\r
1396 mesh.teFaceID = 0;
\r
1401 mesh.LoadMesh(basedir + fileName);
\r
1403 mesh.LoadLODMesh(lod, basedir + fileName);
\r
1405 _defaultmeshes[type] = mesh;
\r
1409 // Next are the textureing params, skipping for the moment
\r
1411 XmlNodeList colors = lad.GetElementsByTagName("global_color");
\r
1413 foreach (XmlNode globalcolornode in colors)
\r
1415 foreach (XmlNode node in globalcolornode.ChildNodes)
\r
1417 if (node.Name == "param")
\r
1419 VisualParamEx vp = new VisualParamEx(node, VisualParamEx.ParamType.TYPE_COLOR);
\r
1425 // Get layer paramaters, a bit of a verbose way to do it but we probably want to get access
\r
1426 // to some of the other data not just the <param> tag
\r
1428 XmlNodeList layer_sets = lad.GetElementsByTagName("layer_set");
\r
1430 foreach (XmlNode layer_set in layer_sets)
\r
1432 foreach (XmlNode layer in layer_set.ChildNodes)
\r
1434 foreach (XmlNode layernode in layer.ChildNodes)
\r
1436 if (layernode.Name == "param")
\r
1438 VisualParamEx vp = new VisualParamEx(layernode, VisualParamEx.ParamType.TYPE_COLOR);
\r
1445 // Next are the driver parameters, these are parameters that change multiple real parameters
\r
1447 XmlNodeList drivers = lad.GetElementsByTagName("driver_parameters");
\r
1449 foreach (XmlNode node in drivers[0].ChildNodes) //lazy
\r
1451 if (node.Name == "param")
\r
1453 VisualParamEx vp = new VisualParamEx(node, VisualParamEx.ParamType.TYPE_DRIVER);
\r
1457 lindenMeshesLoaded = true;
\r
1460 public void morphtest(Avatar av, int param, float weight)
\r
1462 VisualParamEx vpx;
\r
1463 if (VisualParamEx.allParams.TryGetValue(param, out vpx))
\r
1466 Logger.Log(string.Format("Applying visual parameter {0} id {1} value {2}", vpx.Name, vpx.ParamID, weight), Helpers.LogLevel.Info);
\r
1468 //weight = weight * 2.0f;
\r
1469 //weight=weight-1.0f;
\r
1471 float value = vpx.MinValue + ((vpx.MaxValue - vpx.MinValue) * weight);
\r
1474 if (vpx.pType == VisualParamEx.ParamType.TYPE_MORPH)
\r
1478 if (_meshes.TryGetValue(vpx.morphmesh, out mesh))
\r
1480 foreach (LindenMesh.Morph morph in mesh.Morphs) //optimise me to a dictionary
\r
1482 if (morph.Name == vpx.Name)
\r
1484 mesh.morphmesh(morph, value);
\r
1490 // Not a mesh morph
\r
1492 // Its a volume deform, these appear to be related to collision volumes
\r
1494 if (vpx.VolumeDeforms == null)
\r
1496 Logger.Log(String.Format("paramater {0} has invalid mesh {1}", param, vpx.morphmesh), Helpers.LogLevel.Warning);
\r
1500 foreach (KeyValuePair<string, VisualParamEx.VolumeDeform> kvp in vpx.VolumeDeforms)
\r
1502 skel.deformbone(kvp.Key, kvp.Value.pos, kvp.Value.scale);
\r
1512 // Its not a morph, it might be a driver though
\r
1513 if (vpx.pType == VisualParamEx.ParamType.TYPE_DRIVER)
\r
1515 foreach (VisualParamEx.driven child in vpx.childparams)
\r
1517 morphtest(av, child.id, weight); //TO DO use minmax if they are present
\r
1522 //Is this a bone deform?
\r
1523 if (vpx.pType == VisualParamEx.ParamType.TYPE_BONEDEFORM)
\r
1525 foreach (KeyValuePair<string, Vector3> kvp in vpx.BoneDeforms)
\r
1527 skel.deformbone(kvp.Key, new Vector3(0, 0, 0), kvp.Value * value, Quaternion.Identity);
\r
1532 Logger.Log(String.Format("paramater {0} is not a morph and not a driver", param), Helpers.LogLevel.Warning);
\r
1539 Logger.Log("Invalid paramater " + param.ToString(), Helpers.LogLevel.Warning);
\r
1543 foreach (GLMesh mesh in _meshes.Values)
\r
1545 VisualParamEx evp;
\r
1546 if (mesh._evp.TryGetValue(param, out evp))
\r
1548 foreach (LindenMesh.Morph morph in mesh.Morphs)
\r
1550 if (morph.Name == evp.Name)
\r
1552 mesh.morphmesh(morph, weight);
\r
1558 Console.WriteLine("No such visual param in morphs");
\r
1565 public void morph(Avatar av)
\r
1568 if (av.VisualParameters == null)
\r
1572 ThreadPool.QueueUserWorkItem(sync =>
\r
1576 if (av.VisualParameters.Length > 123)
\r
1578 if (av.VisualParameters[31] > 0.5)
\r
1580 msex = VisualParamEx.EparamSex.SEX_MALE;
\r
1584 msex = VisualParamEx.EparamSex.SEX_FEMALE;
\r
1589 foreach (byte vpvalue in av.VisualParameters)
\r
1591 if (vpsent == true && VisualAppearanceParameters[x] == vpvalue)
\r
1597 VisualAppearanceParameters[x] = vpvalue;
\r
1599 if (x >= VisualParamEx.tweakable_params.Count)
\r
1601 Logger.Log("Two many visual paramaters in Avatar appearance", Helpers.LogLevel.Warning);
\r
1605 VisualParamEx vpe = (VisualParamEx)VisualParamEx.tweakable_params.GetByIndex(x);
\r
1607 if (vpe.sex != VisualParamEx.EparamSex.SEX_BOTH && vpe.sex != msex)
\r
1613 float value = (vpvalue / 255.0f);
\r
1614 this.morphtest(av, vpe.ParamID, value);
\r
1621 foreach (GLMesh mesh in _meshes.Values)
\r
1623 mesh.applyjointweights();
\r
1629 public class RenderAvatar : SceneObject
\r
1631 public RenderAvatar()
\r
1633 Type = SceneObjectType.Avatar;
\r
1636 public override Primitive BasePrim
\r
1638 get { return avatar; }
\r
1639 set { if (value is Avatar) avatar = (Avatar)value; }
\r
1642 public override void Step(double time)
\r
1644 if (RenderPosition != SimPosition)
\r
1646 RenderPosition = RHelp.Smoothed1stOrder(RenderPosition, SimPosition, time);
\r
1648 RenderRotation = SimRotation;
\r
1652 public GLAvatar glavatar = new GLAvatar();
\r
1653 public Avatar avatar;
\r
1654 public FaceData[] data = new FaceData[32];
\r
1655 public Dictionary<UUID, Animation> animlist = new Dictionary<UUID, Animation>();
\r
1656 public Dictionary<WearableType, AppearanceManager.WearableData> Wearables = new Dictionary<WearableType, AppearanceManager.WearableData>();
\r
1660 public class skeleton
\r
1662 public Dictionary<string, Bone> mBones;
\r
1663 public static Dictionary<int, string> mUpperMeshMapping = new Dictionary<int, string>();
\r
1664 public static Dictionary<int, string> mLowerMeshMapping = new Dictionary<int, string>();
\r
1665 public static Dictionary<int, string> mHeadMeshMapping = new Dictionary<int, string>();
\r
1667 public static Dictionary<UUID, BinBVHAnimationReader> mKnownAnimations = new Dictionary<UUID, BinBVHAnimationReader>();
\r
1668 public Dictionary<UUID, Animation> animlist = new Dictionary<UUID, Animation>();
\r
1670 public bool mNeedsUpdate = false;
\r
1671 public bool mNeedsMeshRebuild = false;
\r
1676 mBones = new Dictionary<string, Bone>(Bone.mBones); //copy from the static defines
\r
1679 if (mUpperMeshMapping.Count == 0)
\r
1681 mUpperMeshMapping.Add(1, "mPelvis");
\r
1682 mUpperMeshMapping.Add(2, "mTorso");
\r
1683 mUpperMeshMapping.Add(3, "mChest");
\r
1684 mUpperMeshMapping.Add(4, "mNeck");
\r
1685 mUpperMeshMapping.Add(5, "");
\r
1686 mUpperMeshMapping.Add(6, "mCollarLeft");
\r
1687 mUpperMeshMapping.Add(7, "mShoulderLeft");
\r
1688 mUpperMeshMapping.Add(8, "mElbowLeft");
\r
1689 mUpperMeshMapping.Add(9, "mWristLeft");
\r
1690 mUpperMeshMapping.Add(10, "");
\r
1691 mUpperMeshMapping.Add(11, "mCollarRight");
\r
1692 mUpperMeshMapping.Add(12, "mShoulderRight");
\r
1693 mUpperMeshMapping.Add(13, "mElbowRight");
\r
1694 mUpperMeshMapping.Add(14, "mWristRight");
\r
1695 mUpperMeshMapping.Add(15, "");
\r
1697 mLowerMeshMapping.Add(1, "mPelvis");
\r
1698 mLowerMeshMapping.Add(2, "mHipRight");
\r
1699 mLowerMeshMapping.Add(3, "mKneeRight");
\r
1700 mLowerMeshMapping.Add(4, "mAnkleRight");
\r
1701 mLowerMeshMapping.Add(5, "");
\r
1702 mLowerMeshMapping.Add(6, "mHipLeft");
\r
1703 mLowerMeshMapping.Add(7, "mKneeLeft");
\r
1704 mLowerMeshMapping.Add(8, "mAnkleLeft");
\r
1705 mLowerMeshMapping.Add(9, "");
\r
1707 mHeadMeshMapping.Add(1, "mNeck");
\r
1708 mHeadMeshMapping.Add(2, "mHead");
\r
1709 mHeadMeshMapping.Add(3, "");
\r
1714 public void deformbone(string name, Vector3 pos, Vector3 scale, Quaternion rotation)
\r
1717 if (mBones.TryGetValue(name, out bone))
\r
1719 bone.deformbone(pos, scale, rotation);
\r
1723 //TODO check offset and rot calcuations should each offset be multiplied by its parent rotation in
\r
1724 // a standard child/parent rot/offset way?
\r
1725 public Vector3 getOffset(string bonename)
\r
1728 if (mBones.TryGetValue(bonename, out b))
\r
1730 return (b.getOffset());
\r
1734 return Vector3.Zero;
\r
1738 public Quaternion getRotation(string bonename)
\r
1741 if (mBones.TryGetValue(bonename, out b))
\r
1743 return (b.getRotation());
\r
1747 return Quaternion.Identity;
\r
1751 // Add animations to the global decoded list
\r
1752 // TODO garbage collect unused animations somehow
\r
1753 public static void addanimation(OpenMetaverse.Assets.Asset asset)
\r
1755 BinBVHAnimationReader b = new BinBVHAnimationReader(asset.AssetData);
\r
1756 if (!mKnownAnimations.ContainsKey(asset.AssetID))
\r
1758 Logger.Log("Adding new decoded animaton known animations " + asset.AssetID.ToString(), Helpers.LogLevel.Info);
\r
1759 mKnownAnimations.Add(asset.AssetID, b);
\r
1763 public bool addplayinganimation(Animation anim)
\r
1766 if (!animlist.ContainsKey(anim.AnimationID))
\r
1768 Logger.Log("Adding new playing animaton " + anim.AnimationID.ToString(), Helpers.LogLevel.Info);
\r
1769 animlist.Add(anim.AnimationID, anim);
\r
1772 mNeedsUpdate = true;
\r
1774 if (skeleton.mKnownAnimations.ContainsKey(anim.AnimationID))
\r
1783 public void firstkeyframe()
\r
1785 if (mNeedsUpdate == false)
\r
1788 foreach (Animation anim in this.animlist.Values) // TODO priority sort
\r
1790 BinBVHAnimationReader b;
\r
1791 if (skeleton.mKnownAnimations.TryGetValue(anim.AnimationID, out b))
\r
1793 foreach (binBVHJoint joint in b.joints)
\r
1795 //actually get the last for a pose the last frame is probably the static one once
\r
1796 //we have reached position
\r
1797 binBVHJointKey rot = joint.rotationkeys[joint.rotationkeys.Length-1];
\r
1799 Vector3 pos = new Vector3(0, 0, 0);
\r
1800 //binBVHJointKey pos = joint.positionkeys[0];
\r
1802 deformbone(joint.Name, new Vector3(0, 0, 0), pos, new Quaternion(rot.key_element.X, rot.key_element.Y, rot.key_element.Z));
\r
1806 mNeedsMeshRebuild = true;
\r
1810 mNeedsUpdate = false;
\r
1816 public string name;
\r
1817 public Vector3 pos;
\r
1818 public Quaternion rot;
\r
1819 public Vector3 scale;
\r
1820 public Vector3 piviot;
\r
1822 public Vector3 orig_pos;
\r
1823 public Quaternion orig_rot;
\r
1824 public Vector3 orig_scale;
\r
1825 public Vector3 orig_piviot;
\r
1827 Matrix4 mDeformMatrix = Matrix4.Identity;
\r
1829 public Bone parent;
\r
1831 public List<Bone> children = new List<Bone>();
\r
1833 public static Dictionary<string, Bone> mBones = new Dictionary<string, Bone>();
\r
1834 public static Dictionary<int, Bone> mIndexedBones = new Dictionary<int, Bone>();
\r
1835 static int boneaddindex = 0;
\r
1841 public Bone(Bone source)
\r
1843 name = String.Copy(source.name);
\r
1844 pos = new Vector3(source.pos);
\r
1845 rot = new Quaternion(source.rot);
\r
1846 scale = new Vector3(source.scale);
\r
1847 piviot = new Vector3(source.piviot);
\r
1849 orig_piviot = source.orig_piviot;
\r
1850 orig_pos = source.orig_pos;
\r
1851 orig_rot = source.orig_rot;
\r
1852 orig_scale = source.orig_scale;
\r
1854 mDeformMatrix = new Matrix4(source.mDeformMatrix);
\r
1857 public static void loadbones(string skeletonfilename)
\r
1860 string basedir = Directory.GetCurrentDirectory() + System.IO.Path.DirectorySeparatorChar + "character" + System.IO.Path.DirectorySeparatorChar;
\r
1861 XmlDocument skeleton = new XmlDocument();
\r
1862 skeleton.Load(basedir + skeletonfilename);
\r
1863 XmlNode boneslist = skeleton.GetElementsByTagName("linden_skeleton")[0];
\r
1864 addbone(boneslist.ChildNodes[0], null);
\r
1867 public static void addbone(XmlNode bone, Bone parent)
\r
1870 if (bone.Name != "bone")
\r
1873 Bone b = new Bone();
\r
1874 b.name = bone.Attributes.GetNamedItem("name").Value;
\r
1876 string pos = bone.Attributes.GetNamedItem("pos").Value;
\r
1877 string[] posparts = pos.Split(' ');
\r
1878 b.pos = new Vector3(float.Parse(posparts[0]), float.Parse(posparts[1]), float.Parse(posparts[2]));
\r
1879 b.orig_pos = new Vector3(b.pos);
\r
1881 string rot = bone.Attributes.GetNamedItem("rot").Value;
\r
1882 string[] rotparts = rot.Split(' ');
\r
1883 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
1884 b.orig_rot = new Quaternion(b.rot);
\r
1886 string scale = bone.Attributes.GetNamedItem("scale").Value;
\r
1887 string[] scaleparts = scale.Split(' ');
\r
1888 b.scale = new Vector3(float.Parse(scaleparts[0]), float.Parse(scaleparts[1]), float.Parse(scaleparts[2]));
\r
1889 b.orig_scale = new Vector3(b.scale);
\r
1892 float[] deform = Math3D.CreateSRTMatrix(new Vector3(1, 1, 1), b.rot, b.orig_pos);
\r
1893 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
1897 b.parent = parent;
\r
1899 if (parent != null)
\r
1900 parent.children.Add(b);
\r
1902 mBones.Add(b.name, b);
\r
1903 mIndexedBones.Add(boneaddindex++, b);
\r
1905 Logger.Log("Found bone " + b.name, Helpers.LogLevel.Info);
\r
1907 foreach (XmlNode childbone in bone.ChildNodes)
\r
1909 addbone(childbone, b);
\r
1914 public void deformbone(Vector3 pos, Vector3 scale, Quaternion rot)
\r
1916 float[] deform = Math3D.CreateSRTMatrix(scale, rot, this.orig_pos);
\r
1917 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
1918 this.pos = Bone.mBones[name].orig_pos + pos;
\r
1919 this.scale = Bone.mBones[name].orig_scale + scale;
\r
1920 this.rot = Bone.mBones[name].orig_rot * rot;
\r
1923 public Matrix4 getdeform()
\r
1925 if (this.parent != null)
\r
1927 return mDeformMatrix * parent.getdeform();
\r
1931 return mDeformMatrix;
\r
1935 public Vector3 getOffset()
\r
1937 if (parent != null)
\r
1939 Quaternion totalrot = getParentRot(); // we don't want this joints rotation included
\r
1940 Vector3 parento = parent.getOffset();
\r
1941 Vector3 mepre = pos * scale;
\r
1942 mepre = mepre * totalrot;
\r
1943 return parento + mepre;
\r
1947 return (pos * scale) * getRotation();
\r
1951 public Vector3 getMyOffset()
\r
1953 return pos * scale;
\r
1956 public Vector3 getOrigOffset()
\r
1958 if (parent != null)
\r
1960 return ((parent.getOrigOffset()) + orig_pos);
\r
1968 public static Quaternion getRotation(string bonename)
\r
1971 if (mBones.TryGetValue(bonename, out b))
\r
1973 return (b.getRotation());
\r
1977 return Quaternion.Identity;
\r
1982 public Quaternion getParentRot()
\r
1984 Quaternion totalrot = Quaternion.Identity;
\r
1986 if (parent != null)
\r
1988 totalrot = parent.getRotation();
\r
1995 public Quaternion getRotation()
\r
1997 Quaternion totalrot = rot;
\r
1999 if (parent != null)
\r
2001 totalrot = rot * parent.getRotation();
\r
2009 public class VisualParamEx
\r
2012 static public Dictionary<int, VisualParamEx> allParams = new Dictionary<int, VisualParamEx>();
\r
2013 static public Dictionary<int, VisualParamEx> deformParams = new Dictionary<int, VisualParamEx>();
\r
2014 static public Dictionary<int, VisualParamEx> morphParams = new Dictionary<int, VisualParamEx>();
\r
2015 static public Dictionary<int, VisualParamEx> drivenParams = new Dictionary<int, VisualParamEx>();
\r
2016 static public SortedList tweakable_params = new SortedList();
\r
2018 public Dictionary<string, Vector3> BoneDeforms = null;
\r
2019 public Dictionary<string, VolumeDeform> VolumeDeforms = null;
\r
2020 public List<driven> childparams = null;
\r
2022 public string morphmesh = null;
\r
2026 VISUAL_PARAM_GROUP_TWEAKABLE = 0,
\r
2027 VISUAL_PARAM_GROUP_ANIMATABLE,
\r
2028 VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT,
\r
2031 public struct VolumeDeform
\r
2033 public string name;
\r
2034 public Vector3 scale;
\r
2035 public Vector3 pos;
\r
2038 public enum EparamSex
\r
2045 public enum ParamType
\r
2054 public struct driven
\r
2057 public float max1;
\r
2058 public float max2;
\r
2059 public float min1;
\r
2060 public float min2;
\r
2061 public bool hasMinMax;
\r
2064 public string meshname;
\r
2066 /// <summary>Index of this visual param</summary>
\r
2067 public int ParamID;
\r
2068 /// <summary>Internal name</summary>
\r
2069 public string Name;
\r
2070 /// <summary>Group ID this parameter belongs to</summary>
\r
2072 /// <summary>Name of the wearable this parameter belongs to</summary>
\r
2073 public string Wearable;
\r
2074 /// <summary>Displayable label of this characteristic</summary>
\r
2075 public string Label;
\r
2076 /// <summary>Displayable label for the minimum value of this characteristic</summary>
\r
2077 public string LabelMin;
\r
2078 /// <summary>Displayable label for the maximum value of this characteristic</summary>
\r
2079 public string LabelMax;
\r
2080 /// <summary>Default value</summary>
\r
2081 public float DefaultValue;
\r
2082 /// <summary>Minimum value</summary>
\r
2083 public float MinValue;
\r
2084 /// <summary>Maximum value</summary>
\r
2085 public float MaxValue;
\r
2086 /// <summary>Is this param used for creation of bump layer?</summary>
\r
2087 public bool IsBumpAttribute;
\r
2088 /// <summary>Alpha blending/bump info</summary>
\r
2089 public VisualAlphaParam? AlphaParams;
\r
2090 /// <summary>Color information</summary>
\r
2091 public VisualColorParam? ColorParams;
\r
2092 /// <summary>Array of param IDs that are drivers for this parameter</summary>
\r
2093 public int[] Drivers;
\r
2094 /// <summary>The Avatar Sex that this parameter applies to</summary>
\r
2095 public EparamSex sex;
\r
2097 public ParamType pType;
\r
2099 public static int count = 0;
\r
2102 /// Set all the values through the constructor
\r
2104 /// <param name="paramID">Index of this visual param</param>
\r
2105 /// <param name="name">Internal name</param>
\r
2106 /// <param name="group"></param>
\r
2107 /// <param name="wearable"></param>
\r
2108 /// <param name="label">Displayable label of this characteristic</param>
\r
2109 /// <param name="labelMin">Displayable label for the minimum value of this characteristic</param>
\r
2110 /// <param name="labelMax">Displayable label for the maximum value of this characteristic</param>
\r
2111 /// <param name="def">Default value</param>
\r
2112 /// <param name="min">Minimum value</param>
\r
2113 /// <param name="max">Maximum value</param>
\r
2114 /// <param name="isBumpAttribute">Is this param used for creation of bump layer?</param>
\r
2115 /// <param name="drivers">Array of param IDs that are drivers for this parameter</param>
\r
2116 /// <param name="alpha">Alpha blending/bump info</param>
\r
2117 /// <param name="colorParams">Color information</param>
\r
2118 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
2120 ParamID = paramID;
\r
2123 Wearable = wearable;
\r
2125 LabelMin = labelMin;
\r
2126 LabelMax = labelMax;
\r
2127 DefaultValue = def;
\r
2130 IsBumpAttribute = isBumpAttribute;
\r
2131 Drivers = drivers;
\r
2132 AlphaParams = alpha;
\r
2133 ColorParams = colorParams;
\r
2134 sex = EparamSex.SEX_BOTH;
\r
2137 public VisualParamEx(XmlNode node, ParamType pt)
\r
2141 ParamID = Int32.Parse(node.Attributes.GetNamedItem("id").Value);
\r
2142 Name = node.Attributes.GetNamedItem("name").Value;
\r
2143 Group = Int32.Parse(node.Attributes.GetNamedItem("group").Value);
\r
2145 //These dont exist for facal expresion morphs
\r
2146 if (node.Attributes.GetNamedItem("wearable") != null)
\r
2147 Wearable = node.Attributes.GetNamedItem("wearable").Value;
\r
2149 MinValue = float.Parse(node.Attributes.GetNamedItem("value_min").Value);
\r
2150 MaxValue = float.Parse(node.Attributes.GetNamedItem("value_max").Value);
\r
2152 // These do not exists for driven parameters
\r
2153 if (node.Attributes.GetNamedItem("label_min") != null)
\r
2155 LabelMin = node.Attributes.GetNamedItem("label_min").Value;
\r
2158 if (node.Attributes.GetNamedItem("label_max") != null)
\r
2160 LabelMax = node.Attributes.GetNamedItem("label_max").Value;
\r
2163 XmlNode sexnode = node.Attributes.GetNamedItem("sex");
\r
2165 if (sexnode != null)
\r
2167 if (sexnode.Value == "male")
\r
2169 sex = EparamSex.SEX_MALE;
\r
2173 sex = EparamSex.SEX_FEMALE;
\r
2178 Group = int.Parse(node.Attributes.GetNamedItem("group").Value);
\r
2180 if (Group == (int)GroupType.VISUAL_PARAM_GROUP_TWEAKABLE)
\r
2182 if (!tweakable_params.ContainsKey(ParamID)) //stupid duplicate shared params
\r
2184 tweakable_params.Add(this.ParamID, this);
\r
2186 Logger.Log(String.Format("Adding tweakable paramater ID {0} {1}", count, this.Name), Helpers.LogLevel.Info);
\r
2190 //TODO other paramaters but these arew concerned with editing the GUI display so not too fussed at the moment
\r
2194 allParams.Add(ParamID, this);
\r
2196 catch (Exception e)
\r
2198 Logger.Log("Duplicate VisualParam in allParams id " + ParamID.ToString(), Helpers.LogLevel.Info);
\r
2201 if (pt == ParamType.TYPE_BONEDEFORM)
\r
2203 // If we are in the skeleton section then we also have bone deforms to parse
\r
2204 BoneDeforms = new Dictionary<string, Vector3>();
\r
2205 if (node.HasChildNodes && node.ChildNodes[0].HasChildNodes)
\r
2207 ParseBoneDeforms(node.ChildNodes[0].ChildNodes);
\r
2209 deformParams.Add(ParamID, this);
\r
2212 if (pt == ParamType.TYPE_MORPH)
\r
2214 VolumeDeforms = new Dictionary<string, VolumeDeform>();
\r
2215 if (node.HasChildNodes && node.ChildNodes[0].HasChildNodes)
\r
2217 ParseVolumeDeforms(node.ChildNodes[0].ChildNodes);
\r
2222 morphParams.Add(ParamID, this);
\r
2224 catch (Exception e)
\r
2226 Logger.Log("Duplicate VisualParam in morphParams id " + ParamID.ToString(), Helpers.LogLevel.Info);
\r
2231 if (pt == ParamType.TYPE_DRIVER)
\r
2233 childparams = new List<driven>();
\r
2234 if (node.HasChildNodes && node.ChildNodes[0].HasChildNodes) //LAZY
\r
2236 ParseDrivers(node.ChildNodes[0].ChildNodes);
\r
2239 drivenParams.Add(ParamID, this);
\r
2243 if (pt == ParamType.TYPE_COLOR)
\r
2245 if (node.HasChildNodes)
\r
2247 foreach (XmlNode colorchild in node.ChildNodes)
\r
2249 if (colorchild.Name == "param_color")
\r
2251 //TODO extract <value color="50, 25, 5, 255" />
\r
2260 void ParseBoneDeforms(XmlNodeList deforms)
\r
2262 foreach (XmlNode node in deforms)
\r
2264 if (node.Name == "bone")
\r
2266 string name = node.Attributes.GetNamedItem("name").Value;
\r
2267 Vector3 scale = XmlParseVector(node.Attributes.GetNamedItem("scale").Value);
\r
2268 BoneDeforms.Add(name, scale);
\r
2273 void ParseVolumeDeforms(XmlNodeList deforms)
\r
2275 foreach (XmlNode node in deforms)
\r
2277 if (node.Name == "volume_morph")
\r
2279 VolumeDeform vd = new VolumeDeform();
\r
2280 vd.name = node.Attributes.GetNamedItem("name").Value;
\r
2281 vd.name = vd.name.ToLower();
\r
2283 if (node.Attributes.GetNamedItem("scale") != null)
\r
2285 vd.scale = XmlParseVector(node.Attributes.GetNamedItem("scale").Value);
\r
2289 vd.scale = new Vector3(0, 0, 0);
\r
2292 if (node.Attributes.GetNamedItem("pos") != null)
\r
2294 vd.pos = XmlParseVector(node.Attributes.GetNamedItem("pos").Value);
\r
2298 vd.pos = new Vector3(0f, 0f, 0f);
\r
2301 VolumeDeforms.Add(vd.name, vd);
\r
2306 void ParseDrivers(XmlNodeList drivennodes)
\r
2308 foreach (XmlNode node in drivennodes)
\r
2310 if (node.Name == "driven")
\r
2312 driven d = new driven();
\r
2314 d.id = Int32.Parse(node.Attributes.GetNamedItem("id").Value);
\r
2315 XmlNode param = node.Attributes.GetNamedItem("max1");
\r
2316 if (param != null)
\r
2318 d.max1 = float.Parse(param.Value);
\r
2319 d.max2 = float.Parse(node.Attributes.GetNamedItem("max2").Value);
\r
2320 d.min1 = float.Parse(node.Attributes.GetNamedItem("min1").Value);
\r
2321 d.max2 = float.Parse(node.Attributes.GetNamedItem("min2").Value);
\r
2322 d.hasMinMax = true;
\r
2326 d.hasMinMax = false;
\r
2329 childparams.Add(d);
\r
2335 public static Vector3 XmlParseVector(string data)
\r
2337 string[] posparts = data.Split(' ');
\r
2338 return new Vector3(float.Parse(posparts[0]), float.Parse(posparts[1]), float.Parse(posparts[2]));
\r
2341 public static Quaternion XmlParseRotation(string data)
\r
2343 string[] rotparts = data.Split(' ');
\r
2344 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