* @param {number} x - world x position * @param {number} y - world y position * @param {object} settings - configuration * @param {HTMLImageElement|TextureAtlas|string} [settings.image] - the sprite texture (image name, image, or atlas). Alias: `settings.texture`. * @param {number} [settings
(x, y, settings)
| 111 | * @param {number} [settings.alphaCutoff=0.5] - alpha cutout threshold (see {@link Mesh}). The mesh pass is opaque (no alpha blending), so this defaults to `0.5` to discard a sprite's transparent background (clean cutout silhouette, correct depth, no sorting). Set `0` for a fully-opaque quad, or tune the threshold. |
| 112 | */ |
| 113 | constructor(x, y, settings) { |
| 114 | // world-space quad size (in pixels): explicit width/height first, then the |
| 115 | // spritesheet frame size, then the full texture size. The texture atlas |
| 116 | // itself is resolved by the Mesh base class (passing framewidth/frameheight |
| 117 | // through, below) — so Sprite3d never touches the renderer directly. |
| 118 | let w; |
| 119 | let h; |
| 120 | if (typeof settings.width === "number") { |
| 121 | w = settings.width; |
| 122 | h = typeof settings.height === "number" ? settings.height : w; |
| 123 | } else if (typeof settings.framewidth === "number") { |
| 124 | w = settings.framewidth; |
| 125 | h = typeof settings.frameheight === "number" ? settings.frameheight : w; |
| 126 | } else { |
| 127 | const size = imageSize(settings); |
| 128 | w = size.w; |
| 129 | h = size.h; |
| 130 | } |
| 131 | const hw = w / 2; |
| 132 | const hh = h / 2; |
| 133 | // a unit quad with the real pixel size baked in, in the XY plane, facing |
| 134 | // +Z. `normalize: false` + `scale: 1` keeps these coordinates as-is so the |
| 135 | // fixed-orientation (non-billboard) case renders at the right size, and |
| 136 | // the billboard path reuses the local (±hw, ±hh) offsets directly. |
| 137 | const vertices = new Float32Array([ |
| 138 | -hw, |
| 139 | -hh, |
| 140 | 0, |
| 141 | hw, |
| 142 | -hh, |
| 143 | 0, |
| 144 | hw, |
| 145 | hh, |
| 146 | 0, |
| 147 | -hw, |
| 148 | hh, |
| 149 | 0, |
| 150 | ]); |
| 151 | // V flipped (1→0 top to bottom) so the texture renders upright under the |
| 152 | // Y-down render space, matching Sprite. Overwritten per-frame by |
| 153 | // `_applyFrame` once an animation/region is selected. |
| 154 | const uvs = new Float32Array([0, 1, 1, 1, 1, 0, 0, 0]); |
| 155 | const indices = new Uint16Array([0, 1, 2, 0, 2, 3]); |
| 156 | const normals = new Float32Array([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]); |
| 157 | |
| 158 | super(x, y, { |
| 159 | vertices, |
| 160 | uvs, |
| 161 | indices, |
| 162 | normals, |
| 163 | // let Mesh resolve the texture; forward the spritesheet grid so the |
| 164 | // resolved atlas carries animation frames |
| 165 | texture: settings.image ?? settings.texture, |
| 166 | framewidth: settings.framewidth, |
| 167 | frameheight: settings.frameheight, |
| 168 | // width/height drive the frustum-cull bounds; use the larger side so |
| 169 | // the cull sphere always encloses the quad whatever way it faces |
| 170 | width: Math.max(w, h), |
nothing calls this directly
no test coverage detected