Skip to content

Instantly share code, notes, and snippets.

@gleblebedev
Last active February 9, 2020 18:08
Show Gist options
  • Select an option

  • Save gleblebedev/1a6dfd9ce58ad8e1b417abbd23f65ecf to your computer and use it in GitHub Desktop.

Select an option

Save gleblebedev/1a6dfd9ce58ad8e1b417abbd23f65ecf to your computer and use it in GitHub Desktop.

Revisions

  1. gleblebedev revised this gist Aug 3, 2017. 1 changed file with 24 additions and 4 deletions.
    28 changes: 24 additions & 4 deletions UrhoExporter.cs
    Original file line number Diff line number Diff line change
    @@ -323,8 +323,7 @@ private void WriteObject(string prefix, GameObject obj, HashSet<Renderer> exclud
    WriteAttribute(subSubPrefix, "Height Map", "Image;" + heightmapFileName);

    WriteAttribute(subSubPrefix, "Material", "Material;"+ materialFileName);
    WriteTerrainMaterial(terrain.terrainData.splatPrototypes, Path.Combine(_assetsFolder, materialFileName));

    WriteTerrainMaterial(terrain, Path.Combine(_assetsFolder, materialFileName));
    WriteAttribute(subSubPrefix, "Vertex Spacing", new Vector3(terrainSize.x / w, (max - min), terrainSize.z / h));
    Directory.CreateDirectory(Path.GetDirectoryName(Path.Combine(_assetsFolder, heightmapFileName)));
    using (var imageFile = File.Open(Path.Combine(_assetsFolder, heightmapFileName), FileMode.Create, FileAccess.Write,
    @@ -638,8 +637,9 @@ private void WriteMesh(BinaryWriter writer, Mesh _mesh)
    writer.Write(maxZ);
    }
    }
    private void WriteTerrainMaterial(SplatPrototype[] textures, string materialFileName)
    private void WriteTerrainMaterial(Terrain terrain, string materialFileName)
    {
    SplatPrototype[] textures = terrain.terrainData.splatPrototypes;
    Directory.CreateDirectory(Path.GetDirectoryName(materialFileName));
    using (var writer = XmlWriter.Create(materialFileName))
    {
    @@ -653,6 +653,19 @@ private void WriteTerrainMaterial(SplatPrototype[] textures, string materialFile
    writer.WriteEndElement();
    writer.WriteWhitespace(Environment.NewLine);

    writer.WriteStartElement("parameter");
    writer.WriteAttributeString("name", "UOffset");
    writer.WriteAttributeString("value", Format(new Vector4(terrain.terrainData.size.x,0,0,0)));
    writer.WriteEndElement();
    writer.WriteWhitespace(Environment.NewLine);

    writer.WriteStartElement("parameter");
    writer.WriteAttributeString("name", "VOffset");
    writer.WriteAttributeString("value", Format(new Vector4(0, terrain.terrainData.size.z, 0, 0)));
    writer.WriteEndElement();
    writer.WriteWhitespace(Environment.NewLine);


    WriteTexture(textures[0].texture, writer, "diffuse");
    writer.WriteEndElement();
    writer.WriteEndDocument();
    @@ -1035,6 +1048,10 @@ private void WriteAttribute(string prefix, string name, Vector3 pos)
    {
    WriteAttribute(prefix, name, string.Format(CultureInfo.InvariantCulture, "{0} {1} {2}", pos.x, pos.y, pos.z));
    }
    private void WriteAttribute(string prefix, string name, Vector4 pos)
    {
    WriteAttribute(prefix, name, Format(pos));
    }

    private void WriteAttribute(string prefix, string name, Quaternion pos)
    {
    @@ -1056,7 +1073,10 @@ private static string Format(Color pos)
    {
    return string.Format(CultureInfo.InvariantCulture, "{0} {1} {2} {3}", pos.r, pos.g, pos.b, pos.a);
    }

    private static string Format(Vector4 pos)
    {
    return string.Format(CultureInfo.InvariantCulture, "{0} {1} {2} {3}", pos.x, pos.y, pos.z, pos.w);
    }
    private void WriteAttribute(string prefix, string name, bool flag)
    {
    WriteAttribute(prefix, name, flag ? "true" : "false");
  2. gleblebedev revised this gist Aug 2, 2017. 1 changed file with 162 additions and 30 deletions.
    192 changes: 162 additions & 30 deletions UrhoExporter.cs
    Original file line number Diff line number Diff line change
    @@ -244,7 +244,22 @@ private void WriteObject(string prefix, GameObject obj, HashSet<Renderer> exclud
    var terrain = obj.GetComponent<Terrain>();
    var light = obj.GetComponent<Light>();
    var camera = obj.GetComponent<Camera>();
    var reflectionProbe = obj.GetComponent<ReflectionProbe>();

    if (reflectionProbe != null)
    {
    StartCompoent(subPrefix, "Zone");

    WriteAttribute(subSubPrefix, "Bounding Box Min", -(reflectionProbe.size*0.5f));
    WriteAttribute(subSubPrefix, "Bounding Box Max", (reflectionProbe.size * 0.5f));
    var cubemap = reflectionProbe.bakedTexture as Cubemap;
    if (cubemap != null)
    {
    var name = SaveCubemap(cubemap);
    WriteAttribute(subSubPrefix, "Zone Texture", "TextureCube;"+name);
    }
    EndElement(subPrefix);
    }
    if (camera != null)
    {
    StartCompoent(subPrefix, "Camera");
    @@ -283,7 +298,7 @@ private void WriteObject(string prefix, GameObject obj, HashSet<Renderer> exclud
    var h = terrain.terrainData.heightmapHeight;
    float max = float.MinValue;
    float min = float.MaxValue;
    var heights = terrain.terrainData.GetHeights(0,0,w,h);
    var heights = terrain.terrainData.GetHeights(0, 0, w, h);
    foreach (var height in heights)
    {
    if (height > max) max = height;
    @@ -293,18 +308,26 @@ private void WriteObject(string prefix, GameObject obj, HashSet<Renderer> exclud
    {
    max = 1;
    min = 0;
    } else if (max == min)
    }
    else if (max == min)
    {
    max = min + 0.1f;
    }
    WriteAttribute(subPrefix, "Position", new Vector3(-terrainSize.x*0.5f, -min, -terrainSize.z*0.5f));
    WriteAttribute(subPrefix, "Position", new Vector3(terrainSize.x * 0.5f, -min, terrainSize.z * 0.5f));

    StartCompoent(subPrefix, "Terrain");
    var fileName = "Textures/Heightmaps/" + _fileNameWithoutExtension + "/" + Path.GetInvalidFileNameChars().Aggregate(obj.name,(_1,_2)=>_1.Replace(_2,'_')) + ".tga";
    WriteAttribute(subSubPrefix, "Height Map", "Image;"+ fileName);
    WriteAttribute(subSubPrefix, "Vertex Spacing", new Vector3(terrainSize.x / w, (max-min), terrainSize.z / h));
    Directory.CreateDirectory(Path.GetDirectoryName(Path.Combine(_assetsFolder, fileName)));
    using (var imageFile = File.Open(Path.Combine(_assetsFolder, fileName), FileMode.Create, FileAccess.Write,
    var folderAndName = _fileNameWithoutExtension + "/" +
    Path.GetInvalidFileNameChars().Aggregate(obj.name, (_1, _2) => _1.Replace(_2, '_'));
    var heightmapFileName = "Textures/Terrains/" + folderAndName + ".tga";
    var materialFileName = "Materials/Terrains/" + folderAndName + ".xml";
    WriteAttribute(subSubPrefix, "Height Map", "Image;" + heightmapFileName);

    WriteAttribute(subSubPrefix, "Material", "Material;"+ materialFileName);
    WriteTerrainMaterial(terrain.terrainData.splatPrototypes, Path.Combine(_assetsFolder, materialFileName));

    WriteAttribute(subSubPrefix, "Vertex Spacing", new Vector3(terrainSize.x / w, (max - min), terrainSize.z / h));
    Directory.CreateDirectory(Path.GetDirectoryName(Path.Combine(_assetsFolder, heightmapFileName)));
    using (var imageFile = File.Open(Path.Combine(_assetsFolder, heightmapFileName), FileMode.Create, FileAccess.Write,
    FileShare.Read))
    {
    using (var binaryWriter = new BinaryWriter(imageFile))
    @@ -325,13 +348,13 @@ private void WriteObject(string prefix, GameObject obj, HashSet<Renderer> exclud
    {
    for (int x = 0; x < w; ++x)
    {
    var height = (heights[w-x-1,y]-min)/(max-min)*255.0f;
    var height = (heights[w - x - 1, y] - min) / (max - min) * 255.0f;
    binaryWriter.Write((byte)height);
    }
    }
    }
    }
    //WriteMaterialAttribute(subSubPrefix, terrain.terrainData.splatPrototypes);
    //WriteMaterialAttribute(subSubPrefix, terrain.terrainData.splatPrototypes);
    EndElement(subPrefix);
    EndElement(subPrefix);
    }
    @@ -394,6 +417,92 @@ private void WriteObject(string prefix, GameObject obj, HashSet<Renderer> exclud
    _writer.WriteEndElement();
    _writer.WriteWhitespace("\n");
    }

    private string SaveCubemap(Cubemap cubemap)
    {
    var path = "Textures/"+GetRelAssetPath(cubemap);

    var basefileName = Path.Combine(_assetsFolder, path);
    Directory.CreateDirectory(Path.GetDirectoryName(basefileName));
    using (var writer = XmlWriter.Create(basefileName + ".xml"))
    {
    writer.WriteStartDocument();
    writer.WriteStartElement("cubemap");
    writer.WriteWhitespace(Environment.NewLine);

    writer.WriteStartElement("face");
    writer.WriteAttributeString("name", Path.GetFileName(path)+ "_PosX.png");
    writer.WriteEndElement();
    writer.WriteWhitespace(Environment.NewLine);

    writer.WriteStartElement("face");
    writer.WriteAttributeString("name", Path.GetFileName(path) + "_NegX.png");
    writer.WriteEndElement();
    writer.WriteWhitespace(Environment.NewLine);

    writer.WriteStartElement("face");
    writer.WriteAttributeString("name", Path.GetFileName(path) + "_PosY.png");
    writer.WriteEndElement();
    writer.WriteWhitespace(Environment.NewLine);

    writer.WriteStartElement("face");
    writer.WriteAttributeString("name", Path.GetFileName(path) + "_NegY.png");
    writer.WriteEndElement();
    writer.WriteWhitespace(Environment.NewLine);

    writer.WriteStartElement("face");
    writer.WriteAttributeString("name", Path.GetFileName(path) + "_PosZ.png");
    writer.WriteEndElement();
    writer.WriteWhitespace(Environment.NewLine);

    writer.WriteStartElement("face");
    writer.WriteAttributeString("name", Path.GetFileName(path) + "_NegZ.png");
    writer.WriteEndElement();
    writer.WriteWhitespace(Environment.NewLine);

    writer.WriteEndElement();
    writer.WriteEndDocument();
    }

    Texture2D tex;
    byte[] bytes;
    tex = new Texture2D(cubemap.width, cubemap.height, TextureFormat.RGB24, false);
    try
    {
    tex.SetPixels(cubemap.GetPixels(CubemapFace.PositiveX));
    bytes = tex.EncodeToPNG();
    File.WriteAllBytes(Path.Combine(_assetsFolder, path + "_PosX.png"), bytes);

    tex.SetPixels(cubemap.GetPixels(CubemapFace.NegativeX));
    bytes = tex.EncodeToPNG();
    File.WriteAllBytes(Path.Combine(_assetsFolder, path + "_NegX.png"), bytes);

    tex.SetPixels(cubemap.GetPixels(CubemapFace.PositiveY));
    bytes = tex.EncodeToPNG();
    File.WriteAllBytes(Path.Combine(_assetsFolder, path + "_PosY.png"), bytes);

    tex.SetPixels(cubemap.GetPixels(CubemapFace.NegativeY));
    bytes = tex.EncodeToPNG();
    File.WriteAllBytes(Path.Combine(_assetsFolder, path + "_NegY.png"), bytes);

    tex.SetPixels(cubemap.GetPixels(CubemapFace.PositiveZ));
    bytes = tex.EncodeToPNG();
    File.WriteAllBytes(Path.Combine(_assetsFolder, path + "_PosZ.png"), bytes);

    tex.SetPixels(cubemap.GetPixels(CubemapFace.NegativeZ));
    bytes = tex.EncodeToPNG();
    File.WriteAllBytes(Path.Combine(_assetsFolder, path + "_NegZ.png"), bytes);
    }
    catch (Exception ex)
    {
    Debug.LogError(ex);
    }
    Object.DestroyImmediate(tex);

    return path +".xml";
    }


    private void WriteMaterialAttribute(string subSubPrefix, Material[] meshRendererMaterials)
    {
    var material = new StringBuilder();
    @@ -474,7 +583,7 @@ private void WriteMesh(BinaryWriter writer, Mesh _mesh)
    writer.Write(2);
    for (var subMeshIndex = 0; subMeshIndex < _mesh.subMeshCount; ++subMeshIndex)
    for (var i = 0; i < indicesPerSubMesh[subMeshIndex].Length; ++i)
    writer.Write((ushort) indicesPerSubMesh[subMeshIndex][i]);
    writer.Write((ushort)indicesPerSubMesh[subMeshIndex][i]);
    }
    else
    {
    @@ -492,7 +601,7 @@ private void WriteMesh(BinaryWriter writer, Mesh _mesh)
    var numberOfLODLevels = 1;
    writer.Write(numberOfLODLevels);
    writer.Write(0.0f);
    writer.Write((int) PrimitiveType.TRIANGLE_LIST);
    writer.Write((int)PrimitiveType.TRIANGLE_LIST);
    writer.Write(0);
    writer.Write(0);
    writer.Write(totalIndices);
    @@ -529,8 +638,27 @@ private void WriteMesh(BinaryWriter writer, Mesh _mesh)
    writer.Write(maxZ);
    }
    }
    private void WriteTerrainMaterial(SplatPrototype[] textures, string materialFileName)
    {
    Directory.CreateDirectory(Path.GetDirectoryName(materialFileName));
    using (var writer = XmlWriter.Create(materialFileName))
    {
    writer.WriteStartDocument();
    writer.WriteStartElement("material");
    writer.WriteWhitespace(Environment.NewLine);

    writer.WriteStartElement("technique");
    writer.WriteAttributeString("name", "Techniques/Diff.xml");
    writer.WriteAttributeString("quality", "0");
    writer.WriteEndElement();
    writer.WriteWhitespace(Environment.NewLine);

    private void CreateMaterial(string materialFileName, Material material)
    WriteTexture(textures[0].texture, writer, "diffuse");
    writer.WriteEndElement();
    writer.WriteEndDocument();
    }
    }
    private void CreateMaterial(string materialFileName, Material material)
    {
    Directory.CreateDirectory(Path.GetDirectoryName(materialFileName));
    using (var writer = XmlWriter.Create(materialFileName))
    @@ -543,7 +671,7 @@ private void CreateMaterial(string materialFileName, Material material)
    var matSpecColor = new Color(0, 0, 0, 0);
    var matEmissiveColor = Color.black;
    var flags = new MaterialFlags();
    flags.hasAlpha = material.renderQueue == (int) RenderQueue.Transparent;
    flags.hasAlpha = material.renderQueue == (int)RenderQueue.Transparent;
    var shader = material.shader;
    for (var i = 0; i < ShaderUtil.GetPropertyCount(shader); i++)
    {
    @@ -691,7 +819,17 @@ private void CreateMaterial(string materialFileName, Material material)
    }
    }

    private string WriteTexture(Texture texture, XmlWriter writer, string name, bool isNormal = false, bool canCompress = true)
    private void WriteTexture(Texture texture, XmlWriter writer, string name, bool isNormal = false,
    bool canCompress = true)
    {
    writer.WriteStartElement("texture");
    writer.WriteAttributeString("unit", name);
    writer.WriteAttributeString("name", WriteTexture(texture,isNormal,canCompress));
    writer.WriteEndElement();
    writer.WriteWhitespace(Environment.NewLine);
    }

    private string WriteTexture(Texture texture, bool isNormal = false, bool canCompress = true)
    {
    if (texture == null)
    return null;
    @@ -723,12 +861,6 @@ private string WriteTexture(Texture texture, XmlWriter writer, string name, bool
    var dataPath = Application.dataPath;
    var outputPath = "Textures/" + relPath;

    writer.WriteStartElement("texture");
    writer.WriteAttributeString("unit", name);
    writer.WriteAttributeString("name", outputPath);
    writer.WriteEndElement();
    writer.WriteWhitespace(Environment.NewLine);

    var destFileName = Path.Combine(_assetsFolder, outputPath);
    if (!File.Exists(destFileName))
    {
    @@ -743,10 +875,10 @@ private string WriteTexture(Texture texture, XmlWriter writer, string name, bool
    texture2d.LoadRawTextureData(sourceTexture2D.GetRawTextureData());

    texture2d.Apply();

    texture2d.Compress(true);
    var rawCompressedTexture = texture2d.GetRawTextureData();

    var ms = new MemoryStream();
    var br = new BinaryWriter(ms);
    var a = new byte[]
    @@ -784,10 +916,10 @@ private string WriteTexture(Texture texture, XmlWriter writer, string name, bool
    pixelFormatFlags |= 1;
    br.Write(pixelFormatFlags);
    if (texture2d.format == TextureFormat.DXT5)
    br.Write(new byte[] {0x44, 0x58, 0x54, 0x35}); //dwFourCC
    br.Write(new byte[] { 0x44, 0x58, 0x54, 0x35 }); //dwFourCC
    else
    br.Write(new byte[] {0x44, 0x58, 0x54, 0x31}); //dwFourCC
    br.Write(new byte[] { 0x44, 0x58, 0x54, 0x31 }); //dwFourCC

    br.Write(new byte[]
    {
    0x00, 0x00, 0x00, 0x00, //dwRGBBitCount
    @@ -807,7 +939,7 @@ private string WriteTexture(Texture texture, XmlWriter writer, string name, bool
    0x00, 0x00, 0x00, 0x00, //dwCaps4
    0x00, 0x00, 0x00, 0x00, //dwReserved2
    });

    br.Write(rawCompressedTexture);
    File.WriteAllBytes(destFileName, ms.ToArray());

    @@ -820,7 +952,7 @@ private string WriteTexture(Texture texture, XmlWriter writer, string name, bool
    File.Copy(Path.Combine(dataPath, relPath), destFileName);
    }
    }
    return relPath;
    return outputPath;
    }

    private string GetRelAssetPath(Object assetObject)
    @@ -1023,7 +1155,7 @@ internal class MeshVector3Stream : MeshStreamWriter
    public MeshVector3Stream(Vector3[] positions, VertexElementSemantic sem, int index = 0)
    {
    this.positions = positions;
    Element = (int) VertexElementType.TYPE_VECTOR3 | ((int) sem << 8) | (index << 16);
    Element = (int)VertexElementType.TYPE_VECTOR3 | ((int)sem << 8) | (index << 16);
    }

    public override void Write(BinaryWriter writer, int index)
    @@ -1041,7 +1173,7 @@ internal class MeshVector2Stream : MeshStreamWriter
    public MeshVector2Stream(Vector2[] positions, VertexElementSemantic sem, int index = 0)
    {
    this.positions = positions;
    Element = (int) VertexElementType.TYPE_VECTOR2 | ((int) sem << 8) | (index << 16);
    Element = (int)VertexElementType.TYPE_VECTOR2 | ((int)sem << 8) | (index << 16);
    }

    public override void Write(BinaryWriter writer, int index)
  3. gleblebedev revised this gist Aug 1, 2017. 1 changed file with 56 additions and 4 deletions.
    60 changes: 56 additions & 4 deletions UrhoExporter.cs
    Original file line number Diff line number Diff line change
    @@ -274,13 +274,65 @@ private void WriteObject(string prefix, GameObject obj, HashSet<Renderer> exclud
    if (terrain != null)
    {
    var terrainSize = terrain.terrainData.size;
    StartCompoent(subPrefix, "Terrain");
    _writer.WriteWhitespace(subPrefix);
    _writer.WriteStartElement("node");
    _writer.WriteAttributeString("id", (++_id).ToString());
    _writer.WriteWhitespace("\n");

    var w = terrain.terrainData.heightmapWidth;
    var h = terrain.terrainData.heightmapHeight;
    float max = float.MinValue;
    float min = float.MaxValue;
    var heights = terrain.terrainData.GetHeights(0,0,w,h);
    WriteAttribute(subSubPrefix, "Height Map", "Image;Textures/Heightmaps/"+ _fileNameWithoutExtension + ".png");
    WriteAttribute(subSubPrefix, "Vertex Spacing", new Vector3(terrainSize.x / w, terrainSize.y / 255.0f, terrainSize.z / h));
    //WriteMaterialAttribute(subSubPrefix, terrain.terrainData.splatPrototypes);
    foreach (var height in heights)
    {
    if (height > max) max = height;
    if (height < min) min = height;
    }
    if (max < min)
    {
    max = 1;
    min = 0;
    } else if (max == min)
    {
    max = min + 0.1f;
    }
    WriteAttribute(subPrefix, "Position", new Vector3(-terrainSize.x*0.5f, -min, -terrainSize.z*0.5f));

    StartCompoent(subPrefix, "Terrain");
    var fileName = "Textures/Heightmaps/" + _fileNameWithoutExtension + "/" + Path.GetInvalidFileNameChars().Aggregate(obj.name,(_1,_2)=>_1.Replace(_2,'_')) + ".tga";
    WriteAttribute(subSubPrefix, "Height Map", "Image;"+ fileName);
    WriteAttribute(subSubPrefix, "Vertex Spacing", new Vector3(terrainSize.x / w, (max-min), terrainSize.z / h));
    Directory.CreateDirectory(Path.GetDirectoryName(Path.Combine(_assetsFolder, fileName)));
    using (var imageFile = File.Open(Path.Combine(_assetsFolder, fileName), FileMode.Create, FileAccess.Write,
    FileShare.Read))
    {
    using (var binaryWriter = new BinaryWriter(imageFile))
    {
    binaryWriter.Write((byte)0);
    binaryWriter.Write((byte)0);
    binaryWriter.Write((byte)3);
    binaryWriter.Write((short)0);
    binaryWriter.Write((short)0);
    binaryWriter.Write((byte)0);
    binaryWriter.Write((short)0);
    binaryWriter.Write((short)0);
    binaryWriter.Write((short)w);
    binaryWriter.Write((short)h);
    binaryWriter.Write((byte)8);
    binaryWriter.Write((byte)0);
    for (int y = h - 1; y >= 0; --y)
    {
    for (int x = 0; x < w; ++x)
    {
    var height = (heights[w-x-1,y]-min)/(max-min)*255.0f;
    binaryWriter.Write((byte)height);
    }
    }
    }
    }
    //WriteMaterialAttribute(subSubPrefix, terrain.terrainData.splatPrototypes);
    EndElement(subPrefix);
    EndElement(subPrefix);
    }
    if (lodGroup != null)
  4. gleblebedev revised this gist Aug 1, 2017. 1 changed file with 193 additions and 57 deletions.
    250 changes: 193 additions & 57 deletions UrhoExporter.cs
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,9 @@
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Xml;
    using UnityEditor;
    @@ -48,6 +50,12 @@ public enum VertexElementType
    MAX_VERTEX_ELEMENT_TYPES
    }

    public enum DXGI_FORMAT
    {
    Unknown = 0,
    DXGI_FORMAT_R8G8B8A8_SINT = 32,
    DXGI_FORMAT_BC3_UNORM = 77,
    }

    public const uint Magic2 = 0x32444d55;

    @@ -129,6 +137,7 @@ public enum VertexElementType
    private readonly string _outputFileName;
    private readonly TextWriter _stream;
    private readonly XmlTextWriter _writer;
    private readonly string _fileNameWithoutExtension;
    private int _id;

    private Scene _scene;
    @@ -137,6 +146,7 @@ public UrhoExporter(Scene scene, string outputFileName)
    {
    _scene = scene;
    _outputFileName = Path.GetFullPath(outputFileName);
    _fileNameWithoutExtension = Path.GetFileNameWithoutExtension(_outputFileName);
    _assetsFolder = Path.GetDirectoryName(Path.GetDirectoryName(_outputFileName));
    if (string.IsNullOrEmpty(_assetsFolder))
    _assetsFolder = Path.GetDirectoryName(_outputFileName);
    @@ -186,7 +196,7 @@ public void Export()
    StartCompoent(prefix, "PhysicsWorld");
    EndElement(prefix);

    EnumerateObjects(prefix, _scene.GetRootGameObjects());
    EnumerateObjects(prefix, _scene.GetRootGameObjects(), new HashSet<Renderer>());

    WriteAttribute(prefix, "Next Replicated Node ID", _id);
    WriteAttribute(prefix, "Next Replicated Component ID", _id);
    @@ -196,10 +206,10 @@ public void Export()
    }


    private void EnumerateObjects(string prefix, GameObject[] objects)
    private void EnumerateObjects(string prefix, GameObject[] objects, HashSet<Renderer> excludeList)
    {
    foreach (var obj in objects)
    WriteObject(prefix, obj);
    WriteObject(prefix, obj, excludeList);
    }

    private string GetFileName(string name)
    @@ -209,8 +219,9 @@ private string GetFileName(string name)
    return name;
    }

    private void WriteObject(string prefix, GameObject obj)
    private void WriteObject(string prefix, GameObject obj, HashSet<Renderer> excludeList)
    {
    var localExcludeList = new HashSet<Renderer>(excludeList);
    _writer.WriteWhitespace(prefix);
    _writer.WriteStartElement("node");
    _writer.WriteAttributeString("id", (++_id).ToString());
    @@ -228,6 +239,7 @@ private void WriteObject(string prefix, GameObject obj)

    var meshFilter = obj.GetComponent<MeshFilter>();
    var meshRenderer = obj.GetComponent<MeshRenderer>();
    var lodGroup = obj.GetComponent<LODGroup>();
    var meshCollider = obj.GetComponent<MeshCollider>();
    var terrain = obj.GetComponent<Terrain>();
    var light = obj.GetComponent<Light>();
    @@ -262,10 +274,28 @@ private void WriteObject(string prefix, GameObject obj)
    if (terrain != null)
    {
    var terrainSize = terrain.terrainData.size;
    var y = terrain.SampleHeight(new Vector3(0, 0, 0));
    StartCompoent(subPrefix, "Terrain");
    var w = terrain.terrainData.heightmapWidth;
    var h = terrain.terrainData.heightmapHeight;
    var heights = terrain.terrainData.GetHeights(0,0,w,h);
    WriteAttribute(subSubPrefix, "Height Map", "Image;Textures/Heightmaps/"+ _fileNameWithoutExtension + ".png");
    WriteAttribute(subSubPrefix, "Vertex Spacing", new Vector3(terrainSize.x / w, terrainSize.y / 255.0f, terrainSize.z / h));
    //WriteMaterialAttribute(subSubPrefix, terrain.terrainData.splatPrototypes);
    EndElement(subPrefix);
    }

    if (meshRenderer != null)
    if (lodGroup != null)
    {
    var lods = lodGroup.GetLODs();
    foreach (var lod in lods.Skip(1))
    {
    foreach (var renderer in lod.renderers)
    {
    localExcludeList.Add(renderer);
    }
    }
    //lods[0].renderers
    }
    if (meshRenderer != null && !localExcludeList.Contains(meshRenderer))
    if (meshFilter != null)
    {
    StartCompoent(subPrefix, "StaticModel");
    @@ -294,24 +324,7 @@ private void WriteObject(string prefix, GameObject obj)
    }
    }

    var material = new StringBuilder();
    material.Append("Material");
    var meshRendererMaterials = meshRenderer.sharedMaterials;
    for (var i = 0; i < meshRendererMaterials.Length; ++i)
    {
    var meshRendererMaterial = meshRendererMaterials[i];
    var relPath = GetRelAssetPath(meshRendererMaterial);

    var outputMaterialName = "Materials/" + relPath + ".xml";

    material.Append(";");
    material.Append(outputMaterialName);

    var materialFileName = Path.Combine(_assetsFolder, outputMaterialName);
    if (!File.Exists(materialFileName))
    CreateMaterial(materialFileName, meshRendererMaterial);
    }
    WriteAttribute(subSubPrefix, "Material", material.ToString());
    WriteMaterialAttribute(subSubPrefix, meshRenderer.sharedMaterials);
    WriteAttribute(subSubPrefix, "Cast Shadows", meshRenderer.shadowCastingMode != ShadowCastingMode.Off);

    EndElement(subPrefix);
    @@ -323,13 +336,32 @@ private void WriteObject(string prefix, GameObject obj)

    foreach (Transform childTransform in obj.transform)
    if (childTransform.parent.gameObject == obj)
    WriteObject(subPrefix, childTransform.gameObject);
    WriteObject(subPrefix, childTransform.gameObject, localExcludeList);

    _writer.WriteWhitespace(prefix);
    _writer.WriteEndElement();
    _writer.WriteWhitespace("\n");
    }
    private void WriteMaterialAttribute(string subSubPrefix, Material[] meshRendererMaterials)
    {
    var material = new StringBuilder();
    material.Append("Material");
    for (var i = 0; i < meshRendererMaterials.Length; ++i)
    {
    var meshRendererMaterial = meshRendererMaterials[i];
    var relPath = GetRelAssetPath(meshRendererMaterial);

    var outputMaterialName = "Materials/" + relPath + ".xml";

    material.Append(";");
    material.Append(outputMaterialName);

    var materialFileName = Path.Combine(_assetsFolder, outputMaterialName);
    if (!File.Exists(materialFileName))
    CreateMaterial(materialFileName, meshRendererMaterial);
    }
    WriteAttribute(subSubPrefix, "Material", material.ToString());
    }
    private void WriteMesh(BinaryWriter writer, Mesh _mesh)
    {
    writer.Write(Magic2);
    @@ -355,10 +387,8 @@ private void WriteMesh(BinaryWriter writer, Mesh _mesh)
    //{
    // elements.Add(new MeshColorStream(colors, VertexElementSemantic.SEM_COLOR));
    //}
    //if (tangents.Length > 0)
    //{
    // elements.Add(new MeshVector4Stream(tangents, VertexElementSemantic.SEM_TANGENT));
    //}
    if (tangents.Length > 0)
    elements.Add(new MeshVector4Stream(tangents, VertexElementSemantic.SEM_TANGENT, 0));
    if (uvs.Length > 0)
    elements.Add(new MeshVector2Stream(uvs, VertexElementSemantic.SEM_TEXCOORD, 0));
    if (uvs2.Length > 0)
    @@ -472,6 +502,7 @@ private void CreateMaterial(string materialFileName, Material material)
    if (texture != null)
    switch (propertyName)
    {
    case "_Diffuse":
    case "_MainTex":
    flags.hasDiffuse = true;
    WriteTexture(texture, writer, "diffuse");
    @@ -482,9 +513,10 @@ private void CreateMaterial(string materialFileName, Material material)
    break;
    case "_ParallaxMap":
    break;
    case "_Normal":
    case "_BumpMap":
    flags.hasNormal = true;
    WriteTexture(texture, writer, "normal");
    WriteTexture(texture, writer, "normal", true);
    break;
    case "_DetailAlbedoMap":
    break;
    @@ -498,6 +530,8 @@ private void CreateMaterial(string materialFileName, Material material)
    break;
    case "_OcclusionMap":
    break;
    case "_Mask":
    break;
    case "_DetailMask":
    break;
    default:
    @@ -510,6 +544,9 @@ private void CreateMaterial(string materialFileName, Material material)
    var color = material.GetColor(propertyName);
    switch (propertyName)
    {
    case "_FresnelColor":
    break;
    case "_MainColor":
    case "_Color":
    matDiffColor = color;
    break;
    @@ -524,6 +561,52 @@ private void CreateMaterial(string materialFileName, Material material)
    break;
    }
    }
    else if (ShaderUtil.GetPropertyType(shader, i) == ShaderUtil.ShaderPropertyType.Range)
    {
    var value = material.GetFloat(propertyName);
    switch (propertyName)
    {
    case "_Cutoff":
    case "_Glossiness":
    case "_GlossMapScale":
    case "_Parallax":
    case "_OcclusionStrength":
    case "_Specular":
    case "_Gloss":
    case "_FresnelPower":
    case "_FresnelExp":
    case "_Alpha_2":
    case "_RefractionPower":
    break;
    case "_Alpha_1":
    matDiffColor.a = value;
    break;
    default:
    Debug.LogWarning(propertyName);
    break;
    }
    }
    else if (ShaderUtil.GetPropertyType(shader, i) == ShaderUtil.ShaderPropertyType.Float)
    {
    var value = material.GetFloat(propertyName);
    switch (propertyName)
    {
    case "_SmoothnessTextureChannel":
    case "_SpecularHighlights":
    case "_GlossyReflections":
    case "_BumpScale":
    case "_DetailNormalMapScale":
    case "_UVSec":
    case "_Mode":
    case "_SrcBlend":
    case "_DstBlend":
    case "_ZWrite":
    break;
    default:
    Debug.LogWarning(propertyName);
    break;
    }
    }
    }
    if (!flags.hasDiffuse)
    WriteParameter(writer, "\t", "MatDiffColor", Format(matDiffColor));
    @@ -556,7 +639,7 @@ private void CreateMaterial(string materialFileName, Material material)
    }
    }

    private string WriteTexture(Texture texture, XmlWriter writer, string name)
    private string WriteTexture(Texture texture, XmlWriter writer, string name, bool isNormal = false, bool canCompress = true)
    {
    if (texture == null)
    return null;
    @@ -572,12 +655,17 @@ private string WriteTexture(Texture texture, XmlWriter writer, string name)
    {
    var ext = relPath.Substring(extIndex).ToLower();
    if (sourceTexture2D != null)
    if (ext == "psd" || sourceTexture2D.format == TextureFormat.DXT5 ||
    sourceTexture2D.format == TextureFormat.DXT1)
    {
    if (canCompress)// && ext != "tga")
    {
    needFormatConvertion = true;
    relPath = relPath.Substring(0, extIndex) + "dds";
    if ((ext == "psd" || sourceTexture2D.format == TextureFormat.DXT5 ||
    sourceTexture2D.format == TextureFormat.DXT1))
    {
    needFormatConvertion = true;
    relPath = relPath.Substring(0, extIndex) + "dds";
    }
    }
    }
    }

    var dataPath = Application.dataPath;
    @@ -603,10 +691,10 @@ private string WriteTexture(Texture texture, XmlWriter writer, string name)
    texture2d.LoadRawTextureData(sourceTexture2D.GetRawTextureData());

    texture2d.Apply();

    texture2d.Compress(true);
    var rawCompressedTexture = texture2d.GetRawTextureData();

    var ms = new MemoryStream();
    var br = new BinaryWriter(ms);
    var a = new byte[]
    @@ -616,29 +704,58 @@ private string WriteTexture(Texture texture, XmlWriter writer, string name)
    br.Write(a);
    br.Write(sourceTexture2D.width);
    br.Write(sourceTexture2D.height);
    a = new byte[]
    {
    0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,

    0x04, 0x00, 0x00, 0x00
    };
    br.Write(a);
    br.Write(new byte[]
    {
    0x00, 0x00, 0x40, 0x00, //dwPitchOrLinearSize
    0x00, 0x00, 0x00, 0x00, //dwDepth
    });
    int dwMipMapCount = texture2d.mipmapCount;
    br.Write(dwMipMapCount);
    br.Write(new byte[]
    {
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00,
    0x20, 0x00, 0x00, 0x00, //DDS_PIXELFORMAT dwSize
    });
    int pixelFormatFlags = 4;
    if (texture2d.alphaIsTransparency)
    pixelFormatFlags |= 1;
    br.Write(pixelFormatFlags);
    if (texture2d.format == TextureFormat.DXT5)
    a = new byte[] {0x44, 0x58, 0x54, 0x35};
    br.Write(new byte[] {0x44, 0x58, 0x54, 0x35}); //dwFourCC
    else
    a = new byte[] {0x44, 0x58, 0x54, 0x31};
    br.Write(a);
    a = new byte[]
    br.Write(new byte[] {0x44, 0x58, 0x54, 0x31}); //dwFourCC

    br.Write(new byte[]
    {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x40, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    };
    br.Write(a);

    0x00, 0x00, 0x00, 0x00, //dwRGBBitCount
    0x00, 0x00, 0x00, 0x00, //dwRBitMask
    0x00, 0x00, 0x00, 0x00, //dwGBitMask
    0x00, 0x00, 0x00, 0x00, //dwBBitMask
    0x00, 0x00, 0x00, 0x00, //dwABitMask
    });
    int caps = 0x00400008;
    if (texture2d.alphaIsTransparency)
    caps |= 0x00001000;
    br.Write(caps);
    br.Write(new byte[]
    {
    0x00, 0x00, 0x00, 0x00, //dwCaps2
    0x00, 0x00, 0x00, 0x00, //dwCaps3
    0x00, 0x00, 0x00, 0x00, //dwCaps4
    0x00, 0x00, 0x00, 0x00, //dwReserved2
    });

    br.Write(rawCompressedTexture);
    File.WriteAllBytes(destFileName, ms.ToArray());

    @@ -881,4 +998,23 @@ public override void Write(BinaryWriter writer, int index)
    writer.Write(positions[index].y);
    }
    }

    internal class MeshVector4Stream : MeshStreamWriter
    {
    private readonly Vector4[] positions;

    public MeshVector4Stream(Vector4[] positions, VertexElementSemantic sem, int index = 0)
    {
    this.positions = positions;
    Element = (int)VertexElementType.TYPE_VECTOR4 | ((int)sem << 8) | (index << 16);
    }

    public override void Write(BinaryWriter writer, int index)
    {
    writer.Write(positions[index].x);
    writer.Write(positions[index].y);
    writer.Write(positions[index].z);
    writer.Write(positions[index].w);
    }
    }
    }
  5. gleblebedev revised this gist Jun 25, 2017. 1 changed file with 206 additions and 43 deletions.
    249 changes: 206 additions & 43 deletions UrhoExporter.cs
    Original file line number Diff line number Diff line change
    @@ -50,13 +50,88 @@ public enum VertexElementType


    public const uint Magic2 = 0x32444d55;

    public static Technique[] Techniques =
    {
    new Technique {Material = new MaterialFlags(), Name = "NoTexture.xml"},
    new Technique {Material = new MaterialFlags {hasAlpha = true}, Name = "NoTextureAlpha.xml"},
    new Technique {Material = new MaterialFlags {hasNormal = true}, Name = "NoTextureNormal.xml"},
    new Technique
    {
    Material = new MaterialFlags {hasNormal = true, hasAlpha = true},
    Name = "NoTextureNormalAlpha.xml"
    },
    //new Technique
    //{
    // Material = new MaterialFlags {hasNormal = true, hasAlpha = true, hasEmissive = true},
    // Name = "NoTextureNormalEmissiveAlpha.xml"
    //},
    new Technique {Material = new MaterialFlags {hasDiffuse = true}, Name = "Diff.xml"},
    new Technique {Material = new MaterialFlags {hasDiffuse = true, hasAlpha = true}, Name = "DiffAlpha.xml"},
    new Technique {Material = new MaterialFlags {hasDiffuse = true, hasSpecular = true}, Name = "DiffSpec.xml"},
    new Technique
    {
    Material = new MaterialFlags {hasDiffuse = true, hasSpecular = true, hasAlpha = true},
    Name = "DiffSpecAlpha.xml"
    },
    new Technique {Material = new MaterialFlags {hasDiffuse = true, hasNormal = true}, Name = "DiffNormal.xml"},
    new Technique
    {
    Material = new MaterialFlags {hasDiffuse = true, hasNormal = true, hasAlpha = true},
    Name = "DiffNormalAlpha.xml"
    },
    new Technique {Material = new MaterialFlags {hasDiffuse = true, hasEmissive = true}, Name = "DiffEmissive.xml"},
    new Technique
    {
    Material = new MaterialFlags {hasDiffuse = true, hasEmissive = true, hasAlpha = true},
    Name = "DiffEmissiveAlpha.xml"
    },
    new Technique
    {
    Material = new MaterialFlags {hasDiffuse = true, hasSpecular = true, hasNormal = true},
    Name = "DiffNormalSpec.xml"
    },
    new Technique
    {
    Material = new MaterialFlags {hasDiffuse = true, hasSpecular = true, hasNormal = true, hasAlpha = true},
    Name = "DiffNormalSpecAlpha.xml"
    },
    new Technique
    {
    Material = new MaterialFlags {hasDiffuse = true, hasEmissive = true, hasNormal = true},
    Name = "DiffNormalEmissive.xml"
    },
    new Technique
    {
    Material = new MaterialFlags {hasDiffuse = true, hasEmissive = true, hasNormal = true, hasAlpha = true},
    Name = "DiffNormalEmissiveAlpha.xml"
    },
    new Technique
    {
    Material = new MaterialFlags {hasDiffuse = true, hasSpecular = true, hasNormal = true, hasEmissive = true},
    Name = "DiffNormalSpecEmissive.xml"
    },
    new Technique
    {
    Material = new MaterialFlags
    {
    hasDiffuse = true,
    hasSpecular = true,
    hasNormal = true,
    hasEmissive = true,
    hasAlpha = true
    },
    Name = "DiffNormalSpecEmissiveAlpha.xml"
    }
    };

    private readonly string _assetsFolder;
    private int _id;
    private readonly string _outputFileName;

    private Scene _scene;
    private readonly TextWriter _stream;
    private readonly XmlTextWriter _writer;
    private int _id;

    private Scene _scene;

    public UrhoExporter(Scene scene, string outputFileName)
    {
    @@ -196,15 +271,13 @@ private void WriteObject(string prefix, GameObject obj)
    StartCompoent(subPrefix, "StaticModel");

    var sharedMesh = meshFilter.sharedMesh;

    var meshRelFileName = GetRelAssetPath(sharedMesh);

    var subObjectPath = GetMeshPath(meshFilter);

    if (!string.IsNullOrEmpty(subObjectPath))
    {
    meshRelFileName = meshRelFileName + "/" + subObjectPath;
    }

    WriteAttribute(subSubPrefix, "Model", "Model;Models/" + meshRelFileName + ".mdl");

    @@ -382,37 +455,44 @@ private void CreateMaterial(string materialFileName, Material material)
    {
    writer.WriteStartDocument();
    writer.WriteStartElement("material");
    writer.WriteWhitespace(Environment.NewLine);

    Texture diffuse = null;
    Texture specular = null;
    Texture normal = null;
    var matDiffColor = Color.white;
    var matSpecColor = new Color(0, 0, 0, 0);
    var matEmissiveColor = Color.black;
    var flags = new MaterialFlags();
    flags.hasAlpha = material.renderQueue == (int) RenderQueue.Transparent;
    var shader = material.shader;
    for (var i = 0; i < ShaderUtil.GetPropertyCount(shader); i++)
    {
    var propertyName = ShaderUtil.GetPropertyName(shader, i);
    if (ShaderUtil.GetPropertyType(shader, i) == ShaderUtil.ShaderPropertyType.TexEnv)
    {
    var propertyName = ShaderUtil.GetPropertyName(shader, i);
    var texture = material.GetTexture(propertyName);
    if (texture != null)
    {
    switch (propertyName)
    {
    case "_MainTex":
    WriteTexture(diffuse = texture, writer, "diffuse");
    flags.hasDiffuse = true;
    WriteTexture(texture, writer, "diffuse");
    break;
    case "_SpecGlossMap":
    WriteTexture(specular = texture, writer, "specular");
    flags.hasSpecular = true;
    WriteTexture(texture, writer, "specular");
    break;
    case "_ParallaxMap":
    break;
    case "_BumpMap":
    WriteTexture(normal = texture, writer, "normal");
    flags.hasNormal = true;
    WriteTexture(texture, writer, "normal");
    break;
    case "_DetailAlbedoMap":
    break;
    case "_DetailNormalMap":
    break;
    case "_EmissionMap":
    flags.hasEmissive = true;
    WriteTexture(texture, writer, "emissive");
    break;
    case "_MetallicGlossMap":
    break;
    @@ -424,26 +504,52 @@ private void CreateMaterial(string materialFileName, Material material)
    Debug.LogWarning(propertyName);
    break;
    }
    }
    else if (ShaderUtil.GetPropertyType(shader, i) == ShaderUtil.ShaderPropertyType.Color)
    {
    var color = material.GetColor(propertyName);
    switch (propertyName)
    {
    case "_Color":
    matDiffColor = color;
    break;
    case "_EmissionColor":
    matEmissiveColor = color;
    break;
    case "_SpecColor":
    matSpecColor = color;
    break;
    default:
    Debug.LogWarning(propertyName);
    break;
    }
    }
    }
    if (!flags.hasDiffuse)
    WriteParameter(writer, "\t", "MatDiffColor", Format(matDiffColor));
    if (!flags.hasSpecular)
    WriteParameter(writer, "\t", "MatSpecColor", Format(matSpecColor));
    if (!flags.hasEmissive)
    WriteParameter(writer, "\t", "MatEmissiveColor", Format(matEmissiveColor));

    writer.WriteWhitespace(Environment.NewLine);
    writer.WriteStartElement("technique");
    if (diffuse == null)
    {
    if (normal != null)
    writer.WriteAttributeString("name", "Techniques/NoTextureNormal.xml");
    else
    writer.WriteAttributeString("name", "Techniques/NoTexture.xml");
    }
    else
    {
    if (normal != null)
    writer.WriteAttributeString("name", "Techniques/DiffNormal.xml");
    else
    writer.WriteAttributeString("name", "Techniques/Diff.xml");
    }
    var bestTechnique = Techniques[0];
    var bestTechniqueDistance = bestTechnique.Material - flags;
    foreach (var technique in Techniques)
    if (technique.Material.Fits(flags))
    {
    var d = technique.Material - flags;
    if (d < bestTechniqueDistance)
    {
    bestTechnique = technique;
    bestTechniqueDistance = d;
    }
    }
    writer.WriteAttributeString("name", "Techniques/" + bestTechnique.Name);
    writer.WriteAttributeString("quality", "0");
    writer.WriteEndElement();
    writer.WriteWhitespace(Environment.NewLine);

    writer.WriteEndElement();
    writer.WriteEndDocument();
    @@ -481,6 +587,7 @@ private string WriteTexture(Texture texture, XmlWriter writer, string name)
    writer.WriteAttributeString("unit", name);
    writer.WriteAttributeString("name", outputPath);
    writer.WriteEndElement();
    writer.WriteWhitespace(Environment.NewLine);

    var destFileName = Path.Combine(_assetsFolder, outputPath);
    if (!File.Exists(destFileName))
    @@ -537,7 +644,7 @@ private string WriteTexture(Texture texture, XmlWriter writer, string name)

    //var rawCompressedTexture = texture2d.EncodeToPNG();
    //File.WriteAllBytes(destFileName, rawCompressedTexture);
    Object.Destroy(texture2d);
    Object.DestroyImmediate(texture2d);
    }
    else
    {
    @@ -562,7 +669,7 @@ private string GetMeshPath(MeshFilter prefabFilter)
    if (string.IsNullOrEmpty(modelPath))
    return null;

    GameObject model = AssetDatabase.LoadAssetAtPath<GameObject>(modelPath);
    var model = AssetDatabase.LoadAssetAtPath<GameObject>(modelPath);

    if (!model)
    {
    @@ -572,14 +679,12 @@ private string GetMeshPath(MeshFilter prefabFilter)

    MeshFilter modelFilter = null;
    var filters = model.GetComponentsInChildren<MeshFilter>(true);
    foreach (MeshFilter filter in filters)
    {
    foreach (var filter in filters)
    if (filter.sharedMesh == prefabFilter.sharedMesh)
    {
    modelFilter = filter;
    break;
    }
    }

    if (!modelFilter)
    {
    @@ -590,8 +695,8 @@ private string GetMeshPath(MeshFilter prefabFilter)
    if (filters.Length == 1)
    return null;

    Transform transform = modelFilter.transform;
    string path = transform.name;
    var transform = modelFilter.transform;
    var path = transform.name;

    //while (transform.parent)
    //{
    @@ -638,8 +743,17 @@ private void WriteAttribute(string prefix, string name, Quaternion pos)

    private void WriteAttribute(string prefix, string name, Color pos)
    {
    WriteAttribute(prefix, name,
    string.Format(CultureInfo.InvariantCulture, "{0} {1} {2} {3}", pos.r, pos.g, pos.b, pos.a));
    WriteAttribute(_writer, prefix, name, Format(pos));
    }

    private void WriteAttribute(XmlWriter writer, string prefix, string name, Color pos)
    {
    WriteAttribute(writer, prefix, name, Format(pos));
    }

    private static string Format(Color pos)
    {
    return string.Format(CultureInfo.InvariantCulture, "{0} {1} {2} {3}", pos.r, pos.g, pos.b, pos.a);
    }

    private void WriteAttribute(string prefix, string name, bool flag)
    @@ -670,12 +784,61 @@ private void StartCompoent(string prefix, string type)

    private void WriteAttribute(string prefix, string name, string vaue)
    {
    _writer.WriteWhitespace(prefix);
    _writer.WriteStartElement("attribute");
    _writer.WriteAttributeString("name", name);
    _writer.WriteAttributeString("value", vaue);
    _writer.WriteEndElement();
    _writer.WriteWhitespace("\n");
    WriteAttribute(_writer, prefix, name, vaue);
    }

    private void WriteAttribute(XmlWriter writer, string prefix, string name, string vaue)
    {
    writer.WriteWhitespace(prefix);
    writer.WriteStartElement("attribute");
    writer.WriteAttributeString("name", name);
    writer.WriteAttributeString("value", vaue);
    writer.WriteEndElement();
    writer.WriteWhitespace("\n");
    }
    private void WriteParameter(XmlWriter writer, string prefix, string name, string vaue)
    {
    writer.WriteWhitespace(prefix);
    writer.WriteStartElement("parameter");
    writer.WriteAttributeString("name", name);
    writer.WriteAttributeString("value", vaue);
    writer.WriteEndElement();
    writer.WriteWhitespace("\n");
    }
    public class MaterialFlags
    {
    public bool hasAlpha;
    public bool hasDiffuse;
    public bool hasEmissive;
    public bool hasNormal;
    public bool hasSpecular;

    public static int operator -(MaterialFlags a, MaterialFlags b)
    {
    return GetDistance(a.hasDiffuse, b.hasDiffuse) + GetDistance(a.hasSpecular, b.hasSpecular) +
    GetDistance(a.hasNormal, b.hasNormal) + GetDistance(a.hasEmissive, b.hasEmissive) +
    GetDistance(a.hasAlpha, b.hasAlpha);
    }

    private static int GetDistance(bool a, bool b)
    {
    return a != b ? 1 : 0;
    }

    public bool Fits(MaterialFlags b)
    {
    return (!hasDiffuse || b.hasDiffuse)
    && (!hasSpecular || b.hasSpecular)
    && (!hasEmissive || b.hasEmissive)
    && (!hasNormal || b.hasNormal)
    && hasAlpha == b.hasAlpha;
    }
    }

    public class Technique
    {
    public MaterialFlags Material;
    public string Name;
    }

    internal abstract class MeshStreamWriter
  6. gleblebedev revised this gist Jun 24, 2017. 1 changed file with 263 additions and 210 deletions.
    473 changes: 263 additions & 210 deletions UrhoExporter.cs
    Original file line number Diff line number Diff line change
    @@ -5,32 +5,58 @@
    using System.Text;
    using System.Xml;
    using UnityEditor;
    using UnityEditor.SceneManagement;
    using UnityEngine;
    using UnityEngine.Rendering;
    using UnityEngine.SceneManagement;
    using Object = UnityEngine.Object;

    public class UrhoExporter:IDisposable
    public class UrhoExporter : IDisposable
    {
    [MenuItem("Tools/Export Scene To Urho3D")]
    public static void ExportToUrho()
    public enum PrimitiveType
    {
    using (var exporter = new UrhoExporter(EditorSceneManager.GetActiveScene(), EditorUtility.SaveFilePanel(
    "Save scene as Urho XML",
    "",
    "scene.xml",
    "xml")))
    {
    exporter.Export();
    }
    TRIANGLE_LIST = 0,
    LINE_LIST,
    POINT_LIST,
    TRIANGLE_STRIP,
    LINE_STRIP,
    TRIANGLE_FAN
    }

    public enum VertexElementSemantic
    {
    SEM_POSITION = 0,
    SEM_NORMAL,
    SEM_BINORMAL,
    SEM_TANGENT,
    SEM_TEXCOORD,
    SEM_COLOR,
    SEM_BLENDWEIGHTS,
    SEM_BLENDINDICES,
    SEM_OBJECTINDEX,
    MAX_VERTEX_ELEMENT_SEMANTICS
    }

    private Scene _scene;
    public enum VertexElementType
    {
    TYPE_INT = 0,
    TYPE_FLOAT,
    TYPE_VECTOR2,
    TYPE_VECTOR3,
    TYPE_VECTOR4,
    TYPE_UBYTE4,
    TYPE_UBYTE4_NORM,
    MAX_VERTEX_ELEMENT_TYPES
    }


    public const uint Magic2 = 0x32444d55;
    private readonly string _assetsFolder;
    private int _id;
    string _outputFileName;
    private TextWriter _stream;
    private XmlTextWriter _writer;
    private string _assetsFolder;
    private readonly string _outputFileName;

    private Scene _scene;
    private readonly TextWriter _stream;
    private readonly XmlTextWriter _writer;

    public UrhoExporter(Scene scene, string outputFileName)
    {
    @@ -44,6 +70,26 @@ public UrhoExporter(Scene scene, string outputFileName)
    _writer = new XmlTextWriter(_stream);
    }

    public void Dispose()
    {
    if (_stream != null)
    _stream.Dispose();
    }

    [MenuItem("Tools/Export Scene To Urho3D")]
    public static void ExportToUrho()
    {
    var activeScene = SceneManager.GetActiveScene();
    using (var exporter = new UrhoExporter(activeScene, EditorUtility.SaveFilePanel(
    "Save scene as Urho XML",
    "",
    activeScene.name + ".xml",
    "xml")))
    {
    exporter.Export();
    }
    }

    public void Export()
    {
    if (string.IsNullOrEmpty(_outputFileName))
    @@ -67,36 +113,28 @@ public void Export()

    EnumerateObjects(prefix, _scene.GetRootGameObjects());

    WriteAttribute(prefix, "Next Replicated Node ID", _id);
    WriteAttribute(prefix, "Next Replicated Component ID", _id);

    _writer.WriteEndElement();
    _writer.WriteEndDocument();
    }

    public void Dispose()
    {
    if (_stream != null)
    {
    _stream.Dispose();
    }
    }


    private void EnumerateObjects(string prefix, GameObject[] objects)
    {
    foreach (var obj in objects)
    {
    WriteObject(prefix, obj);
    }
    }

    private string GetFileName(string name)
    {
    foreach (var invalidFileNameChar in Path.GetInvalidFileNameChars())
    {
    name = name.Replace(invalidFileNameChar, '_');
    }
    return name;
    }
    private void WriteObject(string prefix,GameObject obj)

    private void WriteObject(string prefix, GameObject obj)
    {
    _writer.WriteWhitespace(prefix);
    _writer.WriteStartElement("node");
    @@ -106,12 +144,12 @@ private void WriteObject(string prefix,GameObject obj)
    var subPrefix = prefix + "\t";
    var subSubPrefix = subPrefix + "\t";

    WriteAttribute(subPrefix, "Is Enabled", obj.activeSelf);
    WriteAttribute(subPrefix, "Name", obj.name);
    WriteAttribute(subPrefix, "Tags", obj.tag);
    WriteAttribute(subPrefix, "Position", obj.transform.localPosition);
    WriteAttribute(subPrefix, "Rotation", obj.transform.localRotation);
    WriteAttribute(subPrefix, "Scale", obj.transform.localScale);
    WriteAttribute(subPrefix, "Is Enabled", obj.activeSelf);
    WriteAttribute(subPrefix, "Name", obj.name);
    WriteAttribute(subPrefix, "Tags", obj.tag);
    WriteAttribute(subPrefix, "Position", obj.transform.localPosition);
    WriteAttribute(subPrefix, "Rotation", obj.transform.localRotation);
    WriteAttribute(subPrefix, "Scale", obj.transform.localScale);

    var meshFilter = obj.GetComponent<MeshFilter>();
    var meshRenderer = obj.GetComponent<MeshRenderer>();
    @@ -134,19 +172,15 @@ private void WriteObject(string prefix,GameObject obj)
    {
    StartCompoent(subPrefix, "Light");
    if (light.type == LightType.Directional)
    {
    WriteAttribute(subSubPrefix, "Light Type", "Directional");
    }
    else if (light.type == LightType.Spot)
    {
    WriteAttribute(subSubPrefix, "Light Type", "Spot");
    }
    else if (light.type == LightType.Point)
    {
    WriteAttribute(subSubPrefix, "Range", light.range);
    }
    WriteAttribute(subSubPrefix, "Color", light.color);
    WriteAttribute(subSubPrefix, "Brightness Multiplier", light.intensity);
    WriteAttribute(subSubPrefix, "Cast Shadows", light.shadows != LightShadows.None);

    EndElement(subPrefix);
    }

    @@ -157,13 +191,22 @@ private void WriteObject(string prefix,GameObject obj)
    }

    if (meshRenderer != null)
    {
    if (meshFilter != null)
    {
    StartCompoent(subPrefix, "StaticModel");
    StartCompoent(subPrefix, "StaticModel");

    var sharedMesh = meshFilter.sharedMesh;

    var meshRelFileName = GetRelAssetPath(sharedMesh);

    var meshRelFileName = GetRelAssetPath(meshFilter.sharedMesh);
    WriteAttribute(subSubPrefix, "Model", "Model;Models/" + meshRelFileName + ".mdl");
    var subObjectPath = GetMeshPath(meshFilter);

    if (!string.IsNullOrEmpty(subObjectPath))
    {
    meshRelFileName = meshRelFileName + "/" + subObjectPath;
    }

    WriteAttribute(subSubPrefix, "Model", "Model;Models/" + meshRelFileName + ".mdl");

    var meshFileName = Path.Combine(Path.Combine(_assetsFolder, "Models"), meshRelFileName + ".mdl");
    if (!File.Exists(meshFileName))
    @@ -173,60 +216,52 @@ private void WriteObject(string prefix,GameObject obj)
    {
    using (var writer = new BinaryWriter(fileStream))
    {
    WriteMesh(writer, meshFilter.sharedMesh);
    WriteMesh(writer, sharedMesh);
    }
    }
    }

    StringBuilder material = new StringBuilder();
    var material = new StringBuilder();
    material.Append("Material");
    var meshRendererMaterials = meshRenderer.sharedMaterials;
    for (int i = 0; i < meshRendererMaterials.Length; ++i)
    for (var i = 0; i < meshRendererMaterials.Length; ++i)
    {
    var meshRendererMaterial = meshRendererMaterials[i];
    var relPath = GetRelAssetPath(meshRendererMaterial);

    var outputMaterialName = "Materials/" + relPath+".xml";
    var outputMaterialName = "Materials/" + relPath + ".xml";

    material.Append(";");
    material.Append(outputMaterialName);

    var materialFileName = Path.Combine(_assetsFolder, outputMaterialName);
    if (!File.Exists(materialFileName))
    {
    CreateMaterial(materialFileName, meshRendererMaterial);
    }
    }
    WriteAttribute(subSubPrefix, "Material", material.ToString());
    WriteAttribute(subSubPrefix, "Cast Shadows", meshRenderer.shadowCastingMode != ShadowCastingMode.Off);

    EndElement(subPrefix);
    }
    }

    if (meshCollider != null)
    {
    }

    foreach (Transform childTransform in obj.transform)
    {
    if (childTransform.parent.gameObject == obj)
    WriteObject(subPrefix, childTransform.gameObject);
    }

    _writer.WriteWhitespace(prefix);
    _writer.WriteEndElement();
    _writer.WriteWhitespace("\n");
    }



    public const uint Magic2 = 0x32444d55;

    private void WriteMesh(BinaryWriter writer, Mesh _mesh)
    {
    writer.Write(Magic2);
    writer.Write(1);
    for (int vbIndex = 0; vbIndex < 1 /*_mesh.vertexBufferCount*/; ++vbIndex)
    for (var vbIndex = 0; vbIndex < 1 /*_mesh.vertexBufferCount*/; ++vbIndex)
    {
    var positions = _mesh.vertices;
    var normals = _mesh.normals;
    @@ -240,13 +275,9 @@ private void WriteMesh(BinaryWriter writer, Mesh _mesh)
    writer.Write(positions.Length);
    var elements = new List<MeshStreamWriter>();
    if (positions.Length > 0)
    {
    elements.Add(new MeshVector3Stream(positions, VertexElementSemantic.SEM_POSITION));
    }
    if (normals.Length > 0)
    {
    elements.Add(new MeshVector3Stream(normals, VertexElementSemantic.SEM_NORMAL));
    }
    //if (colors.Length > 0)
    //{
    // elements.Add(new MeshColorStream(colors, VertexElementSemantic.SEM_COLOR));
    @@ -256,40 +287,26 @@ private void WriteMesh(BinaryWriter writer, Mesh _mesh)
    // elements.Add(new MeshVector4Stream(tangents, VertexElementSemantic.SEM_TANGENT));
    //}
    if (uvs.Length > 0)
    {
    elements.Add(new MeshVector2Stream(uvs, VertexElementSemantic.SEM_TEXCOORD, 0));
    }
    if (uvs2.Length > 0)
    {
    elements.Add(new MeshVector2Stream(uvs2, VertexElementSemantic.SEM_TEXCOORD, 1));
    }
    if (uvs3.Length > 0)
    {
    elements.Add(new MeshVector2Stream(uvs2, VertexElementSemantic.SEM_TEXCOORD, 2));
    }
    if (uvs4.Length > 0)
    {
    elements.Add(new MeshVector2Stream(uvs2, VertexElementSemantic.SEM_TEXCOORD, 3));
    }
    writer.Write(elements.Count);
    for (int i = 0; i < elements.Count; ++i)
    {
    for (var i = 0; i < elements.Count; ++i)
    writer.Write(elements[i].Element);
    }
    int morphableVertexRangeStartIndex = 0;
    int morphableVertexCount = 0;
    var morphableVertexRangeStartIndex = 0;
    var morphableVertexCount = 0;
    writer.Write(morphableVertexRangeStartIndex);
    writer.Write(morphableVertexCount);
    for (int index = 0; index < positions.Length; ++index)
    {
    for (int i = 0; i < elements.Count; ++i)
    {
    elements[i].Write(writer, index);
    }
    }
    for (var index = 0; index < positions.Length; ++index)
    for (var i = 0; i < elements.Count; ++i)
    elements[i].Write(writer, index);
    var indicesPerSubMesh = new List<int[]>();
    int totalIndices = 0;
    for (int subMeshIndex = 0; subMeshIndex < _mesh.subMeshCount; ++subMeshIndex)
    var totalIndices = 0;
    for (var subMeshIndex = 0; subMeshIndex < _mesh.subMeshCount; ++subMeshIndex)
    {
    var indices = _mesh.GetIndices(subMeshIndex);
    indicesPerSubMesh.Add(indices);
    @@ -300,35 +317,27 @@ private void WriteMesh(BinaryWriter writer, Mesh _mesh)
    if (positions.Length < 65536)
    {
    writer.Write(2);
    for (int subMeshIndex = 0; subMeshIndex < _mesh.subMeshCount; ++subMeshIndex)
    {
    for (int i = 0; i < indicesPerSubMesh[subMeshIndex].Length; ++i)
    {
    writer.Write((ushort)indicesPerSubMesh[subMeshIndex][i]);
    }
    }
    for (var subMeshIndex = 0; subMeshIndex < _mesh.subMeshCount; ++subMeshIndex)
    for (var i = 0; i < indicesPerSubMesh[subMeshIndex].Length; ++i)
    writer.Write((ushort) indicesPerSubMesh[subMeshIndex][i]);
    }
    else
    {
    writer.Write(4);
    for (int subMeshIndex = 0; subMeshIndex < _mesh.subMeshCount; ++subMeshIndex)
    {
    for (int i = 0; i < indicesPerSubMesh[subMeshIndex].Length; ++i)
    {
    writer.Write(indicesPerSubMesh[subMeshIndex][i]);
    }
    }
    for (var subMeshIndex = 0; subMeshIndex < _mesh.subMeshCount; ++subMeshIndex)
    for (var i = 0; i < indicesPerSubMesh[subMeshIndex].Length; ++i)
    writer.Write(indicesPerSubMesh[subMeshIndex][i]);
    }
    writer.Write(indicesPerSubMesh.Count);
    totalIndices = 0;
    for (int subMeshIndex = 0; subMeshIndex < indicesPerSubMesh.Count; ++subMeshIndex)
    for (var subMeshIndex = 0; subMeshIndex < indicesPerSubMesh.Count; ++subMeshIndex)
    {
    var numberOfBoneMappingEntries = 0;
    writer.Write(numberOfBoneMappingEntries);
    var numberOfLODLevels = 1;
    writer.Write(numberOfLODLevels);
    writer.Write(0.0f);
    writer.Write((int)PrimitiveType.TRIANGLE_LIST);
    writer.Write((int) PrimitiveType.TRIANGLE_LIST);
    writer.Write(0);
    writer.Write(0);
    writer.Write(totalIndices);
    @@ -342,7 +351,7 @@ private void WriteMesh(BinaryWriter writer, Mesh _mesh)
    float maxX, maxY, maxZ;
    maxX = maxY = maxZ = float.MinValue;
    minX = minY = minZ = float.MaxValue;
    for (int i = 0; i < positions.Length; ++i)
    for (var i = 0; i < positions.Length; ++i)
    {
    if (minX > positions[i].x)
    minX = positions[i].x;
    @@ -365,110 +374,39 @@ private void WriteMesh(BinaryWriter writer, Mesh _mesh)
    writer.Write(maxZ);
    }
    }
    public enum PrimitiveType
    {
    TRIANGLE_LIST = 0,
    LINE_LIST,
    POINT_LIST,
    TRIANGLE_STRIP,
    LINE_STRIP,
    TRIANGLE_FAN
    }
    public enum VertexElementType
    {
    TYPE_INT = 0,
    TYPE_FLOAT,
    TYPE_VECTOR2,
    TYPE_VECTOR3,
    TYPE_VECTOR4,
    TYPE_UBYTE4,
    TYPE_UBYTE4_NORM,
    MAX_VERTEX_ELEMENT_TYPES
    }
    public enum VertexElementSemantic
    {
    SEM_POSITION = 0,
    SEM_NORMAL,
    SEM_BINORMAL,
    SEM_TANGENT,
    SEM_TEXCOORD,
    SEM_COLOR,
    SEM_BLENDWEIGHTS,
    SEM_BLENDINDICES,
    SEM_OBJECTINDEX,
    MAX_VERTEX_ELEMENT_SEMANTICS
    }
    internal abstract class MeshStreamWriter
    {
    public int Element;
    public abstract void Write(BinaryWriter writer, int index);
    }
    internal class MeshVector3Stream: MeshStreamWriter
    {
    private Vector3[] positions;

    public MeshVector3Stream(Vector3[] positions, VertexElementSemantic sem, int index = 0)
    {
    this.positions = positions;
    Element = (int)VertexElementType.TYPE_VECTOR3 | ((int)sem << 8) | (index <<16);
    }

    public override void Write(BinaryWriter writer, int index)
    {
    writer.Write(positions[index].x);
    writer.Write(positions[index].y);
    writer.Write(positions[index].z);
    }
    }

    internal class MeshVector2Stream : MeshStreamWriter
    {
    private Vector2[] positions;
    public MeshVector2Stream(Vector2[] positions, VertexElementSemantic sem, int index = 0)
    {
    this.positions = positions;
    Element = (int)VertexElementType.TYPE_VECTOR2 | ((int)sem << 8) | (index << 16);
    }

    public override void Write(BinaryWriter writer, int index)
    {
    writer.Write(positions[index].x);
    writer.Write(positions[index].y);
    }
    }
    private void CreateMaterial(string materialFileName, Material material)
    {
    Directory.CreateDirectory(Path.GetDirectoryName(materialFileName));
    using (var writer = XmlTextWriter.Create(materialFileName))
    using (var writer = XmlWriter.Create(materialFileName))
    {
    writer.WriteStartDocument();
    writer.WriteStartElement("material");
    writer.WriteStartElement("technique");
    writer.WriteAttributeString("name", "Techniques/Diff.xml");
    writer.WriteAttributeString("quality", "0");
    writer.WriteEndElement();

    Texture diffuse = null;
    Texture specular = null;
    Texture normal = null;
    var shader = material.shader;
    for (int i = 0; i < ShaderUtil.GetPropertyCount(shader); i++)
    for (var i = 0; i < ShaderUtil.GetPropertyCount(shader); i++)
    {
    if (ShaderUtil.GetPropertyType(shader, i) == ShaderUtil.ShaderPropertyType.TexEnv)
    {
    var propertyName = ShaderUtil.GetPropertyName(shader, i);
    Texture texture = material.GetTexture(propertyName);
    var texture = material.GetTexture(propertyName);
    if (texture != null)
    {
    switch (propertyName)
    {
    case "_MainTex":
    WriteTexture(texture, writer, "diffuse");
    WriteTexture(diffuse = texture, writer, "diffuse");
    break;
    case "_SpecGlossMap":
    WriteTexture(texture, writer, "specular");
    WriteTexture(specular = texture, writer, "specular");
    break;
    case "_ParallaxMap":
    break;
    case "_BumpMap":
    WriteTexture(texture, writer, "normal");
    WriteTexture(normal = texture, writer, "normal");
    break;
    case "_DetailAlbedoMap":
    break;
    @@ -489,6 +427,24 @@ private void CreateMaterial(string materialFileName, Material material)
    }
    }
    }
    writer.WriteStartElement("technique");
    if (diffuse == null)
    {
    if (normal != null)
    writer.WriteAttributeString("name", "Techniques/NoTextureNormal.xml");
    else
    writer.WriteAttributeString("name", "Techniques/NoTexture.xml");
    }
    else
    {
    if (normal != null)
    writer.WriteAttributeString("name", "Techniques/DiffNormal.xml");
    else
    writer.WriteAttributeString("name", "Techniques/Diff.xml");
    }
    writer.WriteAttributeString("quality", "0");
    writer.WriteEndElement();

    writer.WriteEndElement();
    writer.WriteEndDocument();
    }
    @@ -503,20 +459,19 @@ private string WriteTexture(Texture texture, XmlWriter writer, string name)

    var relPath = GetRelAssetPath(texture);

    int extIndex = relPath.LastIndexOf('.')+1;
    var extIndex = relPath.LastIndexOf('.') + 1;

    bool needFormatConvertion = false;
    var needFormatConvertion = false;
    if (extIndex > 0)
    {
    string ext = relPath.Substring(extIndex).ToLower();
    var ext = relPath.Substring(extIndex).ToLower();
    if (sourceTexture2D != null)
    {
    if (ext == "psd" || sourceTexture2D.format == TextureFormat.DXT5 || sourceTexture2D.format == TextureFormat.DXT1)
    if (ext == "psd" || sourceTexture2D.format == TextureFormat.DXT5 ||
    sourceTexture2D.format == TextureFormat.DXT1)
    {
    needFormatConvertion = true;
    relPath = relPath.Substring(0, extIndex) + "dds";
    needFormatConvertion = true;
    relPath = relPath.Substring(0, extIndex) + "dds";
    }
    }
    }

    var dataPath = Application.dataPath;
    @@ -527,7 +482,7 @@ private string WriteTexture(Texture texture, XmlWriter writer, string name)
    writer.WriteAttributeString("name", outputPath);
    writer.WriteEndElement();

    string destFileName = Path.Combine(this._assetsFolder, outputPath);
    var destFileName = Path.Combine(_assetsFolder, outputPath);
    if (!File.Exists(destFileName))
    {
    Directory.CreateDirectory(Path.GetDirectoryName(destFileName));
    @@ -536,7 +491,8 @@ private string WriteTexture(Texture texture, XmlWriter writer, string name)
    //var texture2d = new Texture2D(sourceTexture.width, sourceTexture.height);
    //texture2d.SetPixels32(sourceTexture.GetPixels32(0));

    var texture2d = new Texture2D(sourceTexture2D.width, sourceTexture2D.height, sourceTexture2D.format, sourceTexture2D.mipmapCount > 1);
    var texture2d = new Texture2D(sourceTexture2D.width, sourceTexture2D.height, sourceTexture2D.format,
    sourceTexture2D.mipmapCount > 1);
    texture2d.LoadRawTextureData(sourceTexture2D.GetRawTextureData());

    texture2d.Apply();
    @@ -548,31 +504,31 @@ private string WriteTexture(Texture texture, XmlWriter writer, string name)
    var br = new BinaryWriter(ms);
    var a = new byte[]
    {
    0x44, 0x44, 0x53, 0x20, 0x7C, 0x00, 0x00, 0x00, 0x07, 0x10, 0x0A, 0x00,
    0x44, 0x44, 0x53, 0x20, 0x7C, 0x00, 0x00, 0x00, 0x07, 0x10, 0x0A, 0x00
    };
    br.Write(a);
    br.Write(sourceTexture2D.width);
    br.Write(sourceTexture2D.height);
    a = new byte[]
    {
    0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,

    0x04, 0x00, 0x00, 0x00,
    0x04, 0x00, 0x00, 0x00
    };
    br.Write(a);
    if (texture2d.format == TextureFormat.DXT5)
    a = new byte[] { 0x44, 0x58, 0x54, 0x35 };
    a = new byte[] {0x44, 0x58, 0x54, 0x35};
    else
    a = new byte[] { 0x44, 0x58, 0x54, 0x31 };
    a = new byte[] {0x44, 0x58, 0x54, 0x31};
    br.Write(a);
    a = new byte[]
    {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x40, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x40, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    };
    br.Write(a);

    @@ -581,7 +537,7 @@ private string WriteTexture(Texture texture, XmlWriter writer, string name)

    //var rawCompressedTexture = texture2d.EncodeToPNG();
    //File.WriteAllBytes(destFileName, rawCompressedTexture);
    UnityEngine.Object.Destroy(texture2d);
    Object.Destroy(texture2d);
    }
    else
    {
    @@ -591,7 +547,7 @@ private string WriteTexture(Texture texture, XmlWriter writer, string name)
    return relPath;
    }

    private string GetRelAssetPath(UnityEngine.Object assetObject)
    private string GetRelAssetPath(Object assetObject)
    {
    var path = AssetDatabase.GetAssetPath(assetObject);
    if (string.IsNullOrEmpty(path))
    @@ -600,6 +556,52 @@ private string GetRelAssetPath(UnityEngine.Object assetObject)
    return relPath;
    }

    private string GetMeshPath(MeshFilter prefabFilter)
    {
    var modelPath = AssetDatabase.GetAssetPath(prefabFilter.sharedMesh);
    if (string.IsNullOrEmpty(modelPath))
    return null;

    GameObject model = AssetDatabase.LoadAssetAtPath<GameObject>(modelPath);

    if (!model)
    {
    Debug.LogWarning("No game object found at " + modelPath, prefabFilter);
    return null;
    }

    MeshFilter modelFilter = null;
    var filters = model.GetComponentsInChildren<MeshFilter>(true);
    foreach (MeshFilter filter in filters)
    {
    if (filter.sharedMesh == prefabFilter.sharedMesh)
    {
    modelFilter = filter;
    break;
    }
    }

    if (!modelFilter)
    {
    Debug.LogWarning("No game object found for " + prefabFilter, prefabFilter);
    return null;
    }

    if (filters.Length == 1)
    return null;

    Transform transform = modelFilter.transform;
    string path = transform.name;

    //while (transform.parent)
    //{
    // transform = transform.parent;
    // path = transform.name + "/" + path;
    //}

    return path;
    }

    private string TrimInstance(string assetObjectName)
    {
    var instance = " Instance";
    @@ -622,23 +624,34 @@ private void WriteAttribute(string prefix, string name, float pos)
    {
    WriteAttribute(prefix, name, string.Format(CultureInfo.InvariantCulture, "{0}", pos));
    }
    private void WriteAttribute(string prefix,string name, Vector3 pos)

    private void WriteAttribute(string prefix, string name, Vector3 pos)
    {
    WriteAttribute(prefix, name, string.Format(CultureInfo.InvariantCulture, "{0} {1} {2}", pos.x, pos.y, pos.z));
    }

    private void WriteAttribute(string prefix, string name, Quaternion pos)
    {
    WriteAttribute(prefix, name, string.Format(CultureInfo.InvariantCulture, "{0} {1} {2} {3}", pos.w, pos.x, pos.y, pos.z));
    WriteAttribute(prefix, name,
    string.Format(CultureInfo.InvariantCulture, "{0} {1} {2} {3}", pos.w, pos.x, pos.y, pos.z));
    }

    private void WriteAttribute(string prefix, string name, Color pos)
    {
    WriteAttribute(prefix, name, string.Format(CultureInfo.InvariantCulture, "{0} {1} {2} {3}", pos.r, pos.g, pos.b, pos.a));
    WriteAttribute(prefix, name,
    string.Format(CultureInfo.InvariantCulture, "{0} {1} {2} {3}", pos.r, pos.g, pos.b, pos.a));
    }

    private void WriteAttribute(string prefix, string name, bool flag)
    {
    WriteAttribute(prefix, name, flag ? "true" : "false");
    }

    private void WriteAttribute(string prefix, string name, int flag)
    {
    WriteAttribute(prefix, name, flag.ToString(CultureInfo.InvariantCulture));
    }

    private void EndElement(string prefix)
    {
    _writer.WriteWhitespace(prefix);
    @@ -653,7 +666,6 @@ private void StartCompoent(string prefix, string type)
    _writer.WriteAttributeString("type", type);
    _writer.WriteAttributeString("id", (++_id).ToString());
    _writer.WriteWhitespace("\n");

    }

    private void WriteAttribute(string prefix, string name, string vaue)
    @@ -665,4 +677,45 @@ private void WriteAttribute(string prefix, string name, string vaue)
    _writer.WriteEndElement();
    _writer.WriteWhitespace("\n");
    }

    internal abstract class MeshStreamWriter
    {
    public int Element;
    public abstract void Write(BinaryWriter writer, int index);
    }

    internal class MeshVector3Stream : MeshStreamWriter
    {
    private readonly Vector3[] positions;

    public MeshVector3Stream(Vector3[] positions, VertexElementSemantic sem, int index = 0)
    {
    this.positions = positions;
    Element = (int) VertexElementType.TYPE_VECTOR3 | ((int) sem << 8) | (index << 16);
    }

    public override void Write(BinaryWriter writer, int index)
    {
    writer.Write(positions[index].x);
    writer.Write(positions[index].y);
    writer.Write(positions[index].z);
    }
    }

    internal class MeshVector2Stream : MeshStreamWriter
    {
    private readonly Vector2[] positions;

    public MeshVector2Stream(Vector2[] positions, VertexElementSemantic sem, int index = 0)
    {
    this.positions = positions;
    Element = (int) VertexElementType.TYPE_VECTOR2 | ((int) sem << 8) | (index << 16);
    }

    public override void Write(BinaryWriter writer, int index)
    {
    writer.Write(positions[index].x);
    writer.Write(positions[index].y);
    }
    }
    }
  7. gleblebedev revised this gist Jun 21, 2017. 1 changed file with 88 additions and 5 deletions.
    93 changes: 88 additions & 5 deletions UrhoExporter.cs
    Original file line number Diff line number Diff line change
    @@ -474,6 +474,14 @@ private void CreateMaterial(string materialFileName, Material material)
    break;
    case "_DetailNormalMap":
    break;
    case "_EmissionMap":
    break;
    case "_MetallicGlossMap":
    break;
    case "_OcclusionMap":
    break;
    case "_DetailMask":
    break;
    default:
    Debug.LogWarning(propertyName);
    break;
    @@ -486,12 +494,31 @@ private void CreateMaterial(string materialFileName, Material material)
    }
    }

    private void WriteTexture(Texture materialMainTexture, XmlWriter writer, string name)
    private string WriteTexture(Texture texture, XmlWriter writer, string name)
    {
    if (materialMainTexture == null)
    return;
    if (texture == null)
    return null;

    var sourceTexture2D = texture as Texture2D;

    var relPath = GetRelAssetPath(texture);

    int extIndex = relPath.LastIndexOf('.')+1;

    bool needFormatConvertion = false;
    if (extIndex > 0)
    {
    string ext = relPath.Substring(extIndex).ToLower();
    if (sourceTexture2D != null)
    {
    if (ext == "psd" || sourceTexture2D.format == TextureFormat.DXT5 || sourceTexture2D.format == TextureFormat.DXT1)
    {
    needFormatConvertion = true;
    relPath = relPath.Substring(0, extIndex) + "dds";
    }
    }
    }

    var relPath = GetRelAssetPath(materialMainTexture);
    var dataPath = Application.dataPath;
    var outputPath = "Textures/" + relPath;

    @@ -504,8 +531,64 @@ private void WriteTexture(Texture materialMainTexture, XmlWriter writer, string
    if (!File.Exists(destFileName))
    {
    Directory.CreateDirectory(Path.GetDirectoryName(destFileName));
    File.Copy(Path.Combine(dataPath,relPath), destFileName);
    if (needFormatConvertion)
    {
    //var texture2d = new Texture2D(sourceTexture.width, sourceTexture.height);
    //texture2d.SetPixels32(sourceTexture.GetPixels32(0));

    var texture2d = new Texture2D(sourceTexture2D.width, sourceTexture2D.height, sourceTexture2D.format, sourceTexture2D.mipmapCount > 1);
    texture2d.LoadRawTextureData(sourceTexture2D.GetRawTextureData());

    texture2d.Apply();

    texture2d.Compress(true);
    var rawCompressedTexture = texture2d.GetRawTextureData();

    var ms = new MemoryStream();
    var br = new BinaryWriter(ms);
    var a = new byte[]
    {
    0x44, 0x44, 0x53, 0x20, 0x7C, 0x00, 0x00, 0x00, 0x07, 0x10, 0x0A, 0x00,
    };
    br.Write(a);
    br.Write(sourceTexture2D.width);
    br.Write(sourceTexture2D.height);
    a = new byte[]
    {
    0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,

    0x04, 0x00, 0x00, 0x00,
    };
    br.Write(a);
    if (texture2d.format == TextureFormat.DXT5)
    a = new byte[] { 0x44, 0x58, 0x54, 0x35 };
    else
    a = new byte[] { 0x44, 0x58, 0x54, 0x31 };
    br.Write(a);
    a = new byte[]
    {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x10, 0x40, 0x00,
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    };
    br.Write(a);

    br.Write(rawCompressedTexture);
    File.WriteAllBytes(destFileName, ms.ToArray());

    //var rawCompressedTexture = texture2d.EncodeToPNG();
    //File.WriteAllBytes(destFileName, rawCompressedTexture);
    UnityEngine.Object.Destroy(texture2d);
    }
    else
    {
    File.Copy(Path.Combine(dataPath, relPath), destFileName);
    }
    }
    return relPath;
    }

    private string GetRelAssetPath(UnityEngine.Object assetObject)
  8. gleblebedev revised this gist Jun 21, 2017. 1 changed file with 24 additions and 6 deletions.
    30 changes: 24 additions & 6 deletions UrhoExporter.cs
    Original file line number Diff line number Diff line change
    @@ -130,7 +130,7 @@ private void WriteObject(string prefix,GameObject obj)
    EndElement(subPrefix);
    }

    if (light != null)
    if (light != null && light.type != LightType.Area)
    {
    StartCompoent(subPrefix, "Light");
    if (light.type == LightType.Directional)
    @@ -207,11 +207,10 @@ private void WriteObject(string prefix,GameObject obj)
    {
    }

    var transforms = obj.GetComponentsInChildren<Transform>(true);
    for (int i = 0; i < transforms.Length; ++i)
    foreach (Transform childTransform in obj.transform)
    {
    if (transforms[i].gameObject != obj)
    WriteObject(subPrefix, transforms[i].gameObject);
    if (childTransform.parent.gameObject == obj)
    WriteObject(subPrefix, childTransform.gameObject);
    }

    _writer.WriteWhitespace(prefix);
    @@ -513,10 +512,29 @@ private string GetRelAssetPath(UnityEngine.Object assetObject)
    {
    var path = AssetDatabase.GetAssetPath(assetObject);
    if (string.IsNullOrEmpty(path))
    return GetFileName(assetObject.name);
    return GetFileName(TrimInstance(assetObject.name));
    var relPath = path.Substring(path.IndexOf('/') + 1);
    return relPath;
    }

    private string TrimInstance(string assetObjectName)
    {
    var instance = " Instance";
    var instance2 = " (Instance)";
    loop:
    if (assetObjectName.EndsWith(instance))
    {
    assetObjectName = assetObjectName.Substring(0, assetObjectName.Length - instance.Length);
    goto loop;
    }
    if (assetObjectName.EndsWith(instance2))
    {
    assetObjectName = assetObjectName.Substring(0, assetObjectName.Length - instance2.Length);
    goto loop;
    }
    return assetObjectName;
    }

    private void WriteAttribute(string prefix, string name, float pos)
    {
    WriteAttribute(prefix, name, string.Format(CultureInfo.InvariantCulture, "{0}", pos));
  9. gleblebedev revised this gist Jun 21, 2017. 1 changed file with 17 additions and 3 deletions.
    20 changes: 17 additions & 3 deletions UrhoExporter.cs
    Original file line number Diff line number Diff line change
    @@ -137,6 +137,16 @@ private void WriteObject(string prefix,GameObject obj)
    {
    WriteAttribute(subSubPrefix, "Light Type", "Directional");
    }
    else if (light.type == LightType.Spot)
    {
    WriteAttribute(subSubPrefix, "Light Type", "Spot");
    }
    else if (light.type == LightType.Point)
    {
    WriteAttribute(subSubPrefix, "Range", light.range);
    }
    WriteAttribute(subSubPrefix, "Color", light.color);
    WriteAttribute(subSubPrefix, "Brightness Multiplier", light.intensity);
    EndElement(subPrefix);
    }

    @@ -152,7 +162,7 @@ private void WriteObject(string prefix,GameObject obj)
    {
    StartCompoent(subPrefix, "StaticModel");

    var meshRelFileName = GetRelAssetPath(meshFilter.mesh);
    var meshRelFileName = GetRelAssetPath(meshFilter.sharedMesh);
    WriteAttribute(subSubPrefix, "Model", "Model;Models/" + meshRelFileName + ".mdl");

    var meshFileName = Path.Combine(Path.Combine(_assetsFolder, "Models"), meshRelFileName + ".mdl");
    @@ -163,14 +173,14 @@ private void WriteObject(string prefix,GameObject obj)
    {
    using (var writer = new BinaryWriter(fileStream))
    {
    WriteMesh(writer, meshFilter.mesh);
    WriteMesh(writer, meshFilter.sharedMesh);
    }
    }
    }

    StringBuilder material = new StringBuilder();
    material.Append("Material");
    var meshRendererMaterials = meshRenderer.materials;
    var meshRendererMaterials = meshRenderer.sharedMaterials;
    for (int i = 0; i < meshRendererMaterials.Length; ++i)
    {
    var meshRendererMaterial = meshRendererMaterials[i];
    @@ -519,6 +529,10 @@ private void WriteAttribute(string prefix, string name, Quaternion pos)
    {
    WriteAttribute(prefix, name, string.Format(CultureInfo.InvariantCulture, "{0} {1} {2} {3}", pos.w, pos.x, pos.y, pos.z));
    }
    private void WriteAttribute(string prefix, string name, Color pos)
    {
    WriteAttribute(prefix, name, string.Format(CultureInfo.InvariantCulture, "{0} {1} {2} {3}", pos.r, pos.g, pos.b, pos.a));
    }
    private void WriteAttribute(string prefix, string name, bool flag)
    {
    WriteAttribute(prefix, name, flag ? "true" : "false");
  10. gleblebedev revised this gist Jun 21, 2017. 1 changed file with 6 additions and 3 deletions.
    9 changes: 6 additions & 3 deletions UrhoExporter.cs
    Original file line number Diff line number Diff line change
    @@ -158,10 +158,13 @@ private void WriteObject(string prefix,GameObject obj)
    var meshFileName = Path.Combine(Path.Combine(_assetsFolder, "Models"), meshRelFileName + ".mdl");
    if (!File.Exists(meshFileName))
    {

    using (var writer = new BinaryWriter(File.Open(meshFileName, FileMode.Create, FileAccess.Write, FileShare.Read)))
    Directory.CreateDirectory(Path.GetDirectoryName(meshFileName));
    using (var fileStream = File.Open(meshFileName, FileMode.Create, FileAccess.Write, FileShare.Read))
    {
    WriteMesh(writer, meshFilter.mesh);
    using (var writer = new BinaryWriter(fileStream))
    {
    WriteMesh(writer, meshFilter.mesh);
    }
    }
    }

  11. gleblebedev revised this gist Jun 20, 2017. 1 changed file with 28 additions and 5 deletions.
    33 changes: 28 additions & 5 deletions UrhoExporter.cs
    Original file line number Diff line number Diff line change
    @@ -117,7 +117,28 @@ private void WriteObject(string prefix,GameObject obj)
    var meshRenderer = obj.GetComponent<MeshRenderer>();
    var meshCollider = obj.GetComponent<MeshCollider>();
    var terrain = obj.GetComponent<Terrain>();
    var light = obj.GetComponent<Light>();
    var camera = obj.GetComponent<Camera>();

    if (camera != null)
    {
    StartCompoent(subPrefix, "Camera");

    WriteAttribute(subSubPrefix, "Near Clip", camera.nearClipPlane);
    WriteAttribute(subSubPrefix, "Far Clip", camera.farClipPlane);

    EndElement(subPrefix);
    }

    if (light != null)
    {
    StartCompoent(subPrefix, "Light");
    if (light.type == LightType.Directional)
    {
    WriteAttribute(subSubPrefix, "Light Type", "Directional");
    }
    EndElement(subPrefix);
    }

    if (terrain != null)
    {
    @@ -165,9 +186,7 @@ private void WriteObject(string prefix,GameObject obj)
    }
    WriteAttribute(subSubPrefix, "Material", material.ToString());

    _writer.WriteWhitespace(subPrefix);
    _writer.WriteEndElement();
    _writer.WriteWhitespace("\n");
    EndElement(subPrefix);
    }
    }

    @@ -187,6 +206,8 @@ private void WriteObject(string prefix,GameObject obj)
    _writer.WriteWhitespace("\n");
    }



    public const uint Magic2 = 0x32444d55;

    private void WriteMesh(BinaryWriter writer, Mesh _mesh)
    @@ -409,7 +430,6 @@ private void CreateMaterial(string materialFileName, Material material)
    using (var writer = XmlTextWriter.Create(materialFileName))
    {
    writer.WriteStartDocument();
    //<material> <technique name ="Techniques/Diff.xml" quality="0"/> <texture unit="diffuse" name="Textures/_teleport.tga"/></material>
    writer.WriteStartElement("material");
    writer.WriteStartElement("technique");
    writer.WriteAttributeString("name", "Techniques/Diff.xml");
    @@ -484,7 +504,10 @@ private string GetRelAssetPath(UnityEngine.Object assetObject)
    var relPath = path.Substring(path.IndexOf('/') + 1);
    return relPath;
    }

    private void WriteAttribute(string prefix, string name, float pos)
    {
    WriteAttribute(prefix, name, string.Format(CultureInfo.InvariantCulture, "{0}", pos));
    }
    private void WriteAttribute(string prefix,string name, Vector3 pos)
    {
    WriteAttribute(prefix, name, string.Format(CultureInfo.InvariantCulture, "{0} {1} {2}", pos.x, pos.y, pos.z));
  12. gleblebedev created this gist Jun 20, 2017.
    527 changes: 527 additions & 0 deletions UrhoExporter.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,527 @@
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Text;
    using System.Xml;
    using UnityEditor;
    using UnityEditor.SceneManagement;
    using UnityEngine;
    using UnityEngine.SceneManagement;

    public class UrhoExporter:IDisposable
    {
    [MenuItem("Tools/Export Scene To Urho3D")]
    public static void ExportToUrho()
    {
    using (var exporter = new UrhoExporter(EditorSceneManager.GetActiveScene(), EditorUtility.SaveFilePanel(
    "Save scene as Urho XML",
    "",
    "scene.xml",
    "xml")))
    {
    exporter.Export();
    }

    }

    private Scene _scene;
    private int _id;
    string _outputFileName;
    private TextWriter _stream;
    private XmlTextWriter _writer;
    private string _assetsFolder;

    public UrhoExporter(Scene scene, string outputFileName)
    {
    _scene = scene;
    _outputFileName = Path.GetFullPath(outputFileName);
    _assetsFolder = Path.GetDirectoryName(Path.GetDirectoryName(_outputFileName));
    if (string.IsNullOrEmpty(_assetsFolder))
    _assetsFolder = Path.GetDirectoryName(_outputFileName);

    _stream = File.CreateText(_outputFileName);
    _writer = new XmlTextWriter(_stream);
    }

    public void Export()
    {
    if (string.IsNullOrEmpty(_outputFileName))
    return;

    _writer.WriteStartDocument();
    _writer.WriteWhitespace("\n");

    _writer.WriteStartElement("scene");
    _writer.WriteAttributeString("id", (++_id).ToString());
    _writer.WriteWhitespace("\n");


    var prefix = "\t";
    StartCompoent(prefix, "Octree");
    EndElement(prefix);
    StartCompoent(prefix, "DebugRenderer");
    EndElement(prefix);
    StartCompoent(prefix, "PhysicsWorld");
    EndElement(prefix);

    EnumerateObjects(prefix, _scene.GetRootGameObjects());

    _writer.WriteEndElement();
    _writer.WriteEndDocument();
    }

    public void Dispose()
    {
    if (_stream != null)
    {
    _stream.Dispose();
    }
    }


    private void EnumerateObjects(string prefix, GameObject[] objects)
    {
    foreach (var obj in objects)
    {
    WriteObject(prefix, obj);
    }
    }

    private string GetFileName(string name)
    {
    foreach (var invalidFileNameChar in Path.GetInvalidFileNameChars())
    {
    name = name.Replace(invalidFileNameChar, '_');
    }
    return name;
    }
    private void WriteObject(string prefix,GameObject obj)
    {
    _writer.WriteWhitespace(prefix);
    _writer.WriteStartElement("node");
    _writer.WriteAttributeString("id", (++_id).ToString());
    _writer.WriteWhitespace("\n");

    var subPrefix = prefix + "\t";
    var subSubPrefix = subPrefix + "\t";

    WriteAttribute(subPrefix, "Is Enabled", obj.activeSelf);
    WriteAttribute(subPrefix, "Name", obj.name);
    WriteAttribute(subPrefix, "Tags", obj.tag);
    WriteAttribute(subPrefix, "Position", obj.transform.localPosition);
    WriteAttribute(subPrefix, "Rotation", obj.transform.localRotation);
    WriteAttribute(subPrefix, "Scale", obj.transform.localScale);

    var meshFilter = obj.GetComponent<MeshFilter>();
    var meshRenderer = obj.GetComponent<MeshRenderer>();
    var meshCollider = obj.GetComponent<MeshCollider>();
    var terrain = obj.GetComponent<Terrain>();


    if (terrain != null)
    {
    var terrainSize = terrain.terrainData.size;
    var y = terrain.SampleHeight(new Vector3(0, 0, 0));
    }

    if (meshRenderer != null)
    {
    if (meshFilter != null)
    {
    StartCompoent(subPrefix, "StaticModel");

    var meshRelFileName = GetRelAssetPath(meshFilter.mesh);
    WriteAttribute(subSubPrefix, "Model", "Model;Models/" + meshRelFileName + ".mdl");

    var meshFileName = Path.Combine(Path.Combine(_assetsFolder, "Models"), meshRelFileName + ".mdl");
    if (!File.Exists(meshFileName))
    {

    using (var writer = new BinaryWriter(File.Open(meshFileName, FileMode.Create, FileAccess.Write, FileShare.Read)))
    {
    WriteMesh(writer, meshFilter.mesh);
    }
    }

    StringBuilder material = new StringBuilder();
    material.Append("Material");
    var meshRendererMaterials = meshRenderer.materials;
    for (int i = 0; i < meshRendererMaterials.Length; ++i)
    {
    var meshRendererMaterial = meshRendererMaterials[i];
    var relPath = GetRelAssetPath(meshRendererMaterial);

    var outputMaterialName = "Materials/" + relPath+".xml";

    material.Append(";");
    material.Append(outputMaterialName);

    var materialFileName = Path.Combine(_assetsFolder, outputMaterialName);
    if (!File.Exists(materialFileName))
    {
    CreateMaterial(materialFileName, meshRendererMaterial);
    }
    }
    WriteAttribute(subSubPrefix, "Material", material.ToString());

    _writer.WriteWhitespace(subPrefix);
    _writer.WriteEndElement();
    _writer.WriteWhitespace("\n");
    }
    }

    if (meshCollider != null)
    {
    }

    var transforms = obj.GetComponentsInChildren<Transform>(true);
    for (int i = 0; i < transforms.Length; ++i)
    {
    if (transforms[i].gameObject != obj)
    WriteObject(subPrefix, transforms[i].gameObject);
    }

    _writer.WriteWhitespace(prefix);
    _writer.WriteEndElement();
    _writer.WriteWhitespace("\n");
    }

    public const uint Magic2 = 0x32444d55;

    private void WriteMesh(BinaryWriter writer, Mesh _mesh)
    {
    writer.Write(Magic2);
    writer.Write(1);
    for (int vbIndex = 0; vbIndex < 1 /*_mesh.vertexBufferCount*/; ++vbIndex)
    {
    var positions = _mesh.vertices;
    var normals = _mesh.normals;
    var colors = _mesh.colors;
    var tangents = _mesh.tangents;
    var uvs = _mesh.uv;
    var uvs2 = _mesh.uv2;
    var uvs3 = _mesh.uv3;
    var uvs4 = _mesh.uv4;

    writer.Write(positions.Length);
    var elements = new List<MeshStreamWriter>();
    if (positions.Length > 0)
    {
    elements.Add(new MeshVector3Stream(positions, VertexElementSemantic.SEM_POSITION));
    }
    if (normals.Length > 0)
    {
    elements.Add(new MeshVector3Stream(normals, VertexElementSemantic.SEM_NORMAL));
    }
    //if (colors.Length > 0)
    //{
    // elements.Add(new MeshColorStream(colors, VertexElementSemantic.SEM_COLOR));
    //}
    //if (tangents.Length > 0)
    //{
    // elements.Add(new MeshVector4Stream(tangents, VertexElementSemantic.SEM_TANGENT));
    //}
    if (uvs.Length > 0)
    {
    elements.Add(new MeshVector2Stream(uvs, VertexElementSemantic.SEM_TEXCOORD, 0));
    }
    if (uvs2.Length > 0)
    {
    elements.Add(new MeshVector2Stream(uvs2, VertexElementSemantic.SEM_TEXCOORD, 1));
    }
    if (uvs3.Length > 0)
    {
    elements.Add(new MeshVector2Stream(uvs2, VertexElementSemantic.SEM_TEXCOORD, 2));
    }
    if (uvs4.Length > 0)
    {
    elements.Add(new MeshVector2Stream(uvs2, VertexElementSemantic.SEM_TEXCOORD, 3));
    }
    writer.Write(elements.Count);
    for (int i = 0; i < elements.Count; ++i)
    {
    writer.Write(elements[i].Element);
    }
    int morphableVertexRangeStartIndex = 0;
    int morphableVertexCount = 0;
    writer.Write(morphableVertexRangeStartIndex);
    writer.Write(morphableVertexCount);
    for (int index = 0; index < positions.Length; ++index)
    {
    for (int i = 0; i < elements.Count; ++i)
    {
    elements[i].Write(writer, index);
    }
    }
    var indicesPerSubMesh = new List<int[]>();
    int totalIndices = 0;
    for (int subMeshIndex = 0; subMeshIndex < _mesh.subMeshCount; ++subMeshIndex)
    {
    var indices = _mesh.GetIndices(subMeshIndex);
    indicesPerSubMesh.Add(indices);
    totalIndices += indices.Length;
    }
    writer.Write(1);
    writer.Write(totalIndices);
    if (positions.Length < 65536)
    {
    writer.Write(2);
    for (int subMeshIndex = 0; subMeshIndex < _mesh.subMeshCount; ++subMeshIndex)
    {
    for (int i = 0; i < indicesPerSubMesh[subMeshIndex].Length; ++i)
    {
    writer.Write((ushort)indicesPerSubMesh[subMeshIndex][i]);
    }
    }
    }
    else
    {
    writer.Write(4);
    for (int subMeshIndex = 0; subMeshIndex < _mesh.subMeshCount; ++subMeshIndex)
    {
    for (int i = 0; i < indicesPerSubMesh[subMeshIndex].Length; ++i)
    {
    writer.Write(indicesPerSubMesh[subMeshIndex][i]);
    }
    }
    }
    writer.Write(indicesPerSubMesh.Count);
    totalIndices = 0;
    for (int subMeshIndex = 0; subMeshIndex < indicesPerSubMesh.Count; ++subMeshIndex)
    {
    var numberOfBoneMappingEntries = 0;
    writer.Write(numberOfBoneMappingEntries);
    var numberOfLODLevels = 1;
    writer.Write(numberOfLODLevels);
    writer.Write(0.0f);
    writer.Write((int)PrimitiveType.TRIANGLE_LIST);
    writer.Write(0);
    writer.Write(0);
    writer.Write(totalIndices);
    writer.Write(indicesPerSubMesh[subMeshIndex].Length);
    totalIndices += indicesPerSubMesh[subMeshIndex].Length;
    writer.Write(0);
    var numOfBones = 0;
    writer.Write(numOfBones);
    }
    float minX, minY, minZ;
    float maxX, maxY, maxZ;
    maxX = maxY = maxZ = float.MinValue;
    minX = minY = minZ = float.MaxValue;
    for (int i = 0; i < positions.Length; ++i)
    {
    if (minX > positions[i].x)
    minX = positions[i].x;
    if (minY > positions[i].y)
    minY = positions[i].y;
    if (minZ > positions[i].z)
    minZ = positions[i].z;
    if (maxX < positions[i].x)
    maxX = positions[i].x;
    if (maxY < positions[i].y)
    maxY = positions[i].y;
    if (maxZ < positions[i].z)
    maxZ = positions[i].z;
    }
    writer.Write(minX);
    writer.Write(minY);
    writer.Write(minZ);
    writer.Write(maxX);
    writer.Write(maxY);
    writer.Write(maxZ);
    }
    }
    public enum PrimitiveType
    {
    TRIANGLE_LIST = 0,
    LINE_LIST,
    POINT_LIST,
    TRIANGLE_STRIP,
    LINE_STRIP,
    TRIANGLE_FAN
    }
    public enum VertexElementType
    {
    TYPE_INT = 0,
    TYPE_FLOAT,
    TYPE_VECTOR2,
    TYPE_VECTOR3,
    TYPE_VECTOR4,
    TYPE_UBYTE4,
    TYPE_UBYTE4_NORM,
    MAX_VERTEX_ELEMENT_TYPES
    }
    public enum VertexElementSemantic
    {
    SEM_POSITION = 0,
    SEM_NORMAL,
    SEM_BINORMAL,
    SEM_TANGENT,
    SEM_TEXCOORD,
    SEM_COLOR,
    SEM_BLENDWEIGHTS,
    SEM_BLENDINDICES,
    SEM_OBJECTINDEX,
    MAX_VERTEX_ELEMENT_SEMANTICS
    }
    internal abstract class MeshStreamWriter
    {
    public int Element;
    public abstract void Write(BinaryWriter writer, int index);
    }
    internal class MeshVector3Stream: MeshStreamWriter
    {
    private Vector3[] positions;

    public MeshVector3Stream(Vector3[] positions, VertexElementSemantic sem, int index = 0)
    {
    this.positions = positions;
    Element = (int)VertexElementType.TYPE_VECTOR3 | ((int)sem << 8) | (index <<16);
    }

    public override void Write(BinaryWriter writer, int index)
    {
    writer.Write(positions[index].x);
    writer.Write(positions[index].y);
    writer.Write(positions[index].z);
    }
    }

    internal class MeshVector2Stream : MeshStreamWriter
    {
    private Vector2[] positions;
    public MeshVector2Stream(Vector2[] positions, VertexElementSemantic sem, int index = 0)
    {
    this.positions = positions;
    Element = (int)VertexElementType.TYPE_VECTOR2 | ((int)sem << 8) | (index << 16);
    }

    public override void Write(BinaryWriter writer, int index)
    {
    writer.Write(positions[index].x);
    writer.Write(positions[index].y);
    }
    }
    private void CreateMaterial(string materialFileName, Material material)
    {
    Directory.CreateDirectory(Path.GetDirectoryName(materialFileName));
    using (var writer = XmlTextWriter.Create(materialFileName))
    {
    writer.WriteStartDocument();
    //<material> <technique name ="Techniques/Diff.xml" quality="0"/> <texture unit="diffuse" name="Textures/_teleport.tga"/></material>
    writer.WriteStartElement("material");
    writer.WriteStartElement("technique");
    writer.WriteAttributeString("name", "Techniques/Diff.xml");
    writer.WriteAttributeString("quality", "0");
    writer.WriteEndElement();

    var shader = material.shader;
    for (int i = 0; i < ShaderUtil.GetPropertyCount(shader); i++)
    {
    if (ShaderUtil.GetPropertyType(shader, i) == ShaderUtil.ShaderPropertyType.TexEnv)
    {
    var propertyName = ShaderUtil.GetPropertyName(shader, i);
    Texture texture = material.GetTexture(propertyName);
    if (texture != null)
    {
    switch (propertyName)
    {
    case "_MainTex":
    WriteTexture(texture, writer, "diffuse");
    break;
    case "_SpecGlossMap":
    WriteTexture(texture, writer, "specular");
    break;
    case "_ParallaxMap":
    break;
    case "_BumpMap":
    WriteTexture(texture, writer, "normal");
    break;
    case "_DetailAlbedoMap":
    break;
    case "_DetailNormalMap":
    break;
    default:
    Debug.LogWarning(propertyName);
    break;
    }
    }
    }
    }
    writer.WriteEndElement();
    writer.WriteEndDocument();
    }
    }

    private void WriteTexture(Texture materialMainTexture, XmlWriter writer, string name)
    {
    if (materialMainTexture == null)
    return;

    var relPath = GetRelAssetPath(materialMainTexture);
    var dataPath = Application.dataPath;
    var outputPath = "Textures/" + relPath;

    writer.WriteStartElement("texture");
    writer.WriteAttributeString("unit", name);
    writer.WriteAttributeString("name", outputPath);
    writer.WriteEndElement();

    string destFileName = Path.Combine(this._assetsFolder, outputPath);
    if (!File.Exists(destFileName))
    {
    Directory.CreateDirectory(Path.GetDirectoryName(destFileName));
    File.Copy(Path.Combine(dataPath,relPath), destFileName);
    }
    }

    private string GetRelAssetPath(UnityEngine.Object assetObject)
    {
    var path = AssetDatabase.GetAssetPath(assetObject);
    if (string.IsNullOrEmpty(path))
    return GetFileName(assetObject.name);
    var relPath = path.Substring(path.IndexOf('/') + 1);
    return relPath;
    }

    private void WriteAttribute(string prefix,string name, Vector3 pos)
    {
    WriteAttribute(prefix, name, string.Format(CultureInfo.InvariantCulture, "{0} {1} {2}", pos.x, pos.y, pos.z));
    }
    private void WriteAttribute(string prefix, string name, Quaternion pos)
    {
    WriteAttribute(prefix, name, string.Format(CultureInfo.InvariantCulture, "{0} {1} {2} {3}", pos.w, pos.x, pos.y, pos.z));
    }
    private void WriteAttribute(string prefix, string name, bool flag)
    {
    WriteAttribute(prefix, name, flag ? "true" : "false");
    }

    private void EndElement(string prefix)
    {
    _writer.WriteWhitespace(prefix);
    _writer.WriteEndElement();
    _writer.WriteWhitespace("\n");
    }

    private void StartCompoent(string prefix, string type)
    {
    _writer.WriteWhitespace(prefix);
    _writer.WriteStartElement("component");
    _writer.WriteAttributeString("type", type);
    _writer.WriteAttributeString("id", (++_id).ToString());
    _writer.WriteWhitespace("\n");

    }

    private void WriteAttribute(string prefix, string name, string vaue)
    {
    _writer.WriteWhitespace(prefix);
    _writer.WriteStartElement("attribute");
    _writer.WriteAttributeString("name", name);
    _writer.WriteAttributeString("value", vaue);
    _writer.WriteEndElement();
    _writer.WriteWhitespace("\n");
    }
    }