corrade-vassal – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 1 /*
2 * Copyright (c) 2006-2014, openmetaverse.org
3 * All rights reserved.
4 *
5 * - Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * - Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 * - Neither the name of the openmetaverse.org nor the names
11 * of its contributors may be used to endorse or promote products derived from
12 * this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26  
27  
28 using System;
29 using System.Collections.Generic;
30 using System.Text.RegularExpressions;
31 using System.IO;
32 using System.Xml;
33 using System.Drawing;
34 using System.Xml.Serialization;
35 using OpenMetaverse.ImportExport.Collada14;
36 using OpenMetaverse.Rendering;
37 using OpenMetaverse.Imaging;
38  
39 namespace OpenMetaverse.ImportExport
40 {
41 /// <summary>
42 /// Parsing Collada model files into data structures
43 /// </summary>
44 public class ColladaLoader
45 {
46 COLLADA Model;
47 static XmlSerializer Serializer = null;
48 List<Node> Nodes;
49 List<ModelMaterial> Materials;
50 Dictionary<string, string> MatSymTarget;
51 string FileName;
52  
53 class Node
54 {
55 public Matrix4 Transform = Matrix4.Identity;
56 public string Name;
57 public string ID;
58 public string MeshID;
59 }
60  
61 /// <summary>
62 /// Parses Collada document
63 /// </summary>
64 /// <param name="filename">Load .dae model from this file</param>
65 /// <param name="loadImages">Load and decode images for uploading with model</param>
66 /// <returns>A list of mesh prims that were parsed from the collada file</returns>
67 public List<ModelPrim> Load(string filename, bool loadImages)
68 {
69 try
70 {
71 // Create an instance of the XmlSerializer specifying type and namespace.
72 if (Serializer == null)
73 {
74 Serializer = new XmlSerializer(typeof(COLLADA));
75 }
76  
77 this.FileName = filename;
78  
79 // A FileStream is needed to read the XML document.
80 FileStream fs = new FileStream(filename, FileMode.Open);
81 XmlReader reader = XmlReader.Create(fs);
82 Model = (COLLADA)Serializer.Deserialize(reader);
83 fs.Close();
84 var prims = Parse();
85 if (loadImages)
86 {
87 LoadImages(prims);
88 }
89 return prims;
90 }
91 catch (Exception ex)
92 {
93 Logger.Log("Failed parsing collada file: " + ex.Message, Helpers.LogLevel.Error, ex);
94 return new List<ModelPrim>();
95 }
96 }
97  
98 void LoadImages(List<ModelPrim> prims)
99 {
100 foreach (var prim in prims)
101 {
102 foreach (var face in prim.Faces)
103 {
104 if (!string.IsNullOrEmpty(face.Material.Texture))
105 {
106 LoadImage(face.Material);
107 }
108 }
109 }
110 }
111  
112 void LoadImage(ModelMaterial material)
113 {
114 var fname = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(FileName), material.Texture);
115  
116 try
117 {
118 string ext = System.IO.Path.GetExtension(material.Texture).ToLower();
119  
120 Bitmap bitmap = null;
121  
122 if (ext == ".jp2" || ext == ".j2c")
123 {
124 material.TextureData = File.ReadAllBytes(fname);
125 return;
126 }
127  
128 if (ext == ".tga")
129 {
130 bitmap = LoadTGAClass.LoadTGA(fname);
131 }
132 else
133 {
134 bitmap = (Bitmap)Image.FromFile(fname);
135 }
136  
137 int width = bitmap.Width;
138 int height = bitmap.Height;
139  
140 // Handle resizing to prevent excessively large images and irregular dimensions
141 if (!IsPowerOfTwo((uint)width) || !IsPowerOfTwo((uint)height) || width > 1024 || height > 1024)
142 {
143 var origWidth = width;
144 var origHieght = height;
145  
146 width = ClosestPowerOwTwo(width);
147 height = ClosestPowerOwTwo(height);
148  
149 width = width > 1024 ? 1024 : width;
150 height = height > 1024 ? 1024 : height;
151  
152 Logger.Log("Image has irregular dimensions " + origWidth + "x" + origHieght + ". Resizing to " + width + "x" + height, Helpers.LogLevel.Info);
153  
154 Bitmap resized = new Bitmap(width, height, bitmap.PixelFormat);
155 Graphics graphics = Graphics.FromImage(resized);
156  
157 graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
158 graphics.InterpolationMode =
159 System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
160 graphics.DrawImage(bitmap, 0, 0, width, height);
161  
162 bitmap.Dispose();
163 bitmap = resized;
164 }
165  
166 material.TextureData = OpenJPEG.EncodeFromImage(bitmap, false);
167  
168 Logger.Log("Successfully encoded " + fname, Helpers.LogLevel.Info);
169 }
170 catch (Exception ex)
171 {
172 Logger.Log("Failed loading " + fname + ": " + ex.Message, Helpers.LogLevel.Warning);
173 }
174  
175 }
176  
177 bool IsPowerOfTwo(uint n)
178 {
179 return (n & (n - 1)) == 0 && n != 0;
180 }
181  
182 int ClosestPowerOwTwo(int n)
183 {
184 int res = 1;
185  
186 while (res < n)
187 {
188 res <<= 1;
189 }
190  
191 return res > 1 ? res / 2 : 1;
192 }
193  
194 ModelMaterial ExtractMaterial(object diffuse)
195 {
196 ModelMaterial ret = new ModelMaterial();
197 if (diffuse is common_color_or_texture_typeColor)
198 {
199 var col = (common_color_or_texture_typeColor)diffuse;
200 ret.DiffuseColor = new Color4((float)col.Values[0], (float)col.Values[1], (float)col.Values[2], (float)col.Values[3]);
201 }
202 else if (diffuse is common_color_or_texture_typeTexture)
203 {
204 var tex = (common_color_or_texture_typeTexture)diffuse;
205 ret.Texture = tex.texcoord;
206 }
207 return ret;
208  
209 }
210  
211 void ParseMaterials()
212 {
213  
214 if (Model == null) return;
215  
216 Materials = new List<ModelMaterial>();
217  
218 // Material -> effect mapping
219 Dictionary<string, string> matEffect = new Dictionary<string, string>();
220 List<ModelMaterial> tmpEffects = new List<ModelMaterial>();
221  
222 // Image ID -> filename mapping
223 Dictionary<string, string> imgMap = new Dictionary<string, string>();
224  
225 foreach (var item in Model.Items)
226 {
227 if (item is library_images)
228 {
229 var images = (library_images)item;
230 if (images.image != null)
231 {
232 foreach (var image in images.image)
233 {
234 var img = (image)image;
235 string ID = img.id;
236 if (img.Item is string)
237 {
238 imgMap[ID] = (string)img.Item;
239 }
240 }
241 }
242 }
243 }
244  
245 foreach (var item in Model.Items)
246 {
247 if (item is library_materials)
248 {
249 var materials = (library_materials)item;
250 if (materials.material != null)
251 {
252 foreach (var material in materials.material)
253 {
254 var ID = material.id;
255 if (material.instance_effect != null)
256 {
257 if (!string.IsNullOrEmpty(material.instance_effect.url))
258 {
259 matEffect[material.instance_effect.url.Substring(1)] = ID;
260 }
261 }
262 }
263 }
264 }
265 }
266  
267 foreach (var item in Model.Items)
268 {
269 if (item is library_effects)
270 {
271 var effects = (library_effects)item;
272 if (effects.effect != null)
273 {
274 foreach (var effect in effects.effect)
275 {
276 string ID = effect.id;
277 foreach (var effItem in effect.Items)
278 {
279 if (effItem is effectFx_profile_abstractProfile_COMMON)
280 {
281 var teq = ((effectFx_profile_abstractProfile_COMMON)effItem).technique;
282 if (teq != null)
283 {
284 if (teq.Item is effectFx_profile_abstractProfile_COMMONTechniquePhong)
285 {
286 var shader = (effectFx_profile_abstractProfile_COMMONTechniquePhong)teq.Item;
287 if (shader.diffuse != null)
288 {
289 var material = ExtractMaterial(shader.diffuse.Item);
290 material.ID = ID;
291 tmpEffects.Add(material);
292 }
293 }
294 else if (teq.Item is effectFx_profile_abstractProfile_COMMONTechniqueLambert)
295 {
296 var shader = (effectFx_profile_abstractProfile_COMMONTechniqueLambert)teq.Item;
297 if (shader.diffuse != null)
298 {
299 var material = ExtractMaterial(shader.diffuse.Item);
300 material.ID = ID;
301 tmpEffects.Add(material);
302 }
303 }
304 }
305 }
306 }
307 }
308 }
309 }
310 }
311  
312 foreach (var effect in tmpEffects)
313 {
314 if (matEffect.ContainsKey(effect.ID))
315 {
316 effect.ID = matEffect[effect.ID];
317 if (!string.IsNullOrEmpty(effect.Texture))
318 {
319 if (imgMap.ContainsKey(effect.Texture))
320 {
321 effect.Texture = imgMap[effect.Texture];
322 }
323 }
324 Materials.Add(effect);
325 }
326 }
327 }
328  
329 void ProcessNode(node node)
330 {
331 Node n = new Node();
332 n.ID = node.id;
333  
334 if (node.Items != null)
335 // Try finding matrix
336 foreach (var i in node.Items)
337 {
338 if (i is matrix)
339 {
340 var m = (matrix)i;
341 for (int a = 0; a < 4; a++)
342 for (int b = 0; b < 4; b++)
343 {
344 n.Transform[b, a] = (float)m.Values[a * 4 + b];
345 }
346 }
347 }
348  
349 // Find geometry and material
350 if (node.instance_geometry != null && node.instance_geometry.Length > 0)
351 {
352 var instGeom = node.instance_geometry[0];
353 if (!string.IsNullOrEmpty(instGeom.url))
354 {
355 n.MeshID = instGeom.url.Substring(1);
356 }
357 if (instGeom.bind_material != null && instGeom.bind_material.technique_common != null)
358 {
359 foreach (var teq in instGeom.bind_material.technique_common)
360 {
361 var target = teq.target;
362 if (!string.IsNullOrEmpty(target))
363 {
364 target = target.Substring(1);
365 MatSymTarget[teq.symbol] = target;
366 }
367 }
368 }
369 }
370  
371 if (node.Items != null && node.instance_geometry != null && node.instance_geometry.Length > 0)
372 Nodes.Add(n);
373  
374 // Recurse if the scene is hierarchical
375 if (node.node1 != null)
376 foreach (node nd in node.node1)
377 ProcessNode(nd);
378 }
379  
380 void ParseVisualScene()
381 {
382 Nodes = new List<Node>();
383 if (Model == null) return;
384  
385 MatSymTarget = new Dictionary<string, string>();
386  
387 foreach (var item in Model.Items)
388 {
389 if (item is library_visual_scenes)
390 {
391 var scene = ((library_visual_scenes)item).visual_scene[0];
392 foreach (var node in scene.node)
393 {
394 ProcessNode(node);
395 }
396 }
397 }
398 }
399  
400 List<ModelPrim> Parse()
401 {
402 var Prims = new List<ModelPrim>();
403  
404 float DEG_TO_RAD = 0.017453292519943295769236907684886f;
405  
406 if (Model == null) return Prims;
407  
408 Matrix4 transform = Matrix4.Identity;
409  
410 UpAxisType upAxis = UpAxisType.Y_UP;
411  
412 var asset = Model.asset;
413 if (asset != null)
414 {
415 upAxis = asset.up_axis;
416 if (asset.unit != null)
417 {
418 float meter = (float)asset.unit.meter;
419 transform[0, 0] = meter;
420 transform[1, 1] = meter;
421 transform[2, 2] = meter;
422 }
423 }
424  
425 Matrix4 rotation = Matrix4.Identity;
426  
427 if (upAxis == UpAxisType.X_UP)
428 {
429 rotation = Matrix4.CreateFromEulers(0.0f, 90.0f * DEG_TO_RAD, 0.0f);
430 }
431 else if (upAxis == UpAxisType.Y_UP)
432 {
433 rotation = Matrix4.CreateFromEulers(90.0f * DEG_TO_RAD, 0.0f, 0.0f);
434 }
435  
436 rotation = rotation * transform;
437 transform = rotation;
438  
439 ParseVisualScene();
440 ParseMaterials();
441  
442 foreach (var item in Model.Items)
443 {
444 if (item is library_geometries)
445 {
446 var geometries = (library_geometries)item;
447 foreach (var geo in geometries.geometry)
448 {
449 var mesh = geo.Item as mesh;
450 if (mesh == null) continue;
451  
452 var nodes = Nodes.FindAll(n => n.MeshID == geo.id);
453 if (nodes != null)
454 {
455 byte[] mesh_asset = null;
456 foreach (var node in nodes)
457 {
458 var prim = new ModelPrim();
459 prim.ID = node.ID;
460 Prims.Add(prim);
461 Matrix4 primTransform = transform;
462 primTransform = primTransform * node.Transform;
463  
464 AddPositions(mesh, prim, primTransform);
465  
466 foreach (var mitem in mesh.Items)
467 {
468 if (mitem is triangles)
469 {
470 AddFacesFromPolyList(Triangles2Polylist((triangles)mitem), mesh, prim, primTransform);
471 }
472 if (mitem is polylist)
473 {
474 AddFacesFromPolyList((polylist)mitem, mesh, prim, primTransform);
475 }
476 }
477  
478 if (mesh_asset == null)
479 {
480 prim.CreateAsset(UUID.Zero);
481 mesh_asset = prim.Asset;
482 }
483 else
484 prim.Asset = mesh_asset;
485 }
486 }
487 }
488 }
489 }
490  
491 return Prims;
492 }
493  
494 source FindSource(source[] sources, string id)
495 {
496 id = id.Substring(1);
497  
498 foreach (var src in sources)
499 {
500 if (src.id == id)
501 return src;
502 }
503 return null;
504 }
505  
506 void AddPositions(mesh mesh, ModelPrim prim, Matrix4 transform)
507 {
508 prim.Positions = new List<Vector3>();
509 source posSrc = FindSource(mesh.source, mesh.vertices.input[0].source);
510 double[] posVals = ((float_array)posSrc.Item).Values;
511  
512 for (int i = 0; i < posVals.Length / 3; i++)
513 {
514 Vector3 pos = new Vector3((float)posVals[i * 3], (float)posVals[i * 3 + 1], (float)posVals[i * 3 + 2]);
515 pos = Vector3.Transform(pos, transform);
516 prim.Positions.Add(pos);
517 }
518  
519 prim.BoundMin = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue);
520 prim.BoundMax = new Vector3(float.MinValue, float.MinValue, float.MinValue);
521  
522 foreach (var pos in prim.Positions)
523 {
524 if (pos.X > prim.BoundMax.X) prim.BoundMax.X = pos.X;
525 if (pos.Y > prim.BoundMax.Y) prim.BoundMax.Y = pos.Y;
526 if (pos.Z > prim.BoundMax.Z) prim.BoundMax.Z = pos.Z;
527  
528 if (pos.X < prim.BoundMin.X) prim.BoundMin.X = pos.X;
529 if (pos.Y < prim.BoundMin.Y) prim.BoundMin.Y = pos.Y;
530 if (pos.Z < prim.BoundMin.Z) prim.BoundMin.Z = pos.Z;
531 }
532  
533 prim.Scale = prim.BoundMax - prim.BoundMin;
534 prim.Position = prim.BoundMin + (prim.Scale / 2);
535  
536 // Fit vertex positions into identity cube -0.5 .. 0.5
537 for (int i = 0; i < prim.Positions.Count; i++)
538 {
539 Vector3 pos = prim.Positions[i];
540 pos = new Vector3(
541 prim.Scale.X == 0 ? 0 : ((pos.X - prim.BoundMin.X) / prim.Scale.X) - 0.5f,
542 prim.Scale.Y == 0 ? 0 : ((pos.Y - prim.BoundMin.Y) / prim.Scale.Y) - 0.5f,
543 prim.Scale.Z == 0 ? 0 : ((pos.Z - prim.BoundMin.Z) / prim.Scale.Z) - 0.5f
544 );
545 prim.Positions[i] = pos;
546 }
547  
548 }
549  
550 int[] StrToArray(string s)
551 {
552 string[] vals = Regex.Split(s.Trim(), @"\s+");
553 int[] ret = new int[vals.Length];
554  
555 for (int i = 0; i < ret.Length; i++)
556 {
557 int.TryParse(vals[i], out ret[i]);
558 }
559  
560 return ret;
561 }
562  
563 void AddFacesFromPolyList(polylist list, mesh mesh, ModelPrim prim, Matrix4 transform)
564 {
565 string material = list.material;
566 source posSrc = null;
567 source normalSrc = null;
568 source uvSrc = null;
569  
570 ulong stride = 0;
571 int posOffset = -1;
572 int norOffset = -1;
573 int uvOffset = -1;
574  
575 foreach (var inp in list.input)
576 {
577 stride = Math.Max(stride, inp.offset);
578  
579 if (inp.semantic == "VERTEX")
580 {
581 posSrc = FindSource(mesh.source, mesh.vertices.input[0].source);
582 posOffset = (int)inp.offset;
583 }
584 else if (inp.semantic == "NORMAL")
585 {
586 normalSrc = FindSource(mesh.source, inp.source);
587 norOffset = (int)inp.offset;
588 }
589 else if (inp.semantic == "TEXCOORD")
590 {
591 uvSrc = FindSource(mesh.source, inp.source);
592 uvOffset = (int)inp.offset;
593 }
594 }
595  
596 stride += 1;
597  
598 if (posSrc == null) return;
599  
600 var vcount = StrToArray(list.vcount);
601 var idx = StrToArray(list.p);
602  
603 Vector3[] normals = null;
604 if (normalSrc != null)
605 {
606 var norVal = ((float_array)normalSrc.Item).Values;
607 normals = new Vector3[norVal.Length / 3];
608  
609 for (int i = 0; i < normals.Length; i++)
610 {
611 normals[i] = new Vector3((float)norVal[i * 3 + 0], (float)norVal[i * 3 + 1], (float)norVal[i * 3 + 2]);
612 normals[i] = Vector3.TransformNormal(normals[i], transform);
613 normals[i].Normalize();
614 }
615 }
616  
617 Vector2[] uvs = null;
618 if (uvSrc != null)
619 {
620 var uvVal = ((float_array)uvSrc.Item).Values;
621 uvs = new Vector2[uvVal.Length / 2];
622  
623 for (int i = 0; i < uvs.Length; i++)
624 {
625 uvs[i] = new Vector2((float)uvVal[i * 2 + 0], (float)uvVal[i * 2 + 1]);
626 }
627  
628 }
629  
630 ModelFace face = new ModelFace();
631 face.MaterialID = list.material;
632 if (face.MaterialID != null)
633 {
634 if (MatSymTarget.ContainsKey(list.material))
635 {
636 ModelMaterial mat = Materials.Find(m => m.ID == MatSymTarget[list.material]);
637 if (mat != null)
638 {
639 face.Material = mat;
640 }
641 }
642 }
643  
644 int curIdx = 0;
645  
646 for (int i = 0; i < vcount.Length; i++)
647 {
648 var nvert = vcount[i];
649 if (nvert < 3 || nvert > 4)
650 {
651 throw new InvalidDataException("Only triangles and quads supported");
652 }
653  
654 Vertex[] verts = new Vertex[nvert];
655 for (int j = 0; j < nvert; j++)
656 {
657 verts[j].Position = prim.Positions[idx[curIdx + posOffset + (int)stride * j]];
658  
659 if (normals != null)
660 {
661 verts[j].Normal = normals[idx[curIdx + norOffset + (int)stride * j]];
662 }
663  
664 if (uvs != null)
665 {
666 verts[j].TexCoord = uvs[idx[curIdx + uvOffset + (int)stride * j]];
667 }
668 }
669  
670  
671 if (nvert == 3) // add the triangle
672 {
673 face.AddVertex(verts[0]);
674 face.AddVertex(verts[1]);
675 face.AddVertex(verts[2]);
676 }
677 else if (nvert == 4) // quad, add two triangles
678 {
679 face.AddVertex(verts[0]);
680 face.AddVertex(verts[1]);
681 face.AddVertex(verts[2]);
682  
683 face.AddVertex(verts[0]);
684 face.AddVertex(verts[2]);
685 face.AddVertex(verts[3]);
686 }
687  
688 curIdx += (int)stride * nvert;
689 }
690  
691 prim.Faces.Add(face);
692  
693  
694 }
695  
696 polylist Triangles2Polylist(triangles triangles)
697 {
698 polylist poly = new polylist();
699 poly.count = triangles.count;
700 poly.input = triangles.input;
701 poly.material = triangles.material;
702 poly.name = triangles.name;
703 poly.p = triangles.p;
704  
705 string str = "3 ";
706 System.Text.StringBuilder builder = new System.Text.StringBuilder(str.Length * (int)poly.count);
707 for (int i = 0; i < (int)poly.count; i++) builder.Append(str);
708 poly.vcount = builder.ToString();
709  
710 return poly;
711 }
712  
713 }
714 }