MBHaxe/src/Radar.hx
RandomityGuy ad66572c79 fix radar
2024-05-28 20:54:50 +05:30

452 lines
14 KiB
Haxe

package src;
import hxd.res.BitmapFont;
import h3d.Matrix;
import src.DtsObject;
import h3d.Vector;
import gui.Graphics;
import src.GameObject;
import h2d.Scene;
import src.MarbleWorld;
import src.Util;
import src.Marble;
import src.Settings;
import src.ResourceLoader;
class Radar {
var level:MarbleWorld;
var scene2d:Scene;
var g:Graphics;
var marbleNameTexts:Map<Marble, h2d.Text>;
public var ellipseScreenFraction = new Vector(0.79, 0.9);
public var fullArrowLength = 60.0;
public var fullArrowWidth = 40.0;
public var maxArrowAlpha = 0.6;
public var maxTargetAlpha = 0.4;
public var minArrowFraction = 0.4;
var time:Float = 0.0;
var _dirty = false;
public function new(level:MarbleWorld, scene2d:Scene) {
this.level = level;
this.scene2d = scene2d;
this.marbleNameTexts = [];
}
public function init() {
g = new Graphics(scene2d);
}
public function update(dt:Float) {
time += dt;
_dirty = true;
}
public function render() {
if (!_dirty)
return;
g.clear();
var gemCount = 0;
for (gem in level.gems) {
if (!gem.pickedUp) {
renderArrow(gem.boundingCollider.boundingBox.getCenter().toVector(), gem.radarColor);
gemCount++;
}
}
if (@:privateAccess level.endPad != null && gemCount == 0) {
renderArrow(@:privateAccess level.endPad.getAbsPos().getPosition(), 0xE6E6E6);
}
var fadeDistance = level.scene.camera.zFar * 0.1;
for (marble => marbleName in marbleNameTexts) {
if (marbleName != null)
marbleName.alpha = 0;
}
for (marble in level.marbles) {
if (marble != level.marble) {
var shapePos = marble.getAbsPos().getPosition();
var shapeDir = shapePos.sub(level.scene.camera.pos);
var shapeDist = shapeDir.lengthSq();
if (shapeDist == 0 || shapeDist > level.scene.camera.zFar * level.scene.camera.zFar) {
dontRenderName(marble);
continue;
}
var validProjection = frustumHasPoint(level.scene.camera.frustum, shapePos);
if (!validProjection) {
dontRenderName(marble);
continue;
}
shapePos.z += 0.5; // Vertical offset
var projectedPos = level.scene.camera.project(shapePos.x, shapePos.y, shapePos.z, scene2d.width, scene2d.height);
var opacity = (shapeDist < fadeDistance) ? 1.0 : (1.0 - (shapeDist - fadeDistance) / (level.scene.camera.zFar - fadeDistance));
renderName(projectedPos, marble, opacity);
}
}
_dirty = false;
}
public function blink() {
time = 0;
}
public function reset() {
time = 0;
g.clear();
}
public function dispose() {
g.clear();
scene2d.removeChild(g);
g = null;
for (txt in marbleNameTexts) {
if (txt != null) {
scene2d.removeChild(txt);
}
}
marbleNameTexts = null;
}
inline function planeDistance(plane:h3d.col.Plane, p:Vector) {
return @:privateAccess plane.nx * p.x + @:privateAccess plane.ny * p.y + @:privateAccess plane.nz * p.z - @:privateAccess plane.d;
}
function frustumHasPoint(frustum:h3d.col.Frustum, p:Vector) {
if (planeDistance(frustum.pleft, p) < 0)
return false;
if (planeDistance(frustum.pright, p) < 0)
return false;
if (planeDistance(frustum.ptop, p) < 0)
return false;
if (planeDistance(frustum.pbottom, p) < 0)
return false;
if (frustum.checkNearFar) {
if (planeDistance(frustum.pnear, p) < 0)
return false;
if (planeDistance(frustum.pfar, p) < 0)
return false;
}
return true;
}
function renderArrow(pos:Vector, color:Int) {
var validProjection = frustumHasPoint(level.scene.camera.frustum, pos);
var projectedPos = level.scene.camera.project(pos.x, pos.y, pos.z, scene2d.width, scene2d.height);
var fovX = (level.scene.camera.getFovX() * 0.5) * Math.PI / 180.0;
var fovY = (level.scene.camera.fovY * 0.5) * Math.PI / 180.0;
var blink = time < 3 ? ((Std.int(Math.floor((time * 1000) / 500))) % 2 == 1) : false;
var gravityMat = level.getOrientationQuat(level.timeState.currentAttemptTime).toMatrix();
var front = level.scene.camera.target.sub(level.scene.camera.pos).normalized();
var up = gravityMat.up();
var right = up.cross(front).normalized();
gravityMat.invert();
var shapeDist = pos.sub(level.scene.camera.pos);
var shapeDir = shapeDist.normalized();
var distToShape = shapeDist.length();
shapeDir.normalize();
var p1 = front.add(right.multiply(Math.sin(fovX))).add(up.multiply(fovY));
var p2 = front.add(right.multiply(Math.sin(fovX))).sub(up.multiply(fovY));
var camCone1G = p1.transformed(gravityMat);
var camCone2G = p2.transformed(gravityMat);
var shapeDirGrav = shapeDir.transformed(gravityMat);
var cc1 = new Vector(Math.sqrt(camCone1G.x * camCone1G.x + camCone1G.y * camCone1G.y), camCone1G.z, 0);
var cc2 = new Vector(Math.sqrt(camCone2G.x * camCone2G.x + camCone2G.y * camCone2G.y), camCone2G.z, 0);
var sd = new Vector(Math.sqrt(shapeDirGrav.x * shapeDirGrav.x + shapeDirGrav.y * shapeDirGrav.y), shapeDirGrav.z, 0);
cc1.normalize();
cc2.normalize();
sd.normalize();
var arrowPosY = 0.0;
var arrowPosX = 0.0;
if (cc1.y >= sd.y) {
if (cc2.y <= sd.y) {
arrowPosY = scene2d.height * ((sd.x * cc1.y - cc1.x * sd.y) / (sd.y * (cc2.x - cc1.x) - (cc2.y - cc1.y) * sd.x));
} else {
arrowPosY = scene2d.height;
}
}
var r1 = shapeDist.transformed(gravityMat);
var r2 = front.transformed(gravityMat);
r1.z = 0;
r2.z = 0;
r1.normalize();
r2.normalize();
var cp = r1.cross(r2);
var forwardness = r1.dot(r2);
var xfPosAngle = -Math.asin(cp.z);
var normal = new Vector(0, 1, 0);
var foldAmount = 0.0;
var foldArrow = false;
if (forwardness < 0.5) {
foldArrow = true;
foldAmount = (0.5 - forwardness) / 3;
}
// Is the object behind us?
if (forwardness < 0.0) {
// Get the angle into the correct range
if (xfPosAngle >= 0.0)
xfPosAngle += Math.PI;
else
xfPosAngle -= Math.PI;
}
// If aSinZ is between -fov and fov, then
if (-fovX <= xfPosAngle) {
if (fovX >= xfPosAngle) {
// the new x is the fraction of where it is but convert it from an angle to tangent
arrowPosX = scene2d.width * 0.5 + Math.tan(xfPosAngle) * (scene2d.width * 0.5) / Math.tan(fovX);
} else {
// otherwise snap to edge
arrowPosX = scene2d.width;
}
}
var drawPoint = new Vector(arrowPosX, arrowPosY);
if (validProjection) {
drawPoint.load(projectedPos);
if (drawPoint.distanceSq(projectedPos) <= 75 * 75) {
var distOff = drawPoint.distance(projectedPos);
drawPoint = Util.lerpThreeVectors(projectedPos, new Vector(arrowPosX, arrowPosY), distOff / 75);
}
}
var ellipse = new Vector((scene2d.width / 2) * ellipseScreenFraction.x, (scene2d.height / 2) * ellipseScreenFraction.y);
var arrowDir = drawPoint.sub(new Vector(scene2d.width / 2, scene2d.height / 2));
var ellipseDistance = Math.sqrt(arrowDir.x * arrowDir.x / (ellipse.x * ellipse.x) + arrowDir.y * arrowDir.y / (ellipse.y * ellipse.y));
var arrowAlpha = maxArrowAlpha;
var circleAlpha = 0.0;
if (ellipseDistance <= 1) {
if (ellipseDistance <= 0.7) {
arrowAlpha = 0;
circleAlpha = maxTargetAlpha;
} else {
arrowAlpha = (ellipseDistance - 0.7) * (10 / 3) * maxArrowAlpha;
circleAlpha = maxArrowAlpha - arrowAlpha * maxTargetAlpha / maxArrowAlpha;
}
} else {
drawPoint = arrowDir.multiply(1 / ellipseDistance);
drawPoint.x += scene2d.width / 2;
drawPoint.y += scene2d.height / 2;
}
arrowDir.normalize();
if (blink) {
var r = color >> 16 & 0xFF;
var g = color >> 8 & 0xFF;
var b = color & 0xFF;
r = r > 127 ? r : 127;
g = g > 127 ? g : 127;
b = b > 127 ? b : 127;
color = r << 16 | g << 8 | b;
arrowAlpha *= 1.3;
if (arrowAlpha > 1)
arrowAlpha = 1;
circleAlpha *= 1.3;
if (circleAlpha > 1)
circleAlpha = 1;
}
var arrowScale = Util.clamp(1 - distToShape / 100, minArrowFraction, 1);
var yScaleLength = scene2d.height / 480;
if (arrowAlpha != 0) {
var arrowWidth = fullArrowWidth * arrowScale * yScaleLength;
var arrowLength = fullArrowLength * arrowScale * yScaleLength;
var arrowSideVector = new Vector(arrowWidth * arrowDir.y, arrowWidth * -arrowDir.x);
var halfArrowSideVec = arrowSideVector.multiply(0.5);
var arrowForwardVec = arrowDir.multiply(arrowLength);
var arrowBack = drawPoint.sub(arrowForwardVec);
var lowerRight = arrowBack.add(halfArrowSideVec);
var lowerLeft = arrowBack.sub(halfArrowSideVec);
if (foldAmount == 0)
foldArrow = false;
var halfFoldWidth = 0.5 * arrowWidth * foldAmount;
var halfFoldForwardWidthVec = new Vector(halfFoldWidth * arrowDir.y, -halfFoldWidth * arrowDir.x);
var foldLength = foldAmount * arrowLength;
var foldForwardVec = arrowDir.multiply(foldLength);
var foldBack = drawPoint.sub(foldForwardVec);
var foldLowerRight = foldBack.add(halfFoldForwardWidthVec);
var foldLowerLeft = foldBack.sub(halfFoldForwardWidthVec);
var doubleFoldVec = arrowDir.multiply(foldLength + foldLength);
var foldedTip = drawPoint.sub(doubleFoldVec);
g.beginFill(color, arrowAlpha);
g.lineStyle(0, color, arrowAlpha);
if (foldArrow) {
g.moveTo(lowerRight.x, lowerRight.y);
g.lineTo(lowerLeft.x, lowerLeft.y);
g.lineTo(foldLowerLeft.x, foldLowerLeft.y);
g.moveTo(lowerRight.x, lowerRight.y);
g.lineTo(foldLowerRight.x, foldLowerRight.y);
g.lineTo(foldLowerLeft.x, foldLowerLeft.y);
} else {
g.moveTo(drawPoint.x, drawPoint.y);
g.lineTo(lowerRight.x, lowerRight.y);
g.lineTo(lowerLeft.x, lowerLeft.y);
}
g.endFill();
g.lineStyle(1, 0x000000, arrowAlpha);
g.setColor(0x000000, arrowAlpha);
if (foldArrow) {
g.moveTo(lowerRight.x, lowerRight.y);
g.lineTo(foldLowerRight.x, foldLowerRight.y);
g.lineTo(foldLowerLeft.x, foldLowerLeft.y);
g.lineTo(lowerLeft.x, lowerLeft.y);
g.lineTo(lowerRight.x, lowerRight.y);
g.moveTo(foldLowerRight.x, foldLowerRight.y);
g.lineTo(foldedTip.x, foldedTip.y);
g.lineTo(foldLowerLeft.x, foldLowerLeft.y);
g.moveTo(foldedTip.x, foldedTip.y);
} else {
g.moveTo(drawPoint.x, drawPoint.y);
g.lineTo(lowerRight.x, lowerRight.y);
g.lineTo(lowerLeft.x, lowerLeft.y);
g.lineTo(drawPoint.x, drawPoint.y);
}
}
if (circleAlpha != 0) {
var arrowLen = fullArrowLength * arrowScale * 0.4 * yScaleLength;
var halfArrowLen = arrowLen * 0.55;
if (arrowScale >= 0.7) {
if (arrowScale < 0.8)
arrowLen = ((arrowScale - 0.7) * 10.0) * (arrowLen - halfArrowLen) + halfArrowLen;
var topLeft = drawPoint.sub(new Vector(arrowLen, arrowLen));
var midTopLeft = topLeft.add(new Vector(halfArrowLen, halfArrowLen));
var bottomRight = drawPoint.add(new Vector(arrowLen, arrowLen));
var midBottomRight = bottomRight.sub(new Vector(halfArrowLen, halfArrowLen));
// Top Left
g.beginFill(color, circleAlpha);
g.lineStyle(0, color, circleAlpha);
g.moveTo(midTopLeft.x, midTopLeft.y);
g.lineTo(midTopLeft.x, topLeft.y);
g.lineTo(topLeft.x, midTopLeft.y);
g.endFill();
// Top Right
g.beginFill(color, circleAlpha);
g.lineStyle(0, color, circleAlpha);
g.moveTo(midBottomRight.x, midTopLeft.y);
g.lineTo(midBottomRight.x, topLeft.y);
g.lineTo(bottomRight.x, midTopLeft.y);
g.endFill();
// Bottom Right
g.beginFill(color, circleAlpha);
g.lineStyle(0, color, circleAlpha);
g.moveTo(midBottomRight.x, midBottomRight.y);
g.lineTo(midBottomRight.x, bottomRight.y);
g.lineTo(bottomRight.x, midBottomRight.y);
g.endFill();
// Bottom Left
g.beginFill(color, circleAlpha);
g.lineStyle(0, color, circleAlpha);
g.moveTo(midTopLeft.x, midBottomRight.y);
g.lineTo(midTopLeft.x, bottomRight.y);
g.lineTo(topLeft.x, midBottomRight.y);
g.endFill();
// Border
g.lineStyle(1, 0x000000, circleAlpha);
g.setColor(0x000000, circleAlpha);
g.moveTo(midTopLeft.x, topLeft.y);
g.lineTo(topLeft.x, midTopLeft.y);
g.moveTo(midBottomRight.x, topLeft.y);
g.lineTo(bottomRight.x, midTopLeft.y);
g.moveTo(midBottomRight.x, bottomRight.y);
g.lineTo(bottomRight.x, midBottomRight.y);
g.moveTo(midTopLeft.x, bottomRight.y);
g.lineTo(topLeft.x, midBottomRight.y);
} else {
var halfTopLeft = drawPoint.sub(new Vector(halfArrowLen, halfArrowLen));
var halfBottomRight = drawPoint.add(new Vector(halfArrowLen, halfArrowLen));
g.beginFill(color, circleAlpha);
g.lineStyle(0, color, circleAlpha);
g.moveTo(halfTopLeft.x, drawPoint.y);
g.lineTo(drawPoint.x, halfTopLeft.y);
g.lineTo(halfBottomRight.x, drawPoint.y);
g.endFill();
g.beginFill(color, circleAlpha);
g.lineStyle(0, color, circleAlpha);
g.moveTo(halfTopLeft.x, drawPoint.y);
g.lineTo(drawPoint.x, halfBottomRight.y);
g.lineTo(halfBottomRight.x, drawPoint.y);
g.endFill();
g.lineStyle(1, 0x000000, circleAlpha);
g.setColor(0x000000, circleAlpha);
g.moveTo(halfBottomRight.x, drawPoint.y);
g.lineTo(drawPoint.x, halfTopLeft.y);
g.moveTo(drawPoint.x, halfTopLeft.y);
g.lineTo(halfTopLeft.x, drawPoint.y);
g.moveTo(halfTopLeft.x, drawPoint.y);
g.lineTo(drawPoint.x, halfBottomRight.y);
g.moveTo(drawPoint.x, halfBottomRight.y);
g.lineTo(halfBottomRight.x, drawPoint.y);
}
}
}
function renderName(pos:Vector, marble:Marble, opacity:Float) {
if (!marbleNameTexts.exists(marble)) {
var arialb14fontdata = ResourceLoader.getFileEntry("data/font/Arial Bold.fnt");
var arialb14b = new BitmapFont(arialb14fontdata.entry);
@:privateAccess arialb14b.loader = ResourceLoader.loader;
var arialBold14 = arialb14b.toSdfFont(cast 16 * Settings.uiScale, MultiChannel);
var txt = new h2d.Text(arialBold14, scene2d);
marbleNameTexts.set(marble, txt);
txt.textColor = 0xFFFF00;
}
var textObj = marbleNameTexts.get(marble);
textObj.text = @:privateAccess marble.connection.getName();
textObj.setPosition(pos.x - textObj.textWidth / 2, pos.y - textObj.textHeight);
textObj.alpha = opacity;
}
function dontRenderName(marble:Marble) {
if (marbleNameTexts.exists(marble)) {
marbleNameTexts.get(marble).alpha = 0;
}
}
}