Open source Star Ruler 2 source code!

This commit is contained in:
Lucas de Vries
2018-07-17 14:15:37 +02:00
commit cc307720ff
4342 changed files with 2365070 additions and 0 deletions
+40
View File
@@ -0,0 +1,40 @@
//Draws a blackhole and its accretion disk
final class BlackholeNodeScript {
BlackholeNodeScript(Node& node) {
node.transparent = true;
node.customColor = true;
node.autoCull = false;
}
void establish(Node& node, Star& star) {
auto@ gfx = PersistentGfx();
gfx.establish(star, "BlackholeGlitter");
gfx.rotate(quaterniond_fromAxisAngle(vec3d_right(), pi * 0.5));
}
bool preRender(Node& node) {
return isSphereVisible(node.abs_position, 20.0 * node.abs_scale);
}
void render(Node& node) {
double dist = node.sortDistance / (2500.0 * node.abs_scale * pixelSizeRatio);
if(dist < 1.0) {
node.applyTransform();
material::Blackhole.switchTo();
model::Sphere_max.draw(node.sortDistance / (node.abs_scale * pixelSizeRatio));
undoTransform();
renderPlane(material::AccretionDisk, node.abs_position, node.abs_scale * 20.0, Color(0xffffffff));
}
if(dist > 0.5) {
Color col(node.color);
col.a = dist > 1.0 ? 255 : int((dist - 0.5)*255.0/0.5);
renderBillboard(material::DistantStar, node.abs_position, node.abs_scale * 320.0, 0.0, col);
}
}
};
+155
View File
@@ -0,0 +1,155 @@
const double MAX_SIZE = 4000.0;
const double APPROACH_EPSILON = 0.0002;
bool SHOW_FLEET_PLANES = true;
bool SHOW_FLEET_ICONS = true;
void setFleetPlanesShown(bool enabled) {
SHOW_FLEET_PLANES = enabled;
}
bool getFleetPlanesShown() {
return SHOW_FLEET_PLANES;
}
void setFleetIconsShown(bool enabled) {
SHOW_FLEET_ICONS = enabled;
}
bool getFleetIconsShown() {
return SHOW_FLEET_ICONS;
}
const Color FTLColor(0x00c0ff80);
const Color CombatColor(0xffc60080);
vec4f CombatVec;
vec4f FTLVec;
void init() {
CombatColor.toVec4(CombatVec);
FTLColor.toVec4(FTLVec);
}
class FleetPlaneNodeScript {
Object@ leader;
Sprite fleetIcon;
double radius;
bool hasPlane = false;
bool withFleet = false;
bool rotate = true;
bool wasVisible = false;
FleetPlaneNodeScript(Node& node) {
node.transparent = true;
node.visible = false;
node.needsTransform = false;
node.fixedSize = true;
node.createPhysics();
}
void establish(Node& node, Object& obj, double rad) {
@leader = obj;
radius = rad;
if(obj.isShip) {
const Design@ dsg = cast<Ship>(obj).blueprint.design;
if(dsg !is null)
fleetIcon = dsg.fleetIcon;
}
node.scale = radius;
node.position = obj.position;
@node.object = obj;
node.rebuildTransform();
}
void set_hasSupply(bool supply) {
hasPlane = supply;
}
void set_hasFleet(bool has) {
withFleet = has;
}
bool preRender(Node& node) {
if(node.visible && leader !is null) {
node.position = leader.node_position;
double size = leader.radius;
size = 0.2 * (1.0 + size) / (3.0 + size);
if(wasVisible)
size *= node.sortDistance * 0.25;
else
size *= cameraPos.distanceTo(node.position) * 0.25;
size = min(size, MAX_SIZE);
if(leader.selected)
size *= 1.1;
node.scale = size * 0.5;
rotate = !leader.hasOrbit;
node.rebuildTransform();
wasVisible = true;
return true;
}
else {
wasVisible = false;
return false;
}
}
void render(Node& node) {
if(hasPlane && node.sortDistance < 2000.0 && node.sortDistance >= 500.0 && SHOW_FLEET_PLANES) {
Color color(0xffffff14);
if(node.sortDistance < 600.0)
color.a = double(color.a) * (node.sortDistance - 500.0) / 100.0;
renderPlane(material::FleetCircle, node.abs_position, radius, color);
}
double iconDist = 600.0 * leader.radius;
if(node.sortDistance > iconDist && SHOW_FLEET_ICONS) {
//TODO: Use leader's node instead
vec3d camFacing = cameraFacing, camUp = cameraUp;
double rot = 0.0;
if(rotate) {
vec3d objFacing = leader.node_rotation * vec3d_front();
double alongDot = camFacing.dot(objFacing);
if(alongDot > -0.9999 && alongDot < 0.9999) {
objFacing -= camFacing * alongDot;
objFacing.normalize();
vec3d camRight = camFacing.cross(camUp).normalized();
rot = acos(camRight.dot(objFacing));
if(camRight.cross(camFacing).dot(objFacing) < 0)
rot = -rot;
}
else {
rot = alongDot > 0 ? pi * 0.5 : pi * -0.5;
}
}
Empire@ owner = leader.owner;
Color col = owner.color;
if(node.sortDistance < iconDist * 2.0)
col.a = uint8(255.0 * (node.sortDistance - iconDist) / iconDist);
node.color = col;
Ship@ ship = cast<Ship>(leader);
if(ship !is null && ship.isFTLing)
shader::GLOW_COLOR = FTLVec;
else if(owner is playerEmpire && leader.inCombat)
shader::GLOW_COLOR = CombatVec;
else
shader::GLOW_COLOR.w = 0.f;
shader::APPROACH = APPROACH_EPSILON;
if(fleetIcon.valid)
renderBillboard(fleetIcon.sheet, fleetIcon.index, node.abs_position, node.scale * 2.0, rot);
else
renderBillboard(spritesheet::ShipGroupIcons, 0, node.abs_position, node.scale * 2.0, rot);
}
}
};
+109
View File
@@ -0,0 +1,109 @@
import util.convar;
final class GasSprite {
bool draw = true;
uint baseCol = 0xffffff00;
int trueAlpha = 20;
int baseAlpha = 0;
Color resultCol;
double scale = 1.0;
double rotation = randomd(0,twopi);
double sortDist = 0;
uint index;
vec3d position;
GasSprite(const vec3d& pos, double Scale, uint col, bool structured, int baseAlpha = 0) {
position = pos;
scale = Scale * 2.0;
baseCol = col & 0xffffff00;
trueAlpha = int(col & 0xff);
index = structured ? randomi(0,23) : randomi(24,47);
this.baseAlpha = baseAlpha;
}
};
final class GalaxyGasScript {
array<GasSprite@> sprites;
array<int64> sorter;
bool dirty = true;
GalaxyGasScript(Node& node) {
node.transparent = true;
node.autoCull = false;
}
void addSprite(vec3d pos, double scale, uint color, bool structured, int baseAlpha = 0) {
sorter.insertLast( int64(sprites.length) );
sprites.insertLast( GasSprite(pos, scale, color, structured, baseAlpha) );
dirty = true;
}
void recalculateBounds(Node& node) {
vec3d p;
for(uint i = 0, cnt = sprites.length; i < cnt; ++i)
p += sprites[i].position;
p /= double(sprites.length);
node.position = p;
double maxDist = 0;
for(uint i = 0, cnt = sprites.length; i < cnt; ++i)
maxDist = max(maxDist, p.distanceTo(sprites[i].position) + sprites[i].scale);
node.scale = maxDist;
node.rebuildTransform();
}
bool preRender(Node& node) {
if(!settings::bShowGalaxyGas)
return false;
if(dirty) {
dirty = false;
if(sprites.length == 0)
return false;
recalculateBounds(node);
}
if(!node.isInView())
return false;
bool anyVisible = false;
//Update the sprite's color and decide if it needs rendered/sorted
for(uint i = 0, cnt = sprites.length; i < cnt; ++i) {
uint index = uint(sorter[i]) & 0x7fffffff;
GasSprite@ sprite = sprites[index];
double sortDist = getCameraDistance(sprite.position);
if(sortDist < 0.0) {
sprite.draw = false;
continue;
}
double fadeOut = ((sortDist / 30000.0) - 0.3);
int trueAlpha = sprite.trueAlpha;
int a = fadeOut < 1.0 ? int(double(trueAlpha) * fadeOut) : trueAlpha;
a += sprite.baseAlpha;
if(a < 4) {
sprite.draw = false;
continue;
}
sprite.resultCol = Color(sprite.baseCol | a);
sprite.draw = true;
anyVisible = true;
sorter[i] = (int64(sortDist * 100.0) << 32) | int64(index);
}
if(anyVisible)
sorter.sortAsc();
return anyVisible;
}
void render(Node& node) {
for(uint i = 0, cnt = sprites.length; i < cnt; ++i) {
GasSprite@ sprite = sprites[sorter[i] & 0x7fffffff];
if(sprite.draw)
renderBillboard(spritesheet::Nebulas, sprite.index, sprite.position, sprite.scale, sprite.rotation, sprite.resultCol);
}
}
};
+63
View File
@@ -0,0 +1,63 @@
export setGalaxyPlanesShown;
export getGalaxyPlanesShown;
bool SHOW_GALAXY_PLANES = false;
void setGalaxyPlanesShown(bool enabled) {
SHOW_GALAXY_PLANES = enabled;
}
bool getGalaxyPlanesShown() {
return SHOW_GALAXY_PLANES;
}
class GalaxyPlaneScript {
vec3d origin;
double radius;
double planeDist = 0;
GalaxyPlaneScript(Node& node) {
}
void establish(Node& node, vec3d Origin, double Radius) {
origin = Origin;
radius = Radius;
node.scale = radius;
node.position = origin;
node.rebuildTransform();
}
bool preRender(Node& node) {
if(!SHOW_GALAXY_PLANES)
return false;
//Calculate distance to plane
line3dd camLine(cameraPos, cameraPos+cameraFacing);
vec3d intersect;
if(!camLine.intersectY(intersect, origin.y, false)) {
intersect = cameraPos;
intersect.y = origin.y;
planeDist = sqrt(
sqr(max(0.0, intersect.distanceTo(origin) - radius))
+ sqr(cameraPos.y - origin.y));
}
else {
planeDist = intersect.distanceTo(cameraPos);
max(0.0, intersect.distanceTo(origin) - radius);
}
return planeDist > 3000.0;
}
void render(Node& node) {
shader::RADIUS = radius;
shader::PLANE_DISTANCE = planeDist;
drawPolygonStart(PT_Quads, 1, material::GalaxyPlane);
drawPolygonPoint(origin + vec3d(-radius, 0, -radius), vec2f(0.f, 0.f));
drawPolygonPoint(origin + vec3d(+radius, 0, -radius), vec2f(1.f, 0.f));
drawPolygonPoint(origin + vec3d(+radius, 0, +radius), vec2f(1.f, 1.f));
drawPolygonPoint(origin + vec3d(-radius, 0, +radius), vec2f(0.f, 1.f));
drawPolygonEnd();
}
};
+44
View File
@@ -0,0 +1,44 @@
//Draws an expanding, darkening nova
final class NovaNodeScript {
double life = 0, duration = 900.0;
double lastTick = gameTime;
double alpha = 1.0;
NovaNodeScript(Node& node) {
node.transparent = true;
node.customColor = true;
}
bool preRender(Node& node) {
double t = gameTime;
if(t > lastTick) {
double time = t - lastTick;
lastTick = t;
life += time;
if(life > duration) {
node.markForDeletion();
return false;
}
alpha = 1.0 - (life / duration);
node.scale = 1000.0 + 1000.0 * life / duration;
node.rebuildTransform();
}
return true;
}
void render(Node& node) {
node.applyTransform();
shader::NOVA_AGE = life / duration;
material::Nova.switchTo();
model::Sphere_max.draw(node.sortDistance / (node.abs_scale * pixelSizeRatio));
undoTransform();
}
};
+63
View File
@@ -0,0 +1,63 @@
import orbitals;
from nodes.FleetPlane import SHOW_FLEET_PLANES;
const double MAX_SIZE = 500.0;
final class OrbitalNodeScript {
const Orbital@ obj;
const OrbitalModule@ def;
double fleetPlane = 0.0;
OrbitalNodeScript(Node& node) {
node.transparent = true;
node.memorable = true;
}
void establish(Node& node, Orbital& orbital, uint type) {
@obj = orbital;
@def = getOrbitalModule(type);
}
void setFleetPlane(double radius) {
fleetPlane = radius;
}
void render(Node& node) {
if(def is null)
return;
if(fleetPlane != 0 && node.sortDistance < 2000.0 && node.sortDistance >= 150.0 && SHOW_FLEET_PLANES) {
Color color(0xffffff14);
if(node.sortDistance < 250.0)
color.a = double(color.a) * (node.sortDistance - 150.0) / 100.0;
renderPlane(material::FleetCircle, node.abs_position, fleetPlane, color);
}
node.applyTransform();
def.material.switchTo();
def.model.draw();
/*if(obj.Shield > 0) {*/
/* shader::SHIELD_STRENGTH = float(obj.Shield / obj.MaxShield);*/
/* material::Shield.switchTo();*/
/* def.model.draw();*/
/*}*/
undoTransform();
if(node.sortDistance > 600.0 && def.distantIcon.valid) {
double size = obj.radius * def.iconSize;
size *= node.sortDistance * 0.09;
size = min(size, MAX_SIZE);
if(obj.selected)
size *= 1.1;
Empire@ owner = obj.owner;
Color col;
if(owner !is null)
col = owner.color;
renderBillboard(def.distantIcon.sheet, def.distantIcon.index, node.abs_position, size, 0.0, col);
}
}
};
+85
View File
@@ -0,0 +1,85 @@
class PersistentGfxScript {
Object@ obj;
ParticleSystemNode@ gfx;
Region@ prevParent;
bool objRotate = false;
PersistentGfxScript(Node& node) {
node.visible = false;
}
~PersistentGfxScript() {
if(gfx !is null) {
gfx.stop();
@gfx = null;
}
}
void stop(Node& node) {
if(gfx !is null) {
gfx.stop();
@gfx = null;
}
@obj = null;
@prevParent = null;
node.markForDeletion();
}
void establish(Node& node, Object@ Obj, string psName, double scale) {
@obj = Obj;
@gfx = playParticleSystem(psName, obj.position, obj.radius * scale);
@prevParent = obj.region;
gfx.hintParentObject(prevParent, false);
node.visible = obj !is null;
gfx.visible = obj.visible || obj.known;
}
void establish(Node& node, vec3d at, string psName, double scale, Object@ parent) {
@gfx = playParticleSystem(psName, at, scale);
gfx.hintParentObject(parent, false);
node.visible = true;
}
void rotate(quaterniond rot) {
if(gfx !is null)
gfx.emitRot = rot;
}
void setObjectRotation(bool value) {
objRotate = value;
}
bool preRender(Node& node) {
if(gfx is null)
return false;
if(obj !is null) {
if(!obj.valid) {
stop(node);
return false;
}
if(obj.region !is prevParent) {
@prevParent = obj.region;
gfx.hintParentObject(prevParent, false);
}
//TODO: Make less awful
bool visible = obj.visible || obj.known;
gfx.visible = visible;
gfx.position = obj.position;
if(objRotate)
gfx.emitRot = obj.rotation;
gfx.rebuildTransform();
gfx.velocity = obj.velocity;
}
return false;
}
//We don't actually need to render anything ourselves, we just manage a particle effect
//void render(Node& node) {
//}
};
+375
View File
@@ -0,0 +1,375 @@
import resources;
import planet_types;
import util.convar;
import nodes.StrategicIcon;
import planet_levels;
const double APPROACH_EPSILON = 0.002;
const double OUTSIDE_DISTANCE = 12000.0;
const double OUTSIDE_SIZE_MAX = 25000.0;
const double ANIMATE_TIME = 0.45;
const double GRAVITY_DISC_MAX_DIST = 1000.0;
const double FADE_DIST_MIN = 250;
const double FADE_DIST_MAX = 300;
const double VERY_DISTANT_START = 56000.0;
const double VERY_DISTANT_END = 80000.0;
vec4f DISABLE_NORMAL, DISABLE_POPULATION;
ConVar PlanetIconSize("planet_icon_size", 0.0225);
ConVar PlanetResourceSize("planet_resource_size", 0.4);
ConVar PlanetTypeSize("planet_type_size", 0.7);
ConVar PlanetSelectedSize("planet_selected_size", 1.0);
const ResourceClass@ foodClass;
const ResourceClass@ scalableClass;
void init() {
@foodClass = getResourceClass("Food");
@scalableClass = getResourceClass("Scalable");
colors::Red.toVec4(DISABLE_NORMAL);
Color(0xff6300ff).toVec4(DISABLE_POPULATION);
}
bool SHOW_PLANET_PLANES = true;
bool SHOW_PLANET_ICONS = true;
void setPlanetPlanesShown(bool enabled) {
SHOW_PLANET_PLANES = enabled;
}
bool getPlanetPlanesShown() {
return SHOW_PLANET_PLANES;
}
void setPlanetIconsShown(bool enabled) {
SHOW_PLANET_ICONS = enabled;
}
bool getPlanetIconsShown() {
return SHOW_PLANET_ICONS;
}
final class PlanetIconNodeScript : StrategicIcon {
Planet@ pl;
uint level;
uint typeIcon;
uint resourceIcon = uint(-1);
ResourceSheet@ rs;
const Material@ levelMat, flag;
vec4f disableColor;
Color color;
const ResourceType@ type;
Empire@ captureEmp;
float capturePct = 0.f;
float resourceClass = -1.f;
bool plane = false;
bool isPlayer = false;
bool isDisabled = true;
bool isExported = false;
bool isMaterial = false;
bool isDecaying = false;
bool isBeingColonized = false;
bool isMemory = false;
bool isLowPop = false;
PlanetIconNodeScript(Node& node) {
node.visible = false;
super(node);
}
void establish(Node& node, Planet& planet) {
@pl = planet;
typeIcon = getPlanetType(planet.PlanetType).distantIcon.index;
@node.object = planet;
node.position = planet.position;
node.rebuildTransform();
}
void setOwner(Empire@ owner) {
if(owner !is null && owner.valid) {
color = owner.color;
@flag = owner.flag;
isPlayer = true;
}
else {
color = Color(0xb0b0b0ff);
isPlayer = false;
}
}
void setLevel(uint lvl) {
level = lvl;
if(rs !is null) {
@levelMat = rs.getMaterial(level);
}
else switch(level) {
case 0: @levelMat = material::PlanetLevel0; break;
case 1: @levelMat = material::PlanetLevel1; break;
case 2: @levelMat = material::PlanetLevel2; break;
case 3: @levelMat = material::PlanetLevel3; break;
case 4: @levelMat = material::PlanetLevel4; break;
case 5: default: @levelMat = material::PlanetLevel5; break;
}
}
void setResource(uint id) {
@type = getResource(id);
if(type is null) {
resourceIcon = uint(-1);
resourceClass = -1.f;
}
else {
@rs = type.distantSheet;
resourceIcon = type.smallIcon.index;
if(type.level > 0 && type.level <= 3)
resourceClass = 3.f + float(type.level);
else if(type.cls is foodClass)
resourceClass = 7.f;
else if(type.cls is scalableClass)
resourceClass = 16.f;
else
resourceClass = -1.f;
}
setLevel(level);
}
void setState(bool dis, bool exp, bool mat, bool decay) {
isDisabled = dis;
isExported = exp;
isMaterial = mat;
isDecaying = decay;
}
void setBeingColonized(bool value) {
isBeingColonized = value;
}
void setCapture(Empire@ emp, float pct) {
@captureEmp = emp;
capturePct = pct;
}
bool preRender(Node& node) {
if(!SHOW_PLANET_ICONS && !SHOW_PLANET_PLANES)
return false;
if(pl is null)
return false;
isMemory = false;
{
if(pl.visible) {
node.visible = true;
}
else if(pl.known) {
isMemory = true;
node.visible = true;
}
else if(!node.visible) {
return false;
}
}
if(!isMemory) {
Empire@ owner = pl.owner;
if(owner !is null && owner.valid) {
color = owner.color;
isPlayer = true;
}
else {
color = Color(0xb0b0b0ff);
isPlayer = false;
}
}
double dist = node.sortDistance * config::GFX_DISTANCE_MOD;
plane = dist < GRAVITY_DISC_MAX_DIST;
vec3d plPos = pl.node_position;
StrategicIcon::update(node, plPos, PlanetIconSize.value,
OUTSIDE_DISTANCE, OUTSIDE_SIZE_MAX, ANIMATE_TIME,
FADE_DIST_MIN * 10, FADE_DIST_MAX * 10);
if(pl.owner is playerEmpire) {
if(isDisabled) {
isLowPop = false;
if(type is null) {
disableColor = DISABLE_NORMAL;
}
else {
if(pl.resourceLevel >= type.level &&
pl.population < getPlanetLevelRequiredPop(pl, type.level)) {
disableColor = DISABLE_POPULATION;
}
else {
disableColor = DISABLE_NORMAL;
}
}
}
else {
isLowPop = pl.resourceLevel > pl.level;
disableColor = DISABLE_POPULATION;
}
}
if(isMemory)
alpha *= 0.7f;
return true;
}
void render(Node& node) {
Empire@ owner = pl.owner;
Color col = color;
col.a = alpha * 255;
double dist = node.sortDistance * config::GFX_DISTANCE_MOD;
float iconFade = 1.0;
if(dist > VERY_DISTANT_START) {
iconFade = float(clamp(1.0 - ((dist - VERY_DISTANT_START) / (VERY_DISTANT_END - VERY_DISTANT_START)), 0.0, 1.0));
//Distant planet icons are very simple and more numerous, so we check for that first
if(iconFade < 0.01) {
if(pl.selected) {
Color scol(0xffffffff);
scol.a = col.a;
renderBillboard(material::DistantPlanetSelected, node.abs_position,
node.abs_scale * 2.0 * PlanetSelectedSize.value, 0, scol);
shader::APPROACH += APPROACH_EPSILON;
}
renderBillboard(material::FadedPlanet, node.abs_position, node.abs_scale * 2.0, 0, col);
return;
}
}
//Capturing stuff
if(captureEmp !is null && !isMemory) {
captureEmp.color.toVec4(shader::CAPTURE_COLOR);
shader::CAPTURE_PROGRESS = capturePct;
}
else if(pl.Population < 1.0 && !isMemory) {
shader::CAPTURE_PROGRESS = pl.Population;
owner.color.toVec4(shader::CAPTURE_COLOR);
}
else if(isDecaying) {
shader::CAPTURE_PROGRESS = pl.decayTime / (config::LEVEL_DECAY_TIMER / owner.PlanetDecaySpeed);
float pct = abs((frameTime % 1.0) - 0.5f) * 2.f;
colors::Red.interpolate(colors::Orange, pct).toVec4(shader::CAPTURE_COLOR);
}
else {
shader::CAPTURE_COLOR = vec4f();
}
double orbitCircleFadeIn = FADE_DIST_MAX * 10;
//Draw the orbit plane
if(dist < orbitCircleFadeIn && dist > 100.0 && SHOW_PLANET_PLANES) {
double orbitSize = pl.OrbitSize;
shader::CIRCLE_MIN = (pl.radius + 0.5f) / orbitSize;
shader::CIRCLE_MAX = 1.f;
Color orbitColor(0xffffff05);
double orbitCircleFadeOut = FADE_DIST_MIN * 10;
double a = 0.025;
if(pl.selected)
a = 0.035;
else if(dist < 250.0)
a *= (dist - 150.0) / 100.0;
else if(dist > orbitCircleFadeOut)
a *= 1.0 - (dist - orbitCircleFadeOut) / (orbitCircleFadeIn - orbitCircleFadeOut);
if(a <= 0.0)
orbitColor.a = 0;
else if(a >= 1.0)
orbitColor.a = 255;
else
orbitColor.a = uint8(a * 255.0);
vec3d center = node.abs_position;
if(orbitColor.a > 0)
renderPlane(material::OrbitCircle, center, orbitSize, orbitColor);
float prevA = shader::CAPTURE_COLOR.w;
Color flagColor = color;
if(isPlayer && dist > 150.0 && dist < 1850.0) {
//NOTE: The node can be setup before the empire has a flag specified
if(flag is null)
@flag = owner.flag;
flagColor.a = min(a * (6.0 * 255.0) * (1.0 - (dist - 150.0) / 1700.0), 255.0);
if(prevA > 0.f)
shader::CAPTURE_COLOR.w = float(flagColor.a) / 255.f * 1.5f;
}
else {
flagColor.a = 0;
shader::CAPTURE_COLOR.w = 0.f;
}
if(flagColor.a > 4) {
center.y += 0.1;
shader::CAPTURE_COLOR.w = float(flagColor.a) / 255.f;
renderPlane(flag, center + vec3d(orbitSize * -0.9, 0.0, 0.0), orbitSize * 0.1, flagColor, pi * 0.5);
renderPlane(flag, center + vec3d(orbitSize * 0.9, 0.0, 0.0), orbitSize * 0.1, flagColor, pi * 1.5);
renderPlane(flag, center + vec3d(0.0, 0.0, orbitSize * 0.9), orbitSize * 0.1, flagColor, 0.0);
renderPlane(flag, center + vec3d(0.0, 0.0, orbitSize * -0.9), orbitSize * 0.1, flagColor, pi);
}
shader::CAPTURE_COLOR.w = prevA;
}
if(col.a > 2 && SHOW_PLANET_ICONS) {
//Draw selected border
shader::APPROACH = APPROACH_EPSILON * (col.a != 255 ? -8.0 : 1.0);
if(pl.selected) {
Color scol(0xffffffff);
scol.a = col.a;
renderBillboard(material::DistantPlanetSelected, node.abs_position,
node.abs_scale * 2.0 * PlanetSelectedSize.value, 0, scol);
shader::APPROACH += APPROACH_EPSILON;
}
shader::DISTANT_SPRITE_FADE = iconFade;
spritesheet::DistantPlanetType.getSourceUV(typeIcon, shader::DISTANT_SPRITE1);
if(rs !is null)
rs.getUV(resourceIcon, shader::DISTANT_SPRITE2);
else if(resourceIcon < 0xffffffff)
spritesheet::ResourceIconsSmall.getSourceUV(resourceIcon, shader::DISTANT_SPRITE2);
else
shader::DISTANT_SPRITE2 = vec4f();
//Draw planet level icon
shader::DISTANT_SPRITE_SCALE1 = PlanetTypeSize.value;
shader::DISTANT_SPRITE_SCALE2 = PlanetResourceSize.value;
shader::IS_COLONIZING = isBeingColonized ? 1.f : 0.f;
shader::IS_OWNED = (isPlayer && levelMat !is null) ? 1.f : 0.f;
shader::IS_EXPORTED = isExported ? 1.f : 0.f;
shader::IS_DECAYING = isDecaying ? 1.f : 0.f;
shader::RESOURCE_CLASS = resourceClass;
if(owner is playerEmpire) {
shader::IS_DISABLED = (isDisabled || isLowPop) ? 1.f : 0.f;
shader::IS_USED = (isMaterial || isDisabled || isDecaying || isLowPop) ? 1.f : 0.f;
if(isDisabled || isLowPop)
shader::DISABLE_COLOR = disableColor;
}
else {
shader::IS_DISABLED = 0.f;
shader::IS_USED = 1.f;
}
if(levelMat is null)
renderBillboard(material::PlanetLevel0, node.abs_position, node.abs_scale * 2.0, 0, col);
else
renderBillboard(levelMat, node.abs_position, node.abs_scale * 2.0, 0, col);
}
}
};
+346
View File
@@ -0,0 +1,346 @@
import planet_types;
from planets.PlanetSurface import preparePlanetShader;
const double PLANET_DIST_MAX = 4000;
enum PlanetSpecial {
PS_None,
PS_Asteroids,
PS_Ring
};
//Draws the physical star, its corona, and a distant star sprite
final class MoonData {
uint style = 0;
float size = 0.f;
};
final class PlanetNodeScript {
bool Colonized = false;
const Material@ emptyMat = material::ProceduralPlanet;
const Material@ colonyMat = material::ProceduralPlanet;
const Material@ atmosMat;
const Model@ planetModel;
PlanetSpecial special = PS_None;
double ringScale = 1.0, ringAngle = 0.0;
float ringMin = 0.f, ringMax = 1.f;
const Material@ ringMat;
array<MoonData@>@ moons;
uint gfxFlags = 0;
Planet@ obj;
PlanetNodeScript(Node& node) {
node.memorable = true;
node.animInvis = false;
node.autoCull = false;
}
void set_planetType(Node& node, int planetTypeID) {
//Cache values for performance
const PlanetType@ type = getPlanetType(planetTypeID);
@emptyMat = type.emptyMat;
@colonyMat = type.colonyMat;
@planetModel = type.model;
//@dyingMat = type.dyingMat;
@atmosMat = type.atmosMat;
node.transparent = atmosMat !is null;
}
void set_colonized(bool isColonized) {
Colonized = isColonized;
}
void set_flags(uint newFlags) {
gfxFlags = newFlags;
}
void establish(Planet@ obj) {
@this.obj = obj;
}
void addRing(Node& node, uint rnd) {
node.autoCull = false;
special = PS_Ring;
uint matIndex = rnd % 7;
rnd /= 7;
uint scale = rnd % 256;
rnd /= 256;
uint inner = rnd % 128;
rnd /= 128;
uint outer = rnd % 64;
rnd /= 64;
uint angle = rnd % 64;
rnd /= 64;
ringScale = 1.2 + 2.0 * double(scale) / 1024.0;
ringMin = double(inner) * 0.9 / 1024.0;
ringMax = max(1.0 - ((1.0 - ringMin) * double(outer)/1024.0), ringMin + 0.1);
ringAngle = pi * (-0.07 + (0.14 * double(angle)/64.0));
@ringMat = getMaterial("PlanetRing" + (1 + matIndex));
}
void addMoon(Node& node, float size, uint style = 0) {
if(moons is null)
@moons = array<MoonData@>();
MoonData dat;
dat.size = size;
dat.style = style;
moons.insertLast(dat);
}
void giveAsteroids(Node& node) {
node.autoCull = true;
special = PS_Asteroids;
}
bool preRender(Node& node) {
double visScale = node.abs_scale;
double distScale = 1.0;
if(special == PS_Ring) {
visScale *= ringScale * 1.5;
}
if(moons !is null) {
if(node.sortDistance < 800.0 * node.abs_scale)
visScale = max(visScale, 100.0 + node.abs_scale);
}
if(gfxFlags & PGA_Ringworld != 0)
distScale = node.abs_scale / 10.0;
return node.sortDistance * config::GFX_DISTANCE_MOD < PLANET_DIST_MAX * distScale && isSphereVisible(node.abs_position, visScale);
}
void render(Node& node) {
if(gfxFlags & PGA_Ringworld != 0) {
double lodDist = node.sortDistance / (node.abs_scale * pixelSizeRatio);
node.applyTransform();
material::GenericPBR_RingworldOuter.switchTo();
model::RingworldOuter.draw(lodDist);
//Poor man's opposite direction rotation
applyAbsTransform(vec3d(), vec3d(1.0), node.rotation.inverted());
applyAbsTransform(vec3d(), vec3d(1.0), node.rotation.inverted());
material::GenericPBR_RingworldInner.switchTo();
model::RingworldInner.draw(lodDist);
undoTransform();
undoTransform();
preparePlanetShader(obj);
getPlanetMaterial(obj, material::RingworldSurface).switchTo();
model::RingworldLiving.draw(lodDist);
// material::RingworldAtmo.switchTo();
// model::RingworldAtmosphere.draw(lodDist);
undoTransform();
return;
}
bool hasAtmos = atmosMat !is null;
if(hasAtmos && node.sortDistance * config::GFX_DISTANCE_MOD < PLANET_DIST_MAX * pixelSizeRatio * node.abs_scale * 0.25)
drawBuffers();
node.applyTransform();
const Material@ baseMat;
if(Colonized)
@baseMat = colonyMat;
else
@baseMat = emptyMat;
preparePlanetShader(obj);
getPlanetMaterial(obj, baseMat).switchTo();
planetModel.draw(node.sortDistance / (node.abs_scale * pixelSizeRatio));
if(hasAtmos) {
applyAbsTransform(vec3d(), vec3d(1.015), quaterniond_fromAxisAngle(vec3d_up(), fraction(gameTime / 240.0) * twopi));
atmosMat.switchTo();
//Use the same lod as the planet to avoid weirdness
model::Sphere_max.draw(node.sortDistance / (node.abs_scale * pixelSizeRatio));
undoTransform();
}
if(special == PS_Asteroids) {
material::AsteroidPegmatite.switchTo();
applyAbsTransform(vec3d(2.0,2.0,2.0), vec3d(0.01), quaterniond());
model::Asteroid1.draw();
undoTransform();
material::AsteroidMagnetite.switchTo();
applyAbsTransform(vec3d(2.3,1.5,1.95), vec3d(0.0125), quaterniond_fromAxisAngle(vec3d(0,0.32,-0.1).normalize(), 1.3));
model::Asteroid2.draw();
undoTransform();
material::AsteroidTonalite.switchTo();
applyAbsTransform(vec3d(2.4,2.8,2.1), vec3d(0.008), quaterniond_fromAxisAngle(vec3d(1).normalize(), 0.782));
model::Asteroid3.draw();
undoTransform();
}
else if(special == PS_Ring) {
auto ringRot = node.abs_rotation.inverted() *
quaterniond_fromAxisAngle(vec3d_front(), ringAngle) *
quaterniond_fromAxisAngle(vec3d_up(), ((gameTime / 30.0) % (2.0 * pi)));
vec3d starDir = node.parent.abs_position - node.abs_position;
starDir = (node.abs_rotation * ringRot).inverted() * starDir;
shader::STAR_DIRECTION = vec2f(starDir.x, starDir.z);
shader::PLANET_RING_RATIO = 1.0 / ringScale;
shader::RING_MIN = ringMin;
shader::RING_MAX = ringMax;
applyAbsTransform(vec3d(), vec3d(ringScale), ringRot);
ringMat.switchTo();
model::PlanetRing.draw();
undoTransform();
}
//if(gfxFlags & PGA_SpaceElevator != 0) {
//TODO
//}
undoTransform();
if(moons !is null && node.sortDistance < 800.0 * node.abs_scale) {
for(uint i = 0, cnt = moons.length; i < cnt; ++i) {
auto@ dat = moons[i];
uint st = dat.style;
double rot = fraction(gameTime / (1.0 + 12.0 * double(st % 256) / 255.0)) * twopi;
st >>= 8;
double angle = fraction(gameTime / (10.0 + 40.0 * double(st % 256) / 255.0)) * twopi;
st >>= 8;
double distance = double(st % 256) / 255.0 * 7.0 + 2.0;
st >>= 8;
vec3d offset = quaterniond_fromAxisAngle(vec3d_up(), angle) * vec3d_front(distance * node.abs_scale);
applyTransform(node.abs_position + offset, vec3d(dat.size), quaterniond_fromAxisAngle(vec3d_up(), rot));
material::ProceduralMoon.switchTo();
model::Moon_Sphere_max.draw(node.sortDistance / (dat.size * pixelSizeRatio));
undoTransform();
}
}
}
};
class DynTex {
DynamicTexture@ tex;
Object@ obj;
double lastRender = 0.0;
uint modId = uint(-1);
const Material@ baseMat;
double delay = 0;
DynTex() {
@tex = DynamicTexture();
}
void cache(Object& obj, const Material@ baseMat) {
if(!obj.valid)
return;
if(this.obj is obj && modId == obj.surfaceModId && baseMat is this.baseMat)
return;
if(modId != uint(-1) && frameTime < delay && this.obj is obj && baseMat is this.baseMat)
return;
@this.obj = obj;
@this.baseMat = baseMat;
vec2u size = vec2u(obj.originalGridSize);
if(size.x == 0 || size.y == 0) {
@this.obj = null;
return;
}
Image img(size, 4);
uint shown = obj.getSurfaceData(img);
modId = shown;
if(shown == uint(-1)) {
@this.obj = null;
return;
}
const Texture@ prevTex = tex.material.texture7;
tex.material = baseMat;
@tex.material.texture7 = prevTex;
@tex.image[7] = img;
delay = frameTime + 1.0;
}
const Material@ get_material() {
if(obj is null)
return null;
tex.stream();
lastRender = frameTime;
return tex.material;
}
void switchTo() {
if(obj is null)
return;
tex.stream();
tex.material.switchTo();
lastRender = frameTime;
}
};
const uint MIN_CACHED = 8;
const uint MAX_CACHED = 32;
const double DISCARD_TIME = 2.0;
array<DynTex@> cachedTextures;
DynTex@ getPlanetMaterial(Object& obj, const Material@ baseMat) {
DynTex@ tex;
//Check if we already have this planet in our cache
for(uint i = 0, cnt = cachedTextures.length; i < cnt; ++i) {
if(cachedTextures[i].obj is obj) {
@tex = cachedTextures[i];
break;
}
}
//Check if there's any old caches we can override
if(tex is null && cachedTextures.length > MIN_CACHED) {
for(uint i = 0, cnt = cachedTextures.length; i < cnt; ++i) {
if(frameTime - cachedTextures[i].lastRender > DISCARD_TIME) {
@tex = cachedTextures[i];
break;
}
}
}
//Check if we can create a new cache
if(tex is null && cachedTextures.length < MAX_CACHED) {
@tex = DynTex();
cachedTextures.insertLast(tex);
}
//Forcefully override the least recently used cache
if(tex is null) {
double bestTime = INFINITY;
for(uint i = 0, cnt = cachedTextures.length; i < cnt; ++i) {
double rt = cachedTextures[i].lastRender;
if(rt < bestTime) {
@tex = cachedTextures[i];
bestTime = rt;
break;
}
}
}
tex.cache(obj, baseMat);
return tex;
}
+30
View File
@@ -0,0 +1,30 @@
final class ShipyardNodeScript {
const Model@ shipyard = getModel("Shipyard");
const Material@ shipyard_mat = getMaterial("VolkurGenericPBR");
const Design@ design;
float progress = 0;
ShipyardNodeScript(Node& node) {
}
void updateProgress(const Design@ dsgn, float percent) {
@design = dsgn;
progress = percent;
}
void render(Node& node) {
node.applyTransform();
shipyard_mat.switchTo();
shipyard.draw();
if(design !is null) {
applyAbsTransform(vec3d(), vec3d((design.size + 8.0) / (design.size + 16.0)), quaterniond());
design.hull.material.switchTo();
design.hull.model.draw();
undoTransform();
}
undoTransform();
}
};
+58
View File
@@ -0,0 +1,58 @@
//Draws the physical star, its corona, and a distant star sprite
final class StarNodeScript {
//double coronaRot = randomd(-pi,pi);
StarNodeScript(Node& node) {
node.transparent = true;
node.customColor = true;
}
void render(Node& node) {
double dist = node.sortDistance / (250.0 * node.abs_scale * pixelSizeRatio);
if(dist < 1.0) {
drawBuffers();
node.applyTransform();
material::StarSurface.switchTo();
model::Sphere_max.draw(node.sortDistance / (node.abs_scale * pixelSizeRatio));
/*vec4f pos;
for(uint i = 0; i < 8; ++i) {
float angle = float(int(i) - 6) * -0.24f * pi;
pos.x = cos(angle);
pos.y = sin(angle);
pos.w = angle + (pi * 0.5);
shader::BEZIER_POINTS[i] = pos;
}
applyAbsTransform(vec3d(0,1.0,0), vec3d(0.2), quaterniond());
material::Prominence.switchTo();
model::Prominence.draw();
undoTransform();*/
undoTransform();
vec3d upLeft, upRight, center = node.abs_position;
getBillboardVecs(center, upLeft, upRight, 0.0);
upLeft *= node.abs_scale * 1.5;
upRight *= node.abs_scale * 1.5;
drawPolygonStart(PT_Quads, 1, material::Corona);
drawPolygonPoint(center + upLeft, vec2f(0,0));
drawPolygonPoint(center + upRight, vec2f(1,0));
drawPolygonPoint(center - upLeft, vec2f(1,1));
drawPolygonPoint(center - upRight, vec2f(0,1));
drawPolygonEnd();
}
if(dist > 0.5) {
Color col(node.color);
col.a = dist > 1.0 ? 255 : int((dist - 0.5)*255.0/0.5);
renderBillboard(material::DistantStar, node.abs_position, node.abs_scale * 32.0, 0.0, col);
}
}
};
+226
View File
@@ -0,0 +1,226 @@
export StrategicIcon;
bool SHOW_STRATEGIC_ICONS = true;
bool getStrategicIconsShown() {
return SHOW_STRATEGIC_ICONS;
}
void setStrategicIconsShown(bool enabled) {
SHOW_STRATEGIC_ICONS = enabled;
}
const double POS_CHANGE_THRES = 1000.0;
class StrategicIcon {
double animate_pct = 0;
float lastDist = -1e10f;
float alpha = 1.f;
vec3d strategicPos;
vec3d originPos;
vec3d animPos;
bool wasVisible = false;
StrategicIcon(Node& node) {
node.transparent = true;
node.fixedSize = true;
node.needsTransform = false;
node.createPhysics();
}
void setStrategic(Node& node, vec3d position, vec3d origin) {
if(animate_pct > 0 && animate_pct < 1)
return;
if(wasVisible && animate_pct >= 1 && strategicPos.distanceToSQ(position) > POS_CHANGE_THRES) {
animPos = strategicPos;
animate_pct = 0;
}
strategicPos = position;
originPos = origin;
}
void clearStrategic() {
originPos = vec3d();
strategicPos = vec3d();
}
void update(Node& node, const vec3d& realPos, double SIZE, double OUTSIDE_DISTANCE, double OUTSIDE_SIZE_MAX,
double ANIMATE_TIME, double FADE_DIST_MIN, double FADE_DIST_MAX)
{
vec3d real = animPos.zero ? realPos : animPos;
vec3d origin = originPos.zero ? real : originPos;
vec3d strategic = strategicPos.zero ? real : strategicPos;
//Figure out animation state
double originDist = getCameraDistance(origin) * config::GFX_DISTANCE_MOD;
double dist = node.sortDistance * config::GFX_DISTANCE_MOD;
if(abs(dist - lastDist) > OUTSIDE_DISTANCE / 2 || !wasVisible) {
//Snap when changing distance rapidly
if(originDist > OUTSIDE_DISTANCE)
animate_pct = 1.f;
else
animate_pct = 0.f;
}
else {
//Animate moving to the edge of the system
if(originDist > OUTSIDE_DISTANCE)
animate_pct = min(animate_pct + frameLength / ANIMATE_TIME, 1.0);
else
animate_pct = max(0.0, animate_pct - frameLength / ANIMATE_TIME);
}
//Do fading
if(dist < FADE_DIST_MAX) {
if(dist < FADE_DIST_MIN)
alpha = 0.f;
else
alpha = (dist - FADE_DIST_MIN) / (FADE_DIST_MAX - FADE_DIST_MIN);
}
else {
alpha = 1.f;
}
//Figure out animated position and size
vec3d pos;
double width;
if(animate_pct > 0) {
pos = real.interpolate(strategic, animate_pct);
width = SIZE * min(node.sortDistance, OUTSIDE_SIZE_MAX / config::GFX_DISTANCE_MOD) / (pixelSizeRatio / uiScale);
if(animate_pct >= 1)
animPos = vec3d();
}
else {
pos = real;
width = SIZE * node.sortDistance / (pixelSizeRatio / uiScale);
}
wasVisible = true;
if(node.position != pos || node.scale != width) {
node.scale = width;
node.position = pos;
node.rebuildTransform();
}
lastDist = dist;
}
};
const double OUTSIDE_DISTANCE = 12000.0;
const double OUTSIDE_SIZE_MAX = 25000.0;
const double ANIMATE_TIME = 0.45;
const double FADE_DIST_MIN = 750;
const double FADE_DIST_MAX = 1000;
class StrategicIconNodeScript : StrategicIcon {
bool ownerColor = true;
Object@ object;
const SpriteSheet@ sheet;
const Material@ mat;
uint icon;
Color color;
double size;
Region@ region;
StrategicIconNodeScript(Node& node) {
node.transparent = true;
node.visible = false;
node.needsTransform = false;
node.fixedSize = true;
node.createPhysics();
super(node);
}
void establish(Node& node, Object@ obj, double Size, const SpriteSheet& sht, uint index) {
@sheet = sht;
icon = index;
size = Size;
establish(node, obj);
}
void establish(Node& node, Object@ obj, double Size, const Material& material) {
@mat = material;
icon = 0;
size = Size;
establish(node, obj);
}
void establish(Node& node, Object@ obj) {
@node.object = obj;
@object = obj;
@region = obj.region;
node.hintParentObject(region, false);
node.position = obj.position;
node.rebuildTransform();
}
void setColor(Node& node, uint col) {
ownerColor = false;
color = Color(col);
node.color = Colorf(color);
}
void clearColor() {
ownerColor = true;
}
bool preRender(Node& node) {
if(object is null)
return false;
double objDist = getCameraDistance(object.position);
double fadeDist = 1.0e4 * max(5.0, object.radius);
if(objDist > fadeDist)
return false;
bool isMemory = false;
if(object.visible) {
node.visible = true;
}
else if(node.memorable && object.known) {
node.visible = true;
isMemory = true;
}
else if(!node.visible) {
return false;
}
if(object.region !is region) {
@region = object.region;
node.hintParentObject(region, false);
}
double dist = node.sortDistance * config::GFX_DISTANCE_MOD;
StrategicIcon::update(node, object.node_position, size,
OUTSIDE_DISTANCE, OUTSIDE_SIZE_MAX, ANIMATE_TIME,
FADE_DIST_MIN, FADE_DIST_MAX);
if(isMemory)
alpha *= 0.7f;
if(objDist > 0.8 * fadeDist)
alpha *= 1.0 - (objDist - 0.8 * fadeDist) / (0.2 * fadeDist);
if(alpha < 0.004f || !SHOW_STRATEGIC_ICONS)
return false;
node.color.a = alpha;
if(ownerColor) {
Empire@ owner = object.owner;
if(owner !is null) {
color = owner.color;
color.a = 0xff * alpha;
node.color = Colorf(color);
}
}
return true;
}
void render(Node& node) {
if(sheet !is null)
renderBillboard(sheet, icon, node.abs_position, node.scale, 0, color);
else if(mat !is null)
renderBillboard(mat, node.abs_position, node.scale, 0, color);
}
};
+445
View File
@@ -0,0 +1,445 @@
import planet_loyalty;
vec4f ContestedVec;
vec4f GainingVec;
vec4f LosingVec;
vec4f ProtectedVec;
vec4f ZealotVec;
enum DebrisType {
DT_Rock00,
DT_Rock01,
DT_Rock02,
DT_Rock03,
DT_Rock04,
DT_Rock05,
DT_Rock06,
DT_Rock07,
DT_Rock08,
DT_Rock09,
DT_Rock10,
DT_Rock11,
DT_Metal00,
DT_Metal01,
DT_Metal02,
DT_Metal03,
DT_Metal04,
DT_Metal05,
DT_Metal06,
DT_Metal07,
DT_Metal08,
DT_Metal09,
DT_Metal10,
DT_Metal11,
DT_Metal12,
DT_Metal13,
DT_Metal14,
DT_Metal15,
DT_Metal16,
DT_Metal17,
DT_Metal18,
DT_Metal19,
DT_Metal20,
DT_Metal21,
DT_Metal22,
DT_Metal23,
DT_Metal24,
DT_Metal25,
DT_Metal26,
DT_Metal27,
DT_Metal28,
DT_Metal29,
DT_Metal30,
DT_Metal31,
DT_Metal32,
DT_Metal33,
DT_Metal34,
DT_Metal35,
DT_COUNT,
DT_ROCK_START = DT_Rock00,
DT_ROCK_END = DT_Rock11,
DT_METAL_START = DT_Metal00,
DT_METAL_END = DT_Metal35
};
void init() {
ContestedColors[CM_Contested].toVec4(ContestedVec);
ContestedVec.w = 0.5f;
ContestedColors[CM_GainingLoyalty].toVec4(GainingVec);
GainingVec.w = 0.5f;
ContestedColors[CM_LosingLoyalty].toVec4(LosingVec);
LosingVec.w = 0.5f;
ContestedColors[CM_Protected].toVec4(ProtectedVec);
ProtectedVec.w = 0.5f;
ContestedColors[CM_Zealot].toVec4(ZealotVec);
ZealotVec.w = 0.5f;
}
bool SHOW_SYSTEM_PLANES = true;
void setSystemPlanesShown(bool enabled) {
SHOW_SYSTEM_PLANES = enabled;
}
bool getSystemPlanesShown() {
return SHOW_SYSTEM_PLANES;
}
final class SystemDebris {
DebrisType type;
quaterniond rot;
vec3d pos, vel, axis;
float scale, life, rotSpeed;
float spawnTime;
bool draw = false;
double dist = 0;
int opCmp(const SystemDebris& other) {
return int(type) - int(other.type);
}
};
class SystemPlaneNodeScript {
Region@ obj;
vec3d origin;
float outerRadius;
float innerRadius;
uint contested = CM_None;
bool decaying = false;
Color primaryColor;
float alpha = 1.f;
bool drawPlane = false, drawDebris = false;
array<SystemDebris@> debris;
SystemPlaneNodeScript(Node& node) {
}
void setContested(uint mode) {
contested = mode;
}
void establish(Node& node, Region& region) {
@obj = region;
origin = region.position;
outerRadius = region.OuterRadius;
innerRadius = region.InnerRadius;
node.scale = region.radius;
node.position = origin;
node.rebuildTransform();
}
void setPrimaryEmpire(Empire@ emp) {
if(emp !is null)
primaryColor = emp.color;
else
primaryColor = Color(0xaaaaaaff);
}
void addMetalDebris(vec3d position, uint count = 1) {
double spread = double(count) - 1.0;
for(uint i = 0; i < count; ++i) {
SystemDebris d;
d.type = DebrisType(randomi(DT_METAL_START,DT_METAL_END));
vec2d off = random2d(200.0, innerRadius);
d.pos = position + random3d(spread);
d.scale = randomd(1.0,2.0);
d.axis = random3d(1.0);
d.rotSpeed = randomd(pi * -0.125, pi * 0.125);
double dist = cameraPos.distanceTo(d.pos);
d.vel = random3d(-8.0,8.0);
d.life = randomd(30.0,60.0);
d.spawnTime = frameGameTime;
d.rot = quaterniond_fromAxisAngle(random3d(1.0), randomd(0.0,twopi));
debris.insertLast(d);
}
}
void generateDebris() {
uint count = uint(innerRadius * settings::dSystemDebris / 10.0);
if(debris.length < count) {
for(uint i = debris.length; i < count; ++i) {
SystemDebris d;
d.type = DebrisType(randomi(DT_ROCK_START,DT_ROCK_END));
vec2d off = random2d(200.0, innerRadius);
d.pos = origin + vec3d(off.x, randomd(-15.0,15.0), off.y);
d.scale = randomd(1.0,2.0);
d.axis = random3d(1.0);
d.rotSpeed = randomd(pi * -0.125, pi * 0.125);
double dist = cameraPos.distanceTo(d.pos);
if(dist < pixelSizeRatio * 2000.0 * d.scale && isSphereVisible(d.pos, d.scale))
continue;
d.vel = random3d(-8.0,8.0);
d.life = randomd(30.0,60.0);
d.spawnTime = frameGameTime;
d.rot = quaterniond_fromAxisAngle(random3d(1.0), randomd(0.0,twopi));
debris.insertLast(d);
}
debris.sortAsc();
}
}
void tickDebris(double time) {
vec3d cam = cameraPos;
for(int i = int(debris.length-1); i >= 0; --i) {
auto@ d = debris[i];
d.life -= time;
d.pos += d.vel * time;
d.dist = cam.distanceTo(d.pos) / (pixelSizeRatio * d.scale);
d.draw = d.dist < 2000.0 && isSphereVisible(d.pos, d.scale);
if(d.life <= 0 && !d.draw) {
debris.removeAt(i);
continue;
}
if(d.draw)
d.rot = quaterniond_fromAxisAngle(d.axis, d.rotSpeed * time) * d.rot;
}
}
void renderDebris() {
float curTime = frameGameTime;
for(uint i = 0, cnt = debris.length; i < cnt; ++i) {
auto@ d = debris[i];
if(!d.draw)
continue;
applyTransform(d.pos, d.scale, d.rot);
shader::LIFE = curTime - d.spawnTime;
switch(d.type) {
case DT_Rock00:
material::AsteroidPegmatite.switchTo();
model::Asteroid1_lod2.draw(d.dist); break;
case DT_Rock01:
material::AsteroidPegmatite.switchTo();
model::Asteroid2_lod2.draw(d.dist); break;
case DT_Rock02:
material::AsteroidPegmatite.switchTo();
model::Asteroid3_lod2.draw(d.dist); break;
case DT_Rock03:
material::AsteroidPegmatite.switchTo();
model::Asteroid4_lod2.draw(d.dist); break;
case DT_Rock04:
material::AsteroidPegmatite.switchTo();
model::Asteroid1_lod2.draw(d.dist); break;
case DT_Rock05:
material::AsteroidPegmatite.switchTo();
model::Asteroid2_lod2.draw(d.dist); break;
case DT_Rock06:
material::AsteroidPegmatite.switchTo();
model::Asteroid3_lod2.draw(d.dist); break;
case DT_Rock07:
material::AsteroidPegmatite.switchTo();
model::Asteroid4_lod2.draw(d.dist); break;
case DT_Rock08:
material::AsteroidMagnetite.switchTo();
model::Asteroid1_lod2.draw(d.dist); break;
case DT_Rock09:
material::AsteroidMagnetite.switchTo();
model::Asteroid2_lod2.draw(d.dist); break;
case DT_Rock10:
material::AsteroidMagnetite.switchTo();
model::Asteroid3_lod2.draw(d.dist); break;
case DT_Rock11:
material::AsteroidMagnetite.switchTo();
model::Asteroid4_lod2.draw(d.dist); break;
case DT_Metal00:
material::Debris.switchTo();
model::Wreckage00.draw(d.dist); break;
case DT_Metal01:
material::Debris.switchTo();
model::Wreckage01.draw(d.dist); break;
case DT_Metal02:
material::Debris.switchTo();
model::Wreckage02.draw(d.dist); break;
case DT_Metal03:
material::Debris.switchTo();
model::Wreckage03.draw(d.dist); break;
case DT_Metal04:
material::Debris.switchTo();
model::Wreckage04.draw(d.dist); break;
case DT_Metal05:
material::Debris.switchTo();
model::Wreckage05.draw(d.dist); break;
case DT_Metal06:
material::Debris.switchTo();
model::Wreckage06.draw(d.dist); break;
case DT_Metal07:
material::Debris.switchTo();
model::Wreckage07.draw(d.dist); break;
case DT_Metal08:
material::Debris.switchTo();
model::Wreckage08.draw(d.dist); break;
case DT_Metal09:
material::Debris.switchTo();
model::Wreckage09.draw(d.dist); break;
case DT_Metal10:
material::Debris.switchTo();
model::Wreckage10.draw(d.dist); break;
case DT_Metal11:
material::Debris.switchTo();
model::Wreckage11.draw(d.dist); break;
case DT_Metal12:
material::Debris.switchTo();
model::Wreckage12.draw(d.dist); break;
case DT_Metal13:
material::Debris.switchTo();
model::Wreckage13.draw(d.dist); break;
case DT_Metal14:
material::Debris.switchTo();
model::Wreckage14.draw(d.dist); break;
case DT_Metal15:
material::Debris.switchTo();
model::Wreckage15.draw(d.dist); break;
case DT_Metal16:
material::Debris.switchTo();
model::Wreckage16.draw(d.dist); break;
case DT_Metal17:
material::Debris.switchTo();
model::Wreckage17.draw(d.dist); break;
case DT_Metal18:
material::Debris.switchTo();
model::Wreckage18.draw(d.dist); break;
case DT_Metal19:
material::Debris.switchTo();
model::Wreckage19.draw(d.dist); break;
case DT_Metal20:
material::Debris.switchTo();
model::Wreckage20.draw(d.dist); break;
case DT_Metal21:
material::Debris.switchTo();
model::Wreckage21.draw(d.dist); break;
case DT_Metal22:
material::Debris.switchTo();
model::Wreckage22.draw(d.dist); break;
case DT_Metal23:
material::Debris.switchTo();
model::Wreckage23.draw(d.dist); break;
case DT_Metal24:
material::Debris.switchTo();
model::Wreckage24.draw(d.dist); break;
case DT_Metal25:
material::Debris.switchTo();
model::Wreckage25.draw(d.dist); break;
case DT_Metal26:
material::Debris.switchTo();
model::Wreckage26.draw(d.dist); break;
case DT_Metal27:
material::Debris.switchTo();
model::Wreckage27.draw(d.dist); break;
case DT_Metal28:
material::Debris.switchTo();
model::Wreckage28.draw(d.dist); break;
case DT_Metal29:
material::Debris.switchTo();
model::Wreckage29.draw(d.dist); break;
case DT_Metal30:
material::Debris.switchTo();
model::Wreckage30.draw(d.dist); break;
case DT_Metal31:
material::Debris.switchTo();
model::Wreckage31.draw(d.dist); break;
case DT_Metal32:
material::Debris.switchTo();
model::Wreckage32.draw(d.dist); break;
case DT_Metal33:
material::Debris.switchTo();
model::Wreckage33.draw(d.dist); break;
case DT_Metal34:
material::Debris.switchTo();
model::Wreckage34.draw(d.dist); break;
case DT_Metal35:
material::Debris.switchTo();
model::Wreckage35.draw(d.dist); break;
}
undoTransform();
}
}
bool preRender(Node& node) {
if(playerEmpire !is null && playerEmpire.valid && obj.ExploredMask & playerEmpire.visionMask == 0)
return false;
double d = node.abs_scale * pixelSizeRatio;
drawPlane = SHOW_SYSTEM_PLANES && (node.sortDistance < 200.0 * d);
drawDebris = node.sortDistance < 5.0 * node.abs_scale * pixelSizeRatio;
alpha = 1.0 - clamp((node.sortDistance - 150.0 * d) / (50.0 * d), 0.0, 1.0);
if(drawDebris) {
tickDebris(frameLength * gameSpeed);
generateDebris();
}
return drawPlane || drawDebris;
}
void render(Node& node) {
if(drawDebris)
renderDebris();
if(drawPlane) {
shader::RADIUS = outerRadius;
shader::INNER_RADIUS = innerRadius;
//Calculate distance to plane
line3dd camLine(cameraPos, cameraPos+cameraFacing);
vec3d intersect;
if(!camLine.intersectY(intersect, obj.position.y, false)) {
intersect = cameraPos;
intersect.y = obj.position.y;
shader::PLANE_DISTANCE = sqrt(
sqr(max(0.0, intersect.distanceTo(obj.position) - outerRadius))
+ sqr(cameraPos.y - obj.position.y));
}
else {
shader::PLANE_DISTANCE = intersect.distanceTo(cameraPos);
max(0.0, intersect.distanceTo(obj.position) - outerRadius);
}
switch(contested) {
case CM_None:
shader::GLOW_COLOR.w = 0.f;
break;
case CM_Contested:
shader::GLOW_COLOR = ContestedVec;
break;
case CM_LosingLoyalty:
shader::GLOW_COLOR = LosingVec;
break;
case CM_GainingLoyalty:
shader::GLOW_COLOR = GainingVec;
break;
case CM_Protected:
shader::GLOW_COLOR = ProtectedVec;
break;
case CM_Zealot:
shader::GLOW_COLOR = ZealotVec;
break;
}
Color c = primaryColor;
c.a = uint8(alpha * 255.f);
drawPolygonStart(PT_Quads, 1, material::SystemPlane);
drawPolygonPoint(origin + vec3d(-outerRadius, 0, -outerRadius), vec2f(0.f, 0.f), c);
drawPolygonPoint(origin + vec3d(+outerRadius, 0, -outerRadius), vec2f(1.f, 0.f));
drawPolygonPoint(origin + vec3d(+outerRadius, 0, +outerRadius), vec2f(1.f, 1.f));
drawPolygonPoint(origin + vec3d(-outerRadius, 0, +outerRadius), vec2f(0.f, 1.f));
drawPolygonEnd();
}
}
};
+351
View File
@@ -0,0 +1,351 @@
//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();
}
}
};
+308
View File
@@ -0,0 +1,308 @@
import resources;
import systems;
bool SHOW_TRADE_LINES = true;
void setTradeLinesShown(bool enabled) {
SHOW_TRADE_LINES = enabled;
}
bool getTradeLinesShown() {
return SHOW_TRADE_LINES;
}
const Color LINE_START(0x00ffba80);
const Color LINE_MIDDLE(0xaaaaaa80);
const Color LINE_END(0x0000ff80);
const uint8 ALPHA_MIN = 80;
const uint8 ALPHA_MAX = 160;
const uint8 ALPHA_STEP = 10;
const double LOCAL_MAX_DIST = 12000.0;
const double LOCAL_FADE_MIN = 600.0;
const double LOCAL_FADE_MAX = 1000.0;
class TradeDesc {
Object@ origin;
int resId;
Object@ destination;
double originDist = 0.0, destDist = 0.0;
const ResourceType@ type;
};
class TradeLine {
SystemDesc@ to;
line3dd path;
vec3d side;
int trades = 0;
TradeDesc@[] gives;
TradeDesc@[] takes;
};
class TradeLinesNodeScript {
SystemDesc@ system;
TradeLine[] lines;
TradeDesc@[] localTrades;
int dispTrades = 0;
TradeLinesNodeScript(Node& node) {
}
void establish(Node& node, uint sysId) {
@system = getSystem(sysId);
uint adjCnt = system.adjacent.length;
lines.length = adjCnt;
double maxDist = system.radius * 2.0;
for(uint i = 0; i < adjCnt; ++i) {
TradeLine@ line = lines[i];
@line.to = getSystem(system.adjacent[i]);
//Build the line verts
line.path.start = system.position;
line.path.end = line.to.position;
vec3d off = line.path.end - line.path.start;
off.y = 0;
off.normalize();
line.path.start += off * system.radius;
line.path.end -= off * line.to.radius;
line.side = (quaterniond_fromAxisAngle(vec3d_up(), pi * 0.5) * off) * 32.0;
line.path.start -= line.side;
line.path.end -= line.side;
//Discover maximum distance
double dist = line.path.length + system.radius + line.to.radius;
if(dist > maxDist)
maxDist = dist;
}
//Set node position
node.position = system.position;
node.scale = maxDist;
node.rebuildTransform();
}
void addPathing(Node& node, int towards, Object@ origin, Object@ dest, int resId, uint resType) {
if(towards < -1 || towards >= int(lines.length))
return;
TradeDesc trade;
@trade.origin = origin;
trade.originDist = origin.radius;
Planet@ o = cast<Planet>(origin);
if(o !is null)
trade.originDist = o.OrbitSize;
@trade.destination = dest;
trade.destDist = dest.radius;
Planet@ d = cast<Planet>(dest);
if(d !is null)
trade.destDist = d.OrbitSize;
trade.resId = resId;
@trade.type = getResource(resType);
if(towards == -1) {
localTrades.insertLast(trade);
}
else {
TradeLine@ line = lines[towards];
++line.trades;
if(origin.region is system.object)
line.gives.insertLast(trade);
if(dest.region is line.to.object)
line.takes.insertLast(trade);
}
++dispTrades;
node.visible = true;
}
void removePathing(Node& node, int towards, Object@ origin, int resId) {
if(towards < -1 || towards >= int(lines.length))
return;
if(towards == -1) {
uint cnt = localTrades.length;
for(uint i = 0; i < cnt; ++i) {
TradeDesc@ trade = localTrades[i];
if(trade.origin is origin && trade.resId == resId) {
localTrades.removeAt(i);
--dispTrades;
break;
}
}
}
else {
TradeLine@ line = lines[towards];
--line.trades;
--dispTrades;
uint cnt = line.gives.length;
for(uint i = 0; i < cnt; ++i) {
TradeDesc@ trade = line.gives[i];
if(trade.origin is origin && trade.resId == resId) {
line.gives.removeAt(i);
break;
}
}
cnt = line.takes.length;
for(uint i = 0; i < cnt; ++i) {
TradeDesc@ trade = line.takes[i];
if(trade.origin is origin && trade.resId == resId) {
line.takes.removeAt(i);
break;
}
}
}
node.visible = dispTrades != 0;
}
bool preRender(Node& node) {
if(system !is null && lines.length != system.adjacent.length)
establish(node, system.index);
return system !is null && SHOW_TRADE_LINES;
}
void render(Node& node) {
if(system is null) // Why can this ever be true?
return;
bool sysVisible = system.object.VisionMask & playerEmpire.visionMask != 0;
float localAlpha = 1.f;
//Draw local trades
/*if(node.sortDistance < LOCAL_MAX_DIST && sysVisible) {
//Calculate distance to plane
line3dd camLine(cameraPos, cameraPos+cameraFacing);
vec3d intersect;
double planeDist;
if(!camLine.intersectY(intersect, node.position.y, false)) {
intersect = cameraPos;
intersect.y = node.position.y;
planeDist = sqrt(
sqr(max(0.0, intersect.distanceTo(node.position) - system.radius))
+ sqr(cameraPos.y - node.position.y));
}
else {
planeDist = intersect.distanceTo(cameraPos);
max(0.0, intersect.distanceTo(node.position) - system.radius);
}
Color startColor = LINE_START;
Color endColor = LINE_END;
//Fade lines
localAlpha = clamp((planeDist - LOCAL_FADE_MIN) / (LOCAL_FADE_MAX - LOCAL_FADE_MIN), 0.0, 1.0);
startColor.a = localAlpha * 180;
endColor.a = startColor.a;
if(startColor.a != 0) {
uint cnt = localTrades.length;
for(uint i = 0; i < cnt; ++i) {
TradeDesc@ trade = localTrades[i];
vec3d from = trade.origin.position;
vec3d to = trade.destination.position;
drawLine(from + (to - from).normalized(trade.originDist), to + (from - to).normalized(trade.destDist),
startColor, endColor, 0.0);
}
}
}*/
//Draw paths
uint cnt = lines.length;
for(uint i = 0; i < cnt; ++i) {
TradeLine@ line = lines[i];
if(line.trades > 0)
drawPath(line, sysVisible, localAlpha);
}
}
void drawPath(TradeLine@ line, bool sysVisible, float localAlpha) {
bool otherVisible = line.to.object.VisionMask & playerEmpire.visionMask != 0;
double startDist = 0.0;
if(line.gives.length != 0)
startDist = line.gives[0].origin.position.distanceTo(line.path.start);
double dist = line.path.length + startDist;
vec2f startDistLeft(startDist, 0), startDistRight(startDist, 1);
vec2f endDistLeft(dist, 0), endDistRight(dist, 1);
//Draw the line
if(sysVisible || otherVisible) {
Color color = LINE_MIDDLE;
color.a = min(ALPHA_MAX, ALPHA_MIN + ALPHA_STEP * line.trades);
drawPolygonStart(2, material::TradePaths, color);
drawPolygonPoint(line.path.start + line.side, startDistLeft);
drawPolygonPoint(line.path.end + line.side, endDistLeft);
drawPolygonPoint(line.path.start - line.side, startDistRight);
drawPolygonPoint(line.path.end + line.side, endDistLeft);
drawPolygonPoint(line.path.end - line.side, endDistRight);
drawPolygonPoint(line.path.start - line.side, startDistRight);
drawPolygonEnd();
}
//Draw taking
/*if(otherVisible) {
Color midCol = LINE_MIDDLE;
Color endCol = LINE_END;
midCol.a = localAlpha * 180;
endCol.a = midCol.a;
uint cnt = line.takes.length;
for(uint i = 0; i < cnt; ++i) {
auto@ dest = line.takes[i];
vec3d to = dest.destination.position;
vec3d from = line.path.end;
drawLine(from, to + (from - to).normalized(dest.destDist), midCol, endCol, dist);
}
}*/
//Draw giving
/*if(sysVisible) {
Color startCol = LINE_START;
Color midCol = LINE_MIDDLE;
startCol.a = localAlpha * 255;
midCol.a = startCol.a;
uint cnt = line.gives.length;
for(uint i = 0; i < cnt; ++i) {
auto@ dest = line.gives[i];
vec3d from = dest.origin.position;
vec3d to = line.path.start;
drawLine(from + (to - from).normalized(dest.originDist), to, startCol, midCol, 0.0);
}
}*/
}
void drawLine(const vec3d& from, const vec3d& to, const Color& fromColor, const Color& toColor, double len) {
line3dd path(from, to);
vec2f startLeft(len, 0), startRight(len, 1);
len += path.length;
vec2f endLeft(len, 0), endRight(len, 1);
vec3d off = path.end - path.start;
off.y = 0;
off.normalize();
vec3d side = (quaterniond_fromAxisAngle(vec3d_up(), pi * 0.5) * off) * 10.0;
drawPolygonStart(2, material::TradePaths);
drawPolygonPoint(path.start + side, startLeft, fromColor);
drawPolygonPoint(path.end + side, endLeft, toColor);
drawPolygonPoint(path.start - side, startRight, fromColor);
drawPolygonPoint(path.end + side, endLeft, toColor);
drawPolygonPoint(path.end - side, endRight, toColor);
drawPolygonPoint(path.start - side, startRight, fromColor);
drawPolygonEnd();
}
}