Quickstart: 3D printing (DirectX)

[This article is for Windows 8.x and Windows Phone 8.x developers writing Windows Runtime apps. If you’re developing for Windows 10, see the latest documentation]

Add 3D print functionality to your app running in Windows 8.1 using C++ and DirectX.

Prerequisites

To add 3D printing to your app, you must:

  • Be familiar with C++, 3D manufacturing API, and Windows printing.
  • Have Microsoft Visual Studio Express 2013 for Windows installed.
  • Have an app to which you want to add printing. If you don't have your own app, you can download the 3D Print Sample app and use that app.
  • Have a 3D-capable printer installed. If you do not have a 3D printer, you can get a sample 3D print driver package from Microsoft’s 3D Printing SDK.

1. Open the app's source code for editing

This procedure describes how to open the 3D Print Sample. If you are using your own app, open it in Visual Studio Express 2013 for Windows and skip to the next step.

Note  The code examples shown in this Quickstart refer to the 3D Print Sample. You might need to add more code from the sample app to use these examples in your own app.

 

  1. Go to the 3D Print Sample and download it to your computer.
  2. In Visual Studio Express 2013 for Windows, click File > Open Project and go to the folder that contains the solution file of the sample app that you downloaded in the previous step.
  3. Select the Sample3DPrint solution file and click Open.

2. Build and test the app

  1. Click Build > Build Solution to build the app you are working on. Make sure that there are no error messages in the Output pane at the bottom of the screen.
  2. Click Debug > Start Without Debugging.
  3. Verify that, after a few seconds, the screen displays the 3D Print Sample app.
  4. If the app runs without error, return to Visual Studio Express 2013 for Windows and click Debug > Stop Debugging.

3. Register for the Print contract

The first step to add 3D printing to your app is to register for the Print contract. Registering for the Print contract is the same for 3D printing as it is for Printing. First, declare the PrintManager and PrintDocument objects. The PrintManager type is in the Windows.Graphics.Printing namespace along with other types that support printing.

4. Assign a callback method

Provide a callback method for when a print queue is selected from the Print charm, like this.

void DirectXApp::InitializePrint()
{
    // Call static method to get PrintManager object.
    m_printManager = Windows::Graphics::Printing::PrintManager::GetForCurrentView();

    // Start print task event.
    m_printManager->PrintTaskRequested +=
        ref new TypedEventHandler<PrintManager^, PrintTaskRequestedEventArgs^>(this, &DirectXApp::SetPrintTask);
}

5. Generate a preview image from 3D data on the print dialog.

Print previews for 3D printing are handled just like print previews for 2D printing. The app is responsible for providing a two-dimensional preview of the print content. For example, the 3D Print Sample uses Microsoft Direct3D to render a representation of the cube to be printed.

6. Determine whether the target printer is a 2D or 3D printer

Inspect the available package targets and then get the corresponding package writer, like this.

// See if package target supports 3D and get the right kind of package target interface (2D or 3D).
ComPtr<IXpsDocumentPackageTarget>   packageTarget2D;
ComPtr<IXpsDocumentPackageTarget3D> packageTarget3D;
bool is3D = false;

if (SUCCEEDED(hr))
{
    for (auto it = pGuids; !is3D && it != pGuids + targetCount; ++it)
    {
        if (IsEqualGUID(*it, ID_DOCUMENTPACKAGETARGET_OPENXPS_WITH_3D))
        {
            is3D = true;
        }
    }

    if (is3D)
    {
        hr = docPackageTarget->GetPackageTarget(
                    ID_DOCUMENTPACKAGETARGET_OPENXPS_WITH_3D,
                    __uuidof(IXpsDocumentPackageTarget3D),
                    reinterpret_cast<void**>(packageTarget3D.GetAddressOf()));
    }
    else
    {
        hr = docPackageTarget->GetPackageTarget(
                    pGuids[0],  // Either XPS or OXPS package target
                    __uuidof(IXpsDocumentPackageTarget), 
                    reinterpret_cast<void**>(packageTarget2D.GetAddressOf()));
    }
}

CoTaskMemFree(pGuids);

7. Generate a 3MF model stream

In this example, PageRenderer3D::Generate3dModelXml accepts a stream as a parameter and uses it to write 3D content to the printer. In the next step, GetXpsOMPackageWriter3D will use this model stream to get a package writer.

Note  The 3D content must follow the 3D Manufacturing Format (3MF) document specifications.

 

// The output IStream represents the 3D model of the scene. The XML follows the 3MF format.
//
void
PageRenderer3D::Generate3dModelXml(
    _Out_ ComPtr<IStream>& spOutStream
    )
{
…

    ComPtr<IXmlWriter> spWriter;
…
// Output vertices
    DX::ThrowIfFailed(spWriter->WriteStartElement(nullptr, L"vertices", nullptr));

    for (unsigned int i = 0; i < ARRAYSIZE(cubeVertexData); i++)
    {
        DX::ThrowIfFailed(spWriter->WriteStartElement(nullptr, L"vertex", nullptr));
        DX::ThrowIfFailed(spWriter->WriteAttributeString(nullptr, L"x", nullptr, 
            (cubeVertexData[i].pos.x - xOffset).ToString()->Data()));
        DX::ThrowIfFailed(spWriter->WriteAttributeString(nullptr, L"y", nullptr, 
            (cubeVertexData[i].pos.y - xOffset).ToString()->Data()));
        DX::ThrowIfFailed(spWriter->WriteAttributeString(nullptr, L"z", nullptr, 
            (cubeVertexData[i].pos.z - xOffset).ToString()->Data()));
        DX::ThrowIfFailed(spWriter->WriteEndElement());//</vertex>
    }

    DX::ThrowIfFailed(spWriter->WriteEndElement());//</vertices>
…
}

8. Get a package writer

Get a package writer by creating an IXpsOMPackageWriter3D object from the package target.

ComPtr<IXpsOMPackageWriter>   packageWriter2D;
ComPtr<IXpsOMPackageWriter3D> packageWriter3D;
ComPtr<IOpcPartUri> partUriModel;

if (SUCCEEDED(hr))
{
    if (is3D)
    {
        hr = xpsFactory->CreatePartUri( L"/3D/3dModel.model", partUriModel.GetAddressOf() );
            
        if (SUCCEEDED(hr))
        {
            hr = packageTarget3D->GetXpsOMPackageWriter3D(
                 partUriFDS.Get(),
                 NULL,
                 partUriModel.Get(),
                 modelStream.Get(),
                 packageWriter3D.GetAddressOf()
                 );
        }
            
        if (SUCCEEDED(hr))
        {
            hr = (packageWriter3D.Get())->QueryInterface( __uuidof(IXpsOMPackageWriter), 
                reinterpret_cast<void**>(packageWriter2D.GetAddressOf()) );
        }
    }
    else
    {
        hr = packageTarget2D->GetXpsOMPackageWriter(
             partUriFDS.Get(),
             NULL,
             packageWriter2D.GetAddressOf()
             );
    }
}

9. Generate and send the PrintTicket stream to the 3D package writer

  1. Generate a print ticket and write it to a stream.

    ComPtr<IStream> modelStream;
    ...
    
  2. Generate a model PrintTicket URI.

    ComPtr<IOpcPartUri> partUriPT;
    if (SUCCEEDED(hr))
    {
        hr = xpsFactory1->CreatePartUri( L"/3D/Metadata/Model_PT.xml", partUriPT.GetAddressOf() );
    }
    
  3. Add the PrintTicket to the 3D payload.

    if (SUCCEEDED(hr))
    {
        hr = packageWriter3D->SetModelPrintTicket( partUriPT.Get(), modelStream.Get() );
    }
    

10. Add a 2D payload to the 2D package target, like in non-3D printing apps.

Note  You must complete this step, or a blank page will be generated if the user prints to a non-3D printer.

 

Summary

In this Quickstart, you added 3D printing to your Windows Store app using C++ and DirectX.

Supporting 3D printing

XPS Document API

Microsoft 3D Printing Resources

IXpsDocumentPackageTarget3D

IXpsOMPackageWriter3D