Dr. Crunch
Dr. Crunch

Reputation: 27

Autodesk Forge Viewer Api Cannot load markups inside screenshot

Good day,

I am using the latest Autodesk forge viewer and I am trying to take a screenshot that also renders my markups. Right now my code takes a screenshot without any markups. Below is my viewer code. I am loading markups Core and markups Gui extensions. Notice the "takeSnapshot(viewer)" function inside onDocumentLoadSuccess(viewerDocument). The function is defined right before the initializer function.

function takeSnapshot(target){
    $('#snipViewer').click( () => {
        target.getScreenShot(1600, 920, (blobURL) => {
            let snip = blobURL;
            $('#sniplink').attr("href", snip);
            $('#sniplink').html('Not Empty');
            $('#sniplink').css({"background-image": `url(${blobURL})`});
        });
    });        
}

//Autodesk Viewer Code

instance.data.showViewer = function showViewer(viewerAccessToken, viewerUrn){
    localStorage.setItem("viewerAccessTokentoken", viewerAccessToken);
    localStorage.setItem("viewerUrn", viewerUrn);  
    var viewer;
    var options = {
        env: 'AutodeskProduction',
        api: 'derivativeV2',
        getAccessToken: function(onTokenReady) {
            var token = viewerAccessToken;
            var timeInSeconds = 3600;
            onTokenReady(token, timeInSeconds);
        }
    };
    Autodesk.Viewing.Initializer(options, function() {
        let htmlDiv = document.getElementById('forgeViewer');
        viewer = new Autodesk.Viewing.GuiViewer3D(htmlDiv);
        let startedCode = viewer.start();
        viewer.setTheme("light-theme");
        viewer.loadExtension("Autodesk.CustomDocumentBrowser").then(() => {
            viewer.loadExtension("Autodesk.Viewing.MarkupsCore");
            viewer.loadExtension("Autodesk.Viewing.MarkupsGui");
        });
        if (startedCode > 0) {
            console.error('Failed to create a Viewer: WebGL not supported.');
            $("#loadingStatus").html("Failed to create a Viewer: WebGL not supported.");
            return;
        }
        console.log('Initialization complete, loading a model next...');
    });
    var documentId = `urn:` + viewerUrn;
    var derivativeId = `urn:` + instance.derivativeUrn;
    Autodesk.Viewing.Document.load(documentId, onDocumentLoadSuccess, onDocumentLoadFailure);
    function onDocumentLoadSuccess(viewerDocument) {
        var defaultModel = viewerDocument.getRoot().getDefaultGeometry();
        viewer.loadDocumentNode(viewerDocument, defaultModel);
        takeSnapshot(viewer);
    }
    function onDocumentLoadFailure() {
        console.error('Failed fetching Forge manifest');
        $("#loadingStatus").html("Failed fetching Forge manifest.");
    }
}

I have already read this article: https://forge.autodesk.com/blog/screenshot-markups I have tried doing this method but the instructions are very unclear for me. <div style="width:49vw; height:100vh;display:inline-block;"><canvas id="snapshot" style="position:absolute;"></canvas><button onclick="snaphot();" style="position:absolute;">Snapshot!</button></div>

What is the canvas element here for? Am I supposed to renderToCanvas() when I load the markups extension inside the initialize function or in my screenshot function? Is there some way I can implement the renderToCanvas() without changing too much of what I already am using here? I am not an expert with the viewer API so please if you could help me it would be very much appreciated, I am a beginner please don't skip many steps.

Thank you very much!

Upvotes: 1

Views: 344

Answers (1)

Petr Broz
Petr Broz

Reputation: 9942

Here's a bit more simplified logic for generating screenshots with markups in Forge Viewer, with a bit more explanation on why it needs to be done this way below:

function getViewerScreenshot(viewer) {
    return new Promise(function (resolve, reject) {
        const screenshot = new Image();
        screenshot.onload = () => resolve(screenshot);
        screenshot.onerror = err => reject(err);
        viewer.getScreenShot(viewer.container.clientWidth, viewer.container.clientHeight, function (blobURL) {
            screenshot.src = blobURL;
        });
    });
}
function addMarkupsToScreenshot(viewer, screenshot) {
    return new Promise(function (resolve, reject) {
        const markupCoreExt = viewer.getExtension('Autodesk.Viewing.MarkupsCore');
        const canvas = document.createElement('canvas');
        canvas.width = viewer.container.clientWidth;
        canvas.height = viewer.container.clientHeight;
        const context = canvas.getContext('2d');
        context.clearRect(0, 0, canvas.width, canvas.height);
        context.drawImage(screenshot, 0, 0, canvas.width, canvas.height);
        markupCoreExt.renderToCanvas(context, function () {
            resolve(canvas);
        });
    });
}
const screenshot = await getViewerScreenshot(viewer);
const canvas = await addMarkupsToScreenshot(viewer, screenshot);
const link = document.createElement('a');
link.href = canvas.toDataURL();
link.download = 'screenshot.png';
link.click();

Basically, the markups extension can only render its markups (and not the underlying 2D/3D scene) into an existing <canvas> element. That's why this is a multi-step process:

  1. You render the underlying 2D/3D scene using viewer.getScreenShot, getting a blob URL that contains the screenshot image data
  2. You create a new <canvas> element
  3. You insert the screenshot into the canvas (in this case we create a new Image instance and render it into the canvas using context.drawImage)
  4. You call the extension's renderToCanvas that will render the markups in the canvas on top of the screenshot image

Upvotes: 1

Related Questions