生成 3MF 程序包Generate a 3MF package

重要的 APIImportant APIs

本指南介绍了 3D 制造格式文档的结构以及如何使用 Windows.Graphics.Printing3D API 创建和操作该文档。This guide describes the structure of the 3D Manufacturing Format document and how it can be created and manipulated with the Windows.Graphics.Printing3D API.

什么是 3MF?What is 3MF?

3D 制造格式是有关出于制造目的(3D 打印)使用 XML 描述 3D 模型的外观和结构的一组约定。The 3D Manufacturing Format is a set of conventions for using XML to describe the appearance and structure of 3D models for the purpose of manufacturing (3D printing). 它定义一组部件(有些必选,有些可选)及其关系,目的是向 3D 制造设备提供所有必要的信息。It defines a set of parts (some required and some optional) and their relationships, with the goal of providing all necessary information to a 3D manufacturing device. 符合 3D 制造格式的数据集可以另存为带有 .3mf 扩展名的文件。A data set that adheres to the 3D Manufacturing Format can be saved as a file with the .3mf extension.

在 Windows 10 中,Windows.Graphics.Printing3D 命名空间中的 Printing3D3MFPackage 类类似于单个 .3mf 文件,并且其他类映射到该文件中的特定 XML 元素。In Windows 10, the Printing3D3MFPackage class in the Windows.Graphics.Printing3D namespace is analogous to a single .3mf file, and other classes map to the particular XML elements in the file. 本指南介绍如何以编程方式创建和设置 3MF 文档的每个主要部分、如何利用 3MF Materials Extension 以及最后如何将 Printing3D3MFPackage 对象转换并另存为 .3mf 文件。This guide describes how each of the main parts of a 3MF document can be created and set programmatically, how the 3MF Materials Extension can be utilized, and how a Printing3D3MFPackage object can be converted and saved as a .3mf file. 有关 3MF 的标准或 3MF Materials Extension 的详细信息,请参阅 3MF 规范For more information on the standards of 3MF or the 3MF Materials Extension, see the 3MF Specification.

3MF 结构中的核心类Core classes in the 3MF structure

Printing3D3MFPackage 类表示完整的 3MF 文档,并且 3MF 文档的核心是其模型部分,由 Printing3DModel 类表示。The Printing3D3MFPackage class represents a complete 3MF document, and at the core of a 3MF document is its model part, represented by the Printing3DModel class. 我们希望指定的有关 3D 模型的大部分信息都将通过设置 Printing3DModel 类的属性和它们的基础类的属性来存储。Most of the information we wish to specify about a 3D model will be stored by setting the properties of the Printing3DModel class and the properties of their underlying classes.

var localPackage = new Printing3D3MFPackage();
var model = new Printing3DModel();
// specify scaling units for model data
model.Unit = Printing3DModelUnit.Millimeter;

元数据Metadata

3MF 文档的模型部分可以将元数据以存储在 Metadata 属性中的字符串的键/值对的形式保存。The model part of a 3MF document can hold metadata in the form of key/value pairs of strings stored in the Metadata property. 存在大量预定义的元数据名称,但其他对可以添加为扩展名的一部分(在 3MF 规范中有更详细的介绍)。There are a number of predefined names of metadata, but other pairs can be added as part of an extension (described in more detail in the 3MF specification). 由程序包的接收器(一种 3D 制造设备)来确定是否以及如何处理元数据,但最好是在 3MF 程序包中包含尽可能多的基本信息:It is up to the receiver of the package (a 3D manufacturing device) to determine whether and how to handle metadata, but it is good practice to include as much basic info as possible in the 3MF package:

model.Metadata.Add("Title", "Cube");
model.Metadata.Add("Designer", "John Smith");
model.Metadata.Add("CreationDate", "1/1/2016");

网格数据Mesh data

在本指南的上下文中,网格是从单个顶点集构造的三维几何图形(尽管它无需显示为单个顶点)。In the context of this guide, a mesh is a body of 3-dimensional geometry constructed from a single set of vertices (though it does not have to appear as a single solid). 网格部件由 Printing3DMesh 类表示。A mesh part is represented by the Printing3DMesh class. 一个有效的网格对象必须包含有关其所有顶点以及存在于特定顶点集之间的所有三角形面的位置的信息。A valid mesh object must contain information about the location of all of its vertices as well as all the triangle faces that exist between certain sets of vertices.

以下方法将顶点添加到网格,然后提供它们在 3D 空间中的相应位置:The following method adds vertices to a mesh and then gives them locations in 3D space:

private async Task GetVerticesAsync(Printing3DMesh mesh) {
    Printing3DBufferDescription description;

    description.Format = Printing3DBufferFormat.Printing3DDouble;

    // have 3 xyz values
    description.Stride = 3;

    // have 8 vertices in all in this mesh
    mesh.CreateVertexPositions(sizeof(double) * 3 * 8);
    mesh.VertexPositionsDescription = description;

    // set the locations (in 3D coordinate space) of each vertex
    using (var stream = mesh.GetVertexPositions().AsStream()) {
        double[] vertices =
        {
            0, 0, 0,
            10, 0, 0,
            0, 10, 0,
            10, 10, 0,
            0, 0, 10,
            10, 0, 10,
            0, 10, 10,
            10, 10, 10,
        };

        // convert vertex data to a byte array
        byte[] vertexData = vertices.SelectMany(v => BitConverter.GetBytes(v)).ToArray();

        // write the locations to each vertex
        await stream.WriteAsync(vertexData, 0, vertexData.Length);
    }
    // update vertex count: 8 vertices in the cube
    mesh.VertexCount = 8;
}

下一个方法定义在这些顶点上绘制的所有三角形:The next method defines all of the triangles to be drawn across these vertices:

private static async Task SetTriangleIndicesAsync(Printing3DMesh mesh) {

    Printing3DBufferDescription description;

    description.Format = Printing3DBufferFormat.Printing3DUInt;
    // 3 vertex indices
    description.Stride = 3;
    // 12 triangles in all in the cube
    mesh.IndexCount = 12;

    mesh.TriangleIndicesDescription = description;

    // allocate space for 12 triangles
    mesh.CreateTriangleIndices(sizeof(UInt32) * 3 * 12);

    // get a datastream of the triangle indices (should be blank at this point)
    var stream2 = mesh.GetTriangleIndices().AsStream();
    {
        // define a set of triangle indices: each row is one triangle. The values in each row
        // correspond to the index of the vertex. 
        UInt32[] indices =
        {
            1, 0, 2,
            1, 2, 3,
            0, 1, 5,
            0, 5, 4,
            1, 3, 7,
            1, 7, 5,
            2, 7, 3,
            2, 6, 7,
            0, 6, 2,
            0, 4, 6,
            6, 5, 7,
            4, 5, 6,
        };
        // convert index data to byte array
        var vertexData = indices.SelectMany(v => BitConverter.GetBytes(v)).ToArray();
        var len = vertexData.Length;
        // write index data to the triangle indices stream
        await stream2.WriteAsync(vertexData, 0, vertexData.Length);
    }

}

备注

所有三角形都必须以逆时针方向的顺序(当从网格对象外部查看三角形时)定义其索引,以便使它们的面法线矢量指向外部。All triangles must have their indices defined in counter-clockwise order (when viewing the triangle from outside of the mesh object), so that their face-normal vectors point outward.

当 Printing3DMesh 对象包含有效的顶点和三角形集时,应该随后将其添加到模型的 Meshes 属性。When a Printing3DMesh object contains valid sets of vertices and triangles, it should then be added to the model's Meshes property. 程序包中的所有 Printing3DMesh 对象都必须存储在 Printing3DModel 类的 Meshes 属性下。All Printing3DMesh objects in a package must be stored under the Meshes property of the Printing3DModel class.

// add the mesh to the model
model.Meshes.Add(mesh);

创建材料Create materials

3D 模型可以保留多个材料的数据。A 3D model can hold data for multiple materials. 此约定旨在充分利用可在单个打印作业中使用多个材料的 3D 制造设备。This convention is intended to take advantage of 3D manufacturing devices that can use multiple materials on a single print job. 还有多种类型的材料组,每个都能够支持大量不同的单独材料。There are also multiple types of material gropus, each one capable of supporting a number of different individual materals. 每个材料组都必须有唯一的引用 ID 号,并且该组内的每个材料都必须有唯一的 ID。Each material group must have a unique reference id number, and each material within that group must also have a unique id.

然后,模型内的不同网格对象可以引用这些材料。The different mesh objects within a model can then reference these materials. 此外,每个网格上的个别三角形可以指定不同的材料。Furthermore, individual triangles on each mesh can specify different materials. 更进一步,甚至可以在单个三角形内表示不同的材料,其中每个三角形顶点都分配有不同的材料,并将面材料计算为顶点之间的渐变。Further still, different materials can even be represented within a single triangle, with each triangle vertex having a different material assigned to it and the face material calculated as the gradient between them.

本指南将先介绍如何在各自的材料组内创建不同类型的材料,然后将它们存储为模型对象上的资源。This guide will first show how to create different kinds of materials within their respective material groups and store them as resources on the model object. 然后,我们会了解将不同的材料分配到个别网格和个别三角形。Then, we will go about assigning different materials to individual meshes and individual triangles.

基本材料Base materials

默认材料类型为基本材料,该类型具有颜色材料值(如下所述)和旨在指定要使用的材料类型的名称属性。The default material type is Base Material, which has both a Color Material value (described below) and a name attribute that is intended to specify the type of material to use.

// add material group
// all material indices need to start from 1: 0 is a reserved id
// create new base materialgroup with id = 1
var baseMaterialGroup = new Printing3DBaseMaterialGroup(1);

// create color objects
// 'A' should be 255 if alpha = 100%
var darkBlue = Windows.UI.Color.FromArgb(255, 20, 20, 90);
var orange = Windows.UI.Color.FromArgb(255, 250, 120, 45);
var teal = Windows.UI.Color.FromArgb(255, 1, 250, 200);

// create new ColorMaterials, assigning color objects
var colrMat = new Printing3DColorMaterial();
colrMat.Color = darkBlue;

var colrMat2 = new Printing3DColorMaterial();
colrMat2.Color = orange;

var colrMat3 = new Printing3DColorMaterial();
colrMat3.Color = teal;

// setup new materials using the ColorMaterial objects
// set desired material type in the Name property
var baseMaterial = new Printing3DBaseMaterial {
    Name = Printing3DBaseMaterial.Pla,
    Color = colrMat
};

var baseMaterial2 = new Printing3DBaseMaterial {
    Name = Printing3DBaseMaterial.Abs,
    Color = colrMat2
};

// add base materials to the basematerialgroup

// material group index 0
baseMaterialGroup.Bases.Add(baseMaterial);
// material group index 1
baseMaterialGroup.Bases.Add(baseMaterial2);

// add material group to the basegroups property of the model
model.Material.BaseGroups.Add(baseMaterialGroup);

备注

 3D 制造设备将确定哪些可用物理材料映射到存储在 3MF 中的哪些虚拟材料元素。 The 3D manufacturing device will determine which available physical materials map to which virtual material elements stored in the 3MF. 材料映射并不一定是 1:1:如果 3D 打印机只使用一种材料,无论向哪些对象或面分配了不同的材料,它都将以该材料打印整个模型。Material mapping doesn't have to be 1:1: if a 3D printer only uses one material, it will print the whole model in that material, regardless of which objects or faces were assigned different materials.

颜色材料Color materials

颜色材料类似于基本材料,但它们不包含名称。Color Materials are similar to Base Materials, but they do not contain a name. 因此,它们不会提供有关计算机应使用哪些材料类型的说明。Thus, they give no instructions as to what type of material should be used by the machine. 它们仅保留颜色数据,并且让计算机选择材料类型(计算机随后可能提示用户进行选择)。They hold only color data, and let the machine choose the material type (and the machine may then prompt the user to choose). 在以下代码中,独立使用上一方法中的 colrMat 对象。In the code below, the colrMat objects from the previous method are used on their own.

// add ColorMaterials to the Color Material Group (with id 2)
var colorGroup = new Printing3DColorMaterialGroup(2);

// add the previous ColorMaterial objects to this ColorMaterialGroup
colorGroup.Colors.Add(colrMat);
colorGroup.Colors.Add(colrMat2);
colorGroup.Colors.Add(colrMat3);

// add colorGroup to the ColorGroups property on the model
model.Material.ColorGroups.Add(colorGroup);

复合材料Composite materials

复合材料仅指示制造设备使用不同基本材料的统一组合。Composite Materials simply instruct the manufacturing device to use a uniform mixture of different Base Materials. 每个复合材料组都必须仅引用一个要从中抽取成分的基本材料组Each Composite Material Group must reference exactly one Base Material Group from which to draw ingredients. 此外,此组内要提供的基本材料必须在材料索引列表中列出,以供每个复合材料在指定比率(每个复合材料只是基本材料的比率)时引用。Additonally, the Base Materials within this group that are to be made available must be listed out in a Material Indices list, which each Composite Material will then reference when specifying the ratios (every Composite Material is simply a ratio of Base Materials).

// CompositeGroups
// create new composite material group with id = 3
var compositeGroup = new Printing3DCompositeMaterialGroup(3);

// indices point to base materials in BaseMaterialGroup with id =1
compositeGroup.MaterialIndices.Add(0);
compositeGroup.MaterialIndices.Add(1);

// create new composite materials
var compMat = new Printing3DCompositeMaterial();
// fraction adds to 1.0
compMat.Values.Add(0.2); // .2 of first base material in BaseMaterialGroup 1
compMat.Values.Add(0.8); // .8 of second base material in BaseMaterialGroup 1

var compMat2 = new Printing3DCompositeMaterial();
// fraction adds to 1.0
compMat2.Values.Add(0.5);
compMat2.Values.Add(0.5);

var compMat3 = new Printing3DCompositeMaterial();
// fraction adds to 1.0
compMat3.Values.Add(0.8);
compMat3.Values.Add(0.2);

var compMat4 = new Printing3DCompositeMaterial();
// fraction adds to 1.0
compMat4.Values.Add(0.4);
compMat4.Values.Add(0.6);

// add composites to group
compositeGroup.Composites.Add(compMat);
compositeGroup.Composites.Add(compMat2);
compositeGroup.Composites.Add(compMat3);
compositeGroup.Composites.Add(compMat4);

// add group to model
model.Material.CompositeGroups.Add(compositeGroup);

纹理坐标材料Texture coordinate materials

3MF 支持使用 2D 图形为 3D 模型的图面上色。3MF supports the use of 2D images to color the surfaces of 3D models. 通过此方式,该模型可以在每个三角形面上传达更多的颜色数据(与每个三角形顶点只有一个颜色值相反)。This way, the model can convey much more color data per triangle face (as opposed to having just one color value per triangle vertex). 颜色材料一样,纹理坐标材料仅传达颜色数据。Like Color Materials, texture coordinate materials only convery color data. 若要使用 2D 纹理,必须先声明纹理资源:To use a 2D texture, a texture resource must first be declared:

// texture resource setup
Printing3DTextureResource texResource = new Printing3DTextureResource();
// name conveys the path within the 3MF document
texResource.Name = "/3D/Texture/msLogo.png";

// in this case, we reference texture data in the sample appx, convert it to 
// an IRandomAccessStream, and assign it as the TextureData
Uri texUri = new Uri("ms-appx:///Assets/msLogo.png");
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(texUri);
IRandomAccessStreamWithContentType iRandomAccessStreamWithContentType = await file.OpenReadAsync();
texResource.TextureData = iRandomAccessStreamWithContentType;
// add this testure resource to the 3MF Package
localPackage.Textures.Add(texResource);

// assign this texture resource to a Printing3DModelTexture
var modelTexture = new Printing3DModelTexture();
modelTexture.TextureResource = texResource;

备注

纹理数据属于 3MF 程序包本身,而不属于程序包内的模型部件。Texture data belongs to the 3MF Package itself, not to the model part within the package.

接下来,我们必须填写 Texture3Coord 材料Next, we must fill out Texture3Coord Materials. 其中每一个都引用一个纹理资源,并在图像上指定一个特定的点(在 UV 坐标中)。Each of these references a texture resource and specifies a particular point on the image (in UV coordinates).

// texture2Coord Group
// create new Texture2CoordMaterialGroup with id = 4
var tex2CoordGroup = new Printing3DTexture2CoordMaterialGroup(4);

// create texture materials:
// set up four tex2coordmaterial objects with four (u,v) pairs, 
// mapping to each corner of the image:

var tex2CoordMaterial = new Printing3DTexture2CoordMaterial();
tex2CoordMaterial.U = 0.0;
tex2CoordMaterial.V = 1.0;
tex2CoordGroup.Texture2Coords.Add(tex2CoordMaterial);

var tex2CoordMaterial2 = new Printing3DTexture2CoordMaterial();
tex2CoordMaterial2.U = 1.0;
tex2CoordMaterial2.V = 1.0;
tex2CoordGroup.Texture2Coords.Add(tex2CoordMaterial2);

var tex2CoordMaterial3 = new Printing3DTexture2CoordMaterial();
tex2CoordMaterial3.U = 0.0;
tex2CoordMaterial3.V = 0.0;
tex2CoordGroup.Texture2Coords.Add(tex2CoordMaterial3);

var tex2CoordMaterial4 = new Printing3DTexture2CoordMaterial();
tex2CoordMaterial4.U = 1.0;
tex2CoordMaterial4.V = 0.0;
tex2CoordGroup.Texture2Coords.Add(tex2CoordMaterial4);

// add our Printing3DModelTexture to the Texture property of the group
tex2CoordGroup.Texture = modelTexture;

// add metadata about the texture so that u,v values can be used
model.Metadata.Add("tex4", "/3D/Texture/msLogo.png");
// add group to groups on the model's material
model.Material.Texture2CoordGroups.Add(tex2CoordGroup);

将材料映射到面Map materials to faces

为了指示哪些材料映射到每个三角形上的哪些顶点,我们必须在模型的网格对象上执行其他一些工作(如果模型包括多个网格,则每个网格都必须分别分配它们的材料)。In order to dictate which materials are mapped to which vertices on each triangle, we must do some more work on the mesh object of our model (if a model contains multiple meshes, they must each have their materials assigned separately). 如上所述,材料按顶点、按三角形分配。As mentioned above, materials are assigned per-vertex, per-triangle. 请参考下面的代码以查看如何输入和解释此信息。Refer to the code below to see how this information is entered and interpreted.

private static async Task SetMaterialIndicesAsync(Printing3DMesh mesh) {
    // declare a description of the material indices
    Printing3DBufferDescription description;
    description.Format = Printing3DBufferFormat.Printing3DUInt;
    // 4 indices for material description per triangle
    description.Stride = 4;
    // 12 triangles total
    mesh.IndexCount = 12;
    mesh.TriangleMaterialIndicesDescription = description;

    // create space for storing this data
    mesh.CreateTriangleMaterialIndices(sizeof(UInt32) * 4 * 12);

    {
        // each row is a triangle face (in the order they were created)
        // first column is the id of the material group, last 3 columns show which material id (within that group)
        // maps to each triangle vertex (in the order they were listed when creating triangles)
        UInt32[] indices =
        {
            // base materials:
            // in  the BaseMaterialGroup (id=1), the BaseMaterial with id=0 will be applied to these triangle vertices
            1, 0, 0, 0, 
            1, 0, 0, 0,
            // color materials:
            // in the ColorMaterialGroup (id=2), the ColorMaterials with these ids will be applied to these triangle vertices
            2, 1, 1, 1,
            2, 1, 1, 1,
            2, 0, 0, 0,
            2, 0, 0, 0,
            2, 0, 1, 2,
            2, 1, 0, 2,
            // composite materials:
            // in the CompositeMaterialGroup (id=3), the CompositeMaterial with id=0 will be applied to these triangles
            3,0,0,0,
            3,0,0,0,
            // texture materials:
            // in the Texture2CoordMaterialGroup (id=4), each texture coordinate is mapped to the appropriate vertex on these
            // two adjacent triangle faces, so that the square face they create displays the original rectangular image
            4, 0, 3, 1,
            4, 2, 3, 0,
        };

        // get the current (unassigned) vertex data as a stream and write our new 'indices' data to it.
        var stream = mesh.GetTriangleMaterialIndices().AsStream();
        var vertexData = indices.SelectMany(v => BitConverter.GetBytes(v)).ToArray();
        var len = vertexData.Length;
        await stream.WriteAsync(vertexData, 0, vertexData.Length);
    }
}

组件和版本Components and build

组件结构允许用户在可打印的 3D 模型中放置多个网格对象。The component structure allows the user to place more than one mesh object in a printable 3D model. Printing3DComponent 对象包含单个网格和对其他组件的引用列表。A Printing3DComponent object contains a single mesh and a list of references to other components. 这实际上是 Printing3DComponentWithMatrix 对象的列表。This is actually a list of Printing3DComponentWithMatrix objects. 每个 Printing3DComponentWithMatrix 对象都包含一个 Printing3DComponent,重要的是,还包含一个适用于所谓的 Printing3DComponent 的网格和包含组件的转换矩阵。Printing3DComponentWithMatrix objects each contain a Printing3DComponent and, importantly, a transform matrix that applies to the mesh and contained components of said Printing3DComponent.

例如,汽车的模型可能由承载车身网格的“车身”Printing3DComponent 组成。For example, a model of a car might consist of a "Body" Printing3DComponent that holds the mesh for the car's body. 然后,“车身”组件可能包含对四个不同的 Printing3DComponentWithMatrix 对象的引用,这些对象全都引用带有“车轮”网格的相同 Printing3DComponent 并包含四个不同的转换矩阵(将车轮映射到车身上的四个不同位置)。The "Body" component may then contain references to four different Printing3DComponentWithMatrix objects, which all reference the same Printing3DComponent with the "Wheel" mesh and contain four different transform matrices (mapping the wheels to four different positions on the car's body). 在此方案中,“车身”网格和“车轮”网格各自只需存储一次,即使最终产品总共会展示五个网格。In this scenario, the "Body" mesh and "Wheel" mesh would each only need to be stored once, even though the final product would feature five meshes in total.

所有 Printing3DComponent 组件都必须在模型的 Components 属性中直接引用。All Printing3DComponent objects must be directly referenced in the model's Components property. 要在打印作业中使用的单个特定组件存储在 Build 属性中。The one particular component that is to be used in the printing job is stored in the Build Property.

// create new component
Printing3DComponent component = new Printing3DComponent();

// assign mesh to the component's mesh
component.Mesh = mesh;

// add component to the model's list of all used components
// a model can have references to multiple components
model.Components.Add(component);

// create the transform matrix
var componentWithMatrix = new Printing3DComponentWithMatrix();
// assign component to this componentwithmatrix
componentWithMatrix.Component = component;

// create an identity matrix
var identityMatrix = Matrix4x4.Identity;

// use the identity matrix as the transform matrix (no transformation)
componentWithMatrix.Matrix = identityMatrix;

// add component to the build property.
model.Build.Components.Add(componentWithMatrix);

保存程序包Save package

现在我们具有包含已定义材料和组件的模型,我们可以将其保存到程序包。Now that we have a model, with defined materials and components, we can save it to the package.

// save the model to the package:
await localPackage.SaveModelToPackageAsync(model);
// get the model stream
var modelStream = localPackage.ModelPart;

// fix any textures in the model file
localPackage.ModelPart = await FixTextureContentType(modelStream);

此函数可确保正确指定纹理。This function ensures the texture is specified correctly.

/// <summary>
/// Ensure textures are saved correctly.
/// </summary>
/// <param name="modelStream">3dmodel.model data</param>
/// <returns></returns>
private async Task<IRandomAccessStream> FixTextureContentType(IRandomAccessStream modelStream) {
    XDocument xmldoc = XDocument.Load(modelStream.AsStreamForRead());

    var outputStream = new Windows.Storage.Streams.InMemoryRandomAccessStream();
    var writer = new Windows.Storage.Streams.DataWriter(outputStream);
    writer.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8;
    writer.ByteOrder = Windows.Storage.Streams.ByteOrder.LittleEndian;
    writer.WriteString("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");

    var text = xmldoc.ToString();
    // ensure that content type is set correctly
    // texture content can be either png or jpg
    var replacedText = text.Replace("contenttype=\"\"", "contenttype=\"image/png\"");
    writer.WriteString(replacedText);

    await writer.StoreAsync();
    await writer.FlushAsync();
    writer.DetachStream();
    return outputStream;
}

我们可以从此处在应用内启动打印作业(请参阅从应用进行 3D 打印),或者将此 Printing3D3MFPackage 另存为 .3mf 文件。From here, we can either initiate a print job within the app (see 3D printing from your app), or save this Printing3D3MFPackage as a .3mf file.

以下方法选取已完成的 Printing3D3MFPackage 并将其数据保存到.3mf 文件。The following method takes a finished Printing3D3MFPackage and saves its data to a .3mf file.

private async void SaveTo3mf(Printing3D3MFPackage localPackage) {

    // prompt the user to choose a location to save the file to
    FileSavePicker savePicker = new FileSavePicker();
    savePicker.DefaultFileExtension = ".3mf";
    savePicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
    savePicker.FileTypeChoices.Add("3MF File", new[] { ".3mf" });
    var storageFile = await savePicker.PickSaveFileAsync();
    if (storageFile == null) {
        return;
    }

    // save the 3MF Package to an IRandomAccessStream
    using (var stream = await localPackage.SaveAsync()) {
        // go to the beginning of the stream
        stream.Seek(0);

        // read from the file stream and write to a buffer
        using (var dataReader = new DataReader(stream)) {
            await dataReader.LoadAsync((uint)stream.Size);
            var buffer = dataReader.ReadBuffer((uint)stream.Size);

            // write from the buffer to the storagefile specified
            await FileIO.WriteBufferAsync(storageFile, buffer);
        }
    }
}

从应用进行 3D 打印3D printing from your app
3D 打印 UWP 示例3D printing UWP sample