Membuat paket Format Manufaktur 3D

Panduan ini menjelaskan struktur jenis file 3D Manufacturing Format (3MF) dan bagaimana API Windows.Graphics.Printing3D dapat digunakan untuk membuat dan memanipulasinya.

API penting

Apa itu Format Manufaktur 3D?

3MF adalah serangkaian konvensi untuk menggunakan XML untuk menjelaskan tampilan dan struktur model 3D untuk manufaktur (pencetakan 3D). Ini mendefinisikan satu set bagian (diperlukan dan opsional) dan hubungannya, ke perangkat manufaktur 3D. Himpunan data yang mematuhi 3MF dapat disimpan sebagai file dengan ekstensi .3mf.

Kelas Printing3D3MFPackage di namespace Windows.Graphics.Printing3D dianalogikan dengan satu file .3mf, sementara kelas lain memetakan ke elemen XML tertentu dalam file .3mf. Panduan ini menjelaskan bagaimana masing-masing bagian utama dari dokumen 3MF dapat dibuat dan diatur secara terprogram, bagaimana Ekstensi Bahan 3MF dapat digunakan, dan bagaimana objek Printing3D3MFPackage dapat dikonversi dan disimpan sebagai file .3mf. Untuk informasi selengkapnya tentang standar 3MF atau Ekstensi Bahan 3MF, lihat Spesifikasi 3MF.

Kelas inti dalam struktur 3MF

Kelas Printing3D3MFPackage mewakili dokumen 3MF lengkap, dan pada inti dokumen 3MF adalah bagian modelnya, yang diwakili oleh kelas Printing3DModel . Sebagian besar informasi tentang model 3D disimpan dengan mengatur properti kelas Printing3DModel dan properti kelas dasarnya.

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

Metadata

Bagian model dari dokumen 3MF dapat menyimpan metadata dalam bentuk pasangan kunci/nilai string yang disimpan di properti Metadata . Ada metadata yang telah ditentukan sebelumnya, tetapi pasangan kustom dapat ditambahkan sebagai bagian dari ekstensi (dijelaskan secara lebih rinci dalam spesifikasi 3MF). Terserah penerima paket (perangkat manufaktur 3D) untuk menentukan apakah dan bagaimana menangani metadata, tetapi adalah praktik yang baik untuk menyertakan info sebanyak mungkin dalam paket 3MF.

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

Data jala

Dalam panduan ini, jala adalah tubuh geometri 3 dimensi yang dibangun dari satu set simpul (meskipun tidak harus muncul sebagai satu tegas). Bagian jala diwakili oleh kelas Printing3DMesh . Objek jala yang valid harus berisi informasi tentang lokasi semua simpul serta semua wajah segitiga yang ada di antara set simpul tertentu.

Metode berikut menambahkan simpul ke jala lalu memberi mereka lokasi dalam ruang 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;
}

Metode berikutnya ini mendefinisikan semua segitiga yang akan digambar di seluruh simpul ini:

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

}

Catatan

Semua segitiga harus memiliki indeks yang ditentukan dalam urutan berlawanan arah jarum jam (saat melihat segitiga dari luar objek jala), sehingga vektor wajah normal mereka menunjuk ke luar.

Ketika objek Printing3DMesh berisi set simpul dan segitiga yang valid, objek tersebut kemudian harus ditambahkan ke properti Meshes model. Semua objek Printing3DMesh dalam paket harus disimpan di bawah properti Jala dari kelas Printing3DModel , seperti yang ditunjukkan di sini.

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

Membuat bahan

Model 3D dapat menyimpan data untuk beberapa materi. Konvensi ini dimaksudkan untuk memanfaatkan perangkat manufaktur 3D yang dapat menggunakan beberapa bahan pada satu pekerjaan cetak. Ada juga beberapa jenis grup bahan, masing-masing mampu mendukung sejumlah materi individu yang berbeda.

Setiap grup materi harus memiliki nomor ID referensi yang unik, dan setiap materi dalam grup tersebut juga harus memiliki ID unik. Objek jala yang berbeda dalam model kemudian dapat mereferensikan materi.

Selain itu, segitiga individu pada setiap jala dapat menentukan bahan yang berbeda dan bahan yang berbeda bahkan dapat diwakili dalam satu segitiga, dengan setiap verteks segitiga memiliki bahan berbeda yang ditetapkan untuk itu dan bahan wajah dihitung sebagai gradien di antara mereka.

Pertama, kita akan menunjukkan cara membuat berbagai jenis bahan dalam grup materi masing-masing dan menyimpannya sebagai sumber daya pada objek model. Kemudian, kita akan menetapkan bahan yang berbeda untuk jala individu dan segitiga individu.

Bahan dasar

Jenis bahan default adalah Bahan Dasar, yang memiliki nilai Bahan Warna (dijelaskan di bawah) dan atribut nama yang dimaksudkan untuk menentukan jenis bahan yang akan digunakan.

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

Catatan

 Perangkat manufaktur 3D akan menentukan peta bahan fisik mana yang tersedia ke elemen material virtual mana yang disimpan dalam 3MF. Pemetaan material tidak harus 1:1. Jika printer 3D hanya menggunakan satu bahan, printer akan mencetak seluruh model dalam bahan tersebut, terlepas dari objek atau wajah mana yang diberi bahan yang berbeda.

Material warna

Bahan Warna mirip dengan Bahan Dasar, tetapi tidak berisi nama. Dengan demikian, mereka tidak memberikan instruksi tentang jenis bahan apa yang harus digunakan oleh mesin. Mereka hanya menyimpan data warna, dan membiarkan mesin memilih jenis bahan (mesin mungkin meminta pengguna untuk memilih). Dalam contoh berikut, colrMat objek dari metode sebelumnya digunakan sendiri.

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

Bahan komposit

Bahan Komposit menginstruksikan perangkat manufaktur untuk menggunakan campuran seragam dari Bahan Dasar yang berbeda. Setiap Grup Bahan Komposit harus mereferensikan satu Grup Bahan Dasar untuk menggambar bahan. Secara tambahan, Bahan Dasar dalam grup ini yang akan tersedia harus dicantumkan dalam daftar Indeks Bahan , yang akan dirujuk oleh setiap Bahan Komposit saat menentukan rasio (setiap Bahan Komposit adalah rasio Bahan Dasar).

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

Bahan koordinat tekstur

3MF mendukung penggunaan gambar 2D untuk mewarnai permukaan model 3D. Dengan cara ini, model dapat menyampaikan lebih banyak data warna per wajah segitiga (dibandingkan dengan hanya memiliki satu nilai warna per sudut segitiga). Seperti Bahan Warna, bahan koordinat tekstur hanya menyampaikan data warna. Untuk menggunakan tekstur 2D, sumber daya tekstur harus dideklarasikan terlebih dahulu.

Catatan

Data tekstur milik Paket 3MF itu sendiri, bukan ke bagian model dalam paket.

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

Selanjutnya, kita harus mengisi Bahan Texture3Coord. Masing-masing mereferensikan sumber daya tekstur dan menentukan titik tertentu pada gambar (dalam koordinat 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);

Memetakan materi ke wajah

Untuk menentukan bahan mana yang dipetakan ke simpul mana pada setiap segitiga, diperlukan lebih banyak pekerjaan pada objek jala model (jika model berisi beberapa jala, masing-masing harus memiliki materi yang ditetapkan secara terpisah). Seperti disebutkan di atas, bahan ditetapkan per puncak, per segitiga. Contoh berikut menunjukkan bagaimana informasi ini dimasukkan dan ditafsirkan.

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

Komponen dan build

Struktur komponen memungkinkan pengguna untuk menempatkan lebih dari satu objek jala dalam model 3D yang dapat dicetak. Objek Printing3DComponent berisi satu jala dan daftar referensi ke komponen lain. Ini sebenarnya adalah daftar objek Printing3DComponentWithMatrix . Objek Printing3DComponentWithMatrix masing-masing berisi Printing3DComponent dan matriks transformasi yang berlaku untuk jala dan komponen yang terkandung dari Printing3DComponent.

Misalnya, model mobil mungkin terdiri dari "Body" Printing3DComponent yang memegang jala untuk bodi mobil. Komponen "Isi" kemudian dapat berisi referensi ke empat objek Printing3DComponentWithMatrix yang berbeda, yang semuanya mereferensikan Printing3DComponent yang sama sementara jala "Roda" mungkin berisi empat matriks transformasi yang berbeda (memetakan roda ke empat posisi berbeda pada bodi mobil). Dalam skenario ini, jala "Isi" dan jala "Roda" masing-masing hanya perlu disimpan sekali, meskipun produk akhir akan menampilkan total lima jala.

Semua objek Printing3DComponent harus langsung dirujuk dalam properti Komponen model. Satu komponen tertentu yang akan digunakan dalam pekerjaan pencetakan disimpan di Properti 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);

Simpan paket

Sekarang setelah kita memiliki model, dengan bahan dan komponen yang ditentukan, kita dapat menyimpannya ke paket.

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

Fungsi berikut memastikan tekstur ditentukan dengan benar.

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

Dari sini, kita dapat memulai pekerjaan cetak dalam aplikasi (lihat pencetakan 3D dari aplikasi Anda), atau menyimpan Printing3D3MFPackage ini sebagai file .3mf.

Metode berikut mengambil Printing3D3MFPackage yang sudah selesai dan menyimpan datanya ke file .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);
        }
    }
}

Pencetakan 3D dari aplikasi Anda
Sampel UWP pencetakan 3D