352 lines
9.0 KiB
ActionScript 3
352 lines
9.0 KiB
ActionScript 3
//Radius added to every entry
|
|
const double ExtraRadius = 100.0;
|
|
const double BorderThickness = 150.0;
|
|
|
|
bool SHOW_TERRITORY_BORDERS = true;
|
|
void setTerritoryBordersShown(bool enabled) {
|
|
SHOW_TERRITORY_BORDERS = enabled;
|
|
}
|
|
|
|
bool getTerritoryBordersShown() {
|
|
return SHOW_TERRITORY_BORDERS;
|
|
}
|
|
|
|
bool angleFacing(double a, double b) {
|
|
double diff = angleDiff(a,b);
|
|
return diff > -pi * 0.5 && diff < pi * 0.5;
|
|
}
|
|
|
|
final class Sphere {
|
|
vec3d pos;
|
|
double radius;
|
|
double angle;
|
|
double dist;
|
|
int id;
|
|
int count = 1;
|
|
bool edge;
|
|
|
|
Sphere(int ID, const vec3d& Pos, double Radius, bool Edge) {
|
|
id = ID;
|
|
radius = Radius;
|
|
pos = Pos;
|
|
edge = Edge;
|
|
}
|
|
|
|
void updateAngle(const vec3d& center) {
|
|
angle = vec2d(pos.x - center.x, pos.z - center.z).radians();
|
|
dist = center.distanceTo(pos);
|
|
}
|
|
|
|
int opCmp(const Sphere@ other) const {
|
|
double diff = angle - other.angle;
|
|
if(diff > 0)
|
|
return 1;
|
|
else if(diff < 0)
|
|
return -1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
bool include(Sphere@ left, Sphere@ right) {
|
|
if(dist < left.dist && abs(angleDiff(left.angle, angle)) < pi * (5.0/180.0))
|
|
return false;
|
|
if(dist < right.dist && abs(angleDiff(right.angle, angle)) < pi * (5.0/180.0))
|
|
return false;
|
|
|
|
double leftTangent = vec2d(left.pos.x - pos.x, left.pos.z - pos.z).radians() - (pi * 0.5);
|
|
if(!angleFacing(leftTangent, angle))
|
|
leftTangent += pi;
|
|
|
|
double rightTangent = vec2d(right.pos.x - pos.x, right.pos.z - pos.z).radians() - (pi * 0.5);
|
|
if(!angleFacing(rightTangent, angle))
|
|
rightTangent += pi;
|
|
|
|
double diff = angleDiff(rightTangent,leftTangent);
|
|
|
|
return diff > -pi * 0.31;
|
|
}
|
|
|
|
bool generateVerts(Sphere@ left, Sphere@ right, array<vec3d>@ verts) {
|
|
//Get angle to tangent collision, choosing the tangent facing away from the center
|
|
double leftTangent = vec2d(left.pos.x - pos.x, left.pos.z - pos.z).radians() - (pi * 0.5);
|
|
if(!angleFacing(leftTangent, angle))
|
|
leftTangent += pi;
|
|
|
|
double rightTangent = vec2d(right.pos.x - pos.x, right.pos.z - pos.z).radians() - (pi * 0.5);
|
|
if(!angleFacing(rightTangent, angle))
|
|
rightTangent += pi;
|
|
|
|
double diff = angleDiff(rightTangent,leftTangent);
|
|
|
|
//Curve around the edge of this sphere
|
|
if(diff > 0) {
|
|
int points = max(3, int(diff / 0.1309)); //7.5 degrees per point
|
|
|
|
for(int i = 0; i < points; ++i) {
|
|
double a = leftTangent + (diff * double(i) / double(points));
|
|
vec3d dir(cos(a), 0, sin(a));
|
|
verts.insertLast(pos + dir * radius);
|
|
verts.insertLast(pos + dir * (radius - BorderThickness));
|
|
}
|
|
}
|
|
else if(diff > -pi * 0.31) {
|
|
//Bezier interpolation between tangent lines
|
|
vec3d leftDir(cos(leftTangent), 0, sin(leftTangent));
|
|
line3dd leftLine = line3dd(left.pos + leftDir * left.radius, pos + leftDir * radius);
|
|
|
|
vec3d rightDir(cos(rightTangent), 0, sin(rightTangent));
|
|
line3dd rightLine = line3dd(right.pos + rightDir * right.radius, pos + rightDir * radius);
|
|
|
|
for(int i = 0; i < 15; ++i) {
|
|
double p = 0.1 + (0.8 * double(i)/14.0);
|
|
|
|
verts.insertLast(leftLine.midpoint.interpolate(leftLine.end, p).interpolate( rightLine.end.interpolate(rightLine.midpoint, p), p));
|
|
verts.insertLast(verts[verts.length-1] + leftDir.interpolate(rightDir, p).normalized(-BorderThickness));
|
|
}
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class TerritoryNodeScript {
|
|
vec3d inner_center, edge_center;
|
|
array<vec3d> inner_verts, edge_verts;
|
|
array<Sphere@> spheres;
|
|
int innerCount = 0;
|
|
bool delta = false;
|
|
Empire@ owner = defaultEmpire;
|
|
|
|
TerritoryNodeScript(Node& node) {
|
|
node.scale = 1000000000.0;
|
|
node.rebuildTransform();
|
|
}
|
|
|
|
void setOwner(Empire@ emp) {
|
|
@owner = emp;
|
|
}
|
|
|
|
void addInner(Node& node, int sysId, vec3d position, double radius) {
|
|
for(uint i = 0, cnt = spheres.length; i < cnt; ++i) {
|
|
if(spheres[i].id == sysId) {
|
|
if(spheres[i].edge) {
|
|
++innerCount;
|
|
node.visible = true;
|
|
}
|
|
spheres[i].count++;
|
|
spheres[i].edge = false;
|
|
delta = true;
|
|
return;
|
|
}
|
|
}
|
|
spheres.insertLast(Sphere(sysId, position, radius + ExtraRadius, false));
|
|
delta = true;
|
|
|
|
++innerCount;
|
|
node.visible = true;
|
|
}
|
|
|
|
void addEdge(int sysId, vec3d position, double radius) {
|
|
for(uint i = 0, cnt = spheres.length; i < cnt; ++i) {
|
|
if(spheres[i].id == sysId) {
|
|
spheres[i].count++;
|
|
return;
|
|
}
|
|
}
|
|
spheres.insertLast(Sphere(sysId, position, radius + ExtraRadius, true));
|
|
delta = true;
|
|
}
|
|
|
|
void removeInner(Node& node, int sysId) {
|
|
for(uint i = 0, cnt = spheres.length; i < cnt; ++i) {
|
|
if(spheres[i].id == sysId) {
|
|
if(--spheres[i].count == 0) {
|
|
spheres.removeAt(i);
|
|
delta = true;
|
|
}
|
|
else {
|
|
spheres[i].edge = true;
|
|
delta = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
--innerCount;
|
|
node.visible = innerCount != 0;
|
|
}
|
|
|
|
void removeEdge(int sysId) {
|
|
for(uint i = 0, cnt = spheres.length; i < cnt; ++i) {
|
|
if(spheres[i].id == sysId) {
|
|
if(--spheres[i].count == 0) {
|
|
spheres.removeAt(i);
|
|
delta = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void rebuildPortion(array<Sphere@>& regions, array<vec3d>& verts, vec3d& center) {
|
|
center.set(0,0,0);
|
|
if(regions.length == 0) {
|
|
verts.length = 0;
|
|
return;
|
|
}
|
|
|
|
double rad = 0;
|
|
for(uint i = 0; i < regions.length; ++i) {
|
|
center += regions[i].pos;
|
|
rad = max(rad, regions[i].radius);
|
|
}
|
|
center /= double(regions.length);
|
|
|
|
//Update the angles for each sphere and sort
|
|
for(uint i = 0; i < regions.length; ++i) {
|
|
regions[i].updateAngle(center);
|
|
regions[i].radius = rad;
|
|
}
|
|
|
|
regions.sortAsc();
|
|
array<Sphere@>@ final = array<Sphere@>(), temp = @regions;
|
|
|
|
if(temp.length > 1) {
|
|
bool changed = false;
|
|
while(true) {
|
|
Sphere@ prev = temp[temp.length-1];
|
|
for(int i = 0, cnt = temp.length; i < cnt; ++i) {
|
|
Sphere@ sphere = temp[i];
|
|
Sphere@ right = temp[(i+1) % cnt];
|
|
if(sphere.include(prev, right)) {
|
|
final.insertLast(sphere);
|
|
@prev = sphere;
|
|
}
|
|
else {
|
|
changed = true;
|
|
}
|
|
}
|
|
|
|
if(final.length < 3)
|
|
break;
|
|
|
|
if(changed) {
|
|
changed = false;
|
|
@temp = @final;
|
|
@final = array<Sphere@>();
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
final.insertLast(temp[0]);
|
|
}
|
|
|
|
verts.length = 0;
|
|
if(final.length == 1) {
|
|
Sphere@ sphere = final[0];
|
|
for(int i = 0; i < 48; ++i) {
|
|
double a = double(i) * twopi / 48.0;
|
|
vec3d dir(cos(a), 0, sin(a));
|
|
verts.insertLast(sphere.pos + dir * sphere.radius);
|
|
verts.insertLast(sphere.pos + dir * (sphere.radius - BorderThickness));
|
|
}
|
|
}
|
|
else if(final.length == 2) {
|
|
for(int s = 0; s < 2; ++s) {
|
|
Sphere@ sphere = final[s];
|
|
for(int i = 0; i < 24; ++i) {
|
|
double a = double(i) * pi / 24.0 + (sphere.angle - pi * 0.5);
|
|
vec3d dir(cos(a), 0, sin(a));
|
|
verts.insertLast(sphere.pos + dir * sphere.radius);
|
|
verts.insertLast(sphere.pos + dir * (sphere.radius - BorderThickness));
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
Sphere@ prev = final[final.length-1];
|
|
for(int i = 0, cnt = final.length; i < cnt; ++i) {
|
|
Sphere@ right = final[(i+1) % cnt];
|
|
if(final[i].generateVerts(prev, right, verts))
|
|
@prev = final[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
void rebuild() {
|
|
delta = false;
|
|
|
|
array<Sphere@> inner;
|
|
|
|
//Copy over all inner spheres
|
|
for(int i = 0, cnt = spheres.length; i < cnt; ++i)
|
|
if(!spheres[i].edge)
|
|
inner.insertLast(spheres[i]);
|
|
|
|
rebuildPortion(spheres, edge_verts, edge_center);
|
|
rebuildPortion(inner, inner_verts, inner_center);
|
|
}
|
|
|
|
bool preRender(Node& node) {
|
|
if(delta)
|
|
rebuild();
|
|
return inner_verts.length != 0 && owner !is null;
|
|
}
|
|
|
|
void render(Node& node) {
|
|
if(SHOW_TERRITORY_BORDERS) {
|
|
vec2f innerUV(0,0), outerUV(1,0);
|
|
vec2f innerUV_r(0,1), outerUV_r(1,1);
|
|
if(owner is playerEmpire) {
|
|
drawPolygonStart(edge_verts.length, material::Territory, Color(0xffffff80));
|
|
for(int i = 0, cnt = edge_verts.length; i < cnt; i += 2) {
|
|
drawPolygonPoint(edge_verts[i], outerUV);
|
|
drawPolygonPoint(edge_verts[(i+2)%cnt], outerUV);
|
|
drawPolygonPoint(edge_verts[(i+1)%cnt], innerUV);
|
|
|
|
drawPolygonPoint(edge_verts[(i+2)%cnt], outerUV);
|
|
drawPolygonPoint(edge_verts[(i+3)%cnt], innerUV);
|
|
drawPolygonPoint(edge_verts[(i+1)%cnt], innerUV);
|
|
}
|
|
drawPolygonEnd();
|
|
}
|
|
|
|
double offset = 0;
|
|
bool stipple = false;
|
|
Empire@ master = owner.SubjugatedBy;
|
|
if(master !is null) {
|
|
stipple = true;
|
|
master.color.toVec4(shader::STIPPLE_COLOR);
|
|
}
|
|
else {
|
|
shader::STIPPLE_COLOR = vec4f(0.f, 0.f, 0.f, 0.f);
|
|
}
|
|
|
|
drawPolygonStart(inner_verts.length, material::Territory, owner.color);
|
|
for(int i = 0, cnt = inner_verts.length; i < cnt; i += 2) {
|
|
if(stipple) {
|
|
outerUV.y = offset;
|
|
innerUV.y = offset;
|
|
offset += inner_verts[i].distanceTo(inner_verts[(i+2)%cnt]);
|
|
outerUV_r.y = offset;
|
|
innerUV_r.y = offset;
|
|
}
|
|
|
|
drawPolygonPoint(inner_verts[i], outerUV);
|
|
drawPolygonPoint(inner_verts[(i+2)%cnt], outerUV_r);
|
|
drawPolygonPoint(inner_verts[(i+1)%cnt], innerUV);
|
|
|
|
drawPolygonPoint(inner_verts[(i+2)%cnt], outerUV_r);
|
|
drawPolygonPoint(inner_verts[(i+3)%cnt], innerUV_r);
|
|
drawPolygonPoint(inner_verts[(i+1)%cnt], innerUV);
|
|
}
|
|
drawPolygonEnd();
|
|
}
|
|
}
|
|
};
|