corrade-vassal – Blame information for rev 1
?pathlinks?
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 | } |