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
292 public abstract class SceneObject: IComparable
\r
294 public Vector3 SimPosition;
\r
295 public Quaternion SimRotation;
\r
296 public float DistanceSquared;
\r
297 public BoundingVolume BoundingVolume;
\r
298 public bool PositionUpdated;
\r
299 public SceneObjectType Type = SceneObjectType.None;
\r
300 public virtual Primitive BasePrim { get; set; }
\r
302 public virtual int CompareTo(object other)
\r
304 SceneObject o = (SceneObject)other;
\r
305 if (this.DistanceSquared < o.DistanceSquared)
\r
307 else if (this.DistanceSquared > o.DistanceSquared)
\r
314 public class RenderPrimitive : SceneObject, IDisposable
\r
316 public Primitive Prim;
\r
317 public List<Face> Faces;
\r
319 public RenderPrimitive()
\r
321 Type = SceneObjectType.Primitive;
\r
324 public virtual void Dispose()
\r
328 public override Primitive BasePrim
\r
330 get { return Prim; }
\r
331 set { Prim = value; }
\r
334 public override string ToString()
\r
336 uint id = Prim == null ? 0 : Prim.LocalID;
\r
337 double distance = Math.Sqrt(DistanceSquared);
\r
338 return string.Format("LocalID: {0}, distance {0.00}", id, distance);
\r
342 public static class Render
\r
344 public static IRendering Plugin;
\r
347 public static class RHelp
\r
349 static float t1 = 0.075f;
\r
350 static float t2 = t1 / 5.7f;
\r
352 public static Vector3 Smoothed1stOrder(Vector3 curPos, Vector3 targetPos, double lastFrameTime)
\r
354 int numIterations = (int)(lastFrameTime * 100);
\r
357 curPos += (targetPos - curPos) * t1;
\r
360 while (numIterations > 0);
\r
361 if (Vector3.DistanceSquared(curPos, targetPos) < 0.000001)
\r
363 curPos = targetPos;
\r
368 public static Vector3 Smoothed2ndOrder(Vector3 curPos, Vector3 targetPos, ref Vector3 accel, double lastFrameTime)
\r
370 int numIterations = (int)(lastFrameTime * 100);
\r
373 accel += (targetPos - accel - curPos) * t1;
\r
374 curPos += accel * t2;
\r
377 while (numIterations > 0);
\r
378 if (Vector3.DistanceSquared(curPos, targetPos) < 0.000001)
\r
380 curPos = targetPos;
\r
385 public static OpenTK.Vector2 TKVector3(Vector2 v)
\r
387 return new OpenTK.Vector2(v.X, v.Y);
\r
390 public static OpenTK.Vector3 TKVector3(Vector3 v)
\r
392 return new OpenTK.Vector3(v.X, v.Y, v.Z);
\r
395 public static OpenTK.Vector4 TKVector3(Vector4 v)
\r
397 return new OpenTK.Vector4(v.X, v.Y, v.Z, v.W);
\r
402 /// Represents camera object
\r
404 public class Camera
\r
407 Vector3 mFocalPoint;
\r
410 /// <summary>Camera position</summary>
\r
411 public Vector3 Position { get { return mPosition; } set { mPosition = value; Modify(); } }
\r
412 /// <summary>Camera target</summary>
\r
413 public Vector3 FocalPoint { get { return mFocalPoint; } set { mFocalPoint = value; Modify(); } }
\r
414 /// <summary>Zoom level</summary>
\r
416 /// <summary>Draw distance</summary>
\r
418 /// <summary>Has camera been modified</summary>
\r
419 public bool Modified { get { return mModified; } set { mModified = value; } }
\r
421 public double TimeToTarget = 0d;
\r
423 public Vector3 RenderPosition;
\r
424 public Vector3 RenderFocalPoint;
\r
431 public void Step(double time)
\r
433 if (RenderPosition != Position)
\r
435 RenderPosition = RHelp.Smoothed1stOrder(RenderPosition, Position, time);
\r
438 if (RenderFocalPoint != FocalPoint)
\r
440 RenderFocalPoint = RHelp.Smoothed1stOrder(RenderFocalPoint, FocalPoint, time);
\r
445 [Obsolete("Use Step(), left in here for reference")]
\r
446 public void Step2(double time)
\r
448 TimeToTarget -= time;
\r
449 if (TimeToTarget <= time)
\r
457 float pctElapsed = (float)(time / TimeToTarget);
\r
459 if (RenderPosition != Position)
\r
461 float distance = Vector3.Distance(RenderPosition, Position);
\r
462 RenderPosition = Vector3.Lerp(RenderPosition, Position, (float)(distance * pctElapsed));
\r
465 if (RenderFocalPoint != FocalPoint)
\r
467 RenderFocalPoint = Interpolate(RenderFocalPoint, FocalPoint, pctElapsed);
\r
471 Vector3 Interpolate(Vector3 start, Vector3 end, float fraction)
\r
473 float distance = Vector3.Distance(start, end);
\r
474 Vector3 direction = end - start;
\r
475 return start + direction * fraction;
\r
478 public void EndMove()
\r
482 RenderPosition = Position;
\r
483 RenderFocalPoint = FocalPoint;
\r
487 public static class MeshToOBJ
\r
489 public static bool MeshesToOBJ(Dictionary<uint, FacetedMesh> meshes, string filename)
\r
491 StringBuilder obj = new StringBuilder();
\r
492 StringBuilder mtl = new StringBuilder();
\r
494 FileInfo objFileInfo = new FileInfo(filename);
\r
496 string mtlFilename = objFileInfo.FullName.Substring(objFileInfo.DirectoryName.Length + 1,
\r
497 objFileInfo.FullName.Length - (objFileInfo.DirectoryName.Length + 1) - 4) + ".mtl";
\r
499 obj.AppendLine("# Created by libprimrender");
\r
500 obj.AppendLine("mtllib ./" + mtlFilename);
\r
503 mtl.AppendLine("# Created by libprimrender");
\r
507 foreach (FacetedMesh mesh in meshes.Values)
\r
509 for (int j = 0; j < mesh.Faces.Count; j++)
\r
511 Face face = mesh.Faces[j];
\r
513 if (face.Vertices.Count > 2)
\r
515 string mtlName = String.Format("material{0}-{1}", primNr, face.ID);
\r
516 Primitive.TextureEntryFace tex = face.TextureFace;
\r
517 string texName = tex.TextureID.ToString() + ".tga";
\r
519 // FIXME: Convert the source to TGA (if needed) and copy to the destination
\r
521 float shiny = 0.00f;
\r
524 case Shininess.High:
\r
527 case Shininess.Medium:
\r
530 case Shininess.Low:
\r
535 obj.AppendFormat("g face{0}-{1}{2}", primNr, face.ID, Environment.NewLine);
\r
537 mtl.AppendLine("newmtl " + mtlName);
\r
538 mtl.AppendFormat("Ka {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);
\r
539 mtl.AppendFormat("Kd {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);
\r
540 //mtl.AppendFormat("Ks {0} {1} {2}{3}");
\r
541 mtl.AppendLine("Tr " + tex.RGBA.A);
\r
542 mtl.AppendLine("Ns " + shiny);
\r
543 mtl.AppendLine("illum 1");
\r
544 if (tex.TextureID != UUID.Zero && tex.TextureID != Primitive.TextureEntry.WHITE_TEXTURE)
\r
545 mtl.AppendLine("map_Kd ./" + texName);
\r
548 // Write the vertices, texture coordinates, and vertex normals for this side
\r
549 for (int k = 0; k < face.Vertices.Count; k++)
\r
551 Vertex vertex = face.Vertices[k];
\r
555 Vector3 pos = vertex.Position;
\r
558 pos *= mesh.Prim.Scale;
\r
561 pos *= mesh.Prim.Rotation;
\r
563 // The root prim position is sim-relative, while child prim positions are
\r
564 // parent-relative. We want to apply parent-relative translations but not
\r
565 // sim-relative ones
\r
566 if (mesh.Prim.ParentID != 0)
\r
567 pos += mesh.Prim.Position;
\r
569 obj.AppendFormat("v {0} {1} {2}{3}", pos.X, pos.Y, pos.Z, Environment.NewLine);
\r
573 #region Texture Coord
\r
575 obj.AppendFormat("vt {0} {1}{2}", vertex.TexCoord.X, vertex.TexCoord.Y,
\r
576 Environment.NewLine);
\r
578 #endregion Texture Coord
\r
580 #region Vertex Normal
\r
582 // HACK: Sometimes normals are getting set to <NaN,NaN,NaN>
\r
583 if (!Single.IsNaN(vertex.Normal.X) && !Single.IsNaN(vertex.Normal.Y) && !Single.IsNaN(vertex.Normal.Z))
\r
584 obj.AppendFormat("vn {0} {1} {2}{3}", vertex.Normal.X, vertex.Normal.Y, vertex.Normal.Z,
\r
585 Environment.NewLine);
\r
587 obj.AppendLine("vn 0.0 1.0 0.0");
\r
589 #endregion Vertex Normal
\r
592 obj.AppendFormat("# {0} vertices{1}", face.Vertices.Count, Environment.NewLine);
\r
594 obj.AppendLine("usemtl " + mtlName);
\r
598 // Write all of the faces (triangles) for this side
\r
599 for (int k = 0; k < face.Indices.Count / 3; k++)
\r
601 obj.AppendFormat("f -{0}/-{0}/-{0} -{1}/-{1}/-{1} -{2}/-{2}/-{2}{3}",
\r
602 face.Vertices.Count - face.Indices[k * 3 + 0],
\r
603 face.Vertices.Count - face.Indices[k * 3 + 1],
\r
604 face.Vertices.Count - face.Indices[k * 3 + 2],
\r
605 Environment.NewLine);
\r
608 obj.AppendFormat("# {0} elements{1}", face.Indices.Count / 3, Environment.NewLine);
\r
611 #endregion Elements
\r
619 File.WriteAllText(filename, obj.ToString());
\r
620 File.WriteAllText(mtlFilename, mtl.ToString());
\r
631 public static class Math3D
\r
639 public static float[] CreateTranslationMatrix(Vector3 v)
\r
641 float[] mat = new float[16];
\r
646 mat[0] = mat[5] = mat[10] = mat[15] = 1;
\r
651 public static float[] CreateRotationMatrix(Quaternion q)
\r
653 float[] mat = new float[16];
\r
655 // Transpose the quaternion (don't ask me why)
\r
660 float x2 = q.X + q.X;
\r
661 float y2 = q.Y + q.Y;
\r
662 float z2 = q.Z + q.Z;
\r
663 float xx = q.X * x2;
\r
664 float xy = q.X * y2;
\r
665 float xz = q.X * z2;
\r
666 float yy = q.Y * y2;
\r
667 float yz = q.Y * z2;
\r
668 float zz = q.Z * z2;
\r
669 float wx = q.W * x2;
\r
670 float wy = q.W * y2;
\r
671 float wz = q.W * z2;
\r
673 mat[0] = 1.0f - (yy + zz);
\r
679 mat[5] = 1.0f - (xx + zz);
\r
685 mat[10] = 1.0f - (xx + yy);
\r
696 public static float[] CreateSRTMatrix(Vector3 scale, Quaternion q, Vector3 pos)
\r
698 float[] mat = new float[16];
\r
700 // Transpose the quaternion (don't ask me why)
\r
705 float x2 = q.X + q.X;
\r
706 float y2 = q.Y + q.Y;
\r
707 float z2 = q.Z + q.Z;
\r
708 float xx = q.X * x2;
\r
709 float xy = q.X * y2;
\r
710 float xz = q.X * z2;
\r
711 float yy = q.Y * y2;
\r
712 float yz = q.Y * z2;
\r
713 float zz = q.Z * z2;
\r
714 float wx = q.W * x2;
\r
715 float wy = q.W * y2;
\r
716 float wz = q.W * z2;
\r
718 mat[0] = (1.0f - (yy + zz))*scale.X;
\r
719 mat[1] = (xy - wz)*scale.X;
\r
720 mat[2] = (xz + wy) * scale.X;
\r
723 mat[4] = (xy + wz) * scale.Y;
\r
724 mat[5] = (1.0f - (xx + zz)) * scale.Y;
\r
725 mat[6] = (yz - wx) * scale.Y;
\r
728 mat[8] = (xz - wy) * scale.Z;
\r
729 mat[9] = (yz + wx) * scale.Z;
\r
730 mat[10] = (1.0f - (xx + yy)) * scale.Z;
\r
743 public static float[] CreateScaleMatrix(Vector3 v)
\r
745 float[] mat = new float[16];
\r
755 public static float[] Lerp(float[] matrix1, float[] matrix2, float amount)
\r
758 float[] lerp = new float[16];
\r
759 //Probably not doing this as a loop is cheaper(unrolling)
\r
760 //also for performance we probably should not create new objects
\r
762 for (int x = 0; x < 16; x++)
\r
764 lerp[x] = matrix1[x] + ((matrix2[x] - matrix1[x]) * amount);
\r
771 public static bool GluProject(OpenTK.Vector3 objPos, OpenTK.Matrix4 modelMatrix, OpenTK.Matrix4 projMatrix, int[] viewport, out OpenTK.Vector3 screenPos)
\r
773 OpenTK.Vector4 _in;
\r
774 OpenTK.Vector4 _out;
\r
781 _out = OpenTK.Vector4.Transform(_in, modelMatrix);
\r
782 _in = OpenTK.Vector4.Transform(_out, projMatrix);
\r
786 screenPos = OpenTK.Vector3.Zero;
\r
793 /* Map x, y and z to range 0-1 */
\r
794 _in.X = _in.X * 0.5f + 0.5f;
\r
795 _in.Y = _in.Y * 0.5f + 0.5f;
\r
796 _in.Z = _in.Z * 0.5f + 0.5f;
\r
798 /* Map x,y to viewport */
\r
799 _in.X = _in.X * viewport[2] + viewport[0];
\r
800 _in.Y = _in.Y * viewport[3] + viewport[1];
\r
802 screenPos.X = _in.X;
\r
803 screenPos.Y = _in.Y;
\r
804 screenPos.Z = _in.Z;
\r
810 public class attachment_point
\r
812 public string name;
\r
813 public string joint;
\r
814 public Vector3 position;
\r
815 public Quaternion rotation;
\r
819 public GLMesh jointmesh;
\r
820 public int jointmeshindex;
\r
822 public attachment_point(XmlNode node)
\r
824 name = node.Attributes.GetNamedItem("name").Value;
\r
825 joint = node.Attributes.GetNamedItem("joint").Value;
\r
826 position = VisualParamEx.XmlParseVector(node.Attributes.GetNamedItem("position").Value);
\r
827 rotation = VisualParamEx.XmlParseRotation(node.Attributes.GetNamedItem("rotation").Value);
\r
828 id = Int32.Parse(node.Attributes.GetNamedItem("id").Value);
\r
829 group = Int32.Parse(node.Attributes.GetNamedItem("group").Value);
\r
835 /// Subclass of LindenMesh that adds vertex, index, and texture coordinate
\r
836 /// arrays suitable for pushing direct to OpenGL
\r
838 public class GLMesh : LindenMesh
\r
841 /// Subclass of LODMesh that adds an index array suitable for pushing
\r
842 /// direct to OpenGL
\r
846 public int teFaceID;
\r
847 public Dictionary<int, VisualParamEx> _evp = new Dictionary<int, VisualParamEx>();
\r
849 new public class LODMesh : LindenMesh.LODMesh
\r
851 public ushort[] Indices;
\r
853 public override void LoadMesh(string filename)
\r
855 base.LoadMesh(filename);
\r
857 // Generate the index array
\r
858 Indices = new ushort[_numFaces * 3];
\r
860 for (int i = 0; i < _numFaces; i++)
\r
862 Indices[current++] = (ushort)_faces[i].Indices[0];
\r
863 Indices[current++] = (ushort)_faces[i].Indices[1];
\r
864 Indices[current++] = (ushort)_faces[i].Indices[2];
\r
872 public struct GLData
\r
874 public float[] Vertices;
\r
875 public float[] Normals;
\r
876 public ushort[] Indices;
\r
877 public float[] TexCoords;
\r
878 public Vector3 Center;
\r
879 public float[] weights; //strictly these are constant and don't need instancing with the GLMesh
\r
880 public string[] skinJoints; //strictly these are constant and don't need instancing with the GLMesh
\r
883 public static GLData baseRenderData;
\r
884 public GLData RenderData;
\r
885 public GLData OrigRenderData;
\r
886 public GLData MorphRenderData;
\r
888 public GLAvatar av;
\r
890 public GLMesh(string name)
\r
895 public GLMesh(GLMesh source,GLAvatar av)
\r
899 // Make a new GLMesh copy from the supplied source
\r
901 RenderData.Vertices = new float[source.RenderData.Vertices.Length];
\r
902 RenderData.Normals = new float[source.RenderData.Normals.Length];
\r
903 RenderData.TexCoords = new float[source.RenderData.TexCoords.Length];
\r
904 RenderData.Indices = new ushort[source.RenderData.Indices.Length];
\r
906 RenderData.weights = new float[source.RenderData.weights.Length];
\r
907 RenderData.skinJoints = new string[source.RenderData.skinJoints.Length];
\r
909 Array.Copy(source.RenderData.Vertices,RenderData.Vertices,source.RenderData.Vertices.Length);
\r
910 Array.Copy(source.RenderData.Normals, RenderData.Normals, source.RenderData.Normals.Length);
\r
912 Array.Copy(source.RenderData.TexCoords, RenderData.TexCoords, source.RenderData.TexCoords.Length);
\r
913 Array.Copy(source.RenderData.Indices, RenderData.Indices, source.RenderData.Indices.Length);
\r
914 Array.Copy(source.RenderData.weights, RenderData.weights, source.RenderData.weights.Length);
\r
915 Array.Copy(source.RenderData.skinJoints, RenderData.skinJoints, source.RenderData.skinJoints.Length);
\r
918 RenderData.Center = new Vector3(source.RenderData.Center);
\r
920 teFaceID = source.teFaceID;
\r
922 _rotationAngles = new Vector3(source.RotationAngles);
\r
923 _scale = new Vector3(source.Scale);
\r
924 _position = new Vector3(source.Position);
\r
926 // We should not need to instance these the reference from the top should be constant
\r
927 _evp = source._evp;
\r
928 _morphs = source._morphs;
\r
930 OrigRenderData.Indices = new ushort[source.RenderData.Indices.Length];
\r
931 OrigRenderData.TexCoords = new float[source.RenderData.TexCoords.Length];
\r
932 OrigRenderData.Vertices = new float[source.RenderData.Vertices.Length];
\r
934 MorphRenderData.Vertices = new float[source.RenderData.Vertices.Length];
\r
936 Array.Copy(source.RenderData.Vertices, OrigRenderData.Vertices, source.RenderData.Vertices.Length);
\r
937 Array.Copy(source.RenderData.Vertices, MorphRenderData.Vertices, source.RenderData.Vertices.Length);
\r
939 Array.Copy(source.RenderData.TexCoords, OrigRenderData.TexCoords, source.RenderData.TexCoords.Length);
\r
940 Array.Copy(source.RenderData.Indices, OrigRenderData.Indices, source.RenderData.Indices.Length);
\r
946 public void setMeshPos(Vector3 pos)
\r
951 public void setMeshRot(Vector3 rot)
\r
953 _rotationAngles = rot;
\r
956 public override void LoadMesh(string filename)
\r
958 base.LoadMesh(filename);
\r
960 float minX, minY, minZ;
\r
961 minX = minY = minZ = Single.MaxValue;
\r
962 float maxX, maxY, maxZ;
\r
963 maxX = maxY = maxZ = Single.MinValue;
\r
965 // Generate the vertex array
\r
966 RenderData.Vertices = new float[_numVertices * 3];
\r
967 RenderData.Normals = new float[_numVertices * 3];
\r
969 Quaternion quat = Quaternion.CreateFromEulers(0, 0, (float)(Math.PI/4.0));
\r
972 for (int i = 0; i < _numVertices; i++)
\r
975 RenderData.Normals[current] = _vertices[i].Normal.X;
\r
976 RenderData.Vertices[current++] = _vertices[i].Coord.X;
\r
977 RenderData.Normals[current] = _vertices[i].Normal.Y;
\r
978 RenderData.Vertices[current++] = _vertices[i].Coord.Y;
\r
979 RenderData.Normals[current] = _vertices[i].Normal.Z;
\r
980 RenderData.Vertices[current++] = _vertices[i].Coord.Z;
\r
982 if (_vertices[i].Coord.X < minX)
\r
983 minX = _vertices[i].Coord.X;
\r
984 else if (_vertices[i].Coord.X > maxX)
\r
985 maxX = _vertices[i].Coord.X;
\r
987 if (_vertices[i].Coord.Y < minY)
\r
988 minY = _vertices[i].Coord.Y;
\r
989 else if (_vertices[i].Coord.Y > maxY)
\r
990 maxY = _vertices[i].Coord.Y;
\r
992 if (_vertices[i].Coord.Z < minZ)
\r
993 minZ = _vertices[i].Coord.Z;
\r
994 else if (_vertices[i].Coord.Z > maxZ)
\r
995 maxZ = _vertices[i].Coord.Z;
\r
998 // Calculate the center-point from the bounding box edges
\r
999 RenderData.Center = new Vector3((minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2);
\r
1001 // Generate the index array
\r
1002 RenderData.Indices = new ushort[_numFaces * 3];
\r
1004 for (int i = 0; i < _numFaces; i++)
\r
1006 RenderData.Indices[current++] = (ushort)_faces[i].Indices[0];
\r
1007 RenderData.Indices[current++] = (ushort)_faces[i].Indices[1];
\r
1008 RenderData.Indices[current++] = (ushort)_faces[i].Indices[2];
\r
1011 // Generate the texcoord array
\r
1012 RenderData.TexCoords = new float[_numVertices * 2];
\r
1014 for (int i = 0; i < _numVertices; i++)
\r
1016 RenderData.TexCoords[current++] = _vertices[i].TexCoord.X;
\r
1017 RenderData.TexCoords[current++] = _vertices[i].TexCoord.Y;
\r
1020 RenderData.weights = new float[_numVertices];
\r
1021 for (int i = 0; i < _numVertices; i++)
\r
1023 RenderData.weights[i] = _vertices[i].Weight;
\r
1026 RenderData.skinJoints = new string[_skinJoints.Length+3];
\r
1027 for (int i = 1; i < _skinJoints.Length; i++)
\r
1029 RenderData.skinJoints[i] = _skinJoints[i];
\r
1035 public override void LoadLODMesh(int level, string filename)
\r
1037 LODMesh lod = new LODMesh();
\r
1038 lod.LoadMesh(filename);
\r
1039 _lodMeshes[level] = lod;
\r
1042 public void applyjointweights()
\r
1045 /*Each weight actually contains two pieces of information.
\r
1046 * The number to the left of the decimal point is the index of the joint and also
\r
1047 * implicitly indexes to the following joint. The actual weight is to the right of
\r
1048 * the decimal point and interpolates between these two joints. The index is into an
\r
1049 * "expanded" list of joints, not just a linear array of the joints as defined in the
\r
1050 * skeleton file. In particular, any joint that has more than one child will be repeated
\r
1051 * in the list for each of its children.
\r
1054 float weight=-9999;
\r
1061 for (int v = 0, x=0; v < RenderData.Vertices.Length; v=v+3, x++)
\r
1063 if (weight != RenderData.weights[x])
\r
1066 jointindex = (int)Math.Floor(weight = RenderData.weights[x]);
\r
1067 factor = RenderData.weights[x] - jointindex;
\r
1068 weight = weight - jointindex;
\r
1070 string jointname="", jointname2="";
\r
1072 if (this.Name == "upperBodyMesh")
\r
1074 jointname = skeleton.mUpperMeshMapping[jointindex];
\r
1076 jointname2 = skeleton.mUpperMeshMapping[jointindex];
\r
1078 else if (Name == "lowerBodyMesh")
\r
1080 jointname = skeleton.mLowerMeshMapping[jointindex];
\r
1082 jointname2 = skeleton.mLowerMeshMapping[jointindex];
\r
1084 else if (Name == "headMesh")
\r
1086 jointname = skeleton.mHeadMeshMapping[jointindex];
\r
1088 jointname2 = skeleton.mHeadMeshMapping[jointindex];
\r
1092 return; // not interested in this mesh
\r
1096 if (jointname == "")
\r
1098 //Don't yet handle this, its a split joint to two children
\r
1103 ba = av.skel.mBones[jointname];
\r
1106 if(jointname2=="")
\r
1112 bb = av.skel.mBones[jointname2];
\r
1116 //Special cases 0 is not used
\r
1117 // ON upper torso 5 and 10 are not used
\r
1118 // 4 is neck and 6 and 11 are the left and right collar bones
\r
1123 Quaternion rot = ba.getRotation();
\r
1127 Vector3 oa = ba.getOffset() - ba.getOrigOffset();
\r
1128 Vector3 ob = bb.getOffset() - bb.getOrigOffset();
\r
1129 lerp = Vector3.Lerp(oa, ob, weight);
\r
1130 offset = Vector3.Lerp(ba.getOffset(), bb.getOffset(), weight);
\r
1134 lerp = ba.getOffset()- ba.getOrigOffset();
\r
1135 offset = ba.getOffset();
\r
1136 rot = ba.getRotation();
\r
1139 Vector3 pos = new Vector3(MorphRenderData.Vertices[v], MorphRenderData.Vertices[v + 1], MorphRenderData.Vertices[v + 2]);
\r
1140 pos = pos + lerp;
\r
1141 pos = pos - offset;
\r
1143 pos = pos + offset;
\r
1145 RenderData.Vertices[v] = pos.X;
\r
1146 RenderData.Vertices[v + 1] = pos.Y;
\r
1147 RenderData.Vertices[v + 2] = pos.Z;
\r
1151 public void morphmesh(Morph morph, float weight)
\r
1153 for (int v = 0; v < morph.NumVertices; v++)
\r
1155 MorphVertex mvx = morph.Vertices[v];
\r
1157 uint i = mvx.VertexIndex;
\r
1159 MorphRenderData.Vertices[i * 3] = OrigRenderData.Vertices[i*3] + mvx.Coord.X * weight;
\r
1160 MorphRenderData.Vertices[(i * 3) + 1] = OrigRenderData.Vertices[i * 3 + 1] + mvx.Coord.Y * weight;
\r
1161 MorphRenderData.Vertices[(i * 3) + 2] = OrigRenderData.Vertices[i * 3 + 2] + mvx.Coord.Z * weight;
\r
1163 RenderData.TexCoords[i * 2] = OrigRenderData.TexCoords[i * 2] + mvx.TexCoord.X * weight;
\r
1164 RenderData.TexCoords[(i * 2) + 1] = OrigRenderData.TexCoords[i * 2 + 1] + mvx.TexCoord.Y * weight;
\r
1170 public class GLAvatar
\r
1172 private static Dictionary<string, GLMesh> _defaultmeshes = new Dictionary<string, GLMesh>();
\r
1173 public Dictionary<string, GLMesh> _meshes = new Dictionary<string, GLMesh>();
\r
1175 public skeleton skel = new skeleton();
\r
1176 public static Dictionary<int, attachment_point> attachment_points = new Dictionary<int, attachment_point>();
\r
1178 public bool _wireframe = true;
\r
1179 public bool _showSkirt = false;
\r
1181 public VisualParamEx.EparamSex msex;
\r
1183 public byte[] VisualAppearanceParameters = new byte[1024];
\r
1184 bool vpsent = false;
\r
1185 static bool lindenMeshesLoaded = false;
\r
1189 foreach (KeyValuePair<string,GLMesh> kvp in _defaultmeshes)
\r
1191 GLMesh mesh = new GLMesh(kvp.Value,this); // Instance our meshes
\r
1192 _meshes.Add(kvp.Key, mesh);
\r
1197 public static void dumptweaks()
\r
1200 for(int x=0;x<VisualParamEx.tweakable_params.Count;x++)
\r
1202 VisualParamEx vpe = (VisualParamEx)VisualParamEx.tweakable_params.GetByIndex(x);
\r
1203 Console.WriteLine(string.Format("{0} is {1}",x,vpe.Name));
\r
1209 public static void loadlindenmeshes2(string LODfilename)
\r
1211 // Already have mashes loaded?
\r
1212 if (lindenMeshesLoaded) return;
\r
1214 attachment_points.Clear();
\r
1217 string basedir = Directory.GetCurrentDirectory() + System.IO.Path.DirectorySeparatorChar + "character" + System.IO.Path.DirectorySeparatorChar;
\r
1219 XmlDocument lad = new XmlDocument();
\r
1220 lad.Load(basedir + LODfilename);
\r
1222 //Firstly read the skeleton section this contains attachment point info and the bone deform info for visual params
\r
1223 // And load the skeleton file in to the bones class
\r
1225 XmlNodeList skeleton = lad.GetElementsByTagName("skeleton");
\r
1226 string skeletonfilename = skeleton[0].Attributes.GetNamedItem("file_name").Value;
\r
1227 Bone.loadbones(skeletonfilename);
\r
1229 // Next read all the skeleton child nodes, we have attachment points and bone deform params
\r
1230 // attachment points are an offset and rotation from a bone location
\r
1231 // the name of the bone they reference is the joint paramater
\r
1232 // params in the skeleton nodes are bone deforms, eg leg length changes the scale of the leg bones
\r
1234 foreach (XmlNode skeletonnode in skeleton[0].ChildNodes)
\r
1236 if (skeletonnode.Name == "attachment_point")
\r
1238 attachment_point point = new attachment_point(skeletonnode);
\r
1239 attachment_points.Add(point.id, point);
\r
1242 if (skeletonnode.Name == "param")
\r
1244 //Bone deform param
\r
1245 VisualParamEx vp = new VisualParamEx(skeletonnode, VisualParamEx.ParamType.TYPE_BONEDEFORM);
\r
1249 //Now we parse the mesh nodes, mesh nodes reference a particular LLM file with a LOD
\r
1250 //and also list VisualParams for the various mesh morphs that can be applied
\r
1252 XmlNodeList meshes = lad.GetElementsByTagName("mesh");
\r
1253 foreach (XmlNode meshNode in meshes)
\r
1255 string type = meshNode.Attributes.GetNamedItem("type").Value;
\r
1256 int lod = Int32.Parse(meshNode.Attributes.GetNamedItem("lod").Value);
\r
1257 string fileName = meshNode.Attributes.GetNamedItem("file_name").Value;
\r
1259 GLMesh mesh = (_defaultmeshes.ContainsKey(type) ? _defaultmeshes[type] : new GLMesh(type));
\r
1261 if (meshNode.HasChildNodes)
\r
1263 foreach (XmlNode paramnode in meshNode.ChildNodes)
\r
1265 if (paramnode.Name == "param")
\r
1267 VisualParamEx vp = new VisualParamEx(paramnode, VisualParamEx.ParamType.TYPE_MORPH);
\r
1269 mesh._evp.Add(vp.ParamID, vp); //Not sure we really need this may optimise out later
\r
1270 vp.morphmesh = mesh.Name;
\r
1275 // Set up the texture elemenets for each mesh
\r
1276 // And hack the eyeball position
\r
1277 switch (mesh.Name)
\r
1279 case "lowerBodyMesh":
\r
1280 mesh.teFaceID = (int)AvatarTextureIndex.LowerBaked;
\r
1283 case "upperBodyMesh":
\r
1284 mesh.teFaceID = (int)AvatarTextureIndex.UpperBaked;
\r
1288 mesh.teFaceID = (int)AvatarTextureIndex.HeadBaked;
\r
1292 mesh.teFaceID = (int)AvatarTextureIndex.HairBaked;
\r
1295 case "eyelashMesh":
\r
1296 mesh.teFaceID = (int)AvatarTextureIndex.HeadBaked;
\r
1299 case "eyeBallRightMesh":
\r
1300 mesh.setMeshPos(Bone.mBones["mEyeLeft"].getOffset());
\r
1301 //mesh.setMeshRot(Bone.getRotation("mEyeLeft"));
\r
1302 mesh.teFaceID = (int)AvatarTextureIndex.EyesBaked;
\r
1305 case "eyeBallLeftMesh":
\r
1306 mesh.setMeshPos(Bone.mBones["mEyeRight"].getOffset());
\r
1307 //mesh.setMeshRot(Bone.getRotation("mEyeRight"));
\r
1308 mesh.teFaceID = (int)AvatarTextureIndex.EyesBaked;
\r
1312 mesh.teFaceID = (int)AvatarTextureIndex.SkirtBaked;
\r
1316 mesh.teFaceID = 0;
\r
1321 mesh.LoadMesh(basedir + fileName);
\r
1323 mesh.LoadLODMesh(lod, basedir + fileName);
\r
1325 _defaultmeshes[type] = mesh;
\r
1329 // Next are the textureing params, skipping for the moment
\r
1331 XmlNodeList colors = lad.GetElementsByTagName("global_color");
\r
1333 foreach (XmlNode globalcolornode in colors)
\r
1335 foreach (XmlNode node in globalcolornode.ChildNodes)
\r
1337 if (node.Name == "param")
\r
1339 VisualParamEx vp = new VisualParamEx(node, VisualParamEx.ParamType.TYPE_COLOR);
\r
1345 // Get layer paramaters, a bit of a verbose way to do it but we probably want to get access
\r
1346 // to some of the other data not just the <param> tag
\r
1348 XmlNodeList layer_sets = lad.GetElementsByTagName("layer_set");
\r
1350 foreach (XmlNode layer_set in layer_sets)
\r
1352 foreach (XmlNode layer in layer_set.ChildNodes)
\r
1354 foreach (XmlNode layernode in layer.ChildNodes)
\r
1356 if (layernode.Name == "param")
\r
1358 VisualParamEx vp = new VisualParamEx(layernode, VisualParamEx.ParamType.TYPE_COLOR);
\r
1365 // Next are the driver parameters, these are parameters that change multiple real parameters
\r
1367 XmlNodeList drivers = lad.GetElementsByTagName("driver_parameters");
\r
1369 foreach (XmlNode node in drivers[0].ChildNodes) //lazy
\r
1371 if (node.Name == "param")
\r
1373 VisualParamEx vp = new VisualParamEx(node, VisualParamEx.ParamType.TYPE_DRIVER);
\r
1377 lindenMeshesLoaded = true;
\r
1380 public void morphtest(Avatar av, int param, float weight)
\r
1382 VisualParamEx vpx;
\r
1383 if (VisualParamEx.allParams.TryGetValue(param,out vpx))
\r
1386 Logger.Log(string.Format("Applying visual parameter {0} id {1} value {2}", vpx.Name, vpx.ParamID, weight), Helpers.LogLevel.Info);
\r
1388 //weight = weight * 2.0f;
\r
1389 //weight=weight-1.0f;
\r
1391 float value = vpx.MinValue + ((vpx.MaxValue - vpx.MinValue) * weight);
\r
1394 if (vpx.pType == VisualParamEx.ParamType.TYPE_MORPH)
\r
1398 if (_meshes.TryGetValue(vpx.morphmesh, out mesh))
\r
1400 foreach (LindenMesh.Morph morph in mesh.Morphs) //optimise me to a dictionary
\r
1402 if (morph.Name == vpx.Name)
\r
1404 mesh.morphmesh(morph, value);
\r
1410 // Not a mesh morph
\r
1412 // Its a volume deform, these appear to be related to collision volumes
\r
1414 if (vpx.VolumeDeforms == null)
\r
1416 Logger.Log(String.Format("paramater {0} has invalid mesh {1}", param, vpx.morphmesh), Helpers.LogLevel.Warning);
\r
1420 foreach (KeyValuePair<string, VisualParamEx.VolumeDeform> kvp in vpx.VolumeDeforms)
\r
1422 skel.deformbone(kvp.Key, kvp.Value.pos, kvp.Value.scale);
\r
1432 // Its not a morph, it might be a driver though
\r
1433 if (vpx.pType == VisualParamEx.ParamType.TYPE_DRIVER)
\r
1435 foreach (VisualParamEx.driven child in vpx.childparams)
\r
1437 morphtest(av, child.id, weight); //TO DO use minmax if they are present
\r
1442 //Is this a bone deform?
\r
1443 if (vpx.pType == VisualParamEx.ParamType.TYPE_BONEDEFORM)
\r
1445 foreach (KeyValuePair<string, Vector3> kvp in vpx.BoneDeforms)
\r
1447 skel.deformbone(kvp.Key, new Vector3(0,0,0),kvp.Value*value,Quaternion.Identity);
\r
1452 Logger.Log(String.Format("paramater {0} is not a morph and not a driver", param), Helpers.LogLevel.Warning);
\r
1459 Logger.Log("Invalid paramater " + param.ToString(), Helpers.LogLevel.Warning);
\r
1463 foreach (GLMesh mesh in _meshes.Values)
\r
1465 VisualParamEx evp;
\r
1466 if (mesh._evp.TryGetValue(param, out evp))
\r
1468 foreach (LindenMesh.Morph morph in mesh.Morphs)
\r
1470 if (morph.Name == evp.Name)
\r
1472 mesh.morphmesh(morph, weight);
\r
1478 Console.WriteLine("No such visual param in morphs");
\r
1485 public void morph(Avatar av)
\r
1488 if (av.VisualParameters == null)
\r
1492 ThreadPool.QueueUserWorkItem(sync =>
\r
1496 if (av.VisualParameters.Length > 123)
\r
1498 if (av.VisualParameters[31] > 0.5)
\r
1500 msex = VisualParamEx.EparamSex.SEX_MALE;
\r
1504 msex = VisualParamEx.EparamSex.SEX_FEMALE;
\r
1509 foreach (byte vpvalue in av.VisualParameters)
\r
1511 if (vpsent==true && VisualAppearanceParameters[x] == vpvalue)
\r
1517 VisualAppearanceParameters[x] = vpvalue;
\r
1519 if (x >= VisualParamEx.tweakable_params.Count)
\r
1521 Logger.Log("Two many visual paramaters in Avatar appearance", Helpers.LogLevel.Warning);
\r
1525 VisualParamEx vpe = (VisualParamEx)VisualParamEx.tweakable_params.GetByIndex(x);
\r
1527 if (vpe.sex != VisualParamEx.EparamSex.SEX_BOTH && vpe.sex != msex)
\r
1533 float value = (vpvalue / 255.0f);
\r
1534 this.morphtest(av, vpe.ParamID, value);
\r
1541 foreach (GLMesh mesh in _meshes.Values)
\r
1543 mesh.applyjointweights();
\r
1549 public class RenderAvatar : SceneObject
\r
1551 public RenderAvatar()
\r
1553 Type = SceneObjectType.Avatar;
\r
1556 public override Primitive BasePrim
\r
1558 get { return avatar; }
\r
1559 set { if (value is Avatar) avatar = (Avatar)value; }
\r
1562 public GLAvatar glavatar = new GLAvatar();
\r
1563 public Avatar avatar;
\r
1564 public FaceData[] data = new FaceData[32];
\r
1565 public Dictionary<UUID, Animation> animlist = new Dictionary<UUID, Animation>();
\r
1566 public Dictionary<WearableType, AppearanceManager.WearableData> Wearables = new Dictionary<WearableType, AppearanceManager.WearableData>();
\r
1570 public class skeleton
\r
1572 public Dictionary<string, Bone> mBones;
\r
1573 public static Dictionary<int, string> mUpperMeshMapping = new Dictionary<int, string>();
\r
1574 public static Dictionary<int, string> mLowerMeshMapping = new Dictionary<int, string>();
\r
1575 public static Dictionary<int, string> mHeadMeshMapping = new Dictionary<int, string>();
\r
1581 mBones = new Dictionary<string, Bone>(Bone.mBones); //copy from the static defines
\r
1584 if (mUpperMeshMapping.Count == 0)
\r
1586 mUpperMeshMapping.Add(1, "mPelvis");
\r
1587 mUpperMeshMapping.Add(2, "mTorso");
\r
1588 mUpperMeshMapping.Add(3, "mChest");
\r
1589 mUpperMeshMapping.Add(4, "mNeck");
\r
1590 mUpperMeshMapping.Add(5, "");
\r
1591 mUpperMeshMapping.Add(6, "mCollarLeft");
\r
1592 mUpperMeshMapping.Add(7, "mShoulderLeft");
\r
1593 mUpperMeshMapping.Add(8, "mElbowLeft");
\r
1594 mUpperMeshMapping.Add(9, "mWristLeft");
\r
1595 mUpperMeshMapping.Add(10, "");
\r
1596 mUpperMeshMapping.Add(11, "mCollarRight");
\r
1597 mUpperMeshMapping.Add(12, "mShoulderRight");
\r
1598 mUpperMeshMapping.Add(13, "mElbowRight");
\r
1599 mUpperMeshMapping.Add(14, "mWristRight");
\r
1600 mUpperMeshMapping.Add(15, "");
\r
1602 mLowerMeshMapping.Add(1,"mPelvis");
\r
1603 mLowerMeshMapping.Add(2, "mHipRight");
\r
1604 mLowerMeshMapping.Add(3, "mKneeRight");
\r
1605 mLowerMeshMapping.Add(4, "mAnkleRight");
\r
1606 mLowerMeshMapping.Add(5, "");
\r
1607 mLowerMeshMapping.Add(6, "mHipLeft");
\r
1608 mLowerMeshMapping.Add(7, "mKneeLeft");
\r
1609 mLowerMeshMapping.Add(8, "mAnkleLeft");
\r
1610 mLowerMeshMapping.Add(9, "");
\r
1612 mHeadMeshMapping.Add(1, "mNeck");
\r
1613 mHeadMeshMapping.Add(2, "mHead");
\r
1614 mHeadMeshMapping.Add(3, "");
\r
1619 public void deformbone(string name, Vector3 pos, Vector3 scale, Quaternion rotation)
\r
1622 if (mBones.TryGetValue(name, out bone))
\r
1624 bone.deformbone(pos, scale, rotation);
\r
1628 //TODO check offset and rot calcuations should each offset be multiplied by its parent rotation in
\r
1629 // a standard child/parent rot/offset way?
\r
1630 public Vector3 getOffset(string bonename)
\r
1633 if (mBones.TryGetValue(bonename, out b))
\r
1635 return (b.getOffset());
\r
1639 return Vector3.Zero;
\r
1643 public Quaternion getRotation(string bonename)
\r
1646 if (mBones.TryGetValue(bonename, out b))
\r
1648 return (b.getRotation());
\r
1652 return Quaternion.Identity;
\r
1659 public string name;
\r
1660 public Vector3 pos;
\r
1661 public Quaternion rot;
\r
1662 public Vector3 scale;
\r
1663 public Vector3 piviot;
\r
1665 public Vector3 orig_pos;
\r
1666 public Quaternion orig_rot;
\r
1667 public Vector3 orig_scale;
\r
1668 public Vector3 orig_piviot;
\r
1670 Matrix4 mDeformMatrix=Matrix4.Identity;
\r
1672 public Bone parent;
\r
1674 public List<Bone> children = new List<Bone>();
\r
1676 public static Dictionary<string, Bone> mBones = new Dictionary<string, Bone>();
\r
1677 public static Dictionary<int, Bone> mIndexedBones = new Dictionary<int, Bone>();
\r
1678 static int boneaddindex = 0;
\r
1684 public Bone(Bone source)
\r
1686 name = String.Copy(source.name);
\r
1687 pos = new Vector3(source.pos);
\r
1688 rot = new Quaternion(source.rot);
\r
1689 scale = new Vector3(source.scale);
\r
1690 piviot = new Vector3(source.piviot);
\r
1692 orig_piviot = source.orig_piviot;
\r
1693 orig_pos = source.orig_pos;
\r
1694 orig_rot = source.orig_rot;
\r
1695 orig_scale = source.orig_scale;
\r
1697 mDeformMatrix = new Matrix4(source.mDeformMatrix);
\r
1700 public static void loadbones(string skeletonfilename)
\r
1703 string basedir = Directory.GetCurrentDirectory() + System.IO.Path.DirectorySeparatorChar + "character" + System.IO.Path.DirectorySeparatorChar;
\r
1704 XmlDocument skeleton = new XmlDocument();
\r
1705 skeleton.Load(basedir + skeletonfilename);
\r
1706 XmlNode boneslist = skeleton.GetElementsByTagName("linden_skeleton")[0];
\r
1707 addbone(boneslist.ChildNodes[0], null);
\r
1710 public static void addbone(XmlNode bone, Bone parent)
\r
1713 if (bone.Name != "bone")
\r
1716 Bone b = new Bone();
\r
1717 b.name = bone.Attributes.GetNamedItem("name").Value;
\r
1719 string pos = bone.Attributes.GetNamedItem("pos").Value;
\r
1720 string[] posparts = pos.Split(' ');
\r
1721 b.pos = new Vector3(float.Parse(posparts[0]), float.Parse(posparts[1]), float.Parse(posparts[2]));
\r
1722 b.orig_pos = new Vector3(b.pos);
\r
1724 string rot = bone.Attributes.GetNamedItem("rot").Value;
\r
1725 string[] rotparts = rot.Split(' ');
\r
1726 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
1727 b.orig_rot = new Quaternion(b.rot);
\r
1729 string scale = bone.Attributes.GetNamedItem("scale").Value;
\r
1730 string[] scaleparts = scale.Split(' ');
\r
1731 b.scale = new Vector3(float.Parse(scaleparts[0]), float.Parse(scaleparts[1]), float.Parse(scaleparts[2]));
\r
1732 b.orig_scale = new Vector3(b.scale);
\r
1735 float[] deform = Math3D.CreateSRTMatrix(new Vector3(1,1,1), b.rot, b.orig_pos);
\r
1736 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
1740 b.parent = parent;
\r
1742 if (parent != null)
\r
1743 parent.children.Add(b);
\r
1745 mBones.Add(b.name, b);
\r
1746 mIndexedBones.Add(boneaddindex++, b);
\r
1748 Logger.Log("Found bone " + b.name, Helpers.LogLevel.Info);
\r
1750 foreach (XmlNode childbone in bone.ChildNodes)
\r
1752 addbone(childbone, b);
\r
1757 public void deformbone(Vector3 pos, Vector3 scale, Quaternion rot)
\r
1759 float[] deform = Math3D.CreateSRTMatrix(scale, rot, this.orig_pos);
\r
1760 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
1761 this.pos = Bone.mBones[name].orig_pos + pos;
\r
1762 this.scale = Bone.mBones[name].orig_scale + scale;
\r
1763 this.rot = Bone.mBones[name].orig_rot * rot;
\r
1766 public Matrix4 getdeform()
\r
1768 if (this.parent != null)
\r
1770 return mDeformMatrix * parent.getdeform();
\r
1774 return mDeformMatrix;
\r
1778 public Vector3 getOffset()
\r
1780 if (parent != null)
\r
1782 Quaternion totalrot = getParentRot(); // we don't want this joints rotation included
\r
1783 Vector3 parento = parent.getOffset();
\r
1784 Vector3 mepre = pos * scale;
\r
1785 mepre = mepre * totalrot;
\r
1786 return parento+ mepre;
\r
1790 return (pos * scale) *getRotation();
\r
1794 public Vector3 getMyOffset()
\r
1796 return pos * scale;
\r
1799 public Vector3 getOrigOffset()
\r
1801 if (parent != null)
\r
1803 return ((parent.getOrigOffset()) + orig_pos);
\r
1811 public static Quaternion getRotation(string bonename)
\r
1814 if (mBones.TryGetValue(bonename, out b))
\r
1816 return (b.getRotation());
\r
1820 return Quaternion.Identity;
\r
1825 public Quaternion getParentRot()
\r
1827 Quaternion totalrot = Quaternion.Identity;
\r
1829 if (parent != null)
\r
1831 totalrot = parent.getRotation();
\r
1838 public Quaternion getRotation()
\r
1840 Quaternion totalrot = rot;
\r
1842 if (parent != null)
\r
1844 totalrot = rot* parent.getRotation();
\r
1852 public class VisualParamEx
\r
1855 static public Dictionary<int,VisualParamEx> allParams = new Dictionary<int,VisualParamEx>();
\r
1856 static public Dictionary<int, VisualParamEx> deformParams = new Dictionary<int, VisualParamEx>();
\r
1857 static public Dictionary<int, VisualParamEx> morphParams = new Dictionary<int, VisualParamEx>();
\r
1858 static public Dictionary<int, VisualParamEx> drivenParams = new Dictionary<int, VisualParamEx>();
\r
1859 static public SortedList tweakable_params = new SortedList();
\r
1861 public Dictionary<string, Vector3> BoneDeforms = null;
\r
1862 public Dictionary<string, VolumeDeform> VolumeDeforms = null;
\r
1863 public List<driven> childparams = null;
\r
1865 public string morphmesh = null;
\r
1869 VISUAL_PARAM_GROUP_TWEAKABLE = 0,
\r
1870 VISUAL_PARAM_GROUP_ANIMATABLE,
\r
1871 VISUAL_PARAM_GROUP_TWEAKABLE_NO_TRANSMIT,
\r
1874 public struct VolumeDeform
\r
1876 public string name;
\r
1877 public Vector3 scale;
\r
1878 public Vector3 pos;
\r
1881 public enum EparamSex
\r
1888 public enum ParamType
\r
1897 public struct driven
\r
1900 public float max1;
\r
1901 public float max2;
\r
1902 public float min1;
\r
1903 public float min2;
\r
1904 public bool hasMinMax;
\r
1907 public string meshname;
\r
1909 /// <summary>Index of this visual param</summary>
\r
1910 public int ParamID;
\r
1911 /// <summary>Internal name</summary>
\r
1912 public string Name;
\r
1913 /// <summary>Group ID this parameter belongs to</summary>
\r
1915 /// <summary>Name of the wearable this parameter belongs to</summary>
\r
1916 public string Wearable;
\r
1917 /// <summary>Displayable label of this characteristic</summary>
\r
1918 public string Label;
\r
1919 /// <summary>Displayable label for the minimum value of this characteristic</summary>
\r
1920 public string LabelMin;
\r
1921 /// <summary>Displayable label for the maximum value of this characteristic</summary>
\r
1922 public string LabelMax;
\r
1923 /// <summary>Default value</summary>
\r
1924 public float DefaultValue;
\r
1925 /// <summary>Minimum value</summary>
\r
1926 public float MinValue;
\r
1927 /// <summary>Maximum value</summary>
\r
1928 public float MaxValue;
\r
1929 /// <summary>Is this param used for creation of bump layer?</summary>
\r
1930 public bool IsBumpAttribute;
\r
1931 /// <summary>Alpha blending/bump info</summary>
\r
1932 public VisualAlphaParam? AlphaParams;
\r
1933 /// <summary>Color information</summary>
\r
1934 public VisualColorParam? ColorParams;
\r
1935 /// <summary>Array of param IDs that are drivers for this parameter</summary>
\r
1936 public int[] Drivers;
\r
1937 /// <summary>The Avatar Sex that this parameter applies to</summary>
\r
1938 public EparamSex sex;
\r
1940 public ParamType pType;
\r
1942 public static int count = 0;
\r
1945 /// Set all the values through the constructor
\r
1947 /// <param name="paramID">Index of this visual param</param>
\r
1948 /// <param name="name">Internal name</param>
\r
1949 /// <param name="group"></param>
\r
1950 /// <param name="wearable"></param>
\r
1951 /// <param name="label">Displayable label of this characteristic</param>
\r
1952 /// <param name="labelMin">Displayable label for the minimum value of this characteristic</param>
\r
1953 /// <param name="labelMax">Displayable label for the maximum value of this characteristic</param>
\r
1954 /// <param name="def">Default value</param>
\r
1955 /// <param name="min">Minimum value</param>
\r
1956 /// <param name="max">Maximum value</param>
\r
1957 /// <param name="isBumpAttribute">Is this param used for creation of bump layer?</param>
\r
1958 /// <param name="drivers">Array of param IDs that are drivers for this parameter</param>
\r
1959 /// <param name="alpha">Alpha blending/bump info</param>
\r
1960 /// <param name="colorParams">Color information</param>
\r
1961 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
1963 ParamID = paramID;
\r
1966 Wearable = wearable;
\r
1968 LabelMin = labelMin;
\r
1969 LabelMax = labelMax;
\r
1970 DefaultValue = def;
\r
1973 IsBumpAttribute = isBumpAttribute;
\r
1974 Drivers = drivers;
\r
1975 AlphaParams = alpha;
\r
1976 ColorParams = colorParams;
\r
1977 sex = EparamSex.SEX_BOTH;
\r
1980 public VisualParamEx(XmlNode node, ParamType pt)
\r
1984 ParamID = Int32.Parse(node.Attributes.GetNamedItem("id").Value);
\r
1985 Name = node.Attributes.GetNamedItem("name").Value;
\r
1986 Group = Int32.Parse(node.Attributes.GetNamedItem("group").Value);
\r
1988 //These dont exist for facal expresion morphs
\r
1989 if(node.Attributes.GetNamedItem("wearable")!=null)
\r
1990 Wearable = node.Attributes.GetNamedItem("wearable").Value;
\r
1992 MinValue = float.Parse(node.Attributes.GetNamedItem("value_min").Value);
\r
1993 MaxValue = float.Parse(node.Attributes.GetNamedItem("value_max").Value);
\r
1995 // These do not exists for driven parameters
\r
1996 if (node.Attributes.GetNamedItem("label_min") != null)
\r
1998 LabelMin = node.Attributes.GetNamedItem("label_min").Value;
\r
2001 if (node.Attributes.GetNamedItem("label_max") != null)
\r
2003 LabelMax = node.Attributes.GetNamedItem("label_max").Value;
\r
2006 XmlNode sexnode = node.Attributes.GetNamedItem("sex");
\r
2008 if (sexnode != null)
\r
2010 if (sexnode.Value == "male")
\r
2012 sex = EparamSex.SEX_MALE;
\r
2016 sex = EparamSex.SEX_FEMALE;
\r
2021 Group = int.Parse(node.Attributes.GetNamedItem("group").Value);
\r
2023 if (Group == (int)GroupType.VISUAL_PARAM_GROUP_TWEAKABLE)
\r
2025 if(!tweakable_params.ContainsKey(ParamID)) //stupid duplicate shared params
\r
2027 tweakable_params.Add(this.ParamID, this);
\r
2029 Logger.Log(String.Format("Adding tweakable paramater ID {0} {1}", count, this.Name),Helpers.LogLevel.Info);
\r
2033 //TODO other paramaters but these arew concerned with editing the GUI display so not too fussed at the moment
\r
2037 allParams.Add(ParamID, this);
\r
2039 catch (Exception e)
\r
2041 Logger.Log("Duplicate VisualParam in allParams id " + ParamID.ToString(), Helpers.LogLevel.Info);
\r
2044 if (pt == ParamType.TYPE_BONEDEFORM)
\r
2046 // If we are in the skeleton section then we also have bone deforms to parse
\r
2047 BoneDeforms = new Dictionary<string, Vector3>();
\r
2048 if(node.HasChildNodes && node.ChildNodes[0].HasChildNodes)
\r
2050 ParseBoneDeforms(node.ChildNodes[0].ChildNodes);
\r
2052 deformParams.Add(ParamID,this);
\r
2055 if (pt == ParamType.TYPE_MORPH)
\r
2057 VolumeDeforms = new Dictionary<string, VolumeDeform>();
\r
2058 if (node.HasChildNodes && node.ChildNodes[0].HasChildNodes)
\r
2060 ParseVolumeDeforms(node.ChildNodes[0].ChildNodes);
\r
2065 morphParams.Add(ParamID, this);
\r
2067 catch (Exception e)
\r
2069 Logger.Log("Duplicate VisualParam in morphParams id " + ParamID.ToString(), Helpers.LogLevel.Info);
\r
2074 if (pt == ParamType.TYPE_DRIVER)
\r
2076 childparams = new List<driven>();
\r
2077 if (node.HasChildNodes && node.ChildNodes[0].HasChildNodes) //LAZY
\r
2079 ParseDrivers(node.ChildNodes[0].ChildNodes);
\r
2082 drivenParams.Add(ParamID, this);
\r
2086 if (pt == ParamType.TYPE_COLOR)
\r
2088 if (node.HasChildNodes)
\r
2090 foreach (XmlNode colorchild in node.ChildNodes)
\r
2092 if (colorchild.Name == "param_color")
\r
2094 //TODO extract <value color="50, 25, 5, 255" />
\r
2103 void ParseBoneDeforms(XmlNodeList deforms)
\r
2105 foreach (XmlNode node in deforms)
\r
2107 if (node.Name == "bone")
\r
2109 string name = node.Attributes.GetNamedItem("name").Value;
\r
2110 Vector3 scale = XmlParseVector(node.Attributes.GetNamedItem("scale").Value);
\r
2111 BoneDeforms.Add(name, scale);
\r
2116 void ParseVolumeDeforms(XmlNodeList deforms)
\r
2118 foreach (XmlNode node in deforms)
\r
2120 if (node.Name == "volume_morph")
\r
2122 VolumeDeform vd = new VolumeDeform();
\r
2123 vd.name = node.Attributes.GetNamedItem("name").Value;
\r
2124 vd.name = vd.name.ToLower();
\r
2126 if (node.Attributes.GetNamedItem("scale") != null)
\r
2128 vd.scale = XmlParseVector(node.Attributes.GetNamedItem("scale").Value);
\r
2132 vd.scale = new Vector3(0, 0, 0);
\r
2135 if (node.Attributes.GetNamedItem("pos") != null)
\r
2137 vd.pos = XmlParseVector(node.Attributes.GetNamedItem("pos").Value);
\r
2141 vd.pos = new Vector3(0f, 0f, 0f);
\r
2144 VolumeDeforms.Add(vd.name, vd);
\r
2149 void ParseDrivers(XmlNodeList drivennodes)
\r
2151 foreach (XmlNode node in drivennodes)
\r
2153 if (node.Name == "driven")
\r
2155 driven d = new driven();
\r
2157 d.id = Int32.Parse(node.Attributes.GetNamedItem("id").Value);
\r
2158 XmlNode param = node.Attributes.GetNamedItem("max1");
\r
2159 if (param != null)
\r
2161 d.max1 = float.Parse(param.Value);
\r
2162 d.max2 = float.Parse(node.Attributes.GetNamedItem("max2").Value);
\r
2163 d.min1 = float.Parse(node.Attributes.GetNamedItem("min1").Value);
\r
2164 d.max2 = float.Parse(node.Attributes.GetNamedItem("min2").Value);
\r
2165 d.hasMinMax = true;
\r
2169 d.hasMinMax = false;
\r
2172 childparams.Add(d);
\r
2178 public static Vector3 XmlParseVector(string data)
\r
2180 string[] posparts = data.Split(' ');
\r
2181 return new Vector3(float.Parse(posparts[0]), float.Parse(posparts[1]), float.Parse(posparts[2]));
\r
2184 public static Quaternion XmlParseRotation(string data)
\r
2186 string[] rotparts = data.Split(' ');
\r
2187 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