Applying local translation to a Sprite instance

Good day, everyone! It’s me, again, with yet another sprite-related problem (sorry!) :stuck_out_tongue:

This one seems like it might be easily solved, except sprites don’t seem to support what I’m trying to do (apply local translation).

Here’s the scenario:

  • I’m animating a 2D Sprite created via SpritePackedManager, in a 3D environment (think 2.5D graphics)
  • The image displayed by the sprite needs to be moved as part of the animation, in the two local image dimensions (U,V)
  • The goal is to apply this translation locally, so that the movement always appear correct regardless of the camera direction

My initial approach was to simply change the absolute position by the U, V offsets. Unfortunately this doesn’t work if the camera isn’t aligned, i.e. its direction isn’t roughly (0, 0, 1), since in those cases the sprite’s local axes aren’t identical to the world axes. The result is that the underlying mesh moves “towards” and “away” from the camera instead, at varying angles, which of course breaks the animation and causes it to “jump”.

The effect I want to achieve is movement similar to this playground, except the box’s red face (“sprite”) always facing the camera (as they normally do): https://www.babylonjs-playground.com/#EYZE4Q#275

I’ve considered the following solutions:

  • Calculate the local transformation in terms of world coordinates, manually. I can’t seem to get it right, however
  • Creating an actual mesh and rendering the texture on top of it. Since there can be many sprites it might be inefficient, plus it feels awkward to do this manually. I’d rather use the existing Sprite APIs instead, which also fit better logically and allow me to not deal with the desired “billboard” effect myself
  • Poking around in the Sprite or SpritePackedManager objects. Sadly it doesn’t appear there’s an actual mesh object here that I could locally translate (similar to the playground above), which would be the easiest solution. In fact, it looks like the sprite manager simply has all the vertices stored in its private _vertexData field, so I can’t really use this to move an individual sprite instance even if I wanted to

So yeah, I hope one of you more experienced folks has done something similar or knows a way to apply the local translation to a given Sprite instance without breaking everything :slight_smile:

Thanks for your time! I appreciate any suggestions you might have.

Not entirely sure I understand what you’re after. Would moving it according to the camera’s matrix work?
https://playground.babylonjs.com/#EZVJMD

2 Likes

Adding @Pryme8 who knows a lot about the SpritePackedManager :slight_smile:

I think @raggar might have got it.

1 Like

Thank you for the quick reply! This was indeed the solution, and kind of what I was trying to do manually. I somehow didn’t think of using the camera’s view matrix and had tried to calculate the same thing using only the camera’s direction.

In case anyone else wants to do a similar thing, here’s (approximately) what I did:

// The sprite should appear to move U/V pixels on the horizontal/vertical axis, while maintaining its aspect ratio
let rescaledOffsetU = offsetU / PIXELS_PER_WORLD_UNIT
let rescaledOffsetV = offsetV / PIXELS_PER_WORLD_UNIT

// The sprite must move relative to the camera's view in order to appear as if it's moving in its local (U,V) coordinate space
// This is accomplished by taking the absolute direction (in UV space),
// then transforming it with the camera's view matrix (= changes required for any given object to appear orthogonal to it?)
let absoluteDirectionU = new BABYLON.Vector3(1, 0, 0)
let absoluteDirectionV = new BABYLON.Vector3(0, 1, 0)

// The unit vectors are already normalized (length = 1), otherwise one could use this
// absoluteDirectionU.normalize()
// absoluteDirectionV.normalize()

// Transform the sprite's local UV coordinates to world coordinates (XYZ)
let relativeDirectionU = new BABYLON.Vector3(0, 0, 0)
let relativeDirectionV = new BABYLON.Vector3(0, 0, 0)
BABYLON.Vector3.TransformNormalToRef(absoluteDirectionU, activeCamera.getWorldMatrix(), relativeDirectionU)
BABYLON.Vector3.TransformNormalToRef(absoluteDirectionV, activeCamera.getWorldMatrix(), relativeDirectionV)

// Moving in this direction will now make it appear to move in its UV space, despite always facing the camera
relativeDirectionU.normalizeFromLength(1)
relativeDirectionU.scaleInPlace(rescaledOffsetU)
relativeDirectionV.normalizeFromLength(1)
relativeDirectionV.scaleInPlace(rescaledOffsetV)

sprite.position.x = relativeDirectionU.x + relativeDirectionV.x
sprite.position.y = relativeDirectionU.y + relativeDirectionV.y
sprite.position.z = relativeDirectionU.z + relativeDirectionV.z
1 Like