Поделиться через


Создание пакета трехмерного формата производства

В этом руководстве описывается структура типа файла формата 3D (3MF) и способ использования API Windows.Graphics.Printing3D для создания и управления им.

Важные API

Что такое трехмерный формат производства?

3MF — это набор соглашений об использовании XML для описания внешнего вида и структуры трехмерных моделей для производства (трехмерная печать). Он определяет набор частей (обязательных и необязательных) и их связей с трехмерной производственной устройством. Набор данных, который соответствует 3MF, можно сохранить в виде файла с расширением .3mf.

Класс Printing3D3MFPackage в пространстве имен Windows.Graphics.Printing3D аналогилен одному 3mf-файлу, а другие классы сопоставляются с определенными XML-элементами в 3mf-файле. В этом руководстве описывается, как можно создать и задать каждую из основных частей документа 3MF, как можно использовать расширение материалов 3MF и как можно преобразовать и сохранить объект Printing3D3MFPackage в виде 3mf-файла. Дополнительные сведения о стандартах 3MF или расширении материалов 3MF см. в спецификации 3MF.

Основные классы в структуре 3MF

Класс Printing3D3MFPackage представляет полный документ 3MF, а в основе документа 3MF является его частью модели, представленной классом Printing3DModel . Большая часть информации о трехмерной модели хранится путем задания свойств класса Printing3DModel и свойств их базовых классов.

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

Метаданные

Часть модели документа 3MF может содержать метаданные в виде пар "ключ-значение" строк, хранящихся в свойстве метаданных . Существует предопределенные метаданные, но пользовательские пары можно добавить как часть расширения (описано более подробно в спецификации 3MF). Он зависит от получателя пакета (трехмерное производственное устройство), чтобы определить, следует ли обрабатывать метаданные и как обрабатывать метаданные, но рекомендуется включить как можно больше сведений в пакет 3MF.

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

Данные сетки

В этом руководстве сетка является телом трехмерной геометрии, созданной из одного набора вершин (хотя он не должен отображаться как один твердый). Часть сетки представлена классом Printing3DMesh . Допустимый объект сетки должен содержать сведения о расположении всех вершин, а также обо всех лицах треугольников, которые существуют между определенными наборами вершин.

Следующий метод добавляет вершины в сетку, а затем предоставляет им расположения в трехмерном пространстве.

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 в пакете должны храниться в свойстве Meshes класса Printing3DModel, как показано здесь.

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

Создание материалов

Трехмерная модель может хранить данные для нескольких материалов. Это соглашение предназначено для использования трехмерных производственных устройств, которые могут использовать несколько материалов на одном задании печати. Существует также несколько типов материальных групп, каждый из которых может поддерживать ряд различных отдельных материалов.

Каждая группа материалов должна иметь уникальный номер идентификатора ссылки, а каждый материал в этой группе должен иметь уникальный идентификатор. Затем различные объекты сетки в модели могут ссылаться на материалы.

Кроме того, отдельные треугольники на каждой сетке могут указывать различные материалы и различные материалы могут даже быть представлены в одном треугольнике, причем каждая вершина треугольника имеет другой материал, назначенный ему, и материал лица, вычисляемый как градиент между ними.

Сначала мы покажем, как создавать различные виды материалов в соответствующих группах материалов и хранить их в качестве ресурсов в объекте модели. Затем мы назначим различные материалы отдельным сеткам и отдельным треугольникам.

Базовые материалы

Тип материала по умолчанию — 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. Если трехмерный принтер использует только один материал, он будет печатать всю модель в этом материале, независимо от того, какие объекты или лица были назначены разными материалами.

Цветовые материалы

Цветные материалы похожи на базовые материалы, но они не содержат имени. Таким образом, они не дают никаких инструкций о том, какой тип материала должен использоваться машиной. Они содержат только цветовые данные и позволяют компьютеру выбрать тип материала (компьютер может предложить пользователю выбрать). В следующем примере 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);

Составные материалы

Составные материалы указывают производственному устройству использовать однородную смесь различных базовых материалов. Каждая составная группа материалов должна ссылаться ровно на одну базовую группу материалов, из которой следует рисовать ингредиенты. Кроме того, базовые материалы в этой группе, которые должны быть доступны, должны быть перечислены в списке индексов материалов, которые каждый составной материал будет ссылаться при указании коэффициентов (каждый составной материал является соотношением базовых материалов).

// 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-текстуру, необходимо сначала объявить ресурс текстуры.

Примечание.

Данные текстуры принадлежат самому пакету 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;

Далее мы должны заполнить текстуры3Coord Материалы. Каждая из этих ссылок на ресурс текстуры и указывает определенную точку на изображении (в координатах 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);
    }
}

Компоненты и сборка

Структура компонента позволяет пользователю размещать несколько объектов сетки в трехмерной модели. Объект Printing3DComponent содержит одну сетку и список ссылок на другие компоненты. На самом деле это список объектов Printing3DComponentWithMatrix. Объекты Printing3DComponentWithMatrix содержат матрицу преобразования Printing3DComponent и матрицу преобразования, которая применяется к сетке и содержащим компонентам объекта Printing3DComponent.

Например, модель автомобиля может состоять из "Body" Printing3DComponent , который содержит сетку для тела автомобиля. Затем компонент Body может содержать ссылки на четыре разных объекта Printing3DComponentWithMatrix, которые ссылаются на те же объекты Printing3DComponent, в то время как сетка "Колесо" может содержать четыре разных матрицы преобразования (сопоставление колес с четырьмя разными позициями на теле автомобиля). В этом сценарии сетка "Тело" и сетка "Колесо" должны храниться только один раз, хотя окончательный продукт будет содержать пять сетк в общей сложности.

Все объекты Printing3DComponent должны ссылаться непосредственно в свойстве Components модели. Один конкретный компонент, используемый в задании печати, хранится в свойстве сборки .

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

Отсюда можно либо инициировать задание печати в приложении (см . трехмерную печать из приложения), либо сохранить этот файл Print3D3MFPackage в виде 3mf-файла.

Следующий метод принимает завершенную печать3D3MFPackage и сохраняет свои данные в 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-печати UWP