mirror of
https://github.com/RandomityGuy/MBHaxe.git
synced 2025-10-30 08:11:25 +00:00
speed up radar rendering
This commit is contained in:
parent
fd64c23263
commit
ac85f49705
5 changed files with 966 additions and 16 deletions
|
|
@ -19,6 +19,7 @@ import h3d.scene.Mesh;
|
|||
import src.MeshBatch;
|
||||
import src.MarbleGame;
|
||||
import src.ProfilerUI;
|
||||
import src.Settings;
|
||||
|
||||
@:publicFields
|
||||
class MeshBatchInfo {
|
||||
|
|
@ -57,10 +58,8 @@ class InstanceManager {
|
|||
var renderFrustum = scene.camera.frustum;
|
||||
var doFrustumCheck = true;
|
||||
// This sucks holy shit
|
||||
doFrustumCheck = MarbleGame.instance.world != null
|
||||
&& MarbleGame.instance.world.marble != null
|
||||
&& MarbleGame.instance.world.marble.cubemapRenderer != null;
|
||||
// renderFrustums = renderFrustums.concat(MarbleGame.instance.world.marble.cubemapRenderer.getCameraFrustums());
|
||||
doFrustumCheck = MarbleGame.instance.world != null && Settings.optionsSettings.reflectionDetail >= 3;
|
||||
var cameraFrustrums = doFrustumCheck ? MarbleGame.instance.world.marble.cubemapRenderer.getCameraFrustums() : null;
|
||||
|
||||
for (meshes in objects) {
|
||||
for (minfo in meshes) {
|
||||
|
|
@ -71,13 +70,25 @@ class InstanceManager {
|
|||
for (inst in minfo.instances) {
|
||||
// for (frustum in renderFrustums) {
|
||||
// if (frustum.hasBounds(objBounds)) {
|
||||
if (doFrustumCheck) {
|
||||
var objBounds = @:privateAccess cast(minfo.meshbatch.primitive, Instanced).baseBounds.clone();
|
||||
objBounds.transform(inst.emptyObj.getAbsPos());
|
||||
|
||||
if (!renderFrustum.hasBounds(objBounds))
|
||||
var objBounds = @:privateAccess cast(minfo.meshbatch.primitive, Instanced).baseBounds.clone();
|
||||
objBounds.transform(inst.emptyObj.getAbsPos());
|
||||
|
||||
if (cameraFrustrums == null && !renderFrustum.hasBounds(objBounds))
|
||||
continue;
|
||||
|
||||
if (cameraFrustrums != null) {
|
||||
var found = false;
|
||||
for (frustrum in cameraFrustrums) {
|
||||
if (frustrum.hasBounds(objBounds)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (inst.gameObject.currentOpacity == 1)
|
||||
opaqueinstances.push(inst);
|
||||
else if (inst.gameObject.currentOpacity != 0)
|
||||
|
|
|
|||
|
|
@ -1202,11 +1202,12 @@ class MarbleWorld extends Scheduler {
|
|||
var marbleToUpdate = clientMarbles[Net.clientIdMap[client]];
|
||||
// Debug.drawSphere(@:privateAccess marbleToUpdate.newPos, marbleToUpdate._radius);
|
||||
|
||||
// var distFromUs = @:privateAccess marbleToUpdate.newPos.distance(this.marble.newPos);
|
||||
// if (distFromUs < 5)
|
||||
m.calculationTicks = ourQueuedMoves.length;
|
||||
// else
|
||||
// m.calculationTicks = Std.int(Math.max(1, ourQueuedMoves.length - (distFromUs - 5) / 3));
|
||||
var distFromUs = @:privateAccess marbleToUpdate.newPos.distance(this.marble.newPos);
|
||||
if (distFromUs < 5) // {
|
||||
m.calculationTicks = ourQueuedMoves.length;
|
||||
// } else {
|
||||
// m.calculationTicks = Std.int(Math.max(1, ourQueuedMoves.length - (distFromUs - 5) / 3));
|
||||
// }
|
||||
// - Std.int((@:privateAccess Net.clientConnection.moveManager.ackRTT - ourLastMove.moveQueueSize) / 2);
|
||||
|
||||
marblesToTick.set(client, m);
|
||||
|
|
@ -1237,7 +1238,7 @@ class MarbleWorld extends Scheduler {
|
|||
@:privateAccess marbleToUpdate.isNetUpdate = true;
|
||||
@:privateAccess marbleToUpdate.moveMotionDir = m.move.motionDir;
|
||||
@:privateAccess marbleToUpdate.advancePhysics(advanceTimeState, mv, this.collisionWorld, this.pathedInteriors);
|
||||
this.predictions.storeState(marbleToUpdate, @:privateAccess marbleToUpdate.serverTicks);
|
||||
this.predictions.storeState(marbleToUpdate, move.timeState.ticks);
|
||||
@:privateAccess marbleToUpdate.isNetUpdate = false;
|
||||
m.calculationTicks--;
|
||||
}
|
||||
|
|
@ -1475,7 +1476,7 @@ class MarbleWorld extends Scheduler {
|
|||
}
|
||||
this.predictions.storeState(marble, myMove.timeState.ticks);
|
||||
for (client => marble in clientMarbles) {
|
||||
this.predictions.storeState(marble, @:privateAccess marble.serverTicks);
|
||||
this.predictions.storeState(marble, myMove.timeState.ticks);
|
||||
}
|
||||
if (Net.isHost) {
|
||||
for (client => othermarble in clientMarbles) { // Oh no!
|
||||
|
|
|
|||
|
|
@ -1,11 +1,14 @@
|
|||
package src;
|
||||
|
||||
import net.Net;
|
||||
import src.MarbleGame;
|
||||
import h3d.Vector;
|
||||
import hxd.res.DefaultFont;
|
||||
import h2d.Text;
|
||||
|
||||
class ProfilerUI {
|
||||
var fpsCounter:Text;
|
||||
var networkStats:Text;
|
||||
var debugProfiler:h3d.impl.Benchmark;
|
||||
var s2d:h2d.Scene;
|
||||
|
||||
|
|
@ -46,6 +49,7 @@ class ProfilerUI {
|
|||
if (!enabled)
|
||||
return;
|
||||
instance.fpsCounter.text = "FPS: " + fps;
|
||||
updateNetworkStats();
|
||||
}
|
||||
|
||||
public static function setEnabled(val:Bool) {
|
||||
|
|
@ -59,17 +63,47 @@ class ProfilerUI {
|
|||
instance.fpsCounter.remove();
|
||||
instance.fpsCounter = null;
|
||||
}
|
||||
if (instance.networkStats != null) {
|
||||
instance.networkStats.remove();
|
||||
instance.networkStats = null;
|
||||
}
|
||||
instance.debugProfiler = new h3d.impl.Benchmark(instance.s2d);
|
||||
instance.debugProfiler.y = 40;
|
||||
|
||||
instance.fpsCounter = new Text(DefaultFont.get(), instance.s2d);
|
||||
instance.fpsCounter.y = 80;
|
||||
instance.fpsCounter.color = new Vector(1, 1, 1, 1);
|
||||
|
||||
instance.networkStats = new Text(DefaultFont.get(), instance.s2d);
|
||||
instance.networkStats.y = 150;
|
||||
instance.networkStats.color = new Vector(1, 1, 1, 1);
|
||||
} else {
|
||||
instance.debugProfiler.remove();
|
||||
instance.fpsCounter.remove();
|
||||
instance.networkStats.remove();
|
||||
instance.debugProfiler = null;
|
||||
instance.fpsCounter = null;
|
||||
instance.networkStats = null;
|
||||
}
|
||||
}
|
||||
|
||||
static function updateNetworkStats() {
|
||||
if (MarbleGame.instance.world != null && MarbleGame.instance.world.isMultiplayer) {
|
||||
static var lastSentMove = 0;
|
||||
if (Net.isClient && Net.clientConnection.getQueuedMovesLength() > 0) {
|
||||
lastSentMove = @:privateAccess Net.clientConnection.moveManager.queuedMoves[Net.clientConnection.moveManager.queuedMoves.length - 1].id;
|
||||
}
|
||||
|
||||
instance.networkStats.text = 'Client World Ticks: ${MarbleGame.instance.world.timeState.ticks}\n'
|
||||
+ 'Client Marble Ticks: ${@:privateAccess MarbleGame.instance.world.marble.serverTicks}\n'
|
||||
+ 'Server Ticks: ${@:privateAccess MarbleGame.instance.world.lastMoves.myMarbleUpdate.serverTicks}\n'
|
||||
+ 'Client Move Queue Size: ${Net.isClient ? Net.clientConnection.getQueuedMovesLength() : 0}\n'
|
||||
+ 'Server Move Queue Size: ${Net.isClient ? @:privateAccess MarbleGame.instance.world.lastMoves.myMarbleUpdate.moveQueueSize : 0}\n'
|
||||
+ 'Last Sent Move: ${Net.isClient ? lastSentMove : 0}\n'
|
||||
+ 'Last Ack Move: ${Net.isClient ? @:privateAccess Net.clientConnection.moveManager.lastAckMoveId : 0}\n'
|
||||
+ 'Move Ack RTT: ${Net.isClient ? @:privateAccess Net.clientConnection.moveManager.ackRTT : 0}';
|
||||
} else {
|
||||
instance.networkStats.text = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package src;
|
|||
import h3d.Matrix;
|
||||
import src.DtsObject;
|
||||
import h3d.Vector;
|
||||
import h2d.Graphics;
|
||||
import gui.Graphics;
|
||||
import src.GameObject;
|
||||
import h2d.Scene;
|
||||
import src.MarbleWorld;
|
||||
|
|
|
|||
904
src/gui/Graphics.hx
Normal file
904
src/gui/Graphics.hx
Normal file
|
|
@ -0,0 +1,904 @@
|
|||
package gui;
|
||||
|
||||
import h2d.RenderContext;
|
||||
import h2d.impl.BatchDrawState;
|
||||
import hxd.Math;
|
||||
import hxd.impl.Allocator;
|
||||
import h2d.Drawable;
|
||||
|
||||
private typedef GraphicsPoint = hxd.poly2tri.Point;
|
||||
|
||||
@:dox(hide)
|
||||
class GPoint {
|
||||
public var x:Float;
|
||||
public var y:Float;
|
||||
public var r:Float;
|
||||
public var g:Float;
|
||||
public var b:Float;
|
||||
public var a:Float;
|
||||
|
||||
public function new() {}
|
||||
|
||||
public function load(x, y, r, g, b, a) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = a;
|
||||
}
|
||||
}
|
||||
|
||||
private class GraphicsContent extends h3d.prim.Primitive {
|
||||
var tmp:hxd.FloatBuffer;
|
||||
var index:hxd.IndexBuffer;
|
||||
var state:BatchDrawState;
|
||||
|
||||
var bufferDirty:Bool;
|
||||
var indexDirty:Bool;
|
||||
#if track_alloc
|
||||
var allocPos:hxd.impl.AllocPos;
|
||||
#end
|
||||
|
||||
var bufferSize:Int;
|
||||
var ibufferSize:Int;
|
||||
|
||||
public function new() {
|
||||
state = new BatchDrawState();
|
||||
#if track_alloc
|
||||
this.allocPos = new hxd.impl.AllocPos();
|
||||
#end
|
||||
}
|
||||
|
||||
public inline function addIndex(i) {
|
||||
index.push(i);
|
||||
state.add(1);
|
||||
indexDirty = true;
|
||||
}
|
||||
|
||||
public inline function add(x:Float, y:Float, u:Float, v:Float, r:Float, g:Float, b:Float, a:Float) {
|
||||
tmp.push(x);
|
||||
tmp.push(y);
|
||||
tmp.push(u);
|
||||
tmp.push(v);
|
||||
tmp.push(r);
|
||||
tmp.push(g);
|
||||
tmp.push(b);
|
||||
tmp.push(a);
|
||||
bufferDirty = true;
|
||||
}
|
||||
|
||||
public function setTile(tile:h2d.Tile) {
|
||||
state.setTile(tile);
|
||||
}
|
||||
|
||||
public function next() {
|
||||
var nvect = tmp.length >> 3;
|
||||
if (nvect < 1 << 15)
|
||||
return false;
|
||||
tmp = new hxd.FloatBuffer();
|
||||
index = new hxd.IndexBuffer();
|
||||
var tex = state.currentTexture;
|
||||
state = new BatchDrawState();
|
||||
state.setTexture(tex);
|
||||
super.dispose();
|
||||
return true;
|
||||
}
|
||||
|
||||
override function alloc(engine:h3d.Engine) {
|
||||
if (index.length <= 0)
|
||||
return;
|
||||
var alloc = Allocator.get();
|
||||
buffer = alloc.ofFloats(tmp, 8, RawFormat);
|
||||
bufferSize = tmp.length;
|
||||
#if track_alloc
|
||||
@:privateAccess buffer.allocPos = allocPos;
|
||||
#end
|
||||
indexes = alloc.ofIndexes(index);
|
||||
ibufferSize = index.length;
|
||||
bufferDirty = false;
|
||||
indexDirty = false;
|
||||
}
|
||||
|
||||
public function doRender(ctx:h2d.RenderContext) {
|
||||
if (index.length == 0)
|
||||
return;
|
||||
flush();
|
||||
state.drawIndexed(ctx, buffer, indexes, 0, tmp.length >> 3);
|
||||
}
|
||||
|
||||
public function flush() {
|
||||
if (buffer == null || buffer.isDisposed()) {
|
||||
alloc(h3d.Engine.getCurrent());
|
||||
} else {
|
||||
var allocator = Allocator.get();
|
||||
if (bufferDirty) {
|
||||
if (tmp.length > bufferSize) {
|
||||
allocator.disposeBuffer(buffer);
|
||||
|
||||
buffer = new h3d.Buffer(tmp.length >> 3, 8, [RawFormat, Dynamic]);
|
||||
buffer.uploadVector(tmp, 0, tmp.length >> 3);
|
||||
bufferSize = tmp.length;
|
||||
} else {
|
||||
buffer.uploadVector(tmp, 0, tmp.length >> 3);
|
||||
}
|
||||
bufferDirty = false;
|
||||
}
|
||||
if (indexDirty) {
|
||||
if (index.length > ibufferSize) {
|
||||
allocator.disposeIndexBuffer(indexes);
|
||||
indexes = allocator.ofIndexes(index);
|
||||
ibufferSize = index.length;
|
||||
} else {
|
||||
indexes.upload(index, 0, index.length);
|
||||
}
|
||||
indexDirty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override function dispose() {
|
||||
state.clear();
|
||||
// disposeBuffers();
|
||||
|
||||
// super.dispose();
|
||||
}
|
||||
|
||||
function disposeBuffers() {
|
||||
if (buffer != null) {
|
||||
Allocator.get().disposeBuffer(buffer);
|
||||
buffer = null;
|
||||
}
|
||||
if (indexes != null) {
|
||||
Allocator.get().disposeIndexBuffer(indexes);
|
||||
indexes = null;
|
||||
}
|
||||
}
|
||||
|
||||
public function clear() {
|
||||
dispose();
|
||||
tmp = new hxd.FloatBuffer();
|
||||
index = new hxd.IndexBuffer();
|
||||
}
|
||||
|
||||
public function disposeForReal() {
|
||||
state.clear();
|
||||
disposeBuffers();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
A simple interface to draw arbitrary 2D geometry.
|
||||
|
||||
Usage notes:
|
||||
* While Graphics allows for multiple unique textures, each texture swap causes a new drawcall,
|
||||
and due to that it's recommended to minimize the amount of used textures per Graphics instance,
|
||||
ideally limiting to only one texture.
|
||||
* Due to how Graphics operate, removing them from the active `h2d.Scene` will cause a loss of all data.
|
||||
**/
|
||||
class Graphics extends Drawable {
|
||||
var content:GraphicsContent;
|
||||
var tmpPoints:Array<GPoint>;
|
||||
var pindex:Int;
|
||||
var curR:Float;
|
||||
var curG:Float;
|
||||
var curB:Float;
|
||||
var curA:Float;
|
||||
var lineSize:Float;
|
||||
var lineR:Float;
|
||||
var lineG:Float;
|
||||
var lineB:Float;
|
||||
var lineA:Float;
|
||||
var doFill:Bool;
|
||||
|
||||
var xMin:Float;
|
||||
var yMin:Float;
|
||||
var xMax:Float;
|
||||
var yMax:Float;
|
||||
var xMinSize:Float;
|
||||
var yMinSize:Float;
|
||||
var xMaxSize:Float;
|
||||
var yMaxSize:Float;
|
||||
|
||||
var ma:Float = 1.;
|
||||
var mb:Float = 0.;
|
||||
var mc:Float = 0.;
|
||||
var md:Float = 1.;
|
||||
var mx:Float = 0.;
|
||||
var my:Float = 0.;
|
||||
|
||||
/**
|
||||
The Tile used as source of Texture to render.
|
||||
**/
|
||||
public var tile:h2d.Tile;
|
||||
|
||||
/**
|
||||
Adds bevel cut-off at line corners.
|
||||
|
||||
The value is a percentile in range of 0...1, dictating at which point edges get beveled based on their angle.
|
||||
Value of 0 being not beveled and 1 being always beveled.
|
||||
**/
|
||||
public var bevel = 0.25; // 0 = not beveled, 1 = always beveled
|
||||
|
||||
/**
|
||||
Create a new Graphics instance.
|
||||
@param parent An optional parent `h2d.Object` instance to which Graphics adds itself if set.
|
||||
**/
|
||||
public function new(?parent) {
|
||||
super(parent);
|
||||
content = new GraphicsContent();
|
||||
tile = h2d.Tile.fromColor(0xFFFFFF);
|
||||
clear();
|
||||
}
|
||||
|
||||
override function onRemove() {
|
||||
super.onRemove();
|
||||
clear();
|
||||
content.disposeForReal();
|
||||
}
|
||||
|
||||
/**
|
||||
Clears the Graphics contents.
|
||||
**/
|
||||
public function clear() {
|
||||
content.clear();
|
||||
tmpPoints = [];
|
||||
pindex = 0;
|
||||
lineSize = 0;
|
||||
xMin = Math.POSITIVE_INFINITY;
|
||||
yMin = Math.POSITIVE_INFINITY;
|
||||
yMax = Math.NEGATIVE_INFINITY;
|
||||
xMax = Math.NEGATIVE_INFINITY;
|
||||
xMinSize = Math.POSITIVE_INFINITY;
|
||||
yMinSize = Math.POSITIVE_INFINITY;
|
||||
yMaxSize = Math.NEGATIVE_INFINITY;
|
||||
xMaxSize = Math.NEGATIVE_INFINITY;
|
||||
}
|
||||
|
||||
override function getBoundsRec(relativeTo, out, forSize) {
|
||||
super.getBoundsRec(relativeTo, out, forSize);
|
||||
if (tile != null) {
|
||||
if (forSize)
|
||||
addBounds(relativeTo, out, xMinSize, yMinSize, xMaxSize - xMinSize, yMaxSize - yMinSize);
|
||||
else
|
||||
addBounds(relativeTo, out, xMin, yMin, xMax - xMin, yMax - yMin);
|
||||
}
|
||||
}
|
||||
|
||||
function isConvex(points:Array<GPoint>) {
|
||||
var first = true, sign = false;
|
||||
for (i in 0...points.length) {
|
||||
var p1 = points[i];
|
||||
var p2 = points[(i + 1) % points.length];
|
||||
var p3 = points[(i + 2) % points.length];
|
||||
var s = (p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x) > 0;
|
||||
if (first) {
|
||||
first = false;
|
||||
sign = s;
|
||||
} else if (sign != s)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function flushLine(start) {
|
||||
var pts = tmpPoints;
|
||||
var last = pts.length - 1;
|
||||
var prev = pts[last];
|
||||
var p = pts[0];
|
||||
|
||||
content.setTile(h2d.Tile.fromColor(0xFFFFFF));
|
||||
var closed = p.x == prev.x && p.y == prev.y;
|
||||
var count = pts.length;
|
||||
if (!closed) {
|
||||
var prevLast = pts[last - 1];
|
||||
if (prevLast == null)
|
||||
prevLast = p;
|
||||
var gp = new GPoint();
|
||||
gp.load(prev.x * 2 - prevLast.x, prev.y * 2 - prevLast.y, 0, 0, 0, 0);
|
||||
pts.push(gp);
|
||||
var pNext = pts[1];
|
||||
if (pNext == null)
|
||||
pNext = p;
|
||||
var gp = new GPoint();
|
||||
gp.load(p.x * 2 - pNext.x, p.y * 2 - pNext.y, 0, 0, 0, 0);
|
||||
prev = gp;
|
||||
} else if (p != prev) {
|
||||
count--;
|
||||
last--;
|
||||
prev = pts[last];
|
||||
}
|
||||
|
||||
for (i in 0...count) {
|
||||
var next = pts[(i + 1) % pts.length];
|
||||
|
||||
var nx1 = prev.y - p.y;
|
||||
var ny1 = p.x - prev.x;
|
||||
var ns1 = Math.invSqrt(nx1 * nx1 + ny1 * ny1);
|
||||
|
||||
var nx2 = p.y - next.y;
|
||||
var ny2 = next.x - p.x;
|
||||
var ns2 = Math.invSqrt(nx2 * nx2 + ny2 * ny2);
|
||||
|
||||
var nx = nx1 * ns1 + nx2 * ns2;
|
||||
var ny = ny1 * ns1 + ny2 * ns2;
|
||||
var ns = Math.invSqrt(nx * nx + ny * ny);
|
||||
|
||||
nx *= ns;
|
||||
ny *= ns;
|
||||
|
||||
var size = nx * nx1 * ns1 + ny * ny1 * ns1; // N.N1
|
||||
|
||||
// *HACK* we should instead properly detect limits when the angle is too small
|
||||
if (size < 0.1)
|
||||
size = 0.1;
|
||||
|
||||
var d = lineSize * 0.5 / size;
|
||||
nx *= d;
|
||||
ny *= d;
|
||||
|
||||
if (size > bevel) {
|
||||
content.add(p.x + nx, p.y + ny, 0, 0, p.r, p.g, p.b, p.a);
|
||||
content.add(p.x - nx, p.y - ny, 0, 0, p.r, p.g, p.b, p.a);
|
||||
|
||||
var pnext = i == last ? start : pindex + 2;
|
||||
|
||||
if (i < count - 1 || closed) {
|
||||
content.addIndex(pindex);
|
||||
content.addIndex(pindex + 1);
|
||||
content.addIndex(pnext);
|
||||
|
||||
content.addIndex(pindex + 1);
|
||||
content.addIndex(pnext);
|
||||
content.addIndex(pnext + 1);
|
||||
}
|
||||
pindex += 2;
|
||||
} else {
|
||||
// bevel
|
||||
var n0x = next.x - p.x;
|
||||
var n0y = next.y - p.y;
|
||||
var sign = n0x * nx + n0y * ny;
|
||||
|
||||
var nnx = -ny;
|
||||
var nny = nx;
|
||||
|
||||
var size = nnx * nx1 * ns1 + nny * ny1 * ns1;
|
||||
var d = lineSize * 0.5 / size;
|
||||
nnx *= d;
|
||||
nny *= d;
|
||||
|
||||
var pnext = i == last ? start : pindex + 3;
|
||||
|
||||
if (sign > 0) {
|
||||
content.add(p.x + nx, p.y + ny, 0, 0, p.r, p.g, p.b, p.a);
|
||||
content.add(p.x - nnx, p.y - nny, 0, 0, p.r, p.g, p.b, p.a);
|
||||
content.add(p.x + nnx, p.y + nny, 0, 0, p.r, p.g, p.b, p.a);
|
||||
|
||||
content.addIndex(pindex);
|
||||
content.addIndex(pnext);
|
||||
content.addIndex(pindex + 2);
|
||||
|
||||
content.addIndex(pindex + 2);
|
||||
content.addIndex(pnext);
|
||||
content.addIndex(pnext + 1);
|
||||
} else {
|
||||
content.add(p.x + nnx, p.y + nny, 0, 0, p.r, p.g, p.b, p.a);
|
||||
content.add(p.x - nx, p.y - ny, 0, 0, p.r, p.g, p.b, p.a);
|
||||
content.add(p.x - nnx, p.y - nny, 0, 0, p.r, p.g, p.b, p.a);
|
||||
|
||||
content.addIndex(pindex + 1);
|
||||
content.addIndex(pnext);
|
||||
content.addIndex(pindex + 2);
|
||||
|
||||
content.addIndex(pindex + 1);
|
||||
content.addIndex(pnext);
|
||||
content.addIndex(pnext + 1);
|
||||
}
|
||||
|
||||
content.addIndex(pindex);
|
||||
content.addIndex(pindex + 1);
|
||||
content.addIndex(pindex + 2);
|
||||
|
||||
pindex += 3;
|
||||
}
|
||||
|
||||
prev = p;
|
||||
p = next;
|
||||
}
|
||||
content.setTile(tile);
|
||||
}
|
||||
|
||||
static var EARCUT = null;
|
||||
|
||||
function flushFill(i0) {
|
||||
if (tmpPoints.length < 3)
|
||||
return;
|
||||
|
||||
var pts = tmpPoints;
|
||||
var p0 = pts[0];
|
||||
var p1 = pts[pts.length - 1];
|
||||
var last = null;
|
||||
// closed poly
|
||||
if (hxd.Math.abs(p0.x - p1.x) < 1e-9 && hxd.Math.abs(p0.y - p1.y) < 1e-9)
|
||||
last = pts.pop();
|
||||
|
||||
if (isConvex(pts)) {
|
||||
for (i in 1...pts.length - 1) {
|
||||
content.addIndex(i0);
|
||||
content.addIndex(i0 + i);
|
||||
content.addIndex(i0 + i + 1);
|
||||
}
|
||||
} else {
|
||||
var ear = EARCUT;
|
||||
if (ear == null)
|
||||
EARCUT = ear = new hxd.earcut.Earcut();
|
||||
for (i in ear.triangulate(pts))
|
||||
content.addIndex(i + i0);
|
||||
}
|
||||
|
||||
if (last != null)
|
||||
pts.push(last);
|
||||
}
|
||||
|
||||
function flush() {
|
||||
if (tmpPoints.length == 0)
|
||||
return;
|
||||
if (doFill) {
|
||||
flushFill(pindex);
|
||||
pindex += tmpPoints.length;
|
||||
if (content.next())
|
||||
pindex = 0;
|
||||
}
|
||||
if (lineSize > 0) {
|
||||
flushLine(pindex);
|
||||
if (content.next())
|
||||
pindex = 0;
|
||||
}
|
||||
tmpPoints = [];
|
||||
}
|
||||
|
||||
/**
|
||||
Begins a solid color fill.
|
||||
|
||||
Beginning new fill will finish previous fill operation without need to call `Graphics.endFill`.
|
||||
|
||||
@param color An RGB color with which to fill the drawn shapes.
|
||||
@param alpha A transparency of the fill color.
|
||||
**/
|
||||
public function beginFill(color:Int = 0, alpha = 1.) {
|
||||
flush();
|
||||
tile = h2d.Tile.fromColor(0xFFFFFF);
|
||||
content.setTile(tile);
|
||||
setColor(color, alpha);
|
||||
doFill = true;
|
||||
}
|
||||
|
||||
/**
|
||||
Position a virtual tile at the given position and scale. Every draw will display a part of this tile relative
|
||||
to these coordinates.
|
||||
|
||||
Note that in by default, Tile is not wrapped, and in order to render tiling texture, `Drawable.tileWrap` have to be set.
|
||||
Additionally, both `Tile.dx` and `Tile.dy` are ignored (use `dx`/`dy` arguments instead)
|
||||
as well as tile defined size of the tile through `Tile.width` and `Tile.height` (use `scaleX`/`scaleY` relative to texture size).
|
||||
|
||||
Beginning new fill will finish previous fill operation without need to call `Graphics.endFill`.
|
||||
|
||||
@param dx An X offset of the Tile relative to Graphics.
|
||||
@param dy An Y offset of the Tile relative to Graphics.
|
||||
@param scaleX A horizontal scale factor applied to the Tile texture.
|
||||
@param scaleY A vertical scale factor applied to the Tile texture.
|
||||
@param tile The tile to fill with. If null, uses previously used Tile with `beginTileFill` or throws an error.
|
||||
Previous tile is remembered across `Graphics.clear` calls.
|
||||
**/
|
||||
public function beginTileFill(?dx:Float, ?dy:Float, ?scaleX:Float, ?scaleY:Float, ?tile:h2d.Tile) {
|
||||
if (tile == null)
|
||||
tile = this.tile;
|
||||
if (tile == null)
|
||||
throw "Tile not specified";
|
||||
flush();
|
||||
this.tile = tile;
|
||||
content.setTile(tile);
|
||||
setColor(0xFFFFFF);
|
||||
doFill = true;
|
||||
|
||||
if (dx == null)
|
||||
dx = 0;
|
||||
if (dy == null)
|
||||
dy = 0;
|
||||
if (scaleX == null)
|
||||
scaleX = 1;
|
||||
if (scaleY == null)
|
||||
scaleY = 1;
|
||||
dx -= tile.x;
|
||||
dy -= tile.y;
|
||||
|
||||
var tex = tile.getTexture();
|
||||
var pixWidth = 1 / tex.width;
|
||||
var pixHeight = 1 / tex.height;
|
||||
ma = pixWidth / scaleX;
|
||||
mb = 0;
|
||||
mc = 0;
|
||||
md = pixHeight / scaleY;
|
||||
mx = -dx * ma;
|
||||
my = -dy * md;
|
||||
}
|
||||
|
||||
/**
|
||||
Draws a Tile at given position.
|
||||
See `Graphics.beginTileFill` for limitations.
|
||||
|
||||
This methods ends current fill operation.
|
||||
@param x The X position of the tile.
|
||||
@param y The Y position of the tile.
|
||||
@param tile The tile to draw.
|
||||
**/
|
||||
public function drawTile(x:Float, y:Float, tile:h2d.Tile) {
|
||||
beginTileFill(x, y, tile);
|
||||
drawRect(x, y, tile.width, tile.height);
|
||||
endFill();
|
||||
}
|
||||
|
||||
/**
|
||||
Sets an outline style. Changing the line style ends the currently drawn line.
|
||||
|
||||
@param size Width of the outline. Setting size to 0 will remove the outline.
|
||||
@param color An outline RGB color.
|
||||
@param alpha An outline transparency.
|
||||
**/
|
||||
public function lineStyle(size:Float = 0, color = 0, alpha = 1.) {
|
||||
flush();
|
||||
this.lineSize = size;
|
||||
lineA = alpha;
|
||||
lineR = ((color >> 16) & 0xFF) / 255.;
|
||||
lineG = ((color >> 8) & 0xFF) / 255.;
|
||||
lineB = (color & 0xFF) / 255.;
|
||||
}
|
||||
|
||||
/**
|
||||
Ends the current line and starts new one at given position.
|
||||
**/
|
||||
public inline function moveTo(x, y) {
|
||||
flush();
|
||||
lineTo(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
Ends the current fill operation.
|
||||
**/
|
||||
public function endFill() {
|
||||
flush();
|
||||
doFill = false;
|
||||
}
|
||||
|
||||
/**
|
||||
Changes current fill color.
|
||||
Does not interrupt current fill operation and can be utilized to customize color per vertex.
|
||||
During tile fill operation, color serves as a tile color multiplier.
|
||||
@param color The new fill color.
|
||||
@param alpha The new fill transparency.
|
||||
**/
|
||||
public inline function setColor(color:Int, alpha:Float = 1.) {
|
||||
curA = alpha;
|
||||
curR = ((color >> 16) & 0xFF) / 255.;
|
||||
curG = ((color >> 8) & 0xFF) / 255.;
|
||||
curB = (color & 0xFF) / 255.;
|
||||
}
|
||||
|
||||
/**
|
||||
Draws a rectangle with given parameters.
|
||||
@param x The rectangle top-left corner X position.
|
||||
@param y The rectangle top-left corner Y position.
|
||||
@param w The rectangle width.
|
||||
@param h The rectangle height.
|
||||
**/
|
||||
public function drawRect(x:Float, y:Float, w:Float, h:Float) {
|
||||
flush();
|
||||
lineTo(x, y);
|
||||
lineTo(x + w, y);
|
||||
lineTo(x + w, y + h);
|
||||
lineTo(x, y + h);
|
||||
lineTo(x, y);
|
||||
var e = 0.01; // see #776
|
||||
tmpPoints[0].x += e;
|
||||
tmpPoints[0].y += e;
|
||||
tmpPoints[1].y += e;
|
||||
tmpPoints[3].x += e;
|
||||
tmpPoints[4].x += e;
|
||||
tmpPoints[4].y += e;
|
||||
flush();
|
||||
}
|
||||
|
||||
/**
|
||||
Draws a rounded rectangle with given parameters.
|
||||
@param x The rectangle top-left corner X position.
|
||||
@param y The rectangle top-left corner Y position.
|
||||
@param w The rectangle width.
|
||||
@param h The rectangle height.
|
||||
@param radius Radius of the rectangle corners.
|
||||
@param nsegments Amount of segments used for corners. When `0` segment count calculated automatically.
|
||||
**/
|
||||
public function drawRoundedRect(x:Float, y:Float, w:Float, h:Float, radius:Float, nsegments = 0) {
|
||||
if (radius <= 0) {
|
||||
return drawRect(x, y, w, h);
|
||||
}
|
||||
x += radius;
|
||||
y += radius;
|
||||
w -= radius * 2;
|
||||
h -= radius * 2;
|
||||
flush();
|
||||
if (nsegments == 0)
|
||||
nsegments = Math.ceil(Math.abs(radius * hxd.Math.degToRad(90) / 4));
|
||||
if (nsegments < 3)
|
||||
nsegments = 3;
|
||||
var angle = hxd.Math.degToRad(90) / (nsegments - 1);
|
||||
inline function corner(x, y, angleStart) {
|
||||
for (i in 0...nsegments) {
|
||||
var a = i * angle + hxd.Math.degToRad(angleStart);
|
||||
lineTo(x + Math.cos(a) * radius, y + Math.sin(a) * radius);
|
||||
}
|
||||
}
|
||||
lineTo(x, y - radius);
|
||||
lineTo(x + w, y - radius);
|
||||
corner(x + w, y, 270);
|
||||
lineTo(x + w + radius, y + h);
|
||||
corner(x + w, y + h, 0);
|
||||
lineTo(x, y + h + radius);
|
||||
corner(x, y + h, 90);
|
||||
lineTo(x - radius, y);
|
||||
corner(x, y, 180);
|
||||
flush();
|
||||
}
|
||||
|
||||
/**
|
||||
Draws a circle centered at given position.
|
||||
@param cx X center position of the circle.
|
||||
@param cy Y center position of the circle.
|
||||
@param radius Radius of the circle.
|
||||
@param nsegments Amount of segments used to draw the circle. When `0`, amount of segments calculated automatically.
|
||||
**/
|
||||
public function drawCircle(cx:Float, cy:Float, radius:Float, nsegments = 0) {
|
||||
flush();
|
||||
if (nsegments == 0)
|
||||
nsegments = Math.ceil(Math.abs(radius * 3.14 * 2 / 4));
|
||||
if (nsegments < 3)
|
||||
nsegments = 3;
|
||||
var angle = Math.PI * 2 / nsegments;
|
||||
for (i in 0...nsegments + 1) {
|
||||
var a = i * angle;
|
||||
lineTo(cx + Math.cos(a) * radius, cy + Math.sin(a) * radius);
|
||||
}
|
||||
flush();
|
||||
}
|
||||
|
||||
/**
|
||||
Draws an ellipse centered at given position.
|
||||
@param cx X center position of the ellipse.
|
||||
@param cy Y center position of the ellipse.
|
||||
@param radiusX Horizontal radius of an ellipse.
|
||||
@param radiusY Vertical radius of an ellipse.
|
||||
@param rotationAngle Ellipse rotation in radians.
|
||||
@param nsegments Amount of segments used to draw an ellipse. When `0`, amount of segments calculated automatically.
|
||||
**/
|
||||
public function drawEllipse(cx:Float, cy:Float, radiusX:Float, radiusY:Float, rotationAngle:Float = 0, nsegments = 0) {
|
||||
flush();
|
||||
if (nsegments == 0)
|
||||
nsegments = Math.ceil(Math.abs(radiusY * 3.14 * 2 / 4));
|
||||
if (nsegments < 3)
|
||||
nsegments = 3;
|
||||
var angle = Math.PI * 2 / nsegments;
|
||||
var x1, y1;
|
||||
for (i in 0...nsegments + 1) {
|
||||
var a = i * angle;
|
||||
x1 = Math.cos(a) * Math.cos(rotationAngle) * radiusX - Math.sin(a) * Math.sin(rotationAngle) * radiusY;
|
||||
y1 = Math.cos(rotationAngle) * Math.sin(a) * radiusY + Math.cos(a) * Math.sin(rotationAngle) * radiusX;
|
||||
lineTo(cx + x1, cy + y1);
|
||||
}
|
||||
flush();
|
||||
}
|
||||
|
||||
/**
|
||||
Draws a pie centered at given position.
|
||||
@param cx X center position of the pie.
|
||||
@param cy Y center position of the pie.
|
||||
@param radius Radius of the pie.
|
||||
@param angleStart Starting angle of the pie in radians.
|
||||
@param angleLength The pie size in clockwise direction with `2*PI` being full circle.
|
||||
@param nsegments Amount of segments used to draw the pie. When `0`, amount of segments calculated automatically.
|
||||
**/
|
||||
public function drawPie(cx:Float, cy:Float, radius:Float, angleStart:Float, angleLength:Float, nsegments = 0) {
|
||||
if (Math.abs(angleLength) >= Math.PI * 2) {
|
||||
return drawCircle(cx, cy, radius, nsegments);
|
||||
}
|
||||
flush();
|
||||
lineTo(cx, cy);
|
||||
if (nsegments == 0)
|
||||
nsegments = Math.ceil(Math.abs(radius * angleLength / 4));
|
||||
if (nsegments < 3)
|
||||
nsegments = 3;
|
||||
var angle = angleLength / (nsegments - 1);
|
||||
for (i in 0...nsegments) {
|
||||
var a = i * angle + angleStart;
|
||||
lineTo(cx + Math.cos(a) * radius, cy + Math.sin(a) * radius);
|
||||
}
|
||||
lineTo(cx, cy);
|
||||
flush();
|
||||
}
|
||||
|
||||
/**
|
||||
Draws a double-edged pie centered at given position.
|
||||
@param cx X center position of the pie.
|
||||
@param cy Y center position of the pie.
|
||||
@param radius The outer radius of the pie.
|
||||
@param innerRadius The inner radius of the pie.
|
||||
@param angleStart Starting angle of the pie in radians.
|
||||
@param angleLength The pie size in clockwise direction with `2*PI` being full circle.
|
||||
@param nsegments Amount of segments used to draw the pie. When `0`, amount of segments calculated automatically.
|
||||
**/
|
||||
public function drawPieInner(cx:Float, cy:Float, radius:Float, innerRadius:Float, angleStart:Float, angleLength:Float, nsegments = 0) {
|
||||
flush();
|
||||
if (Math.abs(angleLength) >= Math.PI * 2 + 1e-3)
|
||||
angleLength = Math.PI * 2 + 1e-3;
|
||||
|
||||
var cs = Math.cos(angleStart);
|
||||
var ss = Math.sin(angleStart);
|
||||
var ce = Math.cos(angleStart + angleLength);
|
||||
var se = Math.sin(angleStart + angleLength);
|
||||
|
||||
lineTo(cx + cs * innerRadius, cy + ss * innerRadius);
|
||||
|
||||
if (nsegments == 0)
|
||||
nsegments = Math.ceil(Math.abs(radius * angleLength / 4));
|
||||
if (nsegments < 3)
|
||||
nsegments = 3;
|
||||
var angle = angleLength / (nsegments - 1);
|
||||
for (i in 0...nsegments) {
|
||||
var a = i * angle + angleStart;
|
||||
lineTo(cx + Math.cos(a) * radius, cy + Math.sin(a) * radius);
|
||||
}
|
||||
lineTo(cx + ce * innerRadius, cy + se * innerRadius);
|
||||
for (i in 0...nsegments) {
|
||||
var a = (nsegments - 1 - i) * angle + angleStart;
|
||||
lineTo(cx + Math.cos(a) * innerRadius, cy + Math.sin(a) * innerRadius);
|
||||
}
|
||||
flush();
|
||||
}
|
||||
|
||||
/**
|
||||
Draws a rectangular pie centered at given position.
|
||||
@param cx X center position of the pie.
|
||||
@param cy Y center position of the pie.
|
||||
@param width Width of the pie.
|
||||
@param height Height of the pie.
|
||||
@param angleStart Starting angle of the pie in radians.
|
||||
@param angleLength The pie size in clockwise direction with `2*PI` being solid rectangle.
|
||||
@param nsegments Amount of segments used to draw the pie. When `0`, amount of segments calculated automatically.
|
||||
**/
|
||||
public function drawRectanglePie(cx:Float, cy:Float, width:Float, height:Float, angleStart:Float, angleLength:Float, nsegments = 0) {
|
||||
if (Math.abs(angleLength) >= Math.PI * 2) {
|
||||
return drawRect(cx - (width / 2), cy - (height / 2), width, height);
|
||||
}
|
||||
flush();
|
||||
lineTo(cx, cy);
|
||||
if (nsegments == 0)
|
||||
nsegments = Math.ceil(Math.abs(Math.max(width, height) * angleLength / 4));
|
||||
if (nsegments < 3)
|
||||
nsegments = 3;
|
||||
var angle = angleLength / (nsegments - 1);
|
||||
var square2 = Math.sqrt(2);
|
||||
for (i in 0...nsegments) {
|
||||
var a = i * angle + angleStart;
|
||||
|
||||
var _width = Math.cos(a) * (width / 2 + 1) * square2;
|
||||
var _height = Math.sin(a) * (height / 2 + 1) * square2;
|
||||
|
||||
_width = Math.abs(_width) >= width / 2 ? (Math.cos(a) < 0 ? width / 2 * -1 : width / 2) : _width;
|
||||
_height = Math.abs(_height) >= height / 2 ? (Math.sin(a) < 0 ? height / 2 * -1 : height / 2) : _height;
|
||||
|
||||
lineTo(cx + _width, cy + _height);
|
||||
}
|
||||
lineTo(cx, cy);
|
||||
flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a quadratic Bezier curve using the current line style from the current drawing position to (cx, cy) and using the control point that (bx, by) specifies.
|
||||
* IvanK Lib port ( http://lib.ivank.net )
|
||||
*/
|
||||
public function curveTo(bx:Float, by:Float, cx:Float, cy:Float) {
|
||||
var ax = tmpPoints.length == 0 ? 0 : tmpPoints[tmpPoints.length - 1].x;
|
||||
var ay = tmpPoints.length == 0 ? 0 : tmpPoints[tmpPoints.length - 1].y;
|
||||
var t = 2 / 3;
|
||||
cubicCurveTo(ax + t * (bx - ax), ay + t * (by - ay), cx + t * (bx - cx), cy + t * (by - cy), cx, cy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a cubic Bezier curve from the current drawing position to the specified anchor point.
|
||||
* IvanK Lib port ( http://lib.ivank.net )
|
||||
* @param bx control X for start point
|
||||
* @param by control Y for start point
|
||||
* @param cx control X for end point
|
||||
* @param cy control Y for end point
|
||||
* @param dx end X
|
||||
* @param dy end Y
|
||||
* @param nsegments = 40
|
||||
*/
|
||||
public function cubicCurveTo(bx:Float, by:Float, cx:Float, cy:Float, dx:Float, dy:Float, nsegments = 40) {
|
||||
var ax = tmpPoints.length == 0 ? 0 : tmpPoints[tmpPoints.length - 1].x;
|
||||
var ay = tmpPoints.length == 0 ? 0 : tmpPoints[tmpPoints.length - 1].y;
|
||||
var tobx = bx - ax, toby = by - ay;
|
||||
var tocx = cx - bx, tocy = cy - by;
|
||||
var todx = dx - cx, tody = dy - cy;
|
||||
var step = 1 / nsegments;
|
||||
|
||||
for (i in 1...nsegments) {
|
||||
var d = i * step;
|
||||
var px = ax + d * tobx, py = ay + d * toby;
|
||||
var qx = bx + d * tocx, qy = by + d * tocy;
|
||||
var rx = cx + d * todx, ry = cy + d * tody;
|
||||
var toqx = qx - px, toqy = qy - py;
|
||||
var torx = rx - qx, tory = ry - qy;
|
||||
|
||||
var sx = px + d * toqx, sy = py + d * toqy;
|
||||
var tx = qx + d * torx, ty = qy + d * tory;
|
||||
var totx = tx - sx, toty = ty - sy;
|
||||
lineTo(sx + d * totx, sy + d * toty);
|
||||
}
|
||||
lineTo(dx, dy);
|
||||
}
|
||||
|
||||
/**
|
||||
Draws a straight line from the current drawing position to the given position.
|
||||
**/
|
||||
public inline function lineTo(x:Float, y:Float) {
|
||||
addVertex(x, y, curR, curG, curB, curA, x * ma + y * mc + mx, x * mb + y * md + my);
|
||||
}
|
||||
|
||||
/**
|
||||
Advanced usage. Adds new vertex to the current polygon with given parameters and current line style.
|
||||
@param x Vertex X position
|
||||
@param y Vertex Y position
|
||||
@param r Red tint value of the vertex when performing fill operation.
|
||||
@param g Green tint value of the vertex when performing fill operation.
|
||||
@param b Blue tint value of the vertex when performing fill operation.
|
||||
@param a Alpha of the vertex when performing fill operation.
|
||||
@param u Normalized horizontal Texture position from the current Tile fill operation.
|
||||
@param v Normalized vertical Texture position from the current Tile fill operation.
|
||||
**/
|
||||
public function addVertex(x:Float, y:Float, r:Float, g:Float, b:Float, a:Float, u:Float = 0., v:Float = 0.) {
|
||||
var half = lineSize / 2.0;
|
||||
if (x - half < xMin)
|
||||
xMin = x - half;
|
||||
if (y - half < yMin)
|
||||
yMin = y - half;
|
||||
if (x + half > xMax)
|
||||
xMax = x + half;
|
||||
if (y + half > yMax)
|
||||
yMax = y + half;
|
||||
if (x < xMinSize)
|
||||
xMinSize = x;
|
||||
if (y < yMinSize)
|
||||
yMinSize = y;
|
||||
if (x > xMaxSize)
|
||||
xMaxSize = x;
|
||||
if (y > yMaxSize)
|
||||
yMaxSize = y;
|
||||
if (doFill)
|
||||
content.add(x, y, u, v, r, g, b, a);
|
||||
var gp = new GPoint();
|
||||
gp.load(x, y, lineR, lineG, lineB, lineA);
|
||||
tmpPoints.push(gp);
|
||||
}
|
||||
|
||||
override function draw(ctx:RenderContext) {
|
||||
if (!ctx.beginDrawBatchState(this))
|
||||
return;
|
||||
content.doRender(ctx);
|
||||
}
|
||||
|
||||
override function sync(ctx:RenderContext) {
|
||||
super.sync(ctx);
|
||||
flush();
|
||||
content.flush();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue