3D 製造形式パッケージを生成する

このガイドでは、 3D 製造形式 (3MF ) ファイルの種類の構造と、 Windows.Graphics.Printing3D API を使用して作成および操作する方法について説明します。

重要な API

3D 製造形式とは

3MF は、XML を使用して、製造用の 3D モデルの外観と構造 (3D 印刷) を記述するための一連の規則です。 3D 製造デバイスに対する一連のパーツ (必須およびオプション) とその関係を定義します。 3MF に準拠するデータ セットは、拡張子が .3mf のファイルとして保存できます。

Windows.Graphics.Printing3D 名前空間の Printing3D3MFPackage クラスは 1 つの .3mf ファイルに似ていますが、他のクラスは .3mf ファイル内の特定の XML 要素にマップされます。 このガイドでは、3MF ドキュメントの各メイン部分をプログラムで作成および設定する方法、3MF マテリアル拡張機能を使用する方法、および Printing3D3MFPackage オブジェクトを .3mf ファイルとして変換および保存する方法について説明します。 3MF および 3MF 素材の拡張の標準について詳しくは、「3MF の仕様」をご覧ください。

3MF 構造体の主なクラス

Printing3D3MFPackage クラスは完全な 3MF ドキュメントを表します。3MF ドキュメントの中心となるのは、Printing3DModel クラスで表される、モデル パーツです。 3D モデルに関する情報のほとんどは、 Printing3DModel クラスのプロパティとその基になるクラスのプロパティを設定することによって格納されます。

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

メタデータ

3MF ドキュメントのモデル パーツは、Metadata プロパティに保存される文字列のキー/値ペアの形式でメタデータを保持できます。 定義済みのメタデータがありますが、カスタム ペアは拡張機能の一部として追加できます ( 詳細については、3MF 仕様で説明します)。 メタデータを処理するかどうかと処理方法を決定するのはパッケージ (3D 製造デバイス) の受信者次第ですが、3MF パッケージにはできるだけ多くの情報を含めるのが良い方法です。

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

メッシュ データ

このガイドでは、メッシュは 1 つの頂点セットから構築された 3 次元ジオメトリの本体です (ただし、単一のソリッドとして表示する必要はありません)。 メッシュ パーツは Printing3DMesh クラスで表されます。 有効なメッシュ オブジェクトには、すべての頂点の位置に関する情報と、特定の頂点セットの間に存在するすべての三角形の面が含まれている必要があります。

次の方法では、頂点をメッシュに追加し、3D 空間内の位置を指定します。

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;
}

次のメソッドは、これらの頂点全体に描画されるすべての三角形を定義します。

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);
    }

}

注意

すべての三角形では、(三角形をメッシュ オブジェクトの外側から見て) 反時計回りでインデックスを定義する必要があります。これにより、表面の法線ベクトルが外側に向きます。

Printing3DMesh オブジェクトに有効な頂点と三角形のセットが含まれている場合は、モデルの Meshes プロパティに追加する必要があります。 次に示すように、パッケージ内のすべての Printing3DMesh オブジェクトを Printing3DModel クラスの Meshes プロパティの下に格納する必要があります。

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

素材の作成

3D モデルは複数の素材のデータを保持できます。 この規則は、1 回の印刷ジョブで複数の素材を使用できる 3D 製造デバイスを活用するためのものです。 また、複数の 種類 の材料グループがあり、それぞれ複数の異なる個々の材料をサポートできます。

各品目グループには一意の参照 ID 番号が必要であり、そのグループ内の各品目も一意の ID を持っている必要があります。 モデル内の異なるメッシュ オブジェクトは、マテリアルを参照できます。

さらに、各メッシュ上の個々の三角形では、異なるマテリアルを指定でき、1 つの三角形内で異なるマテリアルを表現することもできます。各三角形の頂点には異なるマテリアルが割り当てられ、面のマテリアルはそれらの間のグラデーションとして計算されます。

まず、それぞれのマテリアル グループ内にさまざまな種類のマテリアルを作成し、それらをモデル オブジェクトのリソースとして格納する方法を示します。 次に、個々のメッシュと個々の三角形に異なるマテリアルを割り当てます。

Base materials

既定の素材の種類は Base Material です。これは Color Material 値 (下記を参照) と名前属性を持ちます。これは使用する素材の種類を指定するために使用します。

// 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 に保存されている仮想素材要素のマップを決定します。 マテリアル マッピングは 1:1 である必要はありません。 3D プリンターで使用するマテリアルが 1 つだけの場合は、異なるマテリアルが割り当てられたオブジェクトまたは面に関係なく、そのマテリアル内のモデル全体が印刷されます。

色素材

Color MaterialsBase Materials に似ていますが、名前は含まれません。 そのため、マシンが使用する素材の種類を指定できません。 これらは色データのみを保持し、マシンが材料の種類を選択できるようにします (マシンはユーザーに選択を求める場合があります)。 次の例では、前の colrMat メソッドのオブジェクトが単独で使用されています。

// 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 Material Group は材料を使う 1 つの Base Material Group のみを参照する必要があります。 追加的に、このグループ内で使用可能にする基本材料は、比率を指定するときに各複合材料が参照する材料インデックスリストに一覧表示する必要があります(すべての複合材料、基材の比率です)。

// 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);

テクスチャ座標の素材

3MF は 2D 画像を使って 3D モデルの表面に色を付けることをサポートします。 これによりモデルは、三角形の表面ごとに、より多くの色データを伝えることができます (三角形の頂点ごとに 1 つの色の値を持つ場合と比べて)。 カラー マテリアルと同様に、テクスチャ座標マテリアルはカラー データのみを伝達します。 2D テクスチャを使用するには、まずテクスチャ リソースを宣言する必要があります。

注意

テクスチャ データは、パッケージ内のモデル パーツでなく、3MF パッケージ自体に属しています。

// 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;

次に Texture3Coord Materials を記入する必要があります。 これらはそれぞれテクスチャ リソースを参照し、画像の特定の点を (UV 座標で) 指定します。

// 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);

素材を表面にマップ

各三角形の頂点にマップされるマテリアルを指定するには、モデルのメッシュ オブジェクトに対してさらに多くの作業が必要です (モデルに複数のメッシュが含まれている場合は、それぞれのマテリアルを個別に割り当てる必要があります)。 既に説明したように、素材は頂点ごと、三角形ごとに割り当てられます。 次の例は、この情報の入力方法と解釈方法を示しています。

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);
    }
}

Component とビルド

Component 構造体により、ユーザーは印刷可能な 3D モデルに複数のメッシュ オブジェクトを配置できます。 Printing3DComponent オブジェクトには、1 つのメッシュと他のコンポーネントへの参照のリストが含まれています。 これは実際には、Printing3DComponentWithMatrix オブジェクトのリストです。 Printing3DComponentWithMatrix オブジェクトには、それぞれ Printing3DComponent と、 Printing3DComponent のメッシュおよび包含コンポーネントに適用される変換マトリックスが含まれています。

たとえば、自動車のモデルは、車のボディのメッシュを保持する、1 つの "ボディ" Printing3DComponent から構成されることができます。 "Body" コンポーネントには、4 つの異なる Printing3DComponentWithMatrix オブジェクトへの参照が含まれる場合があります。このオブジェクトはすべて同じ Printing3DComponent を参照しますが、"Wheel" メッシュには 4 つの異なる変換マトリックスが含まれる場合があります (ホイールを自動車のボディ上の 4 つの異なる位置にマッピングします)。 このシナリオでは、最終製品には合計 5 つのメッシュが含まれる場合でも、"ボディ" メッシュと "車輪" メッシュはそれぞれ 1 度だけ保存される必要があります。

すべての Printing3DComponent オブジェクトは、モデルの Components プロパティで直接参照する必要があります。 印刷ジョブで使用される 1 つの特定のコンポーネントは Build プロパティに保存されています。

// 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 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);

次の関数は、テクスチャが正しく指定されていることを確認します。

/// <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 ファイルとして保存します。

次のメソッドは、完成した Printing3D3MFPackage を取得して、そのデータを .3mf ファイルに保存します。

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 印刷の UWP サンプル