' @module BGE
namespace BGE
class DrawablePolygon extends DrawableWithOutline
' the set of points defining a convex polygon
points as object = []
' The number of frames before the polygon is completely finished
drawLevels as integer = 2
' Draw mode for the polygon, bitmask of:
' 1 wireframe (just edges of triangles)
' 2 fill (render with rectangles)
' 4 rays (render with rays)
drawModeMask as integer = 3
protected tempCanvas as object = invalid
protected lastPoints as object = []
protected triangles as object = []
protected hulledPoints as object = []
protected lastDrawMode as integer = 3
protected currentDrawLevel = 0
protected minimumFrameRateTarget as integer = 1
protected maxDrawLevels as integer = 16
function new(owner as GameEntity, canvasBitmap as object, points = [] as object, args = {} as object) as void
super(owner, canvasBitmap, args)
m.points = BGE.cloneArray(points)
m.append(args)
end function
override function draw(additionalRotation = 0 as float) as void
if m.drawLevels < 1
m.drawLevels = 1
end if
if not m.enabled or invalid = m.points
return
end if
m.automaticallyAdjustDrawLevels()
if BGE.pointArraysEqual(m.points, m.lastPoints) and not m.shouldRedraw and m.lastDrawMode = m.drawModeMask
if m.currentDrawLevel < m.drawLevels
m.drawNextLevelOfPolygons()
end if
m.drawRegionToCanvas(m.tempCanvas, additionalRotation)
return
end if
m.lastDrawMode = m.drawModeMask
m.lastPoints = BGE.cloneArray(m.points)
m.hulledPoints = BGE.QuickHull.QuickHull(m.points)
m.triangles = BGE.QuickHull.getTrianglesFromPoints(m.hulledPoints, false)
widthAndOffset = BGE.QuickHull.getMaxWidthAndHorizontalOffset(m.hulledPoints)
heightAndOffset = BGE.QuickHull.getMaxHeightAndVerticalOffset(m.hulledPoints)
m.width = widthAndOffset.width
m.height = heightAndOffset.height
m.offset_x = widthAndOffset.offset
m.offset_y = heightAndOffset.offset
m.lastWidth = m.width
m.lastHeight = m.height
m.currentDrawLevel = 0
m.tempCanvas = CreateObject("roBitmap", {
width: m.width,
height: m.height,
AlphaEnable: true
})
m.drawNextLevelOfPolygons()
m.drawRegionToCanvas(m.tempCanvas, additionalRotation)
m.shouldRedraw = false
end function
' Change the draw levels based on how long the last frame took to render
' If the last frame took longer than expected, increase the draw levels
' If the last frame took 1/2 as long as expected, decrease the draw levels
private sub automaticallyAdjustDrawLevels()
frameExpectedPerTime = (m.owner.game.getDeltaTime() * m.minimumFrameRateTarget)
if m.drawLevels < m.maxDrawLevels and frameExpectedPerTime > 1
m.drawLevels *= 2
if m.drawLevels < m.maxDrawLevels and frameExpectedPerTime > 2
m.drawLevels *= 2
end if
else if frameExpectedPerTime < 1 and m.drawLevels > 4
m.drawLevels /= 2
if m.currentDrawLevel > m.drawLevels
m.currentDrawLevel = 0
end if
end if
end sub
private sub drawNextLevelOfPolygons()
if m.drawModeMask and 2
m.fillPolygonWithRectangles(m.hulledPoints, m.drawLevels, m.currentDrawLevel)
end if
for each tri in m.triangles
if m.drawModeMask and 4
m.fillTriangleWithRays(tri, m.drawLevels, m.currentDrawLevel)
end if
if m.drawModeMask and 1
m.drawTriangleEdges(tri)
if m.drawModeMask = 1
m.currentDrawLevel = m.drawLevels
end if
end if
end for
m.currentDrawLevel++
end sub
' Draw a filled triangle with rays from the apex ato the shortest side of the triangle
'
' @param {object} [tri=[]] an array with 3 {x,y} points
' @param {integer} [levelOfDetail=1] LOD of the triangle - how filled in it should be
' @param {integer} [levelOffset=0] which step of teh fill should be done (0-levelOfDetail)
protected sub fillTriangleWithRays(tri = [] as object, levelOfDetail = 1 as integer, levelOffset = 0 as integer)
if invalid = m.tempCanvas
return
end if
len0 = BGE.Math.TotalDistance(tri[1], tri[2])
len1 = BGE.Math.TotalDistance(tri[0], tri[2])
len2 = BGE.Math.TotalDistance(tri[0], tri[1])
apexIndex = 0
shortLength = 0
if len0 <= len1 and len0 <= len2
apexIndex = 0
shortLength = len0
else if len1 <= len0 and len1 < len0
apexIndex = 1
shortLength = len1
else ' if len2 < len0 and len2 < len1
apexIndex = 2
shortLength = len2
end if
apex = tri[apexIndex]
startP = tri[(apexIndex + 1) mod 3]
endP = tri[(apexIndex + 2) mod 3]
if shortLength > 0
rgba = m.getFillColorRGBA()
for i = levelOffset to shortLength step levelOfDetail
x = BGE.Tweens.LinearTween(startP.x, endP.x, i, shortLength)
y = BGE.Tweens.LinearTween(startP.y, endP.y, i, shortLength)
m.tempCanvas.drawLine(apex.x - m.offset_x, apex.y - m.offset_y, x - m.offset_x, y - m.offset_y, rgba)
end for
end if
end sub
' Draw a triangle edges - uses outlineRGBA if drawOutline is true
'
' @param {object} [tri=[]] an array with 3 {x,y} points
protected sub drawTriangleEdges(tri as object)
if invalid = m.tempCanvas or tri.count() <> 3
return
end if
rgba = m.getFillColorRGBA()
outlineColor = rgba
if m.drawOutline
outlineColor = m.outlineRGBA
end if
p1 = tri[0]
p2 = tri[1]
p3 = tri[2]
m.tempCanvas.drawLine(p1.x - m.offset_x, p1.y - m.offset_y, p2.x - m.offset_x, p2.y - m.offset_y, outlineColor)
m.tempCanvas.drawLine(p1.x - m.offset_x, p1.y - m.offset_y, p3.x - m.offset_x, p3.y - m.offset_y, outlineColor)
m.tempCanvas.drawLine(p2.x - m.offset_x, p2.y - m.offset_y, p3.x - m.offset_x, p3.y - m.offset_y, outlineColor)
end sub
' Draw a filled convex polygon with rectangles
'
' @param {object} [points=[]] an array of {x,y} points
' @param {integer} [levelOfDetail=1] How many steps it should take to get a crisp polygon
' @param {integer} [levelOffset=0] which step of the fill should be done (0-levelOfDetail)
protected sub fillPolygonWithRectangles(points = [] as object, levelOfDetail = 1 as integer, levelOffset = 0 as integer)
if invalid = m.tempCanvas or points.count() < 3
return
end if
nodeX = []
nPoints = points.count()
x1 = m.offset_x
y1 = m.offset_y
maxX = m.width + m.offset_x
maxY = m.height + m.offset_y
rgba = m.getFillColorRGBA()
precision = (levelOfDetail - levelOffset) * 1.0
if precision <= 1
return
end if
for pixelY = y1 to maxY step precision
nodes = 0
j = nPoints - 1
for i = 0 to (nPoints - 1)
if (points[i].y < pixelY and points[j].y >= pixelY) or (points[j].y < pixelY and points[i].y >= pixelY)
if (points[j].y - points[i].y) = 0
nodeX[nodes] = points[i].x
else
nodeX[nodes] = points[i].x + (pixelY - points[i].y) / (points[j].y - points[i].y) * (points[j].x - points[i].x)
end if
nodes++
end if
j = i
end for
nodeX.sort()
for i = 0 to (nodes - 1) step precision
if nodeX[i] > maxX
exit for
end if
pixelX = nodeX[i]
if precision > 1 and i < nodes - precision
if nodeX[i + precision] < pixelX
pixelX = nodeX[i + precision]
end if
end if
rectX = BGE.Math.Max(0, x1 + pixelX - 2 * m.offset_x)
rectY = BGE.Math.Max(0, y1 + pixelY - 2 * m.offset_y - precision / 2)
endX = BGE.Math.Min(cint(i + precision / 2 + 1), nodes - 1)
width = nodeX[endX] - pixelX
m.tempCanvas.DrawRect(rectX, rectY, width, precision, rgba)
end for
end for
end sub
end class
end namespace