public virtual ExportStatus Export(FScene scene, string filename)
        {
            int[]            vertexMap = new int[2048]; // temp
            List <WriteMesh> vMeshes   = new List <WriteMesh>();

            if (WriteFaceGroups)
            {
                throw new Exception("SceneMeshExporter.Export: writing face groups has not yet been implemented!");
            }

            // extract all the mesh data we want to export
            foreach (SceneObject so in scene.SceneObjects)
            {
                if (so.IsTemporary || so.IsSurface == false || SceneUtil.IsVisible(so) == false)
                {
                    continue;
                }
                if (SOFilterF != null && SOFilterF(so) == false)
                {
                    continue;
                }

                // if this SO has an internal mesh we can just copy, use it
                if (so is DMeshSO)
                {
                    DMeshSO meshSO = so as DMeshSO;

                    // todo: flags

                    // make a copy of mesh
                    DMesh3 m = new DMesh3(meshSO.Mesh, true);

                    // transform to scene coords and swap left/right
                    foreach (int vid in m.VertexIndices())
                    {
                        Vector3f v = (Vector3f)m.GetVertex(vid);
                        v = SceneTransforms.ObjectToScene(meshSO, v);
                        v = UnityUtil.SwapLeftRight(v);
                        m.SetVertex(vid, v);
                    }
                    m.ReverseOrientation();

                    vMeshes.Add(new WriteMesh(m, so.Name));
                }


                // Look for lower-level fGameObject items to export. By default
                // this is anything with a MeshFilter, override CollectGOChildren
                // or use GOFilterF to add restrictions
                List <fGameObject> vExports = CollectGOChildren(so);
                if (vExports.Count > 0)
                {
                    SimpleMesh m = new SimpleMesh();
                    m.Initialize(WriteNormals, WriteVertexColors, WriteUVs, WriteFaceGroups);
                    int groupCounter = 1;

                    foreach (fGameObject childgo in vExports)
                    {
                        if (GOFilterF != null && GOFilterF(so, childgo) == false)
                        {
                            continue;
                        }

                        if (AppendGOMesh(childgo, m, vertexMap, scene, groupCounter))
                        {
                            groupCounter++;
                        }
                    }

                    vMeshes.Add(new WriteMesh(m, so.Name));
                }
            }


            // ok, we are independent of Scene now and can write in bg thread
            if (WriteInBackgroundThreads)
            {
                ExportStatus status = new ExportStatus()
                {
                    Exporter = this, IsComputing = true
                };
                WriteOptions useOptions = Options;
                useOptions.ProgressFunc = (cur, max) => {
                    status.Progress    = cur;
                    status.MaxProgress = max;
                };
                BackgroundWriteThread t = new BackgroundWriteThread()
                {
                    Meshes      = vMeshes, options = useOptions, Filename = filename,
                    CompletionF = (result) => {
                        LastWriteStatus         = result.code;
                        LastErrorMessage        = result.message;
                        status.LastErrorMessage = result.message;
                        status.Ok          = (result.code == IOCode.Ok);
                        status.IsComputing = false;
                        if (BackgroundWriteCompleteF != null)
                        {
                            BackgroundWriteCompleteF(this, status);
                        }
                    }
                };
                t.Start();
                return(status);
            }
            else
            {
                IOWriteResult result = StandardMeshWriter.WriteFile(filename, vMeshes, Options);
                LastWriteStatus  = result.code;
                LastErrorMessage = result.message;
                return(new ExportStatus()
                {
                    Exporter = this, IsComputing = false,
                    Ok = (result.code == IOCode.Ok),
                    LastErrorMessage = result.message
                });
            }
        }
        public ExportStatus Export(FScene s, string filename)
        {
            List <WriteMesh> vMeshes = new List <WriteMesh>();

            if (WriteFaceGroups)
            {
                throw new Exception("SceneMeshExporter.Export: writing face groups has not yet been implemented!");
            }

            foreach (SceneObject so in s.SceneObjects)
            {
                if (so.IsTemporary)
                {
                    continue;
                }

                SimpleMesh m = new SimpleMesh();
                m.Initialize(WriteNormals, WriteVertexColors, WriteUVs, WriteFaceGroups);
                int groupCounter = 1;

                GameObject rootgo = so.RootGameObject;

                int[] vertexMap = new int[2048];
                foreach (GameObject childgo in rootgo.Children())
                {
                    MeshFilter filter = childgo.GetComponent <MeshFilter>();
                    if (filter == null || filter.mesh == null)
                    {
                        continue;
                    }
                    if (GOFilterF != null && GOFilterF(so, childgo) == false)
                    {
                        continue;
                    }

                    Mesh      curMesh  = filter.sharedMesh;
                    Vector3[] vertices = curMesh.vertices;
                    Vector3[] normals  = (WriteNormals) ? curMesh.normals : null;
                    Color[]   colors   = (WriteVertexColors) ? curMesh.colors : null;
                    Vector2[] uvs      = (WriteUVs) ? curMesh.uv : null;

                    if (vertexMap.Length < curMesh.vertexCount)
                    {
                        vertexMap = new int[curMesh.vertexCount * 2];
                    }

                    for (int i = 0; i < curMesh.vertexCount; ++i)
                    {
                        NewVertexInfo vi = new NewVertexInfo();
                        vi.bHaveN = WriteNormals; vi.bHaveC = WriteVertexColors; vi.bHaveUV = WriteUVs;

                        Vector3 v = vertices[i];
                        // local to world
                        v = filter.gameObject.transform.TransformPoint(v);
                        // world back to scene
                        vi.v = UnityUtil.SwapLeftRight(s.RootGameObject.transform.InverseTransformPoint(v));

                        if (WriteNormals)
                        {
                            Vector3 n = normals[i];
                            n    = filter.gameObject.transform.TransformDirection(n);
                            vi.n = UnityUtil.SwapLeftRight(s.RootGameObject.transform.InverseTransformDirection(n));
                        }
                        if (WriteVertexColors)
                        {
                            vi.c = colors[i];
                        }
                        if (WriteUVs)
                        {
                            vi.uv = uvs[i];
                        }

                        vertexMap[i] = m.AppendVertex(vi);
                    }

                    int[] triangles  = curMesh.triangles;
                    int   nTriangles = triangles.Length / 3;
                    for (int i = 0; i < nTriangles; ++i)
                    {
                        int a = vertexMap[triangles[3 * i]];
                        int b = vertexMap[triangles[3 * i + 1]];
                        int c = vertexMap[triangles[3 * i + 2]];
                        m.AppendTriangle(a, c, b, groupCounter);  // TRI ORIENTATION IS REVERSED HERE!!
                    }
                    groupCounter++;
                }

                vMeshes.Add(new WriteMesh(m, so.Name));
            }


            if (WriteInBackgroundThreads)
            {
                ExportStatus status = new ExportStatus()
                {
                    Exporter = this, IsComputing = true
                };
                WriteOptions useOptions = Options;
                useOptions.ProgressFunc = (cur, max) => {
                    status.Progress    = cur;
                    status.MaxProgress = max;
                };
                BackgroundWriteThread t = new BackgroundWriteThread()
                {
                    Meshes      = vMeshes, options = useOptions, Filename = filename,
                    CompletionF = (result) => {
                        LastWriteStatus         = result.code;
                        LastErrorMessage        = result.message;
                        status.LastErrorMessage = result.message;
                        status.Ok          = (result.code == IOCode.Ok);
                        status.IsComputing = false;
                    }
                };
                t.Start();
                return(status);
            }
            else
            {
                IOWriteResult result = StandardMeshWriter.WriteFile(filename, vMeshes, Options);
                LastWriteStatus  = result.code;
                LastErrorMessage = result.message;
                return(new ExportStatus()
                {
                    Exporter = this, IsComputing = false,
                    Ok = (result.code == IOCode.Ok),
                    LastErrorMessage = result.message
                });
            }
        }