SceneObjectBillboard

Extends: SceneObject

Properties

  • worldPoints (BGE.Math.CornerPoints)
  • canvasPoints (BGE.Math.CornerPoints)
  • canvasPosition (BGE.Math.Vector)
  • useTempBitmapMap (dynamic)
  • usedTransformedFastDrawLastFrame (boolean) — Whether the most recent oriented/solid draw took the cheap rotate+scale fast path ' (drawRegionAsRotatedQuad, below) instead of the exact drawPinnedCorners warp - see ' canUseTransformedFastDraw() below. Exposed read-only for tests/telemetry via ' usedTransformedFastDraw(). Shared by every single-quad billboard subclass that opts ' into the fast-draw check (SceneObjectCircle/#105, SceneObjectImage+SceneObjectText/#163).

Constructor

new SceneObjectBillboard( name: string, drawableObj: Drawable, objType: SceneObjectType, ): SceneObjectBillboard

Parameters


Instance Methods

performDraw( rendererObj: BGE.Renderer, drawMode: SceneObjectDrawMode, ): boolean

protected

Parameters

Returns

  • boolean

isRedrawToCanvasRequired( rendererObj: BGE.Renderer, drawMode: SceneObjectDrawMode, ): boolean

protected

Determine if we need to redraw directly to the canvas, instead of use previous temp bitmap

Parameters

Returns

  • boolean

usedTransformedFastDraw(): boolean

For tests/telemetry only - see usedTransformedFastDrawLastFrame above.

Returns

  • boolean

isEligibleForFastDrawCheck( drawMode: SceneObjectDrawMode, ): boolean

protected

Opt-in hook (default: never eligible) for the single-quad fast-draw check used by usesTempBitmap()/drawFastPath() below - override to return true for the draw modes where this object's fill is always a flat quad, so it's worth checking whether this frame's projected quad is a plain rotated rectangle (see canUseTransformedFastDraw()).

Parameters

Returns

  • boolean

canUseTransformedFastDraw(): boolean

protected

Whether m.canvasPoints (this frame's projected quad) is close enough to a pure rotation + non-uniform scale of a rectangle that drawRegionAsRotatedQuad() - one native DrawTransformedObject call - is a safe stand-in for the exact drawPinnedCorners triangle warp. True for a flat quad rotated only about the axis facing the camera (including not rotated at all); false once perspective foreshortening skews the quad into a general trapezoid - see issue #105.

Returns

  • boolean

drawFastPath( rendererObj: BGE.Renderer, drawMode: SceneObjectDrawMode, ): boolean

protected

Draws this object's fill via the cheap rotate+scale fast path - only valid to call once isEligibleForFastDrawCheck(drawMode) and canUseTransformedFastDraw() have both been confirmed (usesTempBitmap() below already does this and caches the result into usedTransformedFastDrawLastFrame for the same frame).

Parameters

Returns

  • boolean

usesTempBitmap(drawMode: SceneObjectDrawMode): boolean

protected

Whether this object caches its drawing into a temp bitmap in the given draw mode, so that later frames can blit (or just re-transform) that bitmap instead of rasterizing the object from scratch again. Worth it for anything whose draw involves triangle rasterization; not worth it for a draw that's already a single cheap call.

A subclass opted into the fast-draw check (isEligibleForFastDrawCheck() above) skips the temp bitmap entirely whenever this frame's quad qualifies (issue #105/#163) - unlike every other caller, this can flip frame-to-frame for the same draw mode (see performDraw()'s own note on that). Override useTempBitmapMap/isEligibleForFastDrawCheck to change the decision rather than this method itself.

Parameters

Returns

  • boolean

getTempBitmapCapacity(): BGE.Math.Vector

For tests/telemetry only - the backing temp bitmap's actual allocated size, which can be larger than this frame's own footprint (see createTempBitmap()'s grow-only reuse comment).

Returns

  • BGE.Math.Vector — {x: width, y: height}, or invalid if there's no temp bitmap

createTempBitmap( canvasBounds: Array.<BGE.Math.Vector>, rendererObj: Renderer, drawMode: SceneObjectDrawMode, allowFastDraw?: boolean, ): roBitmap

protected

Create a Temporary bitmap with a the image of the scene object in relation to the current camera viewport

Parameters

  • canvasBounds (Array.<BGE.Math.Vector>) — where in the final render will the bitmap be drawn [TL, BR]
  • rendererObj (Renderer)
  • drawMode (SceneObjectDrawMode)
  • allowFastDraw (boolean, optional, default: false)

Returns

  • roBitmap — the bitmap with the current perspective of the object or invalid if it failed

drawToCanvas( rendererObj: BGE.Renderer, drawMode: SceneObjectDrawMode, ): boolean

protected

Draw this SceneObject directly to the final canvas Override this function in subclasses to provide specific behavior

Parameters

Returns

  • boolean

drawRegionAsRotatedQuad( rendererObj: BGE.Renderer, regionObj: roRegion, quad: BGE.Math.CornerPoints, color: integer, ): boolean

protected

Blits regionObj as a single rotate + non-uniform-scale transform matching the given quad, anchored at its top left corner - a cheap approximation (one native DrawTransformedObject call, no per-pixel warp) that is only correct when the quad is actually a rotated rectangle (see BGE.Math.CornerPoints.isApproximatelyRotatedRectangle). directScaled can always use this (its quad is built from a rotation in the first place - see updateCanvasPointsForCameraFacingQuad); a caller drawing a genuinely projected/oriented quad (e.g. SceneObjectCircle's fast path, issue #105) must check that condition itself first and fall back to the exact drawPinnedCorners warp otherwise.

Parameters

  • rendererObj (BGE.Renderer)
  • regionObj (roRegion) — the region to blit
  • quad (BGE.Math.CornerPoints) — the target quad to match
  • color (integer) — packed RGBA tint/fill color

Returns

  • boolean — true if the draw call succeeded

drawOutlineToCanvas( rendererObj: BGE.Renderer, drawMode: SceneObjectDrawMode, ): boolean

protected

Strokes this object's outline onto the final canvas, on top of whatever has already been drawn for it. Only called when Drawable.hasOutline() is true and the draw mode isn't already outline-only.

Parameters

Returns

  • boolean — true if the outline was drawn

getOutlineCanvasPoints(): dynamic

protected

The canvas points this object's outline is stroked along, in perimeter order - note that this is not the order getAllCanvasPoints() returns, which is bounds order (topLeft, topRight, bottomLeft, bottomRight) and traces a bowtie if stroked directly. Return invalid to opt out of outline drawing entirely, which is what a SceneObject whose shape isn't a single quad (e.g. SceneObjectModel) should do.

Returns

  • dynamic — points to stroke along, or invalid for no outline

getCanvasBounds(): dynamic

protected

Get the AA bounds of all points to be drawn on the final canvas

Returns

  • dynamic — the bounds of the canvas points

getAllCanvasPoints(): dynamic

protected

Get all canvas points

Returns

  • dynamic

getRegionWithIdToDraw( rendererObj: BGE.Renderer, ): BGE.RendererHelpers.RegionWithId

protected

Parameters

Returns

  • BGE.RendererHelpers.RegionWithId

drawToTempBitmap( rendererObj: BGE.Renderer, tempBitmap: ifDraw2d, canvasPointsTopLeftBound: BGE.Math.Vector, drawMode: SceneObjectDrawMode, allowFastDraw?: boolean, ): TempBitmapDrawResult

protected

Parameters

  • rendererObj (BGE.Renderer)
  • tempBitmap (ifDraw2d)
  • canvasPointsTopLeftBound (BGE.Math.Vector)
  • drawMode (SceneObjectDrawMode)
  • allowFastDraw (boolean, optional, default: false)

Returns

getPinnedCornersOptions(): DrawPinnedCornersOptions

protected

Returns

didRegionToDrawChange(): boolean

protected

Returns

  • boolean

getDrawColorRGBA( rendererObj: BGE.Renderer, ignoreColor?: boolean, ): integer

protected

Parameters

  • rendererObj (BGE.Renderer)
  • ignoreColor (boolean, optional, default: false)

Returns

  • integer

getShadedColorRGBA(rendererObj: BGE.Renderer): integer

For tests/telemetry only - the fill color actually used for the most recent draw, including the isShaded brightness adjustment (see applyShading()/ambientBrightness).

Parameters

Returns

  • integer — packed RGBA color

getOutlineDrawColorRGBA( rendererObj: BGE.Renderer, ignoreColor?: boolean, ): integer

protected

The outline stroke color, given the same shading treatment as the fill color. Drawable.getOutlineColorRGBA falls back to the fill color when no outlineRGBA has been set, which is what keeps the wireFrame draw modes looking the way they did before outlines were supported.

Parameters

  • rendererObj (BGE.Renderer)
  • ignoreColor (boolean, optional, default: false) — ignore the drawable's own color and use white

Returns

  • integer — packed RGBA color to stroke with

getNetRotationZ(): float

protected

This object's total rotation about the z axis - its drawable's own rotation plus the rotation of the entity it's attached to. This is the only rotation component the direct (billboard) draw modes apply, since by definition they don't orient in 3D.

Returns

  • float — rotation about z, in radians

updateWorldPosition(drawMode: SceneObjectDrawMode): boolean

protected

Parameters

Returns

  • boolean

getPositionForCameraDistance( drawMode: SceneObjectDrawMode, ): BGE.Math.Vector

Parameters

Returns

  • BGE.Math.Vector

getPositionsForFrustumCheck( drawMode: SceneObjectDrawMode, ): dynamic

protected

Parameters

Returns

  • dynamic

computeBillboardWorldCorners(): dynamic

protected

Computes this object's 4 world-space corners unconditionally, regardless of draw mode - the same pretranslation/size/transform math updateWorldPosition()'s oriented-mode branch already does, duplicated here rather than factored out since updateWorldPosition is a hot path best left alone.

Not itself a getBoundingPoints() override: the base SceneObject.getBoundingPoints() wrapper around getPositionsForFrustumCheck() deliberately collapses to a single point (m.worldPosition) in the screen-aligned draw modes - a correct, cheap optimization for frustum culling, where a billboard's anchor position is enough to tell whether it's even worth a closer look. But overlap detection (see BGE.DepthSort.groupIntoClusters) needs this object's actual extent, not just its anchor - a single point can never be found to overlap another shape, so every screen-aligned billboard (the common case for any 2D game, since Camera2d resolves matchCamera to directToCamera) would otherwise never cluster with anything no matter how much it visually overlaps.

A blanket override here would have been wrong, though: SceneObjectPolygon and SceneObjectModel also extend SceneObjectBillboard, but their drawables never set width/height (they stay at the base Drawable default of 0), and those two classes already have their own correct, multi-point getPositionsForFrustumCheck() overrides (m.polygonWorldPoints / m.modelWorldBounds) that the base class's getBoundingPoints() already delegates to correctly. So only the leaf classes whose drawable genuinely has a width/height - SceneObjectImage, SceneObjectRectangle, SceneObjectCircle, SceneObjectText - override getBoundingPoints() to call this.

Returns

  • dynamic — the object's 4 world-space corners

findCanvasPosition( rendererObj: Renderer, drawMode: SceneObjectDrawMode, ): boolean

protected

Parameters

Returns

  • boolean

updateCanvasPosition( rendererObj: Renderer, drawMode: SceneObjectDrawMode, ): boolean

protected

Parameters

Returns

  • boolean

needsCanvasCornerPoints(): boolean

protected

Whether the four canvas corner points are needed in the direct (billboard) draw modes, which otherwise get by with the single canvasPosition. Computing them isn't free, so it's opt-in: outlines are stroked along those corners, so anything with an outline needs them. Override to require them for other reasons too.

Returns

  • boolean

needsManualPretranslationForDirectMode(): boolean

protected

Whether this object's plain 2D draw path (directToCamera/matchCamera) needs its anchor applied by hand here, rather than getting it for free some other way. True for anything with no native roRegion to lean on (DrawableRectangle, DrawableText) - false (the default) for anything backed by a region (SceneObjectImage), since Roku's own DrawObject/DrawScaledObject already apply the region's pretranslation natively.

Returns

  • boolean

getCanvasPosition(): BGE.Math.Vector

The top-left canvas point this billboard's screen-aligned (direct/directScaled) draw modes last positioned at, or invalid before the first successful draw.

Returns

  • BGE.Math.Vector

getWorldPoints(): BGE.Math.CornerPoints

The world-space corner points this billboard's oriented draw modes last computed.

Returns

  • BGE.Math.CornerPoints

getCanvasPoints(): BGE.Math.CornerPoints

The canvas corner points this billboard last computed - populated for a rotated or outlined direct-mode object (see needsCanvasCornerPoints()), or any oriented one.

Returns

  • BGE.Math.CornerPoints

updateCanvasPointsForDirectMode(): void

protected

Lays the four canvas corner points out around canvasPosition for the direct (billboard) draw modes, which draw a screen-aligned quad rather than projecting each world corner through the camera the way the oriented draw modes do (see updateCanvasPosition). canvasPosition is the quad's top left corner and the point any rotation pivots around, matching how drawToCanvas blits the region.

Returns

  • void

updateCanvasPointsForCameraFacingQuad( rendererObj: Renderer, ): boolean

protected

Builds this object's quad in the plane the camera is looking at, then projects it - which is what makes directScaled a Doom-style sprite: it faces the camera by construction, it shrinks purely with distance because its corners are offset by world units before being projected, and it is completely independent of the object's own rotation. (Deriving the on-screen size from the object's own projected quad instead would fold its orientation into its size, so a sprite turned edge-on would squash - never what a billboard wants.)

The quad is anchored at its top left corner, exactly as directToCamera anchors the region it blits - so an object switched between the two billboard modes stays put, and only its size changes.

Parameters

Returns

  • boolean — true if every corner projected onto the canvas

getDirectModeCanvasSize(): SizeWH

protected

The on-canvas size of this object in the direct (billboard) draw modes - its drawn size scaled by the owning entity, matching the scale drawToCanvas applies to the region it blits.

Returns

computeNormalDebugInfo(rendererObj: Renderer): void

protected

Parameters

Returns

  • void

getNormalDebugPoint(): BGE.Math.Vector

protected

Returns

  • BGE.Math.Vector

attemptTransformTempBitmap( renderObj: Renderer, drawMode: SceneObjectDrawMode, ): TransformTempBitmapDetails

protected

Determine if we can just scale and/or rotate the temp bitmap to match the required position

Parameters

Returns

doNotDrawBecauseBackFace(drawMode: SceneObjectDrawMode): boolean

protected

Is this a back and then we should not draw it?

Parameters

Returns

  • boolean — true if we should not draw

isDeterministicDrawFailure( rendererObj: Renderer, drawMode: SceneObjectDrawMode, ): boolean

protected

A backface cull and an in-frustum-but-entirely-off-canvas rejection are both deterministic for a billboard: neither depends on anything beyond this object's own orientation/position relative to the camera and canvas, both of which are exactly what triggers a recheck in the first place (see objMovedInRelationToCamera() / geometryChanged()). Recomputing doNotDrawBecauseBackFace() here is cheap (it's already just read from m.isMirror, computed once per findCanvasPosition() call) and isBoundingBoxOutsideCanvas() is a plain bounding-box test against points already computed for this draw - neither repeats the actual (expensive) frustum check. See issue #73.

Parameters

Returns

  • boolean

getTempBitmapThreshold(drawMode: SceneObjectDrawMode): integer

protected

Parameters

Returns

  • integer