首页 > 其他分享 >Unity导出场景并导入Godot

Unity导出场景并导入Godot

时间:2024-05-03 20:56:34浏览次数:24  
标签:Godot string private ExportSceneToObj Unity 导入 static autoCut path

使用FBX Exporter导出场景FBX
创建ExportScene.cs导出场景OBJ(目的是创建碰撞体),代码:

点击查看代码
/******************************************/
/*                                        */
/*     Copyright (c) 2018 monitor1394     */
/*     https://github.com/monitor1394     */
/*                                        */
/******************************************/

using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;

public class ExportScene : EditorWindow
{
    private const string CUT_LB_OBJ_PATH = "export/bound_lb";
    private const string CUT_RT_OBJ_PATH = "export/bound_rt";

    private static float autoCutMinX = 1000;
    private static float autoCutMaxX = 0;
    private static float autoCutMinY = 1000;
    private static float autoCutMaxY = 0;

    private static float cutMinX = 0;
    private static float cutMaxX = 0;
    private static float cutMinY = 0;
    private static float cutMaxY = 0;

    private static long startTime = 0;
    private static int totalCount = 0;
    private static int count = 0;
    private static int counter = 0;
    private static int progressUpdateInterval = 10000;

    [MenuItem("ExportScene/ExportSceneToObj")]
    [MenuItem("GameObject/ExportScene/ExportSceneToObj")]
    public static void Export()
    {
        ExportSceneToObj(false);
    }

    [MenuItem("ExportScene/ExportSceneToObj(AutoCut)")]
    [MenuItem("GameObject/ExportScene/ExportSceneToObj(AutoCut)")]
    public static void ExportAutoCut()
    {
        ExportSceneToObj(true);
    }

    [MenuItem("ExportScene/ExportSelectedObj")]
    [MenuItem("GameObject/ExportScene/ExportSelectedObj", priority = 44)]
    public static void ExportObj()
    {
        GameObject selectObj = Selection.activeGameObject;
        if (selectObj == null)
        {
            Debug.LogWarning("Select a GameObject");
            return;
        }
        string path = GetSavePath(false, selectObj);
        if (string.IsNullOrEmpty(path)) return;

        Terrain terrain = selectObj.GetComponent<Terrain>();
        MeshFilter[] mfs = selectObj.GetComponentsInChildren<MeshFilter>();
        SkinnedMeshRenderer[] smrs = selectObj.GetComponentsInChildren<SkinnedMeshRenderer>();
        Debug.Log(mfs.Length + "," + smrs.Length);
        ExportSceneToObj(path, terrain, mfs, smrs, false, false);
    }

    public static void ExportSceneToObj(bool autoCut)
    {
        string path = GetSavePath(autoCut, null);
        if (string.IsNullOrEmpty(path)) return;
        Terrain terrain = UnityEngine.Object.FindObjectOfType<Terrain>();
        MeshFilter[] mfs = UnityEngine.Object.FindObjectsOfType<MeshFilter>();
        SkinnedMeshRenderer[] smrs = UnityEngine.Object.FindObjectsOfType<SkinnedMeshRenderer>();
        ExportSceneToObj(path, terrain, mfs, smrs, autoCut, true);
    }

    public static void ExportSceneToObj(string path, Terrain terrain, MeshFilter[] mfs,
        SkinnedMeshRenderer[] smrs, bool autoCut, bool needCheckRect)
    {
        int vertexOffset = 0;
        string title = "export GameObject to .obj ...";
        StreamWriter writer = new StreamWriter(path);

        startTime = GetMsTime();
        UpdateCutRect(autoCut);
        counter = count = 0;
        progressUpdateInterval = 5;
        totalCount = (mfs.Length + smrs.Length) / progressUpdateInterval;
        foreach (var mf in mfs)
        {
            UpdateProgress(title);
            if (mf.GetComponent<Renderer>() != null &&
                (!needCheckRect || (needCheckRect && IsInCutRect(mf.gameObject))))
            {
                ExportMeshToObj(mf.gameObject, mf.sharedMesh, ref writer, ref vertexOffset);
            }
        }
        foreach (var smr in smrs)
        {
            UpdateProgress(title);
            if (!needCheckRect || (needCheckRect && IsInCutRect(smr.gameObject)))
            {
                ExportMeshToObj(smr.gameObject, smr.sharedMesh, ref writer, ref vertexOffset);
            }
        }
        if (terrain)
        {
            ExportTerrianToObj(terrain.terrainData, terrain.GetPosition(),
                ref writer, ref vertexOffset, autoCut);
        }
        writer.Close();
        EditorUtility.ClearProgressBar();

        long endTime = GetMsTime();
        float time = (float)(endTime - startTime) / 1000;
        Debug.Log("Export SUCCESS:" + path);
        Debug.Log("Export Time:" + time + "s");
        OpenDir(path);
    }

    private static void OpenDir(string path)
    {
        DirectoryInfo dir = Directory.GetParent(path);
        int index = path.LastIndexOf("/");
        OpenCmd("explorer.exe", dir.FullName);
    }

    private static void OpenCmd(string cmd, string args)
    {
        System.Diagnostics.Process.Start(cmd, args);
    }

    private static string GetSavePath(bool autoCut, GameObject selectObject)
    {
        string dataPath = Application.dataPath;
        string dir = dataPath.Substring(0, dataPath.LastIndexOf("/"));
        string sceneName = SceneManager.GetActiveScene().name;
        string defaultName = "";
        if (selectObject == null)
        {
            defaultName = (autoCut ? sceneName + "(autoCut)" : sceneName);
        }
        else
        {
            defaultName = (autoCut ? selectObject.name + "(autoCut)" : selectObject.name);
        }
        return EditorUtility.SaveFilePanel("Export .obj file", dir, defaultName, "obj");
    }

    private static long GetMsTime()
    {
        return System.DateTime.Now.Ticks / 10000;
        //return (System.DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000;
    }

    private static void UpdateCutRect(bool autoCut)
    {
        cutMinX = cutMaxX = cutMinY = cutMaxY = 0;
        if (!autoCut)
        {
            Vector3 lbPos = GetObjPos(CUT_LB_OBJ_PATH);
            Vector3 rtPos = GetObjPos(CUT_RT_OBJ_PATH);
            cutMinX = lbPos.x;
            cutMaxX = rtPos.x;
            cutMinY = lbPos.z;
            cutMaxY = rtPos.z;
        }
    }

    private static void UpdateAutoCutRect(Vector3 v)
    {
        if (v.x < autoCutMinX) autoCutMinX = v.x;
        if (v.x > autoCutMaxX) autoCutMaxX = v.x;
        if (v.z < autoCutMinY) autoCutMinY = v.z;
        if (v.z > autoCutMaxY) autoCutMaxY = v.z;
    }

    private static bool IsInCutRect(GameObject obj)
    {
        if (cutMinX == 0 && cutMaxX == 0 && cutMinY == 0 && cutMaxY == 0) return true;
        Vector3 pos = obj.transform.position;
        if (pos.x >= cutMinX && pos.x <= cutMaxX && pos.z >= cutMinY && pos.z <= cutMaxY)
            return true;
        else
            return false;
    }

    private static void ExportMeshToObj(GameObject obj, Mesh mesh, ref StreamWriter writer, ref int vertexOffset)
    {
        Quaternion r = obj.transform.localRotation;
        StringBuilder sb = new StringBuilder();
        foreach (Vector3 vertice in mesh.vertices)
        {
            Vector3 v = obj.transform.TransformPoint(vertice);
            UpdateAutoCutRect(v);
            sb.AppendFormat("v {0} {1} {2}\n", -v.x, v.y, v.z);
        }
        foreach (Vector3 nn in mesh.normals)
        {
            Vector3 v = r * nn;
            sb.AppendFormat("vn {0} {1} {2}\n", -v.x, -v.y, v.z);
        }
        foreach (Vector3 v in mesh.uv)
        {
            sb.AppendFormat("vt {0} {1}\n", v.x, v.y);
        }
        for (int i = 0; i < mesh.subMeshCount; i++)
        {
            int[] triangles = mesh.GetTriangles(i);
            for (int j = 0; j < triangles.Length; j += 3)
            {
                sb.AppendFormat("f {1} {0} {2}\n",
                    triangles[j] + 1 + vertexOffset,
                    triangles[j + 1] + 1 + vertexOffset,
                    triangles[j + 2] + 1 + vertexOffset);
            }
        }
        vertexOffset += mesh.vertices.Length;
        writer.Write(sb.ToString());
    }

    private static void ExportTerrianToObj(TerrainData terrain, Vector3 terrainPos,
        ref StreamWriter writer, ref int vertexOffset, bool autoCut)
    {
        int tw = terrain.heightmapWidth;
        int th = terrain.heightmapHeight;

        Vector3 meshScale = terrain.size;
        meshScale = new Vector3(meshScale.x / (tw - 1), meshScale.y, meshScale.z / (th - 1));
        Vector2 uvScale = new Vector2(1.0f / (tw - 1), 1.0f / (th - 1));

        Vector2 terrainBoundLB, terrainBoundRT;
        if (autoCut)
        {
            terrainBoundLB = GetTerrainBoundPos(new Vector3(autoCutMinX, 0, autoCutMinY), terrain, terrainPos);
            terrainBoundRT = GetTerrainBoundPos(new Vector3(autoCutMaxX, 0, autoCutMaxY), terrain, terrainPos);
        }
        else
        {
            terrainBoundLB = GetTerrainBoundPos(CUT_LB_OBJ_PATH, terrain, terrainPos);
            terrainBoundRT = GetTerrainBoundPos(CUT_RT_OBJ_PATH, terrain, terrainPos);
        }

        int bw = (int)(terrainBoundRT.x - terrainBoundLB.x);
        int bh = (int)(terrainBoundRT.y - terrainBoundLB.y);

        int w = bh != 0 && bh < th ? bh : th;
        int h = bw != 0 && bw < tw ? bw : tw;

        int startX = (int)terrainBoundLB.y;
        int startY = (int)terrainBoundLB.x;
        if (startX < 0) startX = 0;
        if (startY < 0) startY = 0;

        Debug.Log(string.Format("Terrian:tw={0},th={1},sw={2},sh={3},startX={4},startY={5}",
            tw, th, bw, bh, startX, startY));

        float[,] tData = terrain.GetHeights(0, 0, tw, th);
        Vector3[] tVertices = new Vector3[w * h];
        Vector2[] tUV = new Vector2[w * h];

        int[] tPolys = new int[(w - 1) * (h - 1) * 6];

        for (int y = 0; y < h; y++)
        {
            for (int x = 0; x < w; x++)
            {
                Vector3 pos = new Vector3(-(startY + y), tData[startX + x, startY + y], (startX + x));
                tVertices[y * w + x] = Vector3.Scale(meshScale, pos) + terrainPos;
                tUV[y * w + x] = Vector2.Scale(new Vector2(x, y), uvScale);
            }
        }
        int index = 0;
        for (int y = 0; y < h - 1; y++)
        {
            for (int x = 0; x < w - 1; x++)
            {
                tPolys[index++] = (y * w) + x;
                tPolys[index++] = ((y + 1) * w) + x;
                tPolys[index++] = (y * w) + x + 1;
                tPolys[index++] = ((y + 1) * w) + x;
                tPolys[index++] = ((y + 1) * w) + x + 1;
                tPolys[index++] = (y * w) + x + 1;
            }
        }
        count = counter = 0;
        progressUpdateInterval = 10000;
        totalCount = (tVertices.Length + tUV.Length + tPolys.Length / 3) / progressUpdateInterval;
        string title = "export Terrain to .obj ...";
        for (int i = 0; i < tVertices.Length; i++)
        {
            UpdateProgress(title);
            StringBuilder sb = new StringBuilder(22);
            sb.AppendFormat("v {0} {1} {2}\n", tVertices[i].x, tVertices[i].y, tVertices[i].z);
            writer.Write(sb.ToString());
        }
        for (int i = 0; i < tUV.Length; i++)
        {
            UpdateProgress(title);
            StringBuilder sb = new StringBuilder(20);
            sb.AppendFormat("vt {0} {1}\n", tUV[i].x, tUV[i].y);
            writer.Write(sb.ToString());
        }
        for (int i = 0; i < tPolys.Length; i += 3)
        {
            UpdateProgress(title);
            int x = tPolys[i] + 1 + vertexOffset; ;
            int y = tPolys[i + 1] + 1 + vertexOffset;
            int z = tPolys[i + 2] + 1 + vertexOffset;
            StringBuilder sb = new StringBuilder(30);
            sb.AppendFormat("f {0} {1} {2}\n", x, y, z);
            writer.Write(sb.ToString());
        }
        vertexOffset += tVertices.Length;
    }

    private static Vector2 GetTerrainBoundPos(string path, TerrainData terrain, Vector3 terrainPos)
    {
        var go = GameObject.Find(path);
        if (go)
        {
            Vector3 pos = go.transform.position;
            return GetTerrainBoundPos(pos, terrain, terrainPos);
        }
        return Vector2.zero;
    }

    private static Vector2 GetTerrainBoundPos(Vector3 worldPos, TerrainData terrain, Vector3 terrainPos)
    {
        Vector3 tpos = worldPos - terrainPos;
        return new Vector2((int)(tpos.x / terrain.size.x * terrain.heightmapWidth),
            (int)(tpos.z / terrain.size.z * terrain.heightmapHeight));
    }

    private static Vector3 GetObjPos(string path)
    {
        var go = GameObject.Find(path);
        if (go)
        {
            return go.transform.position;
        }
        return Vector3.zero;
    }

    private static void UpdateProgress(string title)
    {
        if (counter++ == progressUpdateInterval)
        {
            counter = 0;
            float process = Mathf.InverseLerp(0, totalCount, ++count);
            long currTime = GetMsTime();
            float sec = ((float)(currTime - startTime)) / 1000;
            string text = string.Format("{0}/{1}({2:f2} sec.)", count, totalCount, sec);
            EditorUtility.DisplayProgressBar(title, text, process);
        }
    }
}

在Godot中导入场景FBX
创建CSGMesh,将场景OBJ导入,将他们两个重合,隐藏CSGMesh

标签:Godot,string,private,ExportSceneToObj,Unity,导入,static,autoCut,path
From: https://www.cnblogs.com/meny233/p/18171594

相关文章

  • Godot.NET C#IOC重构(9-10):三连击,攻击框
    目录前言AnimationPlayer和AnimatedSprite2D将导出属性添加到关键帧里面。状态机构建核心代码完整代码实现效果碰撞框和受攻击框全局类HitBox:攻击框HurtBox:受击框实现效果添加Player攻击总结前言这篇博客来深入讲解一下Godot中的AnimationPlayerAnimationPlayer和AnimatedSpr......
  • Unity 热更--AssetBundle学习笔记 1.0【AB包资源加载工具类的实现】
    工具类封装通过上文中对AB包加载API的了解和简单使用,对AB包资源加载的几种方法进行封装,将其写入单例类中,如代码展示。确保每个AB资源包只加载一次:在LoadAssetBundleManager单例工具类中,首先提供基本的AB包及其AB包依赖包的加载方法,为保持AssetBundle只加载一次,使用DIctionary......
  • Unity 版本汇总
    直链下载国际版直链格式为:https://download.unity3d.com/download_unity/MD5码/Windows64EditorInstaller/UnitySetup64-版本名.exe替换版本名和MD5码,粘贴到浏览器(或者window+R输入)就会弹出下载框例:已知2021.3.0f1的MD5码为6eacc8284459即直链下载为:https://download.unity3......
  • Unity热更学习笔记--AB包的依赖 0.98
    AB包的依赖接上一小结。在这里我们新建一个红色材质球,赋值给Cube预制体。此时不对材质球进行AB包分类,再次进行打包。运行脚本,发现红色cube成功的从AB包中加载出来。尽管我们没有将cube所依赖的材质球进行打包分类,但是打包时候unity会自动将包中的物体相关依赖打入包中【图三所示......
  • EPAI手绘建模APP导入导出、实体
    4、建模工具栏图 99 建模工具栏-1 图 100 建模工具栏-2(1) 导入导出图 101 导入导出工具栏① 导入brep、iges、step三种cad常用模型文件(autocad、ug、solidwork、freecad等),导入obj、stl、glb、x3d四种非cad建模建模软件常用的三角网格模型文件(3dmax、maya等......
  • Unity 热更--AssetBundle学习笔记 0.7
    AssetBundleAB包是什么?AssetBundle又称AB包,是Unity提供的一种用于存储资源的资源压缩包。Unity中的AssetBundle系统是对资源管理的一种扩展,通过将资源分布在不同的AB包中可以最大程度地减少运行时的内存压力,可以动态地加载和卸载AB包,继而有选择地加载内容。AB包的优势有哪些?......
  • Unity游戏框架设计之背包管理器
    Unity游戏框架设计之背包管理器简单介绍背包系统通常分为两个部分,第一个部分是背包的UI界面,第二个部分是对背包进行逻辑操作的管理器。在下述代码中,实现了对背包的基本逻辑操作,包括向背包添加物品,从背包中取出物品,移动背包中的物品和使用背包中的物品的基本操作,并将这些操作......
  • Unity游戏框架设计之存档管理器
    Unity游戏框架设计之存档管理器存档管理器的主要功能是实现游戏进度的查询、存储(存档)、读取(读档)和删除(删档)。存档主要有三种实现方案。(一)PlayerPrefs。PlayerPrefs类用于在游戏中存储、删除、修改和访问玩家的数据。存储的数据是持久化的,即使玩家关闭游戏或重新启动设备,数据也......
  • Unity2D横版游戏之平台跳跃效果
    Unity2D横版游戏之平台跳跃效果简单介绍平台跳跃效果。玩家允许在平台下方跳跃到平台上方,并且可以在平台上方站立,同时玩家在平台上方允许通过下跳操作到达平台下方。实现步骤(一)将玩家的图片设置为Player、将平台的图层设置为Platform。(二)为平台游戏对象创建XXXCollider2D......
  • Unity2D横板游戏之背景视差与无限滚动效果
    Unity2D横板游戏之背景视差与无限滚动效果简单介绍背景视差效果。在2D横板游戏中,由若干个背景图片构成的背景,在背景移动的过程中,每一个背景图片的移动速度均不同,靠近玩家的背景图片移动速度快,而远离玩家的背景图片移动速度慢,从而形成背景的视差效果,使背景更加立体且富有层级。......