OSDN Git Service

Permissions
[radegast/radegast.git] / Radegast / Core / DAEExport.cs
1 // 
2 // Radegast Metaverse Client
3 // Copyright (c) 2009-2014, Radegast Development Team
4 // All rights reserved.
5 // 
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
8 // 
9 //     * Redistributions of source code must retain the above copyright notice,
10 //       this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above copyright
12 //       notice, this list of conditions and the following disclaimer in the
13 //       documentation and/or other materials provided with the distribution.
14 //     * Neither the name of the application "Radegast", nor the names of its
15 //       contributors may be used to endorse or promote products derived from
16 //       this software without specific prior written permission.
17 // 
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 //
29
30 using System;
31 using System.Collections.Generic;
32 using System.Text;
33 using System.Xml;
34 using System.IO;
35 using System.Drawing;
36 using OpenMetaverse;
37 using OpenMetaverse.Rendering;
38 using OpenMetaverse.Imaging;
39 using OpenMetaverse.Assets;
40
41 namespace Radegast
42 {
43     public class DAEExport : IDisposable
44     {
45         public string FileName { get; set; }
46         public Primitive RootPrim { get; set; }
47         public List<Primitive> Prims = new List<Primitive>();
48         public List<FacetedMesh> MeshedPrims = new List<FacetedMesh>();
49         public List<UUID> Textures = new List<UUID>();
50         public string[] TextureNames;
51         public int ExportablePrims { get; set; }
52         public int ExportableTextures { get; set; }
53         public bool ExportTextures { get; set; }
54         public string ImageFormat { get; set; }
55         public bool ConsolidateMaterials { get; set; }
56         public bool SkipTransparentFaces { get; set; }
57
58         public static readonly List<UUID> BuiltInTextures = new List<UUID>() {
59             new UUID("89556747-24cb-43ed-920b-47caed15465f"), // default
60             new UUID("be293869-d0d9-0a69-5989-ad27f1946fd4"), // default sculpt 
61             new UUID("5748decc-f629-461c-9a36-a35a221fe21f"), // blank
62             new UUID("38b86f85-2575-52a9-a531-23108d8da837"), // invisible
63             new UUID("8dcd4a48-2d37-4909-9f78-f7a9eb4ef903"), // tranparent
64         };
65
66         RadegastInstance Instance;
67         GridClient Client { get { return Instance.Client; } }
68         System.Globalization.CultureInfo invariant = System.Globalization.CultureInfo.InvariantCulture;
69         MeshmerizerR Mesher;
70         XmlDocument Doc;
71
72         public event EventHandler<DAEStatutsEventArgs> Progress;
73
74         void OnProgress(string message)
75         {
76             if (Progress != null)
77             {
78                 Progress(this, new DAEStatutsEventArgs(message));
79             }
80         }
81
82         public DAEExport(RadegastInstance instance, Primitive requestedPrim)
83         {
84             Instance = instance;
85             ImageFormat = "PNG";
86             ConsolidateMaterials = true;
87             SkipTransparentFaces = true;
88             Mesher = new MeshmerizerR();
89             Init(Client.Network.CurrentSim, requestedPrim);
90         }
91
92         public void Dispose()
93         {
94         }
95
96         public void Init(Simulator sim, Primitive requestedPrim)
97         {
98             if (requestedPrim == null || !Client.Network.Connected) return;
99
100             RootPrim = requestedPrim;
101
102             if (requestedPrim.ParentID != 0)
103             {
104                 Primitive parent;
105                 if (sim.ObjectsPrimitives.TryGetValue(requestedPrim.ParentID, out parent))
106                 {
107                     RootPrim = parent;
108                 }
109             }
110
111             Prims.Clear();
112             Primitive root = new Primitive(RootPrim);
113             root.Position = Vector3.Zero;
114             Prims.Add(root);
115
116             sim.ObjectsPrimitives
117                 .FindAll(p => p.ParentID == RootPrim.LocalID)
118                 .ForEach(p =>
119                     {
120                         var child = new Primitive(p);
121                         child.Position = root.Position + child.Position * root.Rotation;
122                         child.Rotation = root.Rotation * child.Rotation;
123                         Prims.Add(child);
124                     });
125             List<uint> select = new List<uint>(Prims.Count);
126             Prims.ForEach(p => select.Add(p.LocalID));
127             Client.Objects.SelectObjects(sim, select.ToArray(), true);
128
129             OnProgress("Checking permissions");
130             ExportablePrims = 0;
131             for (int i = 0; i < Prims.Count; i++)
132             {
133                 if (!CanExport(Prims[i])) continue;
134                 ExportablePrims++;
135
136                 var defaultTexture = Prims[i].Textures.DefaultTexture;
137                 if (defaultTexture != null && !Textures.Contains(defaultTexture.TextureID))
138                 {
139                     Textures.Add(defaultTexture.TextureID);
140                 }
141                 for (int j = 0; j < Prims[i].Textures.FaceTextures.Length; j++)
142                 {
143                     var te = Prims[i].Textures.FaceTextures[j];
144                     if (te == null) continue;
145                     UUID id = te.TextureID;
146                     if (!Textures.Contains(id))
147                     {
148                         Textures.Add(id);
149                     }
150                 }
151             }
152
153             TextureNames = new string[Textures.Count];
154             ExportableTextures = 0;
155             for (int i = 0; i < Textures.Count; i++)
156             {
157                 string name;
158                 if (CanExportTexture(Textures[i], out name))
159                 {
160                     ExportableTextures++;
161                     TextureNames[i] = RadegastInstance.SafeFileName(name).Replace(' ', '_');
162                 }
163             }
164         }
165
166         public void Export(string Filename)
167         {
168             this.FileName = Filename;
169
170             MeshedPrims.Clear();
171
172             if (string.IsNullOrEmpty(FileName))
173             {
174                 return;
175             }
176
177             WorkPool.QueueUserWorkItem(sync =>
178             {
179                 if (ExportTextures)
180                 {
181                     SaveTextures();
182                 }
183                 for (int i = 0; i < Prims.Count; i++)
184                 {
185                     if (!CanExport(Prims[i])) continue;
186
187                     FacetedMesh mesh = MeshPrim(Prims[i]);
188                     if (mesh == null) continue;
189
190                     for (int j = 0; j < mesh.Faces.Count; j++)
191                     {
192                         Face face = mesh.Faces[j];
193
194                         Primitive.TextureEntryFace teFace = mesh.Faces[j].TextureFace;
195                         if (teFace == null) continue;
196
197
198                         // Sculpt UV vertically flipped compared to prims. Flip back
199                         if (Prims[i].Sculpt != null && Prims[i].Sculpt.SculptTexture != UUID.Zero && Prims[i].Sculpt.Type != SculptType.Mesh)
200                         {
201                             teFace = (Primitive.TextureEntryFace)teFace.Clone();
202                             teFace.RepeatV *= -1;
203                         }
204
205                         // Texture transform for this face
206                         Mesher.TransformTexCoords(face.Vertices, face.Center, teFace, Prims[i].Scale);
207
208                     }
209                     MeshedPrims.Add(mesh);
210                 }
211
212                 string msg;
213                 if (MeshedPrims.Count == 0)
214                 {
215                     msg = string.Format("Can export 0 out of {0} prims.{1}{1}Skipping.", Prims.Count, Environment.NewLine);
216                 }
217                 else
218                 {
219                     msg = string.Format("Exported {0} out of {1} objects to{2}{2}{3}", MeshedPrims.Count, Prims.Count, Environment.NewLine, FileName);
220                 }
221                 GenerateCollada();
222                 File.WriteAllText(FileName, DocToString(Doc));
223                 OnProgress(msg);
224             });
225         }
226
227         void SaveTextures()
228         {
229             OnProgress("Exporting textures...");
230             for (int i = 0; i < Textures.Count; i++)
231             {
232                 var id = Textures[i];
233                 if (TextureNames[i] == null)
234                 {
235                     OnProgress("Skipping " + id.ToString() + " due to insufficient permissions");
236                     continue;
237                 }
238
239                 string filename = TextureNames[i] + "." + ImageFormat.ToLower();
240
241                 OnProgress("Fetching texture" + id);
242
243                 ManagedImage mImage = null;
244                 Image wImage = null;
245                 byte[] jpegData = null;
246
247                 try
248                 {
249                     System.Threading.AutoResetEvent gotImage = new System.Threading.AutoResetEvent(false);
250                     Client.Assets.RequestImage(id, ImageType.Normal, (state, asset) =>
251                     {
252                         if (state == TextureRequestState.Finished && asset != null)
253                         {
254                             jpegData = asset.AssetData;
255                             OpenJPEG.DecodeToImage(jpegData, out mImage, out wImage);
256                             gotImage.Set();
257                         }
258                         else if (state != TextureRequestState.Pending && state != TextureRequestState.Started && state != TextureRequestState.Progress)
259                         {
260                             gotImage.Set();
261                         }
262                     });
263                     gotImage.WaitOne(120 * 1000, false);
264
265                     if (wImage != null)
266                     {
267                         string fullFileName = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(FileName), filename);
268                         switch (ImageFormat)
269                         {
270                             case "PNG":
271                                 wImage.Save(fullFileName, System.Drawing.Imaging.ImageFormat.Png);
272                                 break;
273                             case "JPG":
274                                 wImage.Save(fullFileName, System.Drawing.Imaging.ImageFormat.Jpeg);
275                                 break;
276                             case "BMP":
277                                 wImage.Save(fullFileName, System.Drawing.Imaging.ImageFormat.Bmp);
278                                 break;
279                             case "J2C":
280                                 File.WriteAllBytes(fullFileName, jpegData);
281                                 break;
282                             case "TGA":
283                                 File.WriteAllBytes(fullFileName, mImage.ExportTGA());
284                                 break;
285                             default:
286                                 throw new Exception("Unsupported image format");
287                         }
288                         OnProgress("Saved to " + fullFileName);
289                     }
290                     else
291                     {
292                         throw new Exception("Failed to decode image");
293                     }
294
295                 }
296                 catch (Exception ex)
297                 {
298                     OnProgress("Failed: " + ex.ToString());
299                     TextureNames[i] = null;
300                 }
301
302             }
303         }
304
305         public static bool IsFullPerm(Permissions Permissions)
306         {
307             if (
308                 ((Permissions.OwnerMask & PermissionMask.Modify) != 0) &&
309                 ((Permissions.OwnerMask & PermissionMask.Copy) != 0) &&
310                 ((Permissions.OwnerMask & PermissionMask.Transfer) != 0)
311                 )
312             {
313                 return true;
314             }
315             else
316             {
317                 return false;
318             }
319         }
320
321         bool CanExport(Primitive prim)
322         {
323             if (prim.Properties == null) return false;
324
325             return (prim.Properties.OwnerID == Client.Self.AgentID) &&
326                 (prim.Properties.CreatorID == Client.Self.AgentID) ||
327                 (Instance.Netcom.LoginOptions.Grid.Platform != "SecondLife"
328                 && prim.Properties.OwnerID == Client.Self.AgentID
329                 && IsFullPerm(prim.Properties.Permissions)) ||
330                 Instance.advancedDebugging;
331         }
332
333         bool CanExportTexture(UUID id, out string name)
334         {
335             name = id.ToString();
336             if (BuiltInTextures.Contains(id) || Instance.Netcom.LoginOptions.Grid.Platform != "SecondLife")
337             {
338                 return true;
339             }
340
341             InventoryItem item = null;
342             foreach (var pair in Client.Inventory.Store.Items)
343             {
344                 if (pair.Value.Data is InventoryItem)
345                 {
346                     var i = (InventoryItem)pair.Value.Data;
347                     if (i.AssetUUID == id && (InventoryConsole.IsFullPerm(i) || Instance.advancedDebugging))
348                     {
349                         item = i;
350                         break;
351                     }
352                 }
353             }
354
355             if (item != null)
356             {
357                 name = item.Name;
358                 return true;
359             }
360             else if (Instance.advancedDebugging)
361             {
362                 return true;
363             }
364             else
365             {
366                 return false;
367             }
368         }
369
370         FacetedMesh MeshPrim(Primitive prim)
371         {
372             FacetedMesh mesh = null;
373             if (prim.Sculpt == null || prim.Sculpt.SculptTexture == UUID.Zero)
374             {
375                 mesh = Mesher.GenerateFacetedMesh(prim, DetailLevel.Highest);
376             }
377             else if (prim.Sculpt.Type != SculptType.Mesh)
378             {
379                 Image img = null;
380                 if (LoadTexture(prim.Sculpt.SculptTexture, ref img, true))
381                 {
382                     mesh = Mesher.GenerateFacetedSculptMesh(prim, (Bitmap)img, DetailLevel.Highest);
383                 }
384             }
385             else
386             {
387                 var gotMesh = new System.Threading.AutoResetEvent(false);
388
389                 Client.Assets.RequestMesh(prim.Sculpt.SculptTexture, (success, meshAsset) =>
390                 {
391                     if (!success || !FacetedMesh.TryDecodeFromAsset(prim, meshAsset, DetailLevel.Highest, out mesh))
392                     {
393                         Logger.Log("Failed to fetch or decode the mesh asset", Helpers.LogLevel.Warning, Client);
394                     }
395                     gotMesh.Set();
396                 });
397
398                 gotMesh.WaitOne(20 * 1000, false);
399             }
400             return mesh;
401         }
402
403         bool LoadTexture(UUID textureID, ref Image texture, bool removeAlpha)
404         {
405             var gotImage = new System.Threading.ManualResetEvent(false);
406             Image img = null;
407
408             try
409             {
410                 gotImage.Reset();
411                 byte[] tgaData;
412                 Client.Assets.RequestImage(textureID, (TextureRequestState state, AssetTexture assetTexture) =>
413                 {
414                     ManagedImage mi;
415                     if (state == TextureRequestState.Finished && OpenJPEG.DecodeToImage(assetTexture.AssetData, out mi))
416                     {
417
418                         if (removeAlpha)
419                         {
420                             if ((mi.Channels & ManagedImage.ImageChannels.Alpha) != 0)
421                             {
422                                 mi.ConvertChannels(mi.Channels & ~ManagedImage.ImageChannels.Alpha);
423                             }
424                         }
425                         tgaData = mi.ExportTGA();
426                         img = LoadTGAClass.LoadTGA(new MemoryStream(tgaData));
427                     }
428                     gotImage.Set();
429                 });
430                 gotImage.WaitOne(30 * 1000, false);
431
432                 if (img != null)
433                 {
434                     texture = img;
435                     return true;
436                 }
437                 return false;
438             }
439             catch (Exception e)
440             {
441                 Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e);
442                 return false;
443             }
444         }
445
446         XmlNode ColladaInit()
447         {
448             Doc = new XmlDocument();
449             var root = Doc.AppendChild(Doc.CreateElement("COLLADA"));
450             root.Attributes.Append(Doc.CreateAttribute("xmlns")).Value = "http://www.collada.org/2005/11/COLLADASchema";
451             root.Attributes.Append(Doc.CreateAttribute("version")).Value = "1.4.1";
452
453             var asset = root.AppendChild(Doc.CreateElement("asset"));
454             var contributor = asset.AppendChild(Doc.CreateElement("contributor"));
455             contributor.AppendChild(Doc.CreateElement("author")).InnerText = "Radegast User";
456             contributor.AppendChild(Doc.CreateElement("authoring_tool")).InnerText = "Radegast Collada Export";
457
458             asset.AppendChild(Doc.CreateElement("created")).InnerText = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss");
459             asset.AppendChild(Doc.CreateElement("modified")).InnerText = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss");
460
461             var unit = asset.AppendChild(Doc.CreateElement("unit"));
462             unit.Attributes.Append(Doc.CreateAttribute("name")).Value = "meter";
463             unit.Attributes.Append(Doc.CreateAttribute("meter")).Value = "1";
464
465             asset.AppendChild(Doc.CreateElement("up_axis")).InnerText = "Z_UP";
466
467             return root;
468         }
469
470         void AddSource(XmlNode mesh, string src_id, string param, List<float> vals)
471         {
472             var source = mesh.AppendChild(Doc.CreateElement("source"));
473             source.Attributes.Append(Doc.CreateAttribute("id")).InnerText = src_id;
474             var src_array = source.AppendChild(Doc.CreateElement("float_array"));
475
476             src_array.Attributes.Append(Doc.CreateAttribute("id")).InnerText = string.Format("{0}-{1}", src_id, "array");
477             src_array.Attributes.Append(Doc.CreateAttribute("count")).InnerText = vals.Count.ToString();
478
479             StringBuilder sb = new StringBuilder();
480             for (int i = 0; i < vals.Count; i++)
481             {
482                 sb.Append(vals[i].ToString(invariant));
483                 if (i != vals.Count - 1)
484                 {
485                     sb.Append(" ");
486                 }
487             }
488             src_array.InnerText = sb.ToString();
489
490             var acc = source.AppendChild(Doc.CreateElement("technique_common"))
491                 .AppendChild(Doc.CreateElement("accessor"));
492             acc.Attributes.Append(Doc.CreateAttribute("source")).InnerText = string.Format("#{0}-{1}", src_id, "array");
493             acc.Attributes.Append(Doc.CreateAttribute("count")).InnerText = ((int)(vals.Count / param.Length)).ToString();
494             acc.Attributes.Append(Doc.CreateAttribute("stride")).InnerText = param.Length.ToString();
495
496             foreach (char c in param)
497             {
498                 var pX = acc.AppendChild(Doc.CreateElement("param"));
499                 pX.Attributes.Append(Doc.CreateAttribute("name")).InnerText = c.ToString();
500                 pX.Attributes.Append(Doc.CreateAttribute("type")).InnerText = "float";
501             }
502
503         }
504
505         void AddPolygons(XmlNode mesh, string geomID, string materialID, FacetedMesh obj, List<int> faces_to_include)
506         {
507             var polylist = mesh.AppendChild(Doc.CreateElement("polylist"));
508             polylist.Attributes.Append(Doc.CreateAttribute("material")).InnerText = materialID;
509
510             // Vertices semantic
511             {
512                 var input = polylist.AppendChild(Doc.CreateElement("input"));
513                 input.Attributes.Append(Doc.CreateAttribute("semantic")).InnerText = "VERTEX";
514                 input.Attributes.Append(Doc.CreateAttribute("offset")).InnerText = "0";
515                 input.Attributes.Append(Doc.CreateAttribute("source")).InnerText = string.Format("#{0}-{1}", geomID, "vertices");
516             }
517
518             // Normals semantic
519             {
520                 var input = polylist.AppendChild(Doc.CreateElement("input"));
521                 input.Attributes.Append(Doc.CreateAttribute("semantic")).InnerText = "NORMAL";
522                 input.Attributes.Append(Doc.CreateAttribute("offset")).InnerText = "0";
523                 input.Attributes.Append(Doc.CreateAttribute("source")).InnerText = string.Format("#{0}-{1}", geomID, "normals");
524             }
525
526             // UV semantic
527             {
528                 var input = polylist.AppendChild(Doc.CreateElement("input"));
529                 input.Attributes.Append(Doc.CreateAttribute("semantic")).InnerText = "TEXCOORD";
530                 input.Attributes.Append(Doc.CreateAttribute("offset")).InnerText = "0";
531                 input.Attributes.Append(Doc.CreateAttribute("source")).InnerText = string.Format("#{0}-{1}", geomID, "map0");
532             }
533
534             // Save indices
535             var vcount = polylist.AppendChild(Doc.CreateElement("vcount"));
536             var p = polylist.AppendChild(Doc.CreateElement("p"));
537             int index_offset = 0;
538             int num_tris = 0;
539             StringBuilder pBuilder = new StringBuilder();
540             StringBuilder vcountBuilder = new StringBuilder();
541
542             for (int face_num = 0; face_num < obj.Faces.Count; face_num++)
543             {
544                 var face = obj.Faces[face_num];
545                 if (faces_to_include == null || faces_to_include.Contains(face_num))
546                 {
547                     for (int i = 0; i < face.Indices.Count; i++)
548                     {
549                         int index = index_offset + face.Indices[i];
550                         pBuilder.Append(index);
551                         pBuilder.Append(" ");
552                         if (i % 3 == 0)
553                         {
554                             vcountBuilder.Append("3 ");
555                             num_tris++;
556                         }
557                     }
558                 }
559                 index_offset += face.Vertices.Count;
560             }
561
562             p.InnerText = pBuilder.ToString().TrimEnd();
563             vcount.InnerText = vcountBuilder.ToString().TrimEnd();
564             polylist.Attributes.Append(Doc.CreateAttribute("count")).InnerText = num_tris.ToString();
565         }
566
567         void GenerateImagesSection(XmlNode images)
568         {
569             for (int i = 0; i < TextureNames.Length; i++)
570             {
571                 string name = TextureNames[i];
572                 if (name == null) continue;
573                 string colladaName = name + "_" + ImageFormat.ToLower();
574                 var image = images.AppendChild(Doc.CreateElement("image"));
575                 image.Attributes.Append(Doc.CreateAttribute("id")).InnerText = colladaName;
576                 image.Attributes.Append(Doc.CreateAttribute("name")).InnerText = colladaName;
577                 image.AppendChild(Doc.CreateElement("init_from")).InnerText = System.Web.HttpUtility.UrlEncode(name + "." + ImageFormat.ToLower());
578             }
579         }
580
581         class MaterialInfo
582         {
583             public UUID TextureID;
584             public Color4 Color;
585             public string Name;
586
587             public bool Matches(Primitive.TextureEntryFace TextureEntry)
588             {
589                 return TextureID == TextureEntry.TextureID && Color == TextureEntry.RGBA;
590             }
591         }
592
593         List<MaterialInfo> AllMeterials = new List<MaterialInfo>();
594
595         MaterialInfo GetMaterial(Primitive.TextureEntryFace te)
596         {
597             MaterialInfo ret = null;
598             foreach (var mat in AllMeterials)
599             {
600                 if (mat.Matches(te))
601                 {
602                     ret = mat;
603                     break;
604                 }
605             }
606
607             if (ret == null)
608             {
609                 ret = new MaterialInfo()
610                 {
611                     TextureID = te.TextureID,
612                     Color = te.RGBA
613                 };
614                 ret.Name = string.Format("Material{0}", AllMeterials.Count);
615                 AllMeterials.Add(ret);
616             }
617
618             return ret;
619         }
620
621         List<MaterialInfo> GetMaterials(FacetedMesh obj)
622         {
623             var ret = new List<MaterialInfo>();
624
625             for (int face_num = 0; face_num < obj.Faces.Count; face_num++)
626             {
627                 var te = obj.Faces[(int)face_num].TextureFace;
628                 if (SkipTransparentFaces && te.RGBA.A < 0.01f)
629                 {
630                     continue;
631                 }
632                 var mat = GetMaterial(te);
633                 if (!ret.Contains(mat))
634                 {
635                     ret.Add(mat);
636                 }
637             }
638             return ret;
639         }
640
641         List<int> GetFacesWithMaterial(FacetedMesh obj, MaterialInfo mat)
642         {
643             var ret = new List<int>();
644             for (int face_num = 0; face_num < obj.Faces.Count; face_num++)
645             {
646                 if (mat == GetMaterial(obj.Faces[(int)face_num].TextureFace))
647                 {
648                     ret.Add(face_num);
649                 }
650             }
651             return ret;
652         }
653
654         void GenerateEffects(XmlNode effects)
655         {
656             // Effects (face color, alpha)
657             foreach (var mat in AllMeterials)
658             {
659                 var color = mat.Color;
660                 var effect = effects.AppendChild(Doc.CreateElement("effect"));
661                 effect.Attributes.Append(Doc.CreateAttribute("id")).InnerText = mat.Name + "-fx";
662                 var profile = effect.AppendChild(Doc.CreateElement("profile_COMMON"));
663                 string colladaName = null;
664                 if (ExportTextures)
665                 {
666                     UUID textID = UUID.Zero;
667                     int i = 0;
668                     for (; i < Textures.Count; i++)
669                     {
670                         if (mat.TextureID == Textures[i])
671                         {
672                             textID = mat.TextureID;
673                             break;
674                         }
675                     }
676
677                     if (textID != UUID.Zero && TextureNames[i] != null)
678                     {
679                         colladaName = TextureNames[i] + "_" + ImageFormat.ToLower();
680                         var newparam = profile.AppendChild(Doc.CreateElement("newparam"));
681                         newparam.Attributes.Append(Doc.CreateAttribute("sid")).InnerText = colladaName + "-surface";
682                         var surface = newparam.AppendChild(Doc.CreateElement("surface"));
683                         surface.Attributes.Append(Doc.CreateAttribute("type")).InnerText = "2D";
684                         surface.AppendChild(Doc.CreateElement("init_from")).InnerText = colladaName;
685                         newparam = profile.AppendChild(Doc.CreateElement("newparam"));
686                         newparam.Attributes.Append(Doc.CreateAttribute("sid")).InnerText = colladaName + "-sampler";
687                         newparam.AppendChild(Doc.CreateElement("sampler2D"))
688                             .AppendChild(Doc.CreateElement("source"))
689                             .InnerText = colladaName + "-surface";
690                     }
691
692                 }
693                 var t = profile.AppendChild(Doc.CreateElement("technique"));
694                 t.Attributes.Append(Doc.CreateAttribute("sid")).InnerText = "common";
695                 var phong = t.AppendChild(Doc.CreateElement("phong"));
696
697                 var diffuse = phong.AppendChild(Doc.CreateElement("diffuse"));
698                 // Only one <color> or <texture> can appear inside diffuse element
699                 if (colladaName != null)
700                 {
701                     var txtr = diffuse.AppendChild(Doc.CreateElement("texture"));
702                     txtr.Attributes.Append(Doc.CreateAttribute("texture")).InnerText = colladaName + "-sampler";
703                     txtr.Attributes.Append(Doc.CreateAttribute("texcoord")).InnerText = colladaName;
704                 }
705                 else
706                 {
707                     var diffuseColor = diffuse.AppendChild(Doc.CreateElement("color"));
708                     diffuseColor.Attributes.Append(Doc.CreateAttribute("sid")).InnerText = "diffuse";
709                     diffuseColor.InnerText = string.Format("{0} {1} {2} {3}",
710                         color.R.ToString(invariant),
711                         color.G.ToString(invariant),
712                         color.B.ToString(invariant),
713                         color.A.ToString(invariant));
714                 }
715
716                 phong.AppendChild(Doc.CreateElement("transparency"))
717                     .AppendChild(Doc.CreateElement("float"))
718                     .InnerText = color.A.ToString(invariant);
719
720             }
721         }
722
723         void GenerateCollada()
724         {
725             AllMeterials.Clear();
726             var root = ColladaInit();
727             var images = root.AppendChild(Doc.CreateElement("library_images"));
728             var geomLib = root.AppendChild(Doc.CreateElement("library_geometries"));
729             var effects = root.AppendChild(Doc.CreateElement("library_effects"));
730             var materials = root.AppendChild(Doc.CreateElement("library_materials"));
731             var scene = root.AppendChild(Doc.CreateElement("library_visual_scenes"))
732                 .AppendChild(Doc.CreateElement("visual_scene"));
733             scene.Attributes.Append(Doc.CreateAttribute("id")).InnerText = "Scene";
734             scene.Attributes.Append(Doc.CreateAttribute("name")).InnerText = "Scene";
735             if (ExportTextures)
736             {
737                 GenerateImagesSection(images);
738             }
739
740             int prim_nr = 0;
741             foreach (var obj in MeshedPrims)
742             {
743                 int total_num_vertices = 0;
744                 string name = string.Format("prim{0}", prim_nr++);
745                 string geomID = name;
746
747                 var geom = geomLib.AppendChild(Doc.CreateElement("geometry"));
748                 geom.Attributes.Append(Doc.CreateAttribute("id")).InnerText = string.Format("{0}-{1}", geomID, "mesh");
749                 var mesh = geom.AppendChild(Doc.CreateElement("mesh"));
750
751                 List<float> position_data = new List<float>();
752                 List<float> normal_data = new List<float>();
753                 List<float> uv_data = new List<float>();
754
755                 int num_faces = obj.Faces.Count;
756
757                 for (int face_num = 0; face_num < num_faces; face_num++)
758                 {
759                     var face = obj.Faces[face_num];
760                     total_num_vertices += face.Vertices.Count;
761
762                     for (int i = 0; i < face.Vertices.Count; i++)
763                     {
764                         var v = face.Vertices[i];
765                         position_data.Add(v.Position.X);
766                         position_data.Add(v.Position.Y);
767                         position_data.Add(v.Position.Z);
768
769                         normal_data.Add(v.Normal.X);
770                         normal_data.Add(v.Normal.Y);
771                         normal_data.Add(v.Normal.Z);
772
773                         uv_data.Add(v.TexCoord.X);
774                         uv_data.Add(v.TexCoord.Y);
775                     }
776                 }
777
778                 AddSource(mesh, string.Format("{0}-{1}", geomID, "positions"), "XYZ", position_data);
779                 AddSource(mesh, string.Format("{0}-{1}", geomID, "normals"), "XYZ", normal_data);
780                 AddSource(mesh, string.Format("{0}-{1}", geomID, "map0"), "ST", uv_data);
781
782                 // Add the <vertices> element
783                 {
784                     var verticesNode = mesh.AppendChild(Doc.CreateElement("vertices"));
785                     verticesNode.Attributes.Append(Doc.CreateAttribute("id")).InnerText = string.Format("{0}-{1}", geomID, "vertices");
786                     var verticesInput = verticesNode.AppendChild(Doc.CreateElement("input"));
787                     verticesInput.Attributes.Append(Doc.CreateAttribute("semantic")).InnerText = "POSITION";
788                     verticesInput.Attributes.Append(Doc.CreateAttribute("source")).InnerText = string.Format("#{0}-{1}", geomID, "positions");
789                 }
790
791                 var objMaterials = GetMaterials(obj);
792
793                 // Add triangles
794                 foreach (var objMaterial in objMaterials)
795                 {
796                     AddPolygons(mesh, geomID, objMaterial.Name + "-material", obj, GetFacesWithMaterial(obj, objMaterial));
797                 }
798
799                 var node = scene.AppendChild(Doc.CreateElement("node"));
800                 node.Attributes.Append(Doc.CreateAttribute("type")).InnerText = "NODE";
801                 node.Attributes.Append(Doc.CreateAttribute("id")).InnerText = geomID;
802                 node.Attributes.Append(Doc.CreateAttribute("name")).InnerText = geomID;
803
804                 // Set tranform matrix (node position, rotation and scale)
805                 var matrix = node.AppendChild(Doc.CreateElement("matrix"));
806
807                 var srt = Radegast.Rendering.Math3D.CreateSRTMatrix(obj.Prim.Scale, obj.Prim.Rotation, obj.Prim.Position);
808                 string matrixVal = "";
809                 for (int i = 0; i < 4; i++)
810                 {
811                     for (int j = 0; j < 4; j++)
812                     {
813                         matrixVal += srt[j * 4 + i].ToString(invariant) + " ";
814                     }
815                 }
816                 matrix.InnerText = matrixVal.TrimEnd();
817
818                 // Geometry of the node
819                 var nodeGeometry = node.AppendChild(Doc.CreateElement("instance_geometry"));
820
821                 // Bind materials
822                 var tq = nodeGeometry.AppendChild(Doc.CreateElement("bind_material"))
823                     .AppendChild(Doc.CreateElement("technique_common"));
824                 foreach (var objMaterial in objMaterials)
825                 {
826                     var instanceMaterial = tq.AppendChild(Doc.CreateElement("instance_material"));
827                     instanceMaterial.Attributes.Append(Doc.CreateAttribute("symbol")).InnerText = string.Format("{0}-{1}", objMaterial.Name, "material");
828                     instanceMaterial.Attributes.Append(Doc.CreateAttribute("target")).InnerText = string.Format("#{0}-{1}", objMaterial.Name, "material");
829                 }
830
831                 nodeGeometry.Attributes.Append(Doc.CreateAttribute("url")).InnerText = string.Format("#{0}-{1}", geomID, "mesh");
832             }
833
834             GenerateEffects(effects);
835
836             // Materials
837             foreach (var objMaterial in AllMeterials)
838             {
839                 var mat = materials.AppendChild(Doc.CreateElement("material"));
840                 mat.Attributes.Append(Doc.CreateAttribute("id")).InnerText = objMaterial.Name + "-material";
841                 var matEffect = mat.AppendChild(Doc.CreateElement("instance_effect"));
842                 matEffect.Attributes.Append(Doc.CreateAttribute("url")).InnerText = string.Format("#{0}-{1}", objMaterial.Name, "fx");
843             }
844
845             root.AppendChild(Doc.CreateElement("scene"))
846                 .AppendChild(Doc.CreateElement("instance_visual_scene"))
847                 .Attributes.Append(Doc.CreateAttribute("url")).InnerText = "#Scene";
848
849         }
850
851         string DocToString(XmlDocument doc)
852         {
853             string ret = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + Environment.NewLine;
854             try
855             {
856                 using (MemoryStream outs = new MemoryStream())
857                 {
858                     using (XmlTextWriter writter = new XmlTextWriter(outs, Encoding.UTF8))
859                     {
860                         writter.Formatting = Formatting.Indented;
861                         doc.WriteContentTo(writter);
862                         writter.Flush();
863                         outs.Flush();
864                         outs.Position = 0;
865                         using (StreamReader sr = new StreamReader(outs))
866                         {
867                             ret += sr.ReadToEnd();
868                         }
869                     }
870                 }
871             }
872             catch { }
873
874             return ret;
875         }
876     }
877
878     public class DAEStatutsEventArgs : EventArgs
879     {
880         public string Message;
881
882         public DAEStatutsEventArgs(string message)
883         {
884             Message = message;
885         }
886     }
887 }