Jump to content

Programmatically convert between glTF/PBR texture coordinates and legacy/Blinn-Phong coordinates


Recommended Posts

So! PBR is great so far. However, I'd love to be able to do a particular thing with it that I'm not sure how to do mathematically.

See, with legacy textures, textures rotate and scale around the center of the texture ...

Screenshot2024-04-10171102.thumb.png.de44f5f1c961204a4f73709e1454403c.png

... however, with PBR, textures rotate and scale around the upper left corner of the texture ...

Screenshot2024-04-10171253.thumb.png.89f53ea248ba6de6d0c2ca6215ebcbf3.png

The offsets also do the same thing: moving along the X-axis moves them in the same direction, but the Y-axis moves the texture up in glTF, and down in legacy!

Screenshot2024-04-10173051.png.70ba7a0bb2999eac3f500a9d8d4364a6.png

... and, in PBR but not Legacy, when you set one (but not both) of the Scale values to a negative number, the rotation changes direction!

Screenshot2024-04-10191605.thumb.png.268b72d6ea22332032cea5ce4fdf7c6c.png

So, if you want use LSL to set the texture in both glTF and Blinn-Phong (or, I mean, manually either), I have to convert between the two coordinate systems.

When rotation is 0, I have it down pat:

list glTF2LegacyAndBack (vector repeats, vector offsets, float rot, integer isLegacy2glTF)
{
    if (repeats.x < 0 == repeats.y < 0)
        rot = -rot;
    if (rot < 0) rot += TWO_PI;
    offsets.x = -offsets.x;
    offsets.z = repeats.z = 0;

    vector otherOffset;
    // Half the difference between "expanded repeat" and "no repeat".
    otherOffset.x = (llFabs(repeats.x) - 1) / 2;
    otherOffset.y = (llFabs(repeats.y) - 1) / 2;
    // Make sure it stays within the range of [0..1)
    otherOffset.x = otherOffset.x - (integer)otherOffset.x;
    otherOffset.y = otherOffset.y - (integer)otherOffset.y;
    if (otherOffset.x < 0) otherOffset.x += 1;
    if (otherOffset.y < 0) otherOffset.y += 1;

    // Apply the offsets!
    // I wrote it as a single function because interestingly enough, it seems that the only difference
    // between the two processes (at least when rot == 0) is this exact place: you want to negate
    // otherOffset.x when repeats.x is zero when converting from legacy to PBR, and when repeats.x is
    // *non*negative when converting PBR to legacy.
    if (repeats.x < 0 == isLegacy2glTF)
        otherOffset.x = -otherOffset.x;
    if (repeats.y < 0)
        otherOffset.y = -otherOffset.y;
  
    // ... Okay, what do I do with rot now????

    //Constrain it to [0..1) again.
    offsets.x = offsets.x - (integer)offsets.x;
    if (offsets.x < 0) offsets.x += 1;

    offsets.y = offsets.y - (integer)offsets.y;
    if (offsets.y < 0) offsets.y += 1;

    return [ repeats, offsets, rot ];
}

However, I'm completely at a loss for what to do when rotation is nonzero. I tried simply rotating otherOffset around the Z-axis (in either direction, and both before and after applying the modified offsets), but that didn't work at all; it kept ending up in the wrong spot no matter what I did. Help?

Edited by Tizzy Calliope
  • Thanks 1
Link to comment
Share on other sites

Whoops, left out an important item:

// Directly beneath " Okay, what do I do with rot now????":

offsets -= otherOffset;

Also, for reasons I cannot figure out, setting "offsets" to a nonzero value always causes it to move in the opposite direction from the one intended, no matter what I do! I guess I understood this even less than I thought!!

  • Thanks 1
Link to comment
Share on other sites

Picked at the problem some more; figured out the earlier bugs. Aaaand ... it's basically resolved!

list glTF2LegacyAndBack (vector repeats, vector offsets, float rot, integer isLegacy2glTF)
{
    if (repeats.x < 0 == repeats.y < 0)
        rot = -rot;

    // One of X and Y needs to be flipped here, and if it's X, I need to flip both of them in the end.
    offsets.y = -offsets.y;

    vector otherOffset;
    // Half the difference between "expanded repeat" and "no repeat".
    otherOffset.x = (llFabs(repeats.x) - 1) / 2;
    otherOffset.y = (llFabs(repeats.y) - 1) / 2;
    // Make sure it stays within the range of [0..1)
    otherOffset.x = otherOffset.x - llFloor(otherOffset.x);
    otherOffset.y = otherOffset.y - llFloor(otherOffset.y);
    if (otherOffset.x < 0) otherOffset.x += 1;
    if (otherOffset.y < 0) otherOffset.y += 1;

    // Reapply the offsets now that we have the absolute value handled!
    // I wrote it as a single function because interestingly enough, it seems that the only difference
    // between the two processes (at least when rot == 0) is this exact place: you want to negate
    // otherOffset.x when repeats.x is zero when converting from legacy to PBR, and when repeats.x is
    // *non*negative when converting PBR to legacy.
    if (repeats.x < 0 == isLegacy2glTF)
        otherOffset.x = -otherOffset.x;
    if (repeats.y < 0)
        otherOffset.y = -otherOffset.y;

    //Now, rearrange rotation!
    if (rot != 0)
    {
        vector sideOffset = <0.5 * llFabs(repeats.x), 0.5 * llFabs(repeats.y), 0>;
        vector rotBase = <0, 0, rot>;

        // You need to reverse the angle of rotation UNLESS converting glTF to legacy AND
        // repeats.x OR repeats.y is negative (but not both).
        // Starting with the opposite to save an op.
        if (isLegacy2glTF || (repeats.x < 0) == (repeats.y < 0))
            rotBase.z = -rotBase.z;

        if (repeats.x < 0 == isLegacy2glTF)
            sideOffset.x = -sideOffset.x;
        if (repeats.y < 0)
            sideOffset.y = -sideOffset.y;
        sideOffset = sideOffset * llEuler2Rot(rotBase) - sideOffset;

        otherOffset += sideOffset;
    }

    offsets -= otherOffset;

    //Constrain it to [0..1) again.
    offsets.x = offsets.x - llFloor(offsets.x);
    offsets.y = offsets.y - llFloor(offsets.y);

    if (offsets.x < 0) offsets.x += 1;
    if (offsets.y < 0) offsets.y += 1;

    return [ repeats, offsets, rot ];
}

Major caveat: it goes off-kilter in the following circumstances:

  1. rotation is nonzero, and
  2. the absolute values of the X and Y of repeat are different values (i.e. -1.5/1.5 is okay, but not 1.5/2).

The way it goes off-kilter is that the PBR face looks slightly skewed in the above conditions.

Screenshot2024-04-14080626.thumb.png.3ae030f75fdbb9b9dfe9a158483328d7.png

And the reason for this is once again glTF doing the opposite of Legacy: with Legacy, it applies the repeats and then the rotation -- basically Blinn-Phong behaves functionally the same as if you'd set the texture's Repeats to arbitrary values and the rotation to 0, then rotated the prim the texure was on instead of rotating the texture itself. Wheras with PBR, it applies the rotation before the repeats, as if you'd set the repeats to the same absolute value, rotated the texture an arbitrary amount, and then resized the prim.

So: not perfect, but this issue is as close to "done" as it was ever gonna get.

  • Thanks 3
Link to comment
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...