OSDN Git Service

Add a bake index for eyelash mesh, and use correct bake index for eyes.
[radegast/radegast.git] / Radegast / GUI / Rendering / RenderingHelpers.cs
1 using System;\r
2 using System.Collections.Generic;\r
3 using System.Linq;\r
4 using System.Text;\r
5 using System.IO;\r
6 using System.Xml;\r
7 using OpenTK.Graphics.OpenGL;\r
8 using System.Runtime.InteropServices;\r
9 using OpenMetaverse;\r
10 using OpenMetaverse.Rendering;\r
11 \r
12 namespace Radegast.Rendering\r
13 {\r
14     public class FaceData\r
15     {\r
16         public float[] Vertices;\r
17         public ushort[] Indices;\r
18         public float[] TexCoords;\r
19         public float[] Normals;\r
20         public int PickingID = -1;\r
21         public int VertexVBO = -1;\r
22         public int IndexVBO = -1;\r
23         public TextureInfo TextureInfo = new TextureInfo();\r
24         public BoundingSphere BoundingSphere = new BoundingSphere();\r
25         public static int VertexSize = 32; // sizeof (vertex), 2  x vector3 + 1 x vector2 = 8 floats x 4 bytes = 32 bytes \r
26         \r
27         public void CheckVBO(Face face)\r
28         {\r
29             if (VertexVBO == -1)\r
30             {\r
31                 Vertex[] vArray = face.Vertices.ToArray();\r
32                 GL.GenBuffers(1, out VertexVBO);\r
33                 GL.BindBuffer(BufferTarget.ArrayBuffer, VertexVBO);\r
34                 GL.BufferData(BufferTarget.ArrayBuffer, (IntPtr)(vArray.Length * VertexSize), vArray, BufferUsageHint.StreamDraw);\r
35             }\r
36 \r
37             if (IndexVBO == -1)\r
38             {\r
39                 ushort[] iArray = face.Indices.ToArray();\r
40                 GL.GenBuffers(1, out IndexVBO);\r
41                 GL.BindBuffer(BufferTarget.ElementArrayBuffer, IndexVBO);\r
42                 GL.BufferData(BufferTarget.ElementArrayBuffer, (IntPtr)(iArray.Length * sizeof(ushort)), iArray, BufferUsageHint.StreamDraw);\r
43             }\r
44         }\r
45     }\r
46 \r
47     public class TextureInfo\r
48     {\r
49         public System.Drawing.Image Texture;\r
50         public int TexturePointer;\r
51         public bool HasAlpha;\r
52         public UUID TextureID;\r
53     }\r
54 \r
55     public class TextureLoadItem\r
56     {\r
57         public FaceData Data;\r
58         public Primitive Prim;\r
59         public Primitive.TextureEntryFace TeFace;\r
60     }\r
61 \r
62     public enum RenderPass\r
63     {\r
64         Picking,\r
65         Simple,\r
66         Alpha\r
67     }\r
68 \r
69     public static class Render\r
70     {\r
71         public static IRendering Plugin;\r
72     }\r
73 \r
74     public static class RHelp\r
75     {\r
76         public static OpenTK.Vector2 TKVector3(Vector2 v)\r
77         {\r
78             return new OpenTK.Vector2(v.X, v.Y);\r
79         }\r
80 \r
81         public static OpenTK.Vector3 TKVector3(Vector3 v)\r
82         {\r
83             return new OpenTK.Vector3(v.X, v.Y, v.Z);\r
84         }\r
85 \r
86         public static OpenTK.Vector4 TKVector3(Vector4 v)\r
87         {\r
88             return new OpenTK.Vector4(v.X, v.Y, v.Z, v.W);\r
89         }\r
90     }\r
91 \r
92     /// <summary>\r
93     /// Represents camera object\r
94     /// </summary>\r
95     public struct Camera\r
96     {\r
97         Vector3 mPosition;\r
98         Vector3 mFocalPoint;\r
99         bool mModified;\r
100         \r
101         /// <summary>Camera position</summary>\r
102         public Vector3 Position { get { return mPosition; } set { mPosition = value; mModified = true; } }\r
103         /// <summary>Camera target</summary>\r
104         public Vector3 FocalPoint { get { return mFocalPoint; } set { mFocalPoint = value; mModified = true; } }\r
105         /// <summary>Zoom level</summary>\r
106         public float Zoom;\r
107         /// <summary>Draw distance</summary>\r
108         public float Far;\r
109         /// <summary>Has camera been modified</summary>\r
110         public bool Modified { get { return mModified; } set { mModified = value; } }\r
111     }\r
112 \r
113     public static class MeshToOBJ\r
114     {\r
115         public static bool MeshesToOBJ(Dictionary<uint, FacetedMesh> meshes, string filename)\r
116         {\r
117             StringBuilder obj = new StringBuilder();\r
118             StringBuilder mtl = new StringBuilder();\r
119 \r
120             FileInfo objFileInfo = new FileInfo(filename);\r
121 \r
122             string mtlFilename = objFileInfo.FullName.Substring(objFileInfo.DirectoryName.Length + 1,\r
123                 objFileInfo.FullName.Length - (objFileInfo.DirectoryName.Length + 1) - 4) + ".mtl";\r
124 \r
125             obj.AppendLine("# Created by libprimrender");\r
126             obj.AppendLine("mtllib ./" + mtlFilename);\r
127             obj.AppendLine();\r
128 \r
129             mtl.AppendLine("# Created by libprimrender");\r
130             mtl.AppendLine();\r
131 \r
132             int primNr = 0;\r
133             foreach (FacetedMesh mesh in meshes.Values)\r
134             {\r
135                 for (int j = 0; j < mesh.Faces.Count; j++)\r
136                 {\r
137                     Face face = mesh.Faces[j];\r
138 \r
139                     if (face.Vertices.Count > 2)\r
140                     {\r
141                         string mtlName = String.Format("material{0}-{1}", primNr, face.ID);\r
142                         Primitive.TextureEntryFace tex = face.TextureFace;\r
143                         string texName = tex.TextureID.ToString() + ".tga";\r
144 \r
145                         // FIXME: Convert the source to TGA (if needed) and copy to the destination\r
146 \r
147                         float shiny = 0.00f;\r
148                         switch (tex.Shiny)\r
149                         {\r
150                             case Shininess.High:\r
151                                 shiny = 1.00f;\r
152                                 break;\r
153                             case Shininess.Medium:\r
154                                 shiny = 0.66f;\r
155                                 break;\r
156                             case Shininess.Low:\r
157                                 shiny = 0.33f;\r
158                                 break;\r
159                         }\r
160 \r
161                         obj.AppendFormat("g face{0}-{1}{2}", primNr, face.ID, Environment.NewLine);\r
162 \r
163                         mtl.AppendLine("newmtl " + mtlName);\r
164                         mtl.AppendFormat("Ka {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);\r
165                         mtl.AppendFormat("Kd {0} {1} {2}{3}", tex.RGBA.R, tex.RGBA.G, tex.RGBA.B, Environment.NewLine);\r
166                         //mtl.AppendFormat("Ks {0} {1} {2}{3}");\r
167                         mtl.AppendLine("Tr " + tex.RGBA.A);\r
168                         mtl.AppendLine("Ns " + shiny);\r
169                         mtl.AppendLine("illum 1");\r
170                         if (tex.TextureID != UUID.Zero && tex.TextureID != Primitive.TextureEntry.WHITE_TEXTURE)\r
171                             mtl.AppendLine("map_Kd ./" + texName);\r
172                         mtl.AppendLine();\r
173 \r
174                         // Write the vertices, texture coordinates, and vertex normals for this side\r
175                         for (int k = 0; k < face.Vertices.Count; k++)\r
176                         {\r
177                             Vertex vertex = face.Vertices[k];\r
178 \r
179                             #region Vertex\r
180 \r
181                             Vector3 pos = vertex.Position;\r
182 \r
183                             // Apply scaling\r
184                             pos *= mesh.Prim.Scale;\r
185 \r
186                             // Apply rotation\r
187                             pos *= mesh.Prim.Rotation;\r
188 \r
189                             // The root prim position is sim-relative, while child prim positions are\r
190                             // parent-relative. We want to apply parent-relative translations but not\r
191                             // sim-relative ones\r
192                             if (mesh.Prim.ParentID != 0)\r
193                                 pos += mesh.Prim.Position;\r
194 \r
195                             obj.AppendFormat("v {0} {1} {2}{3}", pos.X, pos.Y, pos.Z, Environment.NewLine);\r
196 \r
197                             #endregion Vertex\r
198 \r
199                             #region Texture Coord\r
200 \r
201                             obj.AppendFormat("vt {0} {1}{2}", vertex.TexCoord.X, vertex.TexCoord.Y,\r
202                                 Environment.NewLine);\r
203 \r
204                             #endregion Texture Coord\r
205 \r
206                             #region Vertex Normal\r
207 \r
208                             // HACK: Sometimes normals are getting set to <NaN,NaN,NaN>\r
209                             if (!Single.IsNaN(vertex.Normal.X) && !Single.IsNaN(vertex.Normal.Y) && !Single.IsNaN(vertex.Normal.Z))\r
210                                 obj.AppendFormat("vn {0} {1} {2}{3}", vertex.Normal.X, vertex.Normal.Y, vertex.Normal.Z,\r
211                                     Environment.NewLine);\r
212                             else\r
213                                 obj.AppendLine("vn 0.0 1.0 0.0");\r
214 \r
215                             #endregion Vertex Normal\r
216                         }\r
217 \r
218                         obj.AppendFormat("# {0} vertices{1}", face.Vertices.Count, Environment.NewLine);\r
219                         obj.AppendLine();\r
220                         obj.AppendLine("usemtl " + mtlName);\r
221 \r
222                         #region Elements\r
223 \r
224                         // Write all of the faces (triangles) for this side\r
225                         for (int k = 0; k < face.Indices.Count / 3; k++)\r
226                         {\r
227                             obj.AppendFormat("f -{0}/-{0}/-{0} -{1}/-{1}/-{1} -{2}/-{2}/-{2}{3}",\r
228                                 face.Vertices.Count - face.Indices[k * 3 + 0],\r
229                                 face.Vertices.Count - face.Indices[k * 3 + 1],\r
230                                 face.Vertices.Count - face.Indices[k * 3 + 2],\r
231                                 Environment.NewLine);\r
232                         }\r
233 \r
234                         obj.AppendFormat("# {0} elements{1}", face.Indices.Count / 3, Environment.NewLine);\r
235                         obj.AppendLine();\r
236 \r
237                         #endregion Elements\r
238                     }\r
239                 }\r
240                 primNr++;\r
241             }\r
242 \r
243             try\r
244             {\r
245                 File.WriteAllText(filename, obj.ToString());\r
246                 File.WriteAllText(mtlFilename, mtl.ToString());\r
247             }\r
248             catch (Exception)\r
249             {\r
250                 return false;\r
251             }\r
252 \r
253             return true;\r
254         }\r
255     }\r
256 \r
257     public static class Math3D\r
258     {\r
259         // Column-major:\r
260         // |  0  4  8 12 |\r
261         // |  1  5  9 13 |\r
262         // |  2  6 10 14 |\r
263         // |  3  7 11 15 |\r
264 \r
265         public static float[] CreateTranslationMatrix(Vector3 v)\r
266         {\r
267             float[] mat = new float[16];\r
268 \r
269             mat[12] = v.X;\r
270             mat[13] = v.Y;\r
271             mat[14] = v.Z;\r
272             mat[0] = mat[5] = mat[10] = mat[15] = 1;\r
273 \r
274             return mat;\r
275         }\r
276 \r
277         public static float[] CreateRotationMatrix(Quaternion q)\r
278         {\r
279             float[] mat = new float[16];\r
280 \r
281             // Transpose the quaternion (don't ask me why)\r
282             q.X = q.X * -1f;\r
283             q.Y = q.Y * -1f;\r
284             q.Z = q.Z * -1f;\r
285 \r
286             float x2 = q.X + q.X;\r
287             float y2 = q.Y + q.Y;\r
288             float z2 = q.Z + q.Z;\r
289             float xx = q.X * x2;\r
290             float xy = q.X * y2;\r
291             float xz = q.X * z2;\r
292             float yy = q.Y * y2;\r
293             float yz = q.Y * z2;\r
294             float zz = q.Z * z2;\r
295             float wx = q.W * x2;\r
296             float wy = q.W * y2;\r
297             float wz = q.W * z2;\r
298 \r
299             mat[0] = 1.0f - (yy + zz);\r
300             mat[1] = xy - wz;\r
301             mat[2] = xz + wy;\r
302             mat[3] = 0.0f;\r
303 \r
304             mat[4] = xy + wz;\r
305             mat[5] = 1.0f - (xx + zz);\r
306             mat[6] = yz - wx;\r
307             mat[7] = 0.0f;\r
308 \r
309             mat[8] = xz - wy;\r
310             mat[9] = yz + wx;\r
311             mat[10] = 1.0f - (xx + yy);\r
312             mat[11] = 0.0f;\r
313 \r
314             mat[12] = 0.0f;\r
315             mat[13] = 0.0f;\r
316             mat[14] = 0.0f;\r
317             mat[15] = 1.0f;\r
318 \r
319             return mat;\r
320         }\r
321 \r
322         public static float[] CreateScaleMatrix(Vector3 v)\r
323         {\r
324             float[] mat = new float[16];\r
325 \r
326             mat[0] = v.X;\r
327             mat[5] = v.Y;\r
328             mat[10] = v.Z;\r
329             mat[15] = 1;\r
330 \r
331             return mat;\r
332         }\r
333 \r
334         public static bool GluProject(OpenTK.Vector3 objPos, OpenTK.Matrix4 modelMatrix, OpenTK.Matrix4 projMatrix, int[] viewport, out OpenTK.Vector3 screenPos)\r
335         {\r
336             OpenTK.Vector4 _in;\r
337             OpenTK.Vector4 _out;\r
338 \r
339             _in.X = objPos.X;\r
340             _in.Y = objPos.Y;\r
341             _in.Z = objPos.Z;\r
342             _in.W = 1.0f;\r
343 \r
344             _out = OpenTK.Vector4.Transform(_in, modelMatrix);\r
345             _in = OpenTK.Vector4.Transform(_out, projMatrix);\r
346 \r
347             if (_in.W <= 0.0)\r
348             {\r
349                 screenPos = OpenTK.Vector3.Zero;\r
350                 return false;\r
351             }\r
352 \r
353             _in.X /= _in.W;\r
354             _in.Y /= _in.W;\r
355             _in.Z /= _in.W;\r
356             /* Map x, y and z to range 0-1 */\r
357             _in.X = _in.X * 0.5f + 0.5f;\r
358             _in.Y = _in.Y * 0.5f + 0.5f;\r
359             _in.Z = _in.Z * 0.5f + 0.5f;\r
360 \r
361             /* Map x,y to viewport */\r
362             _in.X = _in.X * viewport[2] + viewport[0];\r
363             _in.Y = _in.Y * viewport[3] + viewport[1];\r
364 \r
365             screenPos.X = _in.X;\r
366             screenPos.Y = _in.Y;\r
367             screenPos.Z = _in.Z;\r
368 \r
369             return true;\r
370         }\r
371     }\r
372 \r
373     public class attachment_point\r
374     {\r
375         public string name;\r
376         public string joint;\r
377         public Vector3 position;\r
378         public Vector3 rotation;\r
379         public int id;\r
380         public int group;\r
381 \r
382         public GLMesh jointmesh;\r
383         public int jointmeshindex;\r
384 \r
385         public Vector3 getposition()\r
386         {\r
387             Vector3 pos;\r
388             pos = jointmesh.Position + position;\r
389             return pos;\r
390         }\r
391 \r
392         public Quaternion getrotation()\r
393         {\r
394             Vector3 rotvec = jointmesh.RotationAngles + rotation;\r
395             rotvec.Normalize();\r
396             Quaternion rot = new Quaternion(rotvec.X, rotvec.Y, rotvec.Z);\r
397             return rot;\r
398         }\r
399     }\r
400 \r
401     /// <summary>\r
402     /// Subclass of LindenMesh that adds vertex, index, and texture coordinate\r
403     /// arrays suitable for pushing direct to OpenGL\r
404     /// </summary>\r
405     public class GLMesh : LindenMesh\r
406     {\r
407         /// <summary>\r
408         /// Subclass of LODMesh that adds an index array suitable for pushing\r
409         /// direct to OpenGL\r
410         /// </summary>\r
411         /// \r
412 \r
413         public int teFaceID;\r
414 \r
415         new public class LODMesh : LindenMesh.LODMesh\r
416         {\r
417             public ushort[] Indices;\r
418 \r
419             public override void LoadMesh(string filename)\r
420             {\r
421                 base.LoadMesh(filename);\r
422 \r
423                 // Generate the index array\r
424                 Indices = new ushort[_numFaces * 3];\r
425                 int current = 0;\r
426                 for (int i = 0; i < _numFaces; i++)\r
427                 {\r
428                     Indices[current++] = (ushort)_faces[i].Indices[0];\r
429                     Indices[current++] = (ushort)_faces[i].Indices[1];\r
430                     Indices[current++] = (ushort)_faces[i].Indices[2];\r
431                 }\r
432             }\r
433         }\r
434 \r
435         /// <summary>\r
436         /// \r
437         /// </summary>\r
438         public struct GLData\r
439         {\r
440             public float[] Vertices;\r
441             public ushort[] Indices;\r
442             public float[] TexCoords;\r
443             public Vector3 Center;\r
444         }\r
445 \r
446         public static GLData baseRenderData;\r
447         public GLData RenderData;\r
448 \r
449         public GLMesh(string name)\r
450             : base(name)\r
451         {\r
452         }\r
453 \r
454         public override void LoadMesh(string filename)\r
455         {\r
456             base.LoadMesh(filename);\r
457 \r
458             float minX, minY, minZ;\r
459             minX = minY = minZ = Single.MaxValue;\r
460             float maxX, maxY, maxZ;\r
461             maxX = maxY = maxZ = Single.MinValue;\r
462 \r
463             // Generate the vertex array\r
464             RenderData.Vertices = new float[_numVertices * 3];\r
465             int current = 0;\r
466             for (int i = 0; i < _numVertices; i++)\r
467             {\r
468                 RenderData.Vertices[current++] = _vertices[i].Coord.X;\r
469                 RenderData.Vertices[current++] = _vertices[i].Coord.Y;\r
470                 RenderData.Vertices[current++] = _vertices[i].Coord.Z;\r
471 \r
472                 if (_vertices[i].Coord.X < minX)\r
473                     minX = _vertices[i].Coord.X;\r
474                 else if (_vertices[i].Coord.X > maxX)\r
475                     maxX = _vertices[i].Coord.X;\r
476 \r
477                 if (_vertices[i].Coord.Y < minY)\r
478                     minY = _vertices[i].Coord.Y;\r
479                 else if (_vertices[i].Coord.Y > maxY)\r
480                     maxY = _vertices[i].Coord.Y;\r
481 \r
482                 if (_vertices[i].Coord.Z < minZ)\r
483                     minZ = _vertices[i].Coord.Z;\r
484                 else if (_vertices[i].Coord.Z > maxZ)\r
485                     maxZ = _vertices[i].Coord.Z;\r
486             }\r
487 \r
488             // Calculate the center-point from the bounding box edges\r
489             RenderData.Center = new Vector3((minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2);\r
490 \r
491             // Generate the index array\r
492             RenderData.Indices = new ushort[_numFaces * 3];\r
493             current = 0;\r
494             for (int i = 0; i < _numFaces; i++)\r
495             {\r
496                 RenderData.Indices[current++] = (ushort)_faces[i].Indices[0];\r
497                 RenderData.Indices[current++] = (ushort)_faces[i].Indices[1];\r
498                 RenderData.Indices[current++] = (ushort)_faces[i].Indices[2];\r
499             }\r
500 \r
501             // Generate the texcoord array\r
502             RenderData.TexCoords = new float[_numVertices * 2];\r
503             current = 0;\r
504             for (int i = 0; i < _numVertices; i++)\r
505             {\r
506                 RenderData.TexCoords[current++] = _vertices[i].TexCoord.X;\r
507                 RenderData.TexCoords[current++] = _vertices[i].TexCoord.Y;\r
508             }\r
509         }\r
510 \r
511         public override void LoadLODMesh(int level, string filename)\r
512         {\r
513             LODMesh lod = new LODMesh();\r
514             lod.LoadMesh(filename);\r
515             _lodMeshes[level] = lod;\r
516         }\r
517     }\r
518 \r
519     public class GLAvatar\r
520     {\r
521         public static Dictionary<string, GLMesh> _meshes = new Dictionary<string, GLMesh>();\r
522         public static bool _wireframe = true;\r
523         public static bool _showSkirt = false;\r
524         public static Dictionary<int, attachment_point> attachment_points = new Dictionary<int, attachment_point>();\r
525 \r
526         public static void loadlindenmeshes(string LODfilename)\r
527         {\r
528             string basedir = Directory.GetCurrentDirectory() + System.IO.Path.DirectorySeparatorChar + "character" + System.IO.Path.DirectorySeparatorChar;\r
529 \r
530             // Parse through avatar_lad.xml to find all of the mesh references\r
531             XmlDocument lad = new XmlDocument();\r
532             lad.Load(basedir + LODfilename);\r
533 \r
534             attachment_points.Clear();\r
535 \r
536             XmlNodeList attach_points = lad.GetElementsByTagName("attachment_point");\r
537             foreach (XmlNode apoint in attach_points)\r
538             {\r
539                 attachment_point point = new attachment_point();\r
540                 point.name = apoint.Attributes.GetNamedItem("name").Value;\r
541                 point.joint = apoint.Attributes.GetNamedItem("joint").Value;\r
542 \r
543                 string pos = apoint.Attributes.GetNamedItem("position").Value;\r
544                 string[] posparts = pos.Split(' ');\r
545                 point.position = new Vector3(float.Parse(posparts[0]), float.Parse(posparts[1]), float.Parse(posparts[2]));\r
546 \r
547                 string rot = apoint.Attributes.GetNamedItem("rotation").Value;\r
548                 string[] rotparts = rot.Split(' ');\r
549                 point.rotation = new Vector3(float.Parse(rotparts[0]), float.Parse(rotparts[1]), float.Parse(rotparts[2]));\r
550 \r
551                 point.id = Int32.Parse(apoint.Attributes.GetNamedItem("id").Value);\r
552                 point.group = Int32.Parse(apoint.Attributes.GetNamedItem("group").Value);\r
553 \r
554                 attachment_points.Add(point.id, point);\r
555 \r
556             }\r
557 \r
558             XmlNodeList bones = lad.GetElementsByTagName("bone");\r
559 \r
560             XmlNodeList meshes = lad.GetElementsByTagName("mesh");\r
561 \r
562             foreach (XmlNode meshNode in meshes)\r
563             {\r
564                 string type = meshNode.Attributes.GetNamedItem("type").Value;\r
565                 int lod = Int32.Parse(meshNode.Attributes.GetNamedItem("lod").Value);\r
566                 string fileName = meshNode.Attributes.GetNamedItem("file_name").Value;\r
567                 //string minPixelWidth = meshNode.Attributes.GetNamedItem("min_pixel_width").Value;\r
568 \r
569                 GLMesh mesh = (_meshes.ContainsKey(type) ? _meshes[type] : new GLMesh(type));\r
570 \r
571                 switch (mesh.Name)\r
572                 {\r
573                     case "lowerBodyMesh":\r
574                         mesh.teFaceID = (int)AvatarTextureIndex.LowerBaked;\r
575                         break;\r
576 \r
577                     case "upperBodyMesh":\r
578                         mesh.teFaceID = (int)AvatarTextureIndex.UpperBaked;\r
579                         break;\r
580 \r
581                     case "headMesh":\r
582                         mesh.teFaceID = (int)AvatarTextureIndex.HeadBaked;\r
583                         break;\r
584 \r
585                     case "hairMesh":\r
586                         mesh.teFaceID = (int)AvatarTextureIndex.HairBaked;\r
587                     break;\r
588 \r
589                     case "eyelashMesh":\r
590                         mesh.teFaceID = (int)AvatarTextureIndex.HeadBaked;\r
591                     break;\r
592 \r
593                     case "eyeBallRightMesh":\r
594                     case "eyeBallLeftMesh":\r
595                         mesh.teFaceID = (int)AvatarTextureIndex.EyesBaked;\r
596                         break;\r
597 \r
598                     case "skirtMesh":\r
599                         mesh.teFaceID = (int)AvatarTextureIndex.SkirtBaked;\r
600                         break;\r
601 \r
602                     default:\r
603                         mesh.teFaceID = 0;\r
604                         break;\r
605                 }\r
606 \r
607                 if (lod == 0)\r
608                 {\r
609                     mesh.LoadMesh(basedir + fileName);\r
610                 }\r
611                 else\r
612                 {\r
613                     mesh.LoadLODMesh(lod, basedir + fileName);\r
614                 }\r
615 \r
616                 _meshes[type] = mesh;\r
617 \r
618                 if (lod == 0)\r
619                 {\r
620                     //Associate each attachment point with the mesh that has its bones/joints\r
621                     foreach (attachment_point apoint in attachment_points.Values)\r
622                     {\r
623                         int index = 0;\r
624 \r
625                         foreach (string jointname in mesh.SkinJoints)\r
626                         {\r
627                             if (jointname == apoint.joint)\r
628                             {\r
629                                 apoint.jointmesh = mesh;\r
630                                 apoint.jointmeshindex = index;\r
631                                 Logger.Log("Adding " + apoint.name +"with joint "+ jointname + "to mesh " + mesh.Name,Helpers.LogLevel.Info);\r
632                             }\r
633                             index++;\r
634                         }\r
635 \r
636                     }\r
637                 }\r
638 \r
639             }\r
640         }\r
641 \r
642     }\r
643 \r
644     class RenderAvatar\r
645     {\r
646         public GLAvatar glavatar;\r
647         public Avatar avatar;\r
648         public FaceData[] data = new FaceData[32];\r
649 \r
650     }\r
651 \r
652 }\r