vgstation-coders / vgstation13

Butts
GNU Affero General Public License v3.0
266 stars 546 forks source link

Parallax 2.0 #14247

Closed Exxion closed 7 years ago

Exxion commented 7 years ago

TL;DR: Just read the final two sentences if you can't be assed to read all of this

I've just had an interesting thought.

  1. Parallax uses a fair bit of CPU time because it has to move something like eighteen screen objects every time a mob with a connected client (with parallax enabled) moves.
  2. Applying a scale transform to a PLANE_MASTER introduces a parallax-like visual effect when an individual moves around static objects on that plane, and does so entirely clientside. However, this only works over part of the screen.
  3. Objects without TILE_BOUND render regardless of whether or not they're actually within view.

Here's my proposal:

  1. Expand the parallax images to fuckhuge dimensions, either in the dmi or programmatically on world startup.
  2. Make invisible and intangible /atom/movables with them as attached images and without TILE_BOUND.
  3. Spawn nine of each for each mob with a client (with parallax enabled) and send the images to their respective clients, with each copy for a given mob on a different plane. (Yes, this uses way more planes than the current approach, but we have a shitload of unused ones right above parallax.)
  4. Spawn one screen object for each parallax object, attached to their respective clients' screens, and use them as PLANE_MASTERs.
  5. To each parallax object, apply a scale transform increasing the size by a factor determined from the client's parallax speed settings and a translation opposite the one described below.
  6. To each respective screen object, apply a transform with reciprocal scale to the parallax object itself and with a translation to tile the covered areas over the screen.
  7. Render the parallax objects onto space by the same mechanism they currently are.
  8. It is now only necessary to move the parallax objects when the mob changes Z-levels, rather than with every movement.

I probably didn't do a great job explaining some of those steps, but oh well. Actually I'm going to elaborate on steps 5 and 6: When you apply a scale transform to a single atom, it just changes its apparent size. Nothing special. When you apply a scale transform to a PLANE_MASTER, though, it scales the entire canvas it draws that plane on. So the sprites are not only smaller, but closer together. It is, again, an effect very similar to parallax. There's a catch, though: This is an effect simply applied to rendering that plane onto your screen. It still only renders the parts of sprites that would be onscreen without the transform. So if you just did this the naive way, you would only have parallax in the middle part of your screen. That's where the other transforms and multiple copies of the objects come in. Say you have a parallax speed that corresponds to a scale transform of 1/2. The central parallax object would only cover the middle half of your screen in any dimension (so middle quarter of the whole screen). So you make eight more parallax objects and apply an additional translation to their respective screen objects to tile it over the whole screen (and disallow parallax speeds low enough to get a scale below 1/3). Now you have parallax covering the whole screen, but it's a repeating tile. So you apply the opposite translations to the parallax objects themselves so that the nine tiles form a single smooth image. Now all you have to do is move the parallax objects when the mob changes Z-levels so that the objects are on the right Z-level and in the proper place to avoid a jump in the parallax.

I THINK this would work, but I have a few worries:

  1. Scaling the images up and back down may make them fuzzy. Depends on how good BYOND is at it.
  2. Since PLANE_MASTERs cause the icons on their plane to be drawn onto one canvas and then drawn over the scene, they may not behave well with the wacky antics we use to render parallax exclusively over space.
  3. I have no idea how well BYOND handles objects with excessively-large attached images without TILE_BOUND.
  4. I know that the lack of TILE_BOUND will allow the icon to render regardless of distance or obstruction, but I'm not 100% sure if it works for attached images. If it doesn't, then a slightly modified version of this approach would still work, but it would come at the cost of losing parallax speed settings. I just tested it. This does work.

Unless someone else thinks this sounds like a fun project and wants to do it themselves, I intend to do this. I opened the issue because

  1. I would probably just get tired and give up or something if I didn't
  2. In case anyone has immediate objections and knows that, for some reason or another, this cannot work, so they can tell me so I don't waste my time
  3. I'd like a test sprite for the Z-level-wide parallax and am too lazy to make it myself

So if anyone has any reasons not to try this, go ahead and say them. And if anyone wants to make me a fucking massive test parallax sprite (doesn't have to look good), please also do that.

Exxion commented 7 years ago

https://gfycat.com/UnderstatedVengefulFlounder

Sorry for the shit quality. New computer. Haven't yet fiddled with capture settings. That's what it looks like with some things scaled to 1/2 and no tiling.

Exxion commented 7 years ago

Oh yeah I said this in the Discord a long-ass time ago but never posted it here To get speeds similar to current Parallax, you'd need vastly more planes than exist. This might be possible with a different approach (forcing the client window to be fucking massive and scaling up the play area rather than scaling down parallax) but even THAT'S difficult and the clientside performance would likely be absolutely awful Oh well

Exxion commented 6 years ago

A change made to PLANE_MASTER clipping in 512 has made this potentially possible, but I'm still not sure whether or not it would kill clientside performance. Also, obviously, we couldn't use such a thing until the viewport freezes are fixed, since it requires the client to be 512