static ExportUtils.GroupPayload BuildGroupPayload(SceneStatePayload payload, ExportUtils.ExportGroup exportGroup) { var group = new ExportUtils.GroupPayload(); group.id = exportGroup.m_group == SketchGroupTag.None ? 0 : payload.groupIdMapping.GetId(exportGroup.m_group); // Flattens the two-level (brush, batch) iteration to a single list of meshes // with heterogeneous materials. foreach (ExportUtils.ExportBrush brush in exportGroup.SplitByBrush()) { var desc = brush.m_desc; foreach (var(batch, batchIndex) in brush.ToGeometryBatches().WithIndex()) { GeometryPool geometry = batch.pool; List <Stroke> strokes = batch.strokes; string legacyUniqueName = $"{desc.m_DurableName}_{desc.m_Guid}_{group.id}_i{batchIndex}"; string friendlyGeometryName = $"brush_{desc.m_DurableName}_g{group.id}_b{batchIndex}"; UnityEngine.Profiling.Profiler.BeginSample("ConvertToMetersAndChangeBasis"); ExportUtils.ConvertUnitsAndChangeBasis(geometry, payload); UnityEngine.Profiling.Profiler.EndSample(); if (payload.reverseWinding) { // Note: this triangle flip intentionally un-does the Unity FBX import flip. ExportUtils.ReverseTriangleWinding(geometry, 1, 2); } if (App.PlatformConfig.EnableExportMemoryOptimization && payload.temporaryDirectory != null) { string filename = Path.Combine( payload.temporaryDirectory, legacyUniqueName + ".Gpoo"); geometry.MakeGeometryNotResident(filename); } group.brushMeshes.Add(new ExportUtils.BrushMeshPayload( payload.groupIdMapping.GetId(exportGroup.m_group)) { legacyUniqueName = legacyUniqueName, // This is the only instance of the mesh, so the node doesn't need an extra instance id nodeName = friendlyGeometryName, xform = Matrix4x4.identity, geometry = geometry, geometryName = friendlyGeometryName, exportableMaterial = brush.m_desc, strokes = strokes, }); } } return(group); }
// widget.ReferenceImage must be != null // Never returns null // id is an instance id static ImageQuadPayload BuildImageQuadPayload( SceneStatePayload payload, ImageWidget widget, DynamicExportableMaterial material, int id) { string nodeName = $"image_{widget.GetExportName()}_{id}"; // The child has no T or R but has some non-uniform S that we need to preserve. // I'm going to do this by baking it into the GeometryPool GameObject widgetChild = widget.m_ImageQuad.gameObject; Matrix4x4 xform = ExportUtils.ChangeBasis(widget.transform, payload); Vector3 localScale = widgetChild.transform.localScale; // localScale is a muddle of aspect ratio and overall size. We don't know which of x or y // was modified to set the aspect ratio, so get the size from z. if (localScale.z > 0) { float overallSize = localScale.z; localScale /= overallSize; xform = xform * Matrix4x4.Scale(Vector3.one * overallSize); } // Create pool and bake in the leftover non-uniform scale GeometryPool pool = new GeometryPool(); pool.Layout = material.VertexLayout; pool.Append(widgetChild.GetComponent <MeshFilter>().sharedMesh, fallbackColor: Color.white); pool.ApplyTransform( Matrix4x4.Scale(localScale), Matrix4x4.identity, 1f, 0, pool.NumVerts); // Important: This transform should only be applied once, since the pools are shared, and // cannot include any mesh-local transformations. ExportUtils.ConvertUnitsAndChangeBasis(pool, payload); if (payload.reverseWinding) { // Note: this triangle flip intentionally un-does the Unity FBX import flip. ExportUtils.ReverseTriangleWinding(pool, 1, 2); } return(new ExportUtils.ImageQuadPayload(payload.groupIdMapping.GetId(widget.Group)) { // because Count always increments, this is guaranteed unique even if two different images // have the same export name legacyUniqueName = $"refimage_i{payload.imageQuads.Count}", // We could (but don't) share the same aspect-ratio'd-quad for all instances of a refimage. // Since the mesh is unique to the node, it can have the same name as the node. geometryName = nodeName, nodeName = nodeName, xform = xform, geometry = pool, exportableMaterial = material, }); }
// Helper for GetModelMeshPayloads() -- this is the level 4 iteration. // Pass: // mesh / meshMaterials - the Mesh to generate geometry for // payload - the payload being exported // prefab - the owner "prefab" // meshIndex - the index of this Mesh within its owner "prefab" static IEnumerable <PrefabGeometry> GetPrefabGameObjMeshes( Mesh mesh, Material[] meshMaterials, SceneStatePayload payload, Model prefab, int meshIndex) { if (meshMaterials.Length != mesh.subMeshCount) { throw new ArgumentException("meshMaterials length"); } string exportName = prefab.GetExportName(); IExportableMaterial[] exportableMaterials = meshMaterials.Select( mat => prefab.GetExportableMaterial(mat)).ToArray(); VertexLayout?[] layouts = exportableMaterials.Select(iem => iem?.VertexLayout).ToArray(); // TODO(b/142396408) // Is it better to write color/texcoord that we don't need, just to satisfy the layout? // Or should we remove color/texcoord from the layout if the Mesh doesn't have them? // Right now we do the former. I think the fix should probably be in the collector; // it shouldn't return an exportableMaterial that is a superset of what the mesh contains. GeometryPool[] subMeshPools = GeometryPool.FromMesh( mesh, layouts, fallbackColor: Color.white, useFallbackTexcoord: true); for (int subMeshIndex = 0; subMeshIndex < mesh.subMeshCount; subMeshIndex++) { // See if this material is a Blocks material. Material mat = meshMaterials[subMeshIndex]; // At import time, ImportMaterialCollector now ensures that _every_ UnityEngine.Material // imported also comes with an IExportableMaterial of some kind (BrushDescriptor for // Blocks/TB, and DynamicExportableMaterial for everything else). This lookup shouldn't fail. IExportableMaterial exportableMaterial = exportableMaterials[subMeshIndex]; if (exportableMaterial == null) { Debug.LogWarning($"Model {prefab.HumanName} has a non-exportable material {mat.name}"); continue; } GeometryPool geo = subMeshPools[subMeshIndex]; // TODO(b/140634751): lingering sanity-checking; we can probably remove this for M24 if (geo.Layout.bUseColors) { Debug.Assert(geo.m_Colors.Count == geo.NumVerts); } // Important: This transform should only be applied once per prefab, since the pools are // shared among all the instances, and cannot include any mesh-local transformations. // If the pools share data (which is currently impossible), then additionally // the transform should only be applied once to each set of vertex data. ExportUtils.ConvertUnitsAndChangeBasis(geo, payload); if (payload.reverseWinding) { // There are many ways of reversing winding; choose the one that matches Unity's fbx flip ExportUtils.ReverseTriangleWinding(geo, 1, 2); } yield return(new PrefabGeometry { model = prefab, name = exportName + "_" + meshIndex + "_" + subMeshIndex, pool = geo, exportableMaterial = exportableMaterial, }); } }