Skip to content

Conversation

@scottwittenburg
Copy link
Collaborator

Currently the screenshot capture functionality captures the render window image as it appears on the screen. However, some applications could benefit from the ability to specify an arbitrary size when capturing an image and have it rendered at full resolution rather than resampled.

I haven't tried this with WebGPU, but just provided an implementation the same as that for OpenGL.

@scottwittenburg scottwittenburg requested a review from jourdain May 11, 2021 20:35
@scottwittenburg
Copy link
Collaborator Author

@jourdain This is working for MIQA, but let me know what you think. Doing the resetCamera, but storing the current parameters first, seems to improve the situation when the requested screenshot size has a drastically different aspect ratio than whatever is displayed currently. But maybe that's not the right approach.

} else {
// Create a stand-in image overlay while we resize and render
const tmpImg = document.createElement('img');
tmpImg.width = model.size[0];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is that needed?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right, I think probably not needed. Will fix.

// Create a stand-in image overlay while we resize and render
const tmpImg = document.createElement('img');
tmpImg.width = model.size[0];
tmpImg.height = model.size[1];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is that needed?

// Without this resetCamera(), we can end up showing only a subset
// of the prop(s) depending on the difference between the aspect
// ratios of the original size and the custom capture size.
ren.resetCamera();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be optional and off by default.

Copy link
Collaborator Author

@scottwittenburg scottwittenburg May 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was wondering about that. It's kind of best effort anyway. To really allow you to get what you want in your screenshot, we'd probably have to call you back before we render and let you compute/set what you want.

But you think if we just make it optional (and default off), the way it is now could be good enough?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What should we call that option? screenshotResetCamera or resetCameraOnResizeScreenshot? Or just resetCamera and document it?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

opts = {
    resetCamera?: false, // Should only be needed if aspect ratio is different (Maybe auto on if size provided / but not sure)
    size?: [width, height],
    scale?: 1, // maintain aspect ratio but allow to adjust size based on original size?
}

publicAPI.modified();
// save camera parameters before resetCamera so we can restore
// them later
model.activeCamParamMap = new WeakMap();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not use a weak map but just rely on the order of the renderer because it really happen in a non-interactive setup.

You should use a private object on the model.

model._screenshot = {
    originalSize: [],
    imagePaceHolder: ...,
    cameras: [],
};

model.originalSize = [model.size[0], model.size[1]];

// hide it
model.canvas.style.display = 'none';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see you reversing it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reversing it is done in the updateWindow method as a result of calling publicAPI.modified().

// save camera parameters before resetCamera so we can restore
// them later
model.activeCamParamMap = new WeakMap();
model.renderable.getRenderers().forEach((ren) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The camera stuff is only required if we do the reset camera.

You can write it better like:

mode._screenshots.cameras = model.renderable.getRenderers().map(
  (renderer) => renderer.getActiveCamera().get('focalPoint', 'position', 'parallelScale')
);

Then when you revert it you can do

model.renderable.getRenderers().forEach((renderer, idx) => {
   renderer.getActiveCamera().set(model._screenshot.cameras[idx]);
});

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Thx.

Copy link
Collaborator

@jourdain jourdain May 11, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could even bundle the fn with it like

mode._screenshots.cameras = model.renderable.getRenderers().map((renderer) => {
   const camera = renderer.getActiveCamera();
   const arg = camera.get('focalPoint', 'position', 'parallelScale');
   const fn = camera.set;
   const resetCamera =  renderer.resetCamera;
   return { fn, arg, resetCamera };
});

// resetCamera (after all the param capture as cross camera link could mess up your parameter capture)
mode._screenshots.cameras.forEach(({ resetCamera }) => resetCamera());

// reset
mode._screenshots.cameras.forEach(({ fn, arg }) => fn(arg));

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you may have to clone the arg depending if you get a ref or a copy of each array.

clone = (obj) => JSON.parse(JSON.stringify(obj))

model.canvas.style.display = 'none';

// resize it
model.size[0] = model.customCaptureSize[0];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

model._screenshot.originalSize = model.size;
model.size = model._screenshot.size;

[...]

model.size = model._screenshot.originalSize;

};

publicAPI.captureImages = (format = 'image/png') => {
publicAPI.captureImages = (opts = {}) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add opts as a second argument to prevent a BREAKING_CHANGE...

const tmpImg = document.createElement('img');
tmpImg.width = model.size[0];
tmpImg.height = model.size[1];
tmpImg.style = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may want to have that style as a const at the module level. tmpImg.style = SCREENSHOT_PLACE_HOLDER;

@scottwittenburg scottwittenburg force-pushed the resizable-screenshots branch 2 times, most recently from e22423e to 097074c Compare May 13, 2021 21:25
@scottwittenburg scottwittenburg force-pushed the resizable-screenshots branch from 097074c to b4e0114 Compare May 13, 2021 21:32
@scottwittenburg
Copy link
Collaborator Author

@jourdain Let me know if I should at least document the duplication between the OpenGL and WebGPU implementations or something. Or if there's anything else you think this needs. Otherwise maybe we can target merging it tomorrow? Thanks!

@scottwittenburg
Copy link
Collaborator Author

@jourdain Do you think this is still missing something? If just rebase and force-push the branch and the checks are still green, could we merge it?

@scottwittenburg scottwittenburg force-pushed the resizable-screenshots branch from b4e0114 to ab45a23 Compare May 21, 2021 14:27
@jourdain jourdain merged commit 12f97e6 into master May 21, 2021
@jourdain jourdain deleted the resizable-screenshots branch May 21, 2021 15:09
@github-actions
Copy link

🎉 This PR is included in version 18.1.2 🎉

The release is available on:

Your semantic-release bot 📦🚀

@github-actions github-actions bot added the released Automated label label May 21, 2021
@scottwittenburg
Copy link
Collaborator Author

Thanks @jourdain 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

released Automated label

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants