Partager via


Générer un package de format de fabrication 3D

Ce guide décrit la structure du type de fichier 3D Manufacturing Format (3MF) et la façon dont l’API Windows.Graphics.Printing3D peut être utilisée pour créer et manipuler celle-ci.

API importantes

Qu’est-ce que le format de fabrication 3D ?

3MF est un ensemble de conventions pour l’utilisation de XML pour décrire l’apparence et la structure des modèles 3D pour la fabrication (impression 3D). Il définit un ensemble de parties (obligatoires et facultatives) et leurs relations, à un appareil de fabrication 3D. Un jeu de données qui respecte la 3MF peut être enregistré en tant que fichier avec l’extension .3mf.

La classe Printing3D3MFPackage dans l’espace de noms Windows.Graphics.Printing3D est analogue à un seul fichier .3mf, tandis que d’autres classes correspondent aux éléments XML particuliers du fichier .3mf. Ce guide décrit comment chacune des parties principales d’un document 3MF peut être créée et définie par programme, comment l’extension de matériaux 3MF peut être utilisée et comment un objet Printing3D3MFPackage peut être converti et enregistré en tant que fichier .3mf. Pour plus d’informations sur les normes de 3MF ou de l’extension de matériaux 3MF, consultez la spécification 3MF.

Classes principales dans la structure 3MF

La classe Printing3D3MFPackage représente un document 3MF complet, et au cœur d’un document 3MF est sa partie modèle, représentée par la classe Printing3DModel. La plupart des informations relatives à un modèle 3D sont stockées en définissant les propriétés de la classe Printing3DModel et les propriétés de leurs classes sous-jacentes.

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

Métadonnées

La partie modèle d’un document 3MF peut contenir des métadonnées sous la forme de paires clé/valeur de chaînes stockées dans la propriété Metadata . Il existe des métadonnées prédéfinies, mais des paires personnalisées peuvent être ajoutées dans le cadre d’une extension (décrite plus en détail dans la spécification 3MF). Il incombe au destinataire du package (un appareil de fabrication 3D) de déterminer si et comment gérer les métadonnées, mais il est recommandé d’inclure autant d’informations que possible dans le package 3MF.

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

Données de maillage

Dans ce guide, un maillage est un corps de géométrie 3 dimensions construit à partir d’un seul ensemble de sommets (bien qu’il ne soit pas obligé d’apparaître comme un seul solid). Une partie de maillage est représentée par la classe Printing3DMesh. Un objet de maillage valide doit contenir des informations sur l’emplacement de tous les sommets ainsi que sur tous les visages triangles qui existent entre certains ensembles de sommets.

La méthode suivante ajoute des sommets à un maillage, puis leur donne des emplacements dans l’espace 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;
}

Cette méthode suivante définit tous les triangles à dessiner sur ces sommets :

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

}

Remarque

Tous les triangles doivent avoir leurs index définis dans l’ordre inverse (lors de l’affichage du triangle à partir de l’extérieur de l’objet de maillage), afin que leurs vecteurs normaux face pointent vers l’extérieur.

Lorsqu’un objet Printing3DMesh contient des ensembles valides de sommets et de triangles, il doit ensuite être ajouté à la propriété Meshes du modèle. Tous les objets Printing3DMesh d’un package doivent être stockés sous la propriété Meshes de la classe Printing3DModel , comme illustré ici.

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

Créer des matériaux

Un modèle 3D peut contenir des données pour plusieurs matériaux. Cette convention vise à tirer parti des appareils de fabrication 3D qui peuvent utiliser plusieurs matériaux sur un seul travail d’impression. Il existe également plusieurs types de groupes de matériaux, chacun capable de prendre en charge un certain nombre de matériaux individuels différents.

Chaque groupe de matériaux doit avoir un numéro d’ID de référence unique, et chaque matériau au sein de ce groupe doit également avoir un ID unique. Les différents objets de maillage au sein d’un modèle peuvent ensuite référencer les matériaux.

En outre, les triangles individuels sur chaque maillage peuvent spécifier différents matériaux et différents matériaux peuvent même être représentés dans un triangle unique, chaque sommet triangle ayant un matériau différent lui étant attribué et le matériau du visage calculé comme dégradé entre eux.

Tout d’abord, nous allons montrer comment créer différents types de matériaux au sein de leurs groupes de matériaux respectifs et les stocker en tant que ressources sur l’objet modèle. Ensuite, nous allons affecter différents matériaux à des maillages individuels et à des triangles individuels.

Matériaux de base

Le type de matériau par défaut est Base Material, qui a à la fois une valeur Color Material (décrite ci-dessous) et un attribut de nom destiné à spécifier le type de matériau à utiliser.

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

Remarque

 L’appareil de fabrication 3D détermine les matériaux physiques disponibles mappés aux éléments de matériaux virtuels stockés dans la 3MF. Le mappage de matériel n’a pas besoin d’être 1:1. Si une imprimante 3D n’utilise qu’un seul matériau, elle imprime le modèle entier dans ce matériau, quels que soient les objets ou les visages affectés à différents matériaux.

Matériaux couleur

Les matériaux de couleur sont similaires aux matériaux de base, mais ils ne contiennent pas de nom. Ainsi, ils ne donnent aucune instruction quant au type de matériau à utiliser par la machine. Ils contiennent uniquement des données de couleur et permettent à l’ordinateur de choisir le type de matériau (l’ordinateur peut inviter l’utilisateur à choisir). Dans l’exemple suivant, les colrMat objets de la méthode précédente sont utilisés eux-mêmes.

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

Matériaux composites

Les matériaux composites indiquent à l’appareil de fabrication d’utiliser un mélange uniforme de différents matériaux de base. Chaque groupe de matériaux composites doit référencer exactement un groupe de matières de base à partir duquel dessiner des ingrédients. En outre, les matériaux de base au sein de ce groupe qui doivent être mis à disposition doivent être répertoriés dans une liste d’indices de matériau, que chaque matériau composite référence lors de la spécification des ratios (chaque matériau composite est un rapport de matériaux de base).

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

Matériaux de coordonnées de texture

3MF prend en charge l’utilisation d’images 2D pour colorer les surfaces des modèles 3D. De cette façon, le modèle peut transmettre beaucoup plus de données de couleur par face triangle (par opposition à une seule valeur de couleur par vertex de triangle). Comme les matériaux de couleur, les matériaux de coordonnées de texture transmettent uniquement les données de couleur. Pour utiliser une texture 2D, une ressource de texture doit d’abord être déclarée.

Remarque

Les données de texture appartiennent au package 3MF lui-même, et non à la partie modèle du package.

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

Ensuite, nous devons remplir texture3Coord Materials. Chacune de ces références fait référence à une ressource de texture et spécifie un point particulier sur l’image (en coordonnées 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);

Mapper des matériaux aux visages

Pour spécifier les matériaux mappés aux sommets de chaque triangle, un travail supplémentaire est nécessaire sur l’objet de maillage du modèle (si un modèle contient plusieurs maillages, ils doivent chacun avoir leurs matériaux affectés séparément). Comme mentionné ci-dessus, les matériaux sont attribués par vertex, par triangle. L’exemple suivant montre comment ces informations sont entrées et interprétées.

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

Composants et build

La structure des composants permet à l’utilisateur de placer plusieurs objets de maillage dans un modèle 3D imprimable. Un objet Printing3DComponent contient un maillage unique et une liste de références à d’autres composants. Il s’agit en fait d’une liste d’objets Printing3DComponentWithMatrix . Les objets Printing3DComponentWithMatrix contiennent chacun une matrice Printing3DComponent et une matrice de transformation qui s’applique au maillage et aux composants contenus de Printing3DComponent.

Par exemple, un modèle d’une voiture peut se composer d’un « Body » Printing3DComponent qui contient le maillage du corps de la voiture. Le composant « Body » peut ensuite contenir des références à quatre objets Printing3DComponentWithMatrix différents, qui référencent tous les mêmes Printing3DComponent , tandis que le maillage « Wheel » peut contenir quatre matrices de transformation différentes (mappage des roues à quatre positions différentes sur le corps de la voiture). Dans ce scénario, le maillage « Corps » et le maillage « Roue » doivent être stockés une seule fois, même si le produit final comporterait cinq maillages au total.

Tous les objets Printing3DComponent doivent être directement référencés dans la propriété Components du modèle. Le composant particulier à utiliser dans le travail d’impression est stocké dans la propriété 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);

Enregistrer le package

Maintenant que nous avons un modèle, avec des matériaux et des composants définis, nous pouvons l’enregistrer dans le 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);

La fonction suivante garantit que la texture est spécifiée correctement.

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

À partir de là, nous pouvons lancer un travail d’impression dans l’application (voir impression 3D à partir de votre application) ou enregistrer ce package Printing3D3MFPackage en tant que fichier .3mf.

La méthode suivante prend un printing3D3MFPackage terminé et enregistre ses données dans un fichier .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);
        }
    }
}

Impression 3D à partir de votre application
Exemple d’impression 3D UWP