diff --git a/OpenMetaverse/ImportExport/ColladalLoader.cs b/OpenMetaverse/ImportExport/ColladalLoader.cs new file mode 100644 index 00000000..74ef129a --- /dev/null +++ b/OpenMetaverse/ImportExport/ColladalLoader.cs @@ -0,0 +1,533 @@ +/* + * Copyright (c) 2006-2014, openmetaverse.org + * All rights reserved. + * + * - Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Neither the name of the openmetaverse.org nor the names + * of its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + + +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using System.IO; +using System.Xml; +using System.Xml.Serialization; +using OpenMetaverse.Rendering; +using OpenMetaverse.StructuredData; +using OpenMetaverse.Http; + +namespace OpenMetaverse.ImportExport +{ + public class ColladaLoader + { + COLLADA Model; + static XmlSerializer Serializer = null; + List Nodes; + List Materials; + + class Node + { + public Matrix4 Transform = Matrix4.Identity; + public string Name; + public string ID; + public string MeshID; + } + + public List Load(string filename) + { + try + { + // Create an instance of the XmlSerializer specifying type and namespace. + if (Serializer == null) + { + Serializer = new XmlSerializer(typeof(COLLADA)); + } + + // A FileStream is needed to read the XML document. + FileStream fs = new FileStream(filename, FileMode.Open); + XmlReader reader = XmlReader.Create(fs); + Model = (COLLADA)Serializer.Deserialize(reader); + fs.Close(); + return Parse(); + } + catch (Exception ex) + { + Logger.Log("Failed parsing collada file: " + ex.Message, Helpers.LogLevel.Error, ex); + return new List(); + } + } + + + ModelMaterial ExtractMaterial(object diffuse) + { + ModelMaterial ret = new ModelMaterial(); + if (diffuse is common_color_or_texture_typeColor) + { + var col = (common_color_or_texture_typeColor)diffuse; + ret.DiffuseColor = new Color4((float)col.Values[0], (float)col.Values[1], (float)col.Values[2], (float)col.Values[3]); + } + else if (diffuse is common_color_or_texture_typeTexture) + { + var tex = (common_color_or_texture_typeTexture)diffuse; + ret.Texture = tex.texcoord; + } + return ret; + + } + + void ParseMaterials() + { + + if (Model == null) return; + + Materials = new List(); + + // Material -> effect mapping + Dictionary matEffect = new Dictionary(); + List tmpEffects = new List(); + + // Image ID -> filename mapping + Dictionary imgMap = new Dictionary(); + + foreach (var item in Model.Items) + { + if (item is library_images) + { + var images = (library_images)item; + if (images.image != null) + { + foreach (var image in images.image) + { + var img = (image)image; + string ID = img.id; + if (img.Item is string) + { + imgMap[ID] = (string)img.Item; + } + } + } + } + } + + foreach (var item in Model.Items) + { + if (item is library_materials) + { + var materials = (library_materials)item; + if (materials.material != null) + { + foreach (var material in materials.material) + { + var ID = material.id; + if (material.instance_effect != null) + { + if (!string.IsNullOrEmpty(material.instance_effect.url)) + { + matEffect[material.instance_effect.url.Substring(1)] = ID; + } + } + } + } + } + } + + foreach (var item in Model.Items) + { + if (item is library_effects) + { + var effects = (library_effects)item; + if (effects.effect != null) + { + foreach (var effect in effects.effect) + { + string ID = effect.id; + foreach (var effItem in effect.Items) + { + if (effItem is effectFx_profile_abstractProfile_COMMON) + { + var teq = ((effectFx_profile_abstractProfile_COMMON)effItem).technique; + if (teq != null) + { + if (teq.Item is effectFx_profile_abstractProfile_COMMONTechniquePhong) + { + var shader = (effectFx_profile_abstractProfile_COMMONTechniquePhong)teq.Item; + if (shader.diffuse != null) + { + var material = ExtractMaterial(shader.diffuse.Item); + material.ID = ID; + tmpEffects.Add(material); + } + } + else if (teq.Item is effectFx_profile_abstractProfile_COMMONTechniqueLambert) + { + var shader = (effectFx_profile_abstractProfile_COMMONTechniqueLambert)teq.Item; + if (shader.diffuse != null) + { + var material = ExtractMaterial(shader.diffuse.Item); + material.ID = ID; + tmpEffects.Add(material); + } + } + } + } + } + } + } + } + } + + foreach (var effect in tmpEffects) + { + if (matEffect.ContainsKey(effect.ID)) + { + effect.ID = matEffect[effect.ID]; + if (!string.IsNullOrEmpty(effect.Texture)) + { + if (imgMap.ContainsKey(effect.Texture)) + { + effect.Texture = imgMap[effect.Texture]; + } + } + Materials.Add(effect); + } + } + } + + void ParseVisualScene() + { + Nodes = new List(); + if (Model == null) return; + + foreach (var item in Model.Items) + { + if (item is library_visual_scenes) + { + var scene = ((library_visual_scenes)item).visual_scene[0]; + foreach (var node in scene.node) + { + Node n = new Node(); + n.ID = node.id; + + // Try finding matrix + foreach (var i in node.Items) + { + if (i is matrix) + { + var m = (matrix)i; + for (int a = 0; a < 4; a++) + for (int b = 0; b < 4; b++) + { + n.Transform[b, a] = (float)m.Values[a * 4 + b]; + } + } + } + + // Find geopmetry and material + if (node.instance_geometry != null && node.instance_geometry.Length > 0) + { + var instGeom = node.instance_geometry[0]; + if (!string.IsNullOrEmpty(instGeom.url)) + { + n.MeshID = instGeom.url.Substring(1); + } + + } + + Nodes.Add(n); + } + } + } + } + + List Parse() + { + var Prims = new List(); + + float DEG_TO_RAD = 0.017453292519943295769236907684886f; + + if (Model == null) return Prims; + + Matrix4 tranform = Matrix4.Identity; + + UpAxisType upAxis = UpAxisType.Y_UP; + + var asset = Model.asset; + if (asset != null) + { + upAxis = asset.up_axis; + if (asset.unit != null) + { + float meter = (float)asset.unit.meter; + tranform[0, 0] = meter; + tranform[1, 1] = meter; + tranform[2, 2] = meter; + } + } + + Matrix4 rotation = Matrix4.Identity; + + if (upAxis == UpAxisType.X_UP) + { + rotation = Matrix4.CreateFromEulers(0.0f, 90.0f * DEG_TO_RAD, 0.0f); + } + else if (upAxis == UpAxisType.Y_UP) + { + rotation = Matrix4.CreateFromEulers(90.0f * DEG_TO_RAD, 0.0f, 0.0f); + } + + rotation = rotation * tranform; + tranform = rotation; + + ParseVisualScene(); + ParseMaterials(); + + foreach (var item in Model.Items) + { + if (item is library_geometries) + { + var geometries = (library_geometries)item; + foreach (var geo in geometries.geometry) + { + var mesh = geo.Item as mesh; + if (mesh == null) continue; + var prim = new ModelPrim(); + prim.ID = geo.id; + Prims.Add(prim); + Matrix4 primTranform = tranform; + + var node = Nodes.Find(n => n.MeshID == prim.ID); + if (node != null) + { + primTranform = primTranform * node.Transform; + } + + AddPositions(mesh, prim, primTranform); + + foreach (var mitem in mesh.Items) + { + if (mitem is polylist) + { + AddFacesFromPolyList((polylist)mitem, mesh, prim); + } + } + + prim.CreateAsset(UUID.Zero); + + } + } + } + + return Prims; + } + + source FindSource(source[] sources, string id) + { + id = id.Substring(1); + + foreach (var src in sources) + { + if (src.id == id) + return src; + } + return null; + } + + void AddPositions(mesh mesh, ModelPrim prim, Matrix4 transform) + { + prim.Positions = new List(); + source posSrc = FindSource(mesh.source, mesh.vertices.input[0].source); + double[] posVals = ((float_array)posSrc.Item).Values; + + for (int i = 0; i < posVals.Length / 3; i++) + { + Vector3 pos = new Vector3((float)posVals[i * 3], (float)posVals[i * 3 + 1], (float)posVals[i * 3 + 2]); + pos = Vector3.Transform(pos, transform); + prim.Positions.Add(pos); + } + + prim.BoundMin = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); + prim.BoundMax = new Vector3(float.MinValue, float.MinValue, float.MinValue); + + foreach (var pos in prim.Positions) + { + if (pos.X > prim.BoundMax.X) prim.BoundMax.X = pos.X; + if (pos.Y > prim.BoundMax.Y) prim.BoundMax.Y = pos.Y; + if (pos.Z > prim.BoundMax.Z) prim.BoundMax.Z = pos.Z; + + if (pos.X < prim.BoundMin.X) prim.BoundMin.X = pos.X; + if (pos.Y < prim.BoundMin.Y) prim.BoundMin.Y = pos.Y; + if (pos.Z < prim.BoundMin.Z) prim.BoundMin.Z = pos.Z; + } + + prim.Scale = prim.BoundMax - prim.BoundMin; + prim.Position = prim.BoundMin + (prim.Scale / 2); + + // Fit vertex positions into identity cube -0.5 .. 0.5 + for (int i = 0; i < prim.Positions.Count; i++) + { + Vector3 pos = prim.Positions[i]; + pos = new Vector3( + prim.Scale.X == 0 ? 0 : ((pos.X - prim.BoundMin.X) / prim.Scale.X) - 0.5f, + prim.Scale.Y == 0 ? 0 : ((pos.Y - prim.BoundMin.Y) / prim.Scale.Y) - 0.5f, + prim.Scale.Z == 0 ? 0 : ((pos.Z - prim.BoundMin.Z) / prim.Scale.Z) - 0.5f + ); + prim.Positions[i] = pos; + } + + } + + int[] StrToArray(string s) + { + string[] vals = Regex.Split(s.Trim(), @"\s+"); + int[] ret = new int[vals.Length]; + + for (int i = 0; i < ret.Length; i++) + { + int.TryParse(vals[i], out ret[i]); + } + + return ret; + } + + void AddFacesFromPolyList(polylist list, mesh mesh, ModelPrim prim) + { + string material = list.material; + source posSrc = null; + source normalSrc = null; + source uvSrc = null; + + ulong stride = 0; + int posOffset = -1; + int norOffset = -1; + int uvOffset = -1; + + foreach (var inp in list.input) + { + stride = Math.Max(stride, inp.offset); + + if (inp.semantic == "VERTEX") + { + posSrc = FindSource(mesh.source, mesh.vertices.input[0].source); + posOffset = (int)inp.offset; + } + else if (inp.semantic == "NORMAL") + { + normalSrc = FindSource(mesh.source, inp.source); + norOffset = (int)inp.offset; + } + else if (inp.semantic == "TEXCOORD") + { + uvSrc = FindSource(mesh.source, inp.source); + uvOffset = (int)inp.offset; + } + } + + stride += 1; + + if (posSrc == null) return; + + var vcount = StrToArray(list.vcount); + var idx = StrToArray(list.p); + + Vector3[] normals = null; + if (normalSrc != null) + { + var norVal = ((float_array)normalSrc.Item).Values; + normals = new Vector3[norVal.Length / 3]; + + for (int i = 0; i < normals.Length; i++) + { + normals[i] = new Vector3((float)norVal[i * 3 + 0], (float)norVal[i * 3 + 1], (float)norVal[i * 3 + 2]); + } + + } + + Vector2[] uvs = null; + if (uvSrc != null) + { + var uvVal = ((float_array)uvSrc.Item).Values; + uvs = new Vector2[uvVal.Length / 2]; + + for (int i = 0; i < uvs.Length; i++) + { + uvs[i] = new Vector2((float)uvVal[i * 2 + 0], (float)uvVal[i * 2 + 1]); + } + + } + + ModelFace face = new ModelFace(); + face.MaterialID = list.material; + ModelMaterial mat = Materials.Find(m => m.ID == face.MaterialID); + if (mat != null) + { + face.Material = mat; + } + + int curIdx = 0; + + for (int i = 0; i < vcount.Length; i++) + { + var npoly = vcount[i]; + if (npoly != 3) + { + throw new InvalidDataException("Only triangulated meshes supported"); + } + + int v1i = idx[curIdx + posOffset + (int)stride * 0]; + int v2i = idx[curIdx + posOffset + (int)stride * 1]; + int v3i = idx[curIdx + posOffset + (int)stride * 2]; + + Vertex v1 = new Vertex(); + Vertex v2 = new Vertex(); + Vertex v3 = new Vertex(); + + v1.Position = prim.Positions[v1i]; + v2.Position = prim.Positions[v2i]; + v3.Position = prim.Positions[v3i]; + + if (normals != null) + { + v1.Normal = normals[idx[curIdx + norOffset + (int)stride * 0]]; + v2.Normal = normals[idx[curIdx + norOffset + (int)stride * 1]]; + v3.Normal = normals[idx[curIdx + norOffset + (int)stride * 2]]; + } + + if (uvs != null) + { + v1.TexCoord = uvs[idx[curIdx + uvOffset + (int)stride * 0]]; + v2.TexCoord = uvs[idx[curIdx + uvOffset + (int)stride * 1]]; + v3.TexCoord = uvs[idx[curIdx + uvOffset + (int)stride * 2]]; + } + + face.AddVertex(v1); + face.AddVertex(v2); + face.AddVertex(v3); + + curIdx += (int)stride * npoly; + } + + prim.Faces.Add(face); + + + } + } +} diff --git a/OpenMetaverse/ImportExport/Model.cs b/OpenMetaverse/ImportExport/Model.cs new file mode 100644 index 00000000..79cc6359 --- /dev/null +++ b/OpenMetaverse/ImportExport/Model.cs @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2006-2014, openmetaverse.org + * All rights reserved. + * + * - Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Neither the name of the openmetaverse.org nor the names + * of its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using OpenMetaverse.Rendering; +using OpenMetaverse.StructuredData; + +namespace OpenMetaverse.ImportExport +{ + + public class ModelMaterial + { + public string ID; + public Color4 DiffuseColor = Color4.White; + public string Texture; + } + + public class ModelFace + { + public List Vertices = new List(); + public List Indices = new List(); + public string MaterialID = string.Empty; + public ModelMaterial Material = new ModelMaterial(); + + public void AddVertex(Vertex v) + { + int index = -1; + + for (int i = 0; i < Vertices.Count; i++) + { + if ( + v.Position == Vertices[i].Position && + v.Normal == Vertices[i].Normal && + v.TexCoord == Vertices[i].TexCoord + ) + { + index = i; + break; + } + } + + if (index == -1) + { + index = Vertices.Count; + Vertices.Add(v); + } + + Indices.Add((uint)index); + } + } + + public class ModelPrim + { + public List Positions; + public Vector3 BoundMin = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); + public Vector3 BoundMax = new Vector3(float.MinValue, float.MinValue, float.MinValue); + public Vector3 Position; + public Vector3 Scale; + public Quaternion Rotation = Quaternion.Identity; + public List Faces = new List(); + public string ID; + public byte[] Asset; + + public void CreateAsset(UUID creator) + { + OSDMap header = new OSDMap(); + header["version"] = 1; + header["creator"] = creator; + header["date"] = DateTime.Now; + + header["rez_position"] = Position; + header["rez_scale"] = Scale; + + OSDArray faces = new OSDArray(); + foreach (var face in Faces) + { + OSDMap faceMap = new OSDMap(); + + // Find UV min/max + Vector2 uvMin = new Vector2(float.MaxValue, float.MaxValue); + Vector2 uvMax = new Vector2(float.MinValue, float.MinValue); + foreach (var v in face.Vertices) + { + if (v.TexCoord.X < uvMin.X) uvMin.X = v.TexCoord.X; + if (v.TexCoord.Y < uvMin.Y) uvMin.Y = v.TexCoord.Y; + + if (v.TexCoord.X > uvMax.X) uvMax.X = v.TexCoord.X; + if (v.TexCoord.Y > uvMax.Y) uvMax.Y = v.TexCoord.Y; + } + OSDMap uvDomain = new OSDMap(); + uvDomain["Min"] = uvMin; + uvDomain["Max"] = uvMax; + faceMap["TexCoord0Domain"] = uvDomain; + + + OSDMap positionDomain = new OSDMap(); + positionDomain["Min"] = new Vector3(-0.5f, -0.5f, -0.5f); + positionDomain["Max"] = new Vector3(0.5f, 0.5f, 0.5f); + faceMap["PositionDomain"] = positionDomain; + + List posBytes = new List(face.Vertices.Count * sizeof(UInt16) * 3); + List norBytes = new List(face.Vertices.Count * sizeof(UInt16) * 3); + List uvBytes = new List(face.Vertices.Count * sizeof(UInt16) * 2); + + foreach (var v in face.Vertices) + { + posBytes.AddRange(Utils.UInt16ToBytes(Utils.FloatToUInt16(v.Position.X, -0.5f, 0.5f))); + posBytes.AddRange(Utils.UInt16ToBytes(Utils.FloatToUInt16(v.Position.Y, -0.5f, 0.5f))); + posBytes.AddRange(Utils.UInt16ToBytes(Utils.FloatToUInt16(v.Position.Z, -0.5f, 0.5f))); + + norBytes.AddRange(Utils.UInt16ToBytes(Utils.FloatToUInt16(v.Normal.X, -1f, 1f))); + norBytes.AddRange(Utils.UInt16ToBytes(Utils.FloatToUInt16(v.Normal.Y, -1f, 1f))); + norBytes.AddRange(Utils.UInt16ToBytes(Utils.FloatToUInt16(v.Normal.Z, -1f, 1f))); + + uvBytes.AddRange(Utils.UInt16ToBytes(Utils.FloatToUInt16(v.TexCoord.X, uvMin.X, uvMax.X))); + uvBytes.AddRange(Utils.UInt16ToBytes(Utils.FloatToUInt16(v.TexCoord.Y, uvMin.Y, uvMax.Y))); + } + + faceMap["Position"] = posBytes.ToArray(); + faceMap["Normal"] = norBytes.ToArray(); + faceMap["TexCoord0"] = uvBytes.ToArray(); + + List indexBytes = new List(face.Indices.Count * sizeof(UInt16)); + foreach (var t in face.Indices) + { + indexBytes.AddRange(Utils.UInt16ToBytes((ushort)t)); + } + faceMap["TriangleList"] = indexBytes.ToArray(); + + faces.Add(faceMap); + } + + byte[] physicStubBytes = Helpers.ZCompressOSD(PhysicsStub()); + + byte[] meshBytes = Helpers.ZCompressOSD(faces); + int n = 0; + + OSDMap lodParms = new OSDMap(); + lodParms["offset"] = n; + lodParms["size"] = meshBytes.Length; + header["high_lod"] = lodParms; + n += meshBytes.Length; + + lodParms = new OSDMap(); + lodParms["offset"] = n; + lodParms["size"] = physicStubBytes.Length; + header["physics_convex"] = lodParms; + n += physicStubBytes.Length; + + byte[] headerBytes = OSDParser.SerializeLLSDBinary(header, false); + n += headerBytes.Length; + + Asset = new byte[n]; + + int offset = 0; + Buffer.BlockCopy(headerBytes, 0, Asset, offset, headerBytes.Length); + offset += headerBytes.Length; + + Buffer.BlockCopy(meshBytes, 0, Asset, offset, meshBytes.Length); + offset += meshBytes.Length; + + Buffer.BlockCopy(physicStubBytes, 0, Asset, offset, physicStubBytes.Length); + offset += physicStubBytes.Length; + + } + + public static OSD PhysicsStub() + { + OSDMap ret = new OSDMap(); + ret["Max"] = new Vector3(0.5f, 0.5f, 0.5f); + ret["Min"] = new Vector3(-0.5f, -0.5f, -0.5f); + ret["BoundingVerts"] = new byte[] { 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 255, 255, 255, 127, 0, 0, 255, 255, 255, 127, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 0, 0, 255, 255, 0, 0, 255, 127, 255, 255, 255, 255, 255, 255, 0, 0, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, 255, 255, 0, 0, 255, 255 }; + return ret; + } + + } +} \ No newline at end of file diff --git a/OpenMetaverse/ImportExport/ModelUploader.cs b/OpenMetaverse/ImportExport/ModelUploader.cs new file mode 100644 index 00000000..e83e989f --- /dev/null +++ b/OpenMetaverse/ImportExport/ModelUploader.cs @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2006-2014, openmetaverse.org + * All rights reserved. + * + * - Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * - Neither the name of the openmetaverse.org nor the names + * of its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using System.IO; +using System.Xml; +using System.Xml.Serialization; +using OpenMetaverse.StructuredData; +using OpenMetaverse.Http; + +namespace OpenMetaverse.ImportExport +{ + public class ModelUploader + { + GridClient Client; + List Prims; + + public ModelUploader(GridClient client, List prims) + { + this.Client = client; + this.Prims = prims; + } + + OSD AssetResources() + { + OSDArray instanceList = new OSDArray(); + List meshes = new List(); + List textures = new List(); + + foreach (var prim in Prims) + { + OSDMap primMap = new OSDMap(); + + OSDArray faceList = new OSDArray(); + + foreach (var face in prim.Faces) + { + OSDMap faceMap = new OSDMap(); + + faceMap["diffuse_color"] = face.Material.DiffuseColor; + faceMap["fullbright"] = false; + + faceList.Add(faceMap); + } + + primMap["face_list"] = faceList; + + primMap["position"] = prim.Position; + primMap["rotation"] = prim.Rotation; + primMap["scale"] = prim.Scale; + + primMap["material"] = 3; // always sent as "wood" material + primMap["physics_shape_type"] = 2; // always sent as "convex hull"; + primMap["mesh"] = meshes.Count; + meshes.Add(prim.Asset); + + + instanceList.Add(primMap); + } + + OSDMap resources = new OSDMap(); + resources["instance_list"] = instanceList; + + OSDArray meshList = new OSDArray(); + foreach (var mesh in meshes) + { + meshList.Add(OSD.FromBinary(mesh)); + } + resources["mesh_list"] = meshList; + + OSDArray textureList = new OSDArray(); + resources["texture_list"] = textureList; + + resources["metric"] = "MUT_Unspecified"; + + + return resources; + } + + public void PrepareUpload() + { + Uri url = null; + if (Client.Network.CurrentSim == null || + Client.Network.CurrentSim.Caps == null || + null == (url = Client.Network.CurrentSim.Caps.CapabilityURI("NewFileAgentInventory"))) + { + Logger.Log("Cannot upload mesh, no connection or NewFileAgentInventory not available", Helpers.LogLevel.Warning); + return; + } + + OSDMap req = new OSDMap(); + req["name"] = "Test upload object"; + req["description"] = "Radegast mesh upload " + DateTime.Now.ToString(); + + req["asset_resources"] = AssetResources(); + req["asset_type"] = "mesh"; + req["inventory_type"] = "object"; + + req["folder_id"] = Client.Inventory.FindFolderForType(AssetType.Object); + req["texture_folder_id"] = Client.Inventory.FindFolderForType(AssetType.Texture); + + req["everyone_mask"] = (int)PermissionMask.All; + req["group_mask"] = (int)PermissionMask.All; ; + req["next_owner_mask"] = (int)PermissionMask.All; + + CapsClient request = new CapsClient(url); + request.OnComplete += (client, result, error) => + { + if (error != null || result == null || result.Type != OSDType.Map) + { + Logger.Log("Mesh upload request failure", Helpers.LogLevel.Error); + return; + } + OSDMap res = (OSDMap)result; + + if (res["state"] != "upload") + { + Logger.Log("Mesh upload failure: " + res["message"], Helpers.LogLevel.Error); + return; + } + + Logger.Log("Response from mesh upload prepare:\n" + OSDParser.SerializeLLSDNotationFormatted(result), Helpers.LogLevel.Debug); + Uri uploader = new Uri(res["uploader"]); + PerformUpload(uploader); + }; + + request.BeginGetResponse(req, OSDFormat.Xml, 60 * 1000); + + } + + public void PerformUpload(Uri uploader) + { + CapsClient request = new CapsClient(uploader); + request.OnComplete += (client, result, error) => + { + if (error != null || result == null || result.Type != OSDType.Map) + { + Logger.Log("Mesh upload request failure", Helpers.LogLevel.Error); + return; + } + OSDMap res = (OSDMap)result; + Logger.Log("Response from mesh upload perform:\n" + OSDParser.SerializeLLSDNotationFormatted(result), Helpers.LogLevel.Debug); + }; + + request.BeginGetResponse(AssetResources(), OSDFormat.Xml, 60 * 1000); + } + + + } +} \ No newline at end of file