// If geometry does not contain normals, colors, and/or uvs, dummy values // will be added. public ExportMesh(GeometryPool pool) { m_pool = pool; FbxUtils.ApplyFbxTexcoordHack(m_pool); m_linearColor = ExportUtils.ConvertToLinearColorspace(m_pool.m_Colors); var layout = m_pool.Layout; var numVerts = m_pool.m_Vertices.Count; // TODO: all this padding code seems super bogus; try to remove. if (!layout.bUseNormals) { var lst = m_pool.m_Normals; lst.SetCount(numVerts); for (int i = 0; i < numVerts; ++i) { lst[i] = Vector3.up; } } if (!layout.bUseColors) { var lst = m_linearColor; lst.SetCount(numVerts); for (int i = 0; i < numVerts; ++i) { lst[i] = Color.white; } } }
// -------------------------------------------------------------------------------------------- // // Export Logic // -------------------------------------------------------------------------------------------- // /// Exports either all brush strokes or the given selection to the specified file. static public void ExportPayload(string outputFile) { // Would be nice to find a way to kick this off automatically. // Redundant calls are ignored. if (!InitUsd.Initialize()) { return; } // Unity is left handed (DX), USD is right handed (GL) var payload = ExportCollector.GetExportPayload(AxisConvention.kUsd); var brushCatalog = BrushCatalog.m_Instance; // The Scene object provids serialization methods arbitrary C# objects to USD. USD.NET.Scene scene = USD.NET.Scene.Create(outputFile); // The target time at which samples will be written. // // In this case, all data is being written to the "default" time, which means it can be // overridden by animated values later. scene.Time = null; // Bracketing times to specify the valid animation range. scene.StartTime = 1.0; scene.EndTime = 1.0; const string kGeomName = "/Geom"; const string kCurvesName = "/Curves"; string path = ""; AddSketchRoot(scene, GetSketchPath()); // Create: </Sketch> CreateXform(scene, GetStrokesPath()); // Create: </Sketch/Strokes> CreateXform(scene, GetModelsPath()); // Create: </Sketch/Models> // Main export loop. try { foreach (ExportUtils.GroupPayload group in payload.groups) { // Example: </Sketch/Strokes/Group_0> path = GetGroupPath(group.id); CreateXform(scene, path); // Example: </Sketch/Strokes/Group_0/Geom> CreateXform(scene, path + kGeomName); // Example: </Sketch/Strokes/Group_0/Curves> CreateXform(scene, path + kCurvesName); int iBrushMeshPayload = -1; foreach (var brushMeshPayload in group.brushMeshes) { ++iBrushMeshPayload; // Conditionally moves Normal into Texcoord1 so that the normal semantic is respected. // This only has an effect when layout.bFbxExportNormalAsTexcoord1 == true. // Note that this modifies the GeometryPool in place. FbxUtils.ApplyFbxTexcoordHack(brushMeshPayload.geometry); // Brushes are expected to be batched by type/GUID. Guid brushGuid = brushMeshPayload.strokes[0].m_BrushGuid; string brushName = "/" + SanitizeIdentifier(brushCatalog.GetBrush(brushGuid).DurableName) + "_"; // Example: </Sketch/Strokes/Group_0/Geom/Marker_0> string meshPath = path + kGeomName + brushName; // Example: </Sketch/Strokes/Group_0/Curves/Marker_0> string curvePath = path + kCurvesName + brushName; var geomPool = brushMeshPayload.geometry; var strokes = brushMeshPayload.strokes; var mat44 = Matrix4x4.identity; var meshPrimPath = new pxr.SdfPath(meshPath + iBrushMeshPayload.ToString()); var curvesPrimPath = new pxr.SdfPath(curvePath + iBrushMeshPayload.ToString()); // // Geometry // BrushSample brushSample = GetBrushSample(geomPool, strokes, mat44); // Write the BrushSample to the same point in the scenegraph at which it exists in Tilt // Brush. Notice this method is Async, it is queued to a background thread to perform I/O // which means it is not safe to read from the scene until WaitForWrites() is called. scene.Write(meshPrimPath, brushSample); // // Stroke Curves // var curvesSample = GetCurvesSample(payload, strokes, Matrix4x4.identity); scene.Write(curvesPrimPath, curvesSample); // // Materials // double?oldTime = scene.Time; scene.Time = null; string materialPath = CreateMaterialNetwork( scene, brushMeshPayload.exportableMaterial, GetStrokesPath()); BindMaterial(scene, meshPrimPath.ToString(), materialPath); BindMaterial(scene, curvesPrimPath.ToString(), materialPath); scene.Time = oldTime; } } // // Models // var knownModels = new Dictionary <Model, string>(); int iModelMeshPayload = -1; foreach (var modelMeshPayload in payload.modelMeshes) { ++iModelMeshPayload; var modelId = modelMeshPayload.modelId; var modelNamePrefix = "/Model_" + SanitizeIdentifier(modelMeshPayload.model.GetExportName()) + "_"; var modelName = modelNamePrefix + modelId; var xf = modelMeshPayload.xform; // Geometry pools may be repeated and should be turned into references. var geomPool = modelMeshPayload.geometry; var modelRootPath = new pxr.SdfPath(GetModelsPath() + modelName); // Example: </Sketch/Models/Model_Andy_0> CreateXform(scene, modelRootPath, xf); // Example: </Sketch/Models/Model_Andy_0/Geom> CreateXform(scene, modelRootPath + kGeomName); string modelPathToReference; if (knownModels.TryGetValue(modelMeshPayload.model, out modelPathToReference) && modelPathToReference != modelRootPath) { // Create an Xform, note that the world transform here will override the referenced model. var meshXf = new MeshXformSample(); meshXf.transform = xf; scene.Write(modelRootPath, meshXf); // Add a USD reference to previously created model. var prim = scene.Stage.GetPrimAtPath(modelRootPath); prim.GetReferences().AddReference("", new pxr.SdfPath(modelPathToReference)); continue; } // Example: </Sketch/Models/Geom/Model_Andy_0/Mesh_0> path = modelRootPath + kGeomName + "/Mesh_" + iModelMeshPayload.ToString(); var meshPrimPath = new pxr.SdfPath(path); var meshSample = new MeshSample(); GetMeshSample(geomPool, Matrix4x4.identity, meshSample); scene.Write(path, meshSample); scene.Stage.GetPrimAtPath(new pxr.SdfPath(path)).SetInstanceable(true); // // Materials // // Author at default time. double?oldTime = scene.Time; scene.Time = null; // Model materials must live under the model root, since we will reference the model. string materialPath = CreateMaterialNetwork( scene, modelMeshPayload.exportableMaterial, modelRootPath); BindMaterial(scene, meshPrimPath.ToString(), materialPath); // Continue authoring at the desired time index. scene.Time = oldTime; // // Setup to be referenced. // if (!knownModels.ContainsKey(modelMeshPayload.model)) { knownModels.Add(modelMeshPayload.model, modelRootPath); } } } catch { scene.Save(); scene.Close(); throw; } // Save will force a sync with all async reads and writes. scene.Save(); scene.Close(); }