Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Canvas works, SVG works, WebGL renders nothing #736

Closed
dan-fritchman opened this issue Sep 3, 2024 · 19 comments
Closed

Canvas works, SVG works, WebGL renders nothing #736

dan-fritchman opened this issue Sep 3, 2024 · 19 comments
Assignees
Labels

Comments

@dan-fritchman
Copy link

This is probably gonna be a pretty crappy bug report, but I'm not really sure what to report.

In our application SVG and Canvas have always worked. WebGL has never worked. It just renders a blank canvas.
It's a big complicated application; I'm not sure how to peel off a minimal repro case.

Currently using Two v0.8.14. Mostly in current Chrome, but also tried in Firefox & Safari, all on current MacOS.
And I did notice something today: calling functions like makeRectangle and makeCircle on our Two instance in the browser developer console does get them to show up:

image image

And noting, our application generally does not use those makeXyz functions. We do something more like:

const top = new Group();
const middle = new Group();
top.add(middle);
const bottom = new Rectangle(someArgs);
bottom.addTo(middle);
// etc

I guess the question is: does anything come to mind as to why that combination wouldn't work?

@dan-fritchman
Copy link
Author

I should add: leafing through that same browser console, it does appear that our application's content is in the two.scene; it has a big hierarchy of children (generally deeply nested Groups).

@jonobr1
Copy link
Owner

jonobr1 commented Sep 4, 2024

Hmmm, that is sufficiently vague. Couple things stand out as potential next steps:

  1. Does it work in older versions? Currently the WebGL examples do work on the two.js.org site which are using the latest NPM version
  2. Would you be open to adding me as a collaborator to debug? It is very possible that there is some discrepancy between the renderers that needs to be addressed. But, it's hard to tell from this vague of a use case.

Generally, if you're not using the makeXYZ methods on a specific instance. You'll need to do this:

const two = new Two({ type: Two.Types.webgl, autostart: true }).appendTo(document.body);
// If you haven't autostarted you need
// to call two.update();
const rectangle = new Two.Rectangle(x, y, width, height);
two.add(rectangle);

With groups as intermediaries it is possible that all the nested elements in a group could get missed on the update invocation. But, without an example it's hard to know for sure.

@dan-fritchman
Copy link
Author

  • So I don't think (2) will fly. Certainly not quickly.
  • FWIW we do use autostart. Just tested with it turned off, and running update() in the debug console, to no avail.
  • I can check some older versions. Probably started trying about a year ago. Definitely before the changes in Text Baseline in SVG Backend #716.

With groups as intermediaries it is possible that all the nested elements in a group could get missed on the update invocation. But, without an example it's hard to know for sure.

What do ya mean there? Is there anything we could dig into? Or something about the conditions which might make that happen?

Thanks again!

@jonobr1
Copy link
Owner

jonobr1 commented Sep 4, 2024

Could you export one scene in SVG? Then I could import it into Two.js using WebGL and it should produce the same error.

What I mean regarding groups, is if you can tell me the exact order that elements are added then maybe I can reproduce the issue.

To confirm, this only happens with simple paths? Or also with text and images?

This could give a hint because Text and Images are not based on Two.Path. All other primitive shapes are.

@dan-fritchman
Copy link
Author

OK that makes sense, I'll export some SVGs.
To more quickly answer your question - we use text, but no images. And definitely lots of Paths and Groups.

@dan-fritchman
Copy link
Author

OK attaching an example SVG.
That is generated with the (I think recommended method), something like:

  svgString = (): Result<string> => {
    this.two.update();
    const { innerHTML } = this.twoParentDiv;

    // Stick that `xmlns` attribute in there, so most readers will accept it.
    const s = innerHTML.replace(
      "<svg",
      '<svg xmlns="http://www.w3.org/2000/svg"'
    );
    return Result.Ok(s);
  };

example

Just double-checked that swapping to the webgl renderer renders this blank.

@jonobr1
Copy link
Owner

jonobr1 commented Sep 6, 2024

🤔 There are some differences. But when I load this SVG into the same webpage with a WebGL and an SVG instance they both render:

Screenshot 2024-09-06 at 12 33 37 PM

Left is WebGL Right is SVG

This is the code:

const a = new Two({
  type: Two.Types.webgl,
  autostart: true,
  width: 400,
  height: 400
}).appendTo(document.body);

const b = new Two({
  type: Two.Types.svg,
  autostart: true,
  width: 400,
  height: 400
}).appendTo(document.body);

a.renderer.domElement.style.border = '1px solid #ccc';
b.renderer.domElement.style.border = '1px solid #ccc';

a.add(a.load('./736.svg', (svg) => { svg.scale = 0.25 }));
b.add(b.load('./736.svg', (svg) => { svg.scale = 0.25 }));

@jonobr1
Copy link
Owner

jonobr1 commented Sep 6, 2024

An unrelated note: if at all possible I would highly recommend making the grid a single path.

@jonobr1 jonobr1 added question and removed bug labels Sep 6, 2024
@dan-fritchman
Copy link
Author

Very interesting indeed!
I guess that points to something about how we load/ add elements and eventually update/ render?

And one big path sounds like a pretty good idea.
I suppose just "snaking" that around offscreen would get it done.

@jonobr1
Copy link
Owner

jonobr1 commented Sep 6, 2024

Yep, it would reduce the draw calls

@dan-fritchman
Copy link
Author

FWIW passing that SVG content to two.load in the dev-console of our app running the WebGL back-end does in fact render that SVG content. (And continues to render nothing from our other groups.)

@jonobr1
Copy link
Owner

jonobr1 commented Sep 9, 2024

Hmm, that would indicate that there's something you're doing (or not doing) that two.load does.

This is all the method does once the content is loaded and available in the DOM

      for (i = 0; i < dom.temp.children.length; i++) {
        elem = dom.temp.children[i];
        child = this.interpret(elem, false, false);
        if (child !== null) {
          group.add(child);
        }
      }

@dan-fritchman
Copy link
Author

Is it possible that transforming (scaling & positioning) a group works differently in the webgl renderer than in the others?
We are generally transforming the coordinates in some external data into on-screen ones with something like this:

  // Apply to a `Two.Shape`.
  // Notably `Two.Group` is a `Shape`, and is the most common use-case.
  apply: (t: CoordTransform, shape: Shape) => {
    const { scale, offset } = t;
    // Note y-axis inversion is applied right here!
    shape.scale = new Vector(scale, -scale);
    shape.position = new Vector(offset.x, offset.y);
  },

Usually the shape argument to this function is a Group, which is in turn the sole child of our two.scene group.
This doesn't seem to be updating as it does with the other renderers. I'm wondering if everything is rendering, but is just off-screen due to a misunderstanding of this coordinate transform.

@dan-fritchman
Copy link
Author

I'll try to make a minimal demo case.

@dan-fritchman
Copy link
Author

OK yeah that's it, or something to do with it.
Here's a minimal example:
https://codepen.io/dan-fritchman/pen/vYqqLwz

import Two from 'https://cdn.skypack.dev/two.js@latest';

const two = new Two({
  type: Two.Types.svg,
  fullscreen: true,
  autostart: true
}).appendTo(document.body);

// Create a single child group
const g = new Two.Group();
g.addTo(two);

// Add a rectangle to it
const r = new Two.Rectangle(100,-100,100,100);
r.fill = "red";
r.addTo(g);

// Now scale and shift the parent group
g.scale = new Two.Vector(1, -1);
g.position = new Two.Vector(100, 100);

two.render();

Looks like:

image

Change to:

  type: Two.Types.webgl,

And it looks like this:

image

@jonobr1
Copy link
Owner

jonobr1 commented Sep 18, 2024

Ah, hah! Thank you! I'll look into this.

@jonobr1 jonobr1 added bug and removed question labels Sep 18, 2024
@jonobr1 jonobr1 self-assigned this Sep 18, 2024
@dan-fritchman
Copy link
Author

Thanks!

FWIW after toying with it some more, it seems to come up in the cases where the scale has opposite signs in x vs y. Eg in the example code scale is (1,-1) (where we are essentially converting "positive y is up" coordinates into "web browser coordinates"). Something similar goes wrong when setting it to (-1,1).

@jonobr1
Copy link
Owner

jonobr1 commented Sep 19, 2024

Ohhhhh, yes this makes sense. In WebGL you can define which "side" you want to render textures to (aka paths, images, text). In Two.js I believe I only have front facing enabled. By enabling both front and back this should fix the issue. I'll work on this right now.

@jonobr1
Copy link
Owner

jonobr1 commented Sep 21, 2024

The above PR addresses your issue. Thanks for reporting!

@jonobr1 jonobr1 closed this as completed Nov 7, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants