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,
): SceneObjectBillboardParameters
name(string)drawableObj(Drawable)objType(SceneObjectType)
Instance Methods
performDraw(
rendererObj: BGE.Renderer,
drawMode: SceneObjectDrawMode,
): boolean
Parameters
rendererObj(BGE.Renderer)drawMode(SceneObjectDrawMode)
Returns
boolean
isRedrawToCanvasRequired(
rendererObj: BGE.Renderer,
drawMode: SceneObjectDrawMode,
): boolean
Determine if we need to redraw directly to the canvas, instead of use previous temp bitmap
Parameters
rendererObj(BGE.Renderer)drawMode(SceneObjectDrawMode)
Returns
boolean
usedTransformedFastDraw(): boolean
For tests/telemetry only - see usedTransformedFastDrawLastFrame above.
Returns
boolean
isEligibleForFastDrawCheck(
drawMode: SceneObjectDrawMode,
): boolean
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
drawMode(SceneObjectDrawMode)
Returns
boolean
canUseTransformedFastDraw(): boolean
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
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
rendererObj(BGE.Renderer)drawMode(SceneObjectDrawMode)
Returns
boolean
usesTempBitmap(drawMode: SceneObjectDrawMode): boolean
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
drawMode(SceneObjectDrawMode)
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
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
Draw this SceneObject directly to the final canvas Override this function in subclasses to provide specific behavior
Parameters
rendererObj(BGE.Renderer)drawMode(SceneObjectDrawMode)
Returns
boolean
drawRegionAsRotatedQuad(
rendererObj: BGE.Renderer,
regionObj: roRegion,
quad: BGE.Math.CornerPoints,
color: integer,
): boolean
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 blitquad(BGE.Math.CornerPoints) — the target quad to matchcolor(integer) — packed RGBA tint/fill color
Returns
boolean— true if the draw call succeeded
drawOutlineToCanvas(
rendererObj: BGE.Renderer,
drawMode: SceneObjectDrawMode,
): boolean
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
rendererObj(BGE.Renderer)drawMode(SceneObjectDrawMode)
Returns
boolean— true if the outline was drawn
getOutlineCanvasPoints(): dynamic
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, orinvalidfor no outline
getCanvasBounds(): dynamic
Get the AA bounds of all points to be drawn on the final canvas
Returns
dynamic— the bounds of the canvas points
getAllCanvasPoints(): dynamic
Get all canvas points
Returns
dynamic
getRegionWithIdToDraw(
rendererObj: BGE.Renderer,
): BGE.RendererHelpers.RegionWithId
Parameters
rendererObj(BGE.Renderer)
Returns
BGE.RendererHelpers.RegionWithId
drawToTempBitmap(
rendererObj: BGE.Renderer,
tempBitmap: ifDraw2d,
canvasPointsTopLeftBound: BGE.Math.Vector,
drawMode: SceneObjectDrawMode,
allowFastDraw?: boolean,
): TempBitmapDrawResult
Parameters
rendererObj(BGE.Renderer)tempBitmap(ifDraw2d)canvasPointsTopLeftBound(BGE.Math.Vector)drawMode(SceneObjectDrawMode)allowFastDraw(boolean, optional, default: false)
Returns
getPinnedCornersOptions(): DrawPinnedCornersOptions
Returns
didRegionToDrawChange(): boolean
Returns
boolean
getDrawColorRGBA(
rendererObj: BGE.Renderer,
ignoreColor?: boolean,
): integer
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
rendererObj(BGE.Renderer)
Returns
integer— packed RGBA color
getOutlineDrawColorRGBA(
rendererObj: BGE.Renderer,
ignoreColor?: boolean,
): integer
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
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
Parameters
drawMode(SceneObjectDrawMode)
Returns
boolean
getPositionForCameraDistance(
drawMode: SceneObjectDrawMode,
): BGE.Math.Vector
Parameters
drawMode(SceneObjectDrawMode)
Returns
BGE.Math.Vector
getPositionsForFrustumCheck(
drawMode: SceneObjectDrawMode,
): dynamic
Parameters
drawMode(SceneObjectDrawMode)
Returns
dynamic
computeBillboardWorldCorners(): dynamic
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
Parameters
rendererObj(Renderer)drawMode(SceneObjectDrawMode)
Returns
boolean
updateCanvasPosition(
rendererObj: Renderer,
drawMode: SceneObjectDrawMode,
): boolean
Parameters
rendererObj(Renderer)drawMode(SceneObjectDrawMode)
Returns
boolean
needsCanvasCornerPoints(): boolean
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
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
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
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
rendererObj(Renderer)
Returns
boolean— true if every corner projected onto the canvas
getDirectModeCanvasSize(): SizeWH
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
Parameters
rendererObj(Renderer)
Returns
void
getNormalDebugPoint(): BGE.Math.Vector
Returns
BGE.Math.Vector
attemptTransformTempBitmap(
renderObj: Renderer,
drawMode: SceneObjectDrawMode,
): TransformTempBitmapDetails
Determine if we can just scale and/or rotate the temp bitmap to match the required position
Parameters
renderObj(Renderer)drawMode(SceneObjectDrawMode)
Returns
TransformTempBitmapDetails— the details of the transformation or invalid if it cannot be done
doNotDrawBecauseBackFace(drawMode: SceneObjectDrawMode): boolean
Is this a back and then we should not draw it?
Parameters
drawMode(SceneObjectDrawMode) — some drawModes explicitly should draw the backface
Returns
boolean— true if we should not draw
isDeterministicDrawFailure(
rendererObj: Renderer,
drawMode: SceneObjectDrawMode,
): boolean
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
rendererObj(Renderer)drawMode(SceneObjectDrawMode)
Returns
boolean
getTempBitmapThreshold(drawMode: SceneObjectDrawMode): integer
Parameters
drawMode(SceneObjectDrawMode)
Returns
integer