Merging changes from private branch

This commit is contained in:
Anuken
2025-04-04 11:47:35 -04:00
parent cf5c6d0905
commit b7dbe54d76
161 changed files with 2484 additions and 1137 deletions

View File

@@ -49,6 +49,7 @@ public class BlockRenderer{
private IntSet procLinks = new IntSet(), procLights = new IntSet();
private BlockQuadtree blockTree = new BlockQuadtree(new Rect(0, 0, 1, 1));
private BlockLightQuadtree blockLightTree = new BlockLightQuadtree(new Rect(0, 0, 1, 1));
private FloorQuadtree floorTree = new FloorQuadtree(new Rect(0, 0, 1, 1));
public BlockRenderer(){
@@ -64,7 +65,9 @@ public class BlockRenderer{
Events.on(WorldLoadEvent.class, event -> {
blockTree = new BlockQuadtree(new Rect(0, 0, world.unitWidth(), world.unitHeight()));
blockLightTree = new BlockLightQuadtree(new Rect(0, 0, world.unitWidth(), world.unitHeight()));
floorTree = new FloorQuadtree(new Rect(0, 0, world.unitWidth(), world.unitHeight()));
shadowEvents.clear();
updateFloors.clear();
lastCamY = lastCamX = -99; //invalidate camera position so blocks get updated
@@ -93,7 +96,7 @@ public class BlockRenderer{
tile.build.wasVisible = true;
}
if(tile.block().hasShadow && (tile.build == null || tile.build.wasVisible)){
if(tile.block().displayShadow(tile) && (tile.build == null || tile.build.wasVisible)){
Fill.rect(tile.x + 0.5f, tile.y + 0.5f, 1, 1);
}
}
@@ -116,7 +119,10 @@ public class BlockRenderer{
Events.on(TilePreChangeEvent.class, event -> {
if(blockTree == null || floorTree == null) return;
if(indexBlock(event.tile)) blockTree.remove(event.tile);
if(indexBlock(event.tile)){
blockTree.remove(event.tile);
blockLightTree.remove(event.tile);
}
if(indexFloor(event.tile)) floorTree.remove(event.tile);
});
@@ -209,7 +215,10 @@ public class BlockRenderer{
}
void recordIndex(Tile tile){
if(indexBlock(tile)) blockTree.insert(tile);
if(indexBlock(tile)){
blockTree.insert(tile);
blockLightTree.insert(tile);
}
if(indexFloor(tile)) floorTree.insert(tile);
}
@@ -295,7 +304,7 @@ public class BlockRenderer{
for(Tile tile : shadowEvents){
if(tile == null) continue;
//draw white/shadow color depending on blend
Draw.color((!tile.block().hasShadow || (state.rules.fog && tile.build != null && !tile.build.wasVisible)) ? Color.white : blendShadowColor);
Draw.color((!tile.block().displayShadow(tile) || (state.rules.fog && tile.build != null && !tile.build.wasVisible)) ? Color.white : blendShadowColor);
Fill.rect(tile.x + 0.5f, tile.y + 0.5f, 1, 1);
}
@@ -354,16 +363,17 @@ public class BlockRenderer{
//draw floor lights
floorTree.intersect(bounds, lightview::add);
blockLightTree.intersect(bounds, tile -> {
if(tile.block().emitLight && (tile.build == null || procLights.add(tile.build.pos()))){
lightview.add(tile);
}
});
blockTree.intersect(bounds, tile -> {
if(tile.build == null || procLinks.add(tile.build.id)){
tileview.add(tile);
}
//lights are drawn even in the expanded range
if(((tile.build != null && procLights.add(tile.build.pos())) || tile.block().emitLight)){
lightview.add(tile);
}
if(tile.build != null && tile.build.power != null && tile.build.power.links.size > 0){
for(Building other : tile.build.getPowerConnections(outArray2)){
if(other.block instanceof PowerNode && procLinks.add(other.id)){ //TODO need a generic way to render connections!
@@ -519,6 +529,24 @@ public class BlockRenderer{
}
}
static class BlockLightQuadtree extends QuadTree<Tile>{
public BlockLightQuadtree(Rect bounds){
super(bounds);
}
@Override
public void hitbox(Tile tile){
var block = tile.block();
tmp.setCentered(tile.worldx() + block.offset, tile.worldy() + block.offset, block.lightClipSize, block.lightClipSize);
}
@Override
protected QuadTree<Tile> newChild(Rect rect){
return new BlockLightQuadtree(rect);
}
}
static class FloorQuadtree extends QuadTree<Tile>{
public FloorQuadtree(Rect bounds){
@@ -528,7 +556,7 @@ public class BlockRenderer{
@Override
public void hitbox(Tile tile){
var floor = tile.floor();
tmp.setCentered(tile.worldx(), tile.worldy(), floor.clipSize, floor.clipSize);
tmp.setCentered(tile.worldx(), tile.worldy(), floor.lightClipSize, floor.lightClipSize);
}
@Override

View File

@@ -17,6 +17,7 @@ public class CacheLayer{
public static CacheLayer[] all = {};
public int id;
public boolean liquid;
/** Registers cache layers that will render before the 'normal' layer. */
public static void add(CacheLayer... layers){
@@ -66,7 +67,7 @@ public class CacheLayer{
slag = new ShaderLayer(Shaders.slag),
arkycite = new ShaderLayer(Shaders.arkycite),
cryofluid = new ShaderLayer(Shaders.cryofluid),
space = new ShaderLayer(Shaders.space),
space = new ShaderLayer(Shaders.space, false),
normal = new CacheLayer(),
walls = new CacheLayer()
);
@@ -86,7 +87,11 @@ public class CacheLayer{
public @Nullable Shader shader;
public ShaderLayer(Shader shader){
//shader will be null on headless backend, but that's ok
this(shader, true);
}
public ShaderLayer(@Nullable Shader shader, boolean liquid){
this.liquid = liquid;
this.shader = shader;
}
@@ -94,7 +99,6 @@ public class CacheLayer{
public void begin(){
if(!Core.settings.getBool("animatedwater")) return;
renderer.blocks.floor.endc();
renderer.effectBuffer.begin();
Core.graphics.clear(Color.clear);
renderer.blocks.floor.beginc();
@@ -104,11 +108,8 @@ public class CacheLayer{
public void end(){
if(!Core.settings.getBool("animatedwater")) return;
renderer.blocks.floor.endc();
renderer.effectBuffer.end();
renderer.effectBuffer.blit(shader);
renderer.blocks.floor.beginc();
}
}

View File

@@ -26,6 +26,10 @@ public class Drawf{
}
}
public static void underwater(Runnable run){
renderer.blocks.floor.drawUnderwater(run);
}
//TODO offset unused
public static void flame(float x, float y, int divisions, float rotation, float length, float width, float pan){
float len1 = length * pan, len2 = length * (1f - pan);
@@ -130,6 +134,17 @@ public class Drawf{
additive(region, color, 1f, x, y, rotation, layer);
}
public static void additive(TextureRegion region, Color color, float alpha, float x, float y, float width, float height, float layer){
float pz = Draw.z();
Draw.z(layer);
Draw.color(color, alpha * color.a);
Draw.blend(Blending.additive);
Draw.rect(region, x, y, width, height, 0f);
Draw.blend();
Draw.color();
Draw.z(pz);
}
public static void additive(TextureRegion region, Color color, float alpha, float x, float y, float rotation, float layer){
float pz = Draw.z();
Draw.z(layer);
@@ -141,6 +156,17 @@ public class Drawf{
Draw.z(pz);
}
public static void additive(TextureRegion region, Color color, float alpha, float x, float y, float rotation, float layer, float originX, float originY){
float pz = Draw.z(), w = region.width * region.scl() * Draw.xscl, h = region.height * region.scl() * Draw.yscl;
Draw.z(layer);
Draw.color(color, alpha * color.a);
Draw.blend(Blending.additive);
Draw.rect(region, x, y, w, h, w / 2f + originX * region.scl() * Draw.xscl, h / 2f + originY * region.scl() * Draw.yscl, rotation);
Draw.blend();
Draw.color();
Draw.z(pz);
}
public static void limitLine(Position start, Position dest, float len1, float len2, Color color){
if(start.within(dest, len1 + len2)){
return;
@@ -267,7 +293,7 @@ public class Drawf{
}
public static void selected(Building tile, Color color){
selected(tile.tile(), color);
selected(tile.tile, color);
}
public static void selected(Tile tile, Color color){

View File

@@ -42,21 +42,29 @@ public class FloorRenderer{
private static final boolean dynamic = false;
private float[] vertices = new float[maxSprites * vertexSize * 4];
private short[] indices = new short[maxSprites * 6];
private int vidx;
private FloorRenderBatch batch = new FloorRenderBatch();
private Shader shader;
private Texture texture;
private TextureRegion error;
private Mesh[][][] cache;
private IndexData indexData;
private ChunkMesh[][][] cache;
private IntSet drawnLayerSet = new IntSet();
private IntSet recacheSet = new IntSet();
private IntSeq drawnLayers = new IntSeq();
private ObjectSet<CacheLayer> used = new ObjectSet<>();
private Seq<Runnable> underwaterDraw = new Seq<>(Runnable.class);
//alpha value of pixels cannot exceed the alpha of the surface they're being drawn on
private Blending underwaterBlend = new Blending(
Gl.srcAlpha, Gl.oneMinusSrcAlpha,
Gl.dstAlpha, Gl.oneMinusSrcAlpha
);
public FloorRenderer(){
short j = 0;
short[] indices = new short[maxSprites * 6];
for(int i = 0; i < indices.length; i += 6, j += 4){
indices[i] = j;
indices[i + 1] = (short)(j + 1);
@@ -66,6 +74,14 @@ public class FloorRenderer{
indices[i + 5] = j;
}
indexData = new IndexBufferObject(true, indices.length){
@Override
public void dispose(){
//there is never a need to dispose this index buffer
}
};
indexData.set(indices, 0, indices.length);
shader = new Shader(
"""
attribute vec4 a_position;
@@ -121,6 +137,8 @@ public class FloorRenderer{
drawnLayers.clear();
drawnLayerSet.clear();
Rect bounds = camera.bounds(Tmp.r3);
//preliminary layer check
for(int x = minx; x <= maxx; x++){
for(int y = miny; y <= maxy; y++){
@@ -131,11 +149,11 @@ public class FloorRenderer{
cacheChunk(x, y);
}
Mesh[] chunk = cache[x][y];
ChunkMesh[] chunk = cache[x][y];
//loop through all layers, and add layer index if it exists
for(int i = 0; i < layers; i++){
if(chunk[i] != null && i != CacheLayer.walls.id){
if(chunk[i] != null && i != CacheLayer.walls.id && chunk[i].bounds.overlaps(bounds)){
drawnLayerSet.add(i);
}
}
@@ -149,16 +167,13 @@ public class FloorRenderer{
drawnLayers.sort();
Draw.flush();
beginDraw();
for(int i = 0; i < drawnLayers.size; i++){
CacheLayer layer = CacheLayer.all[drawnLayers.get(i)];
drawLayer(layer);
drawLayer(CacheLayer.all[drawnLayers.get(i)]);
}
endDraw();
underwaterDraw.clear();
}
public void beginc(){
@@ -167,29 +182,6 @@ public class FloorRenderer{
//only ever use the base environment texture
texture.bind(0);
//enable all mesh attributes; TODO remove once the attribute cache bug is fixed
if(Core.gl30 == null){
for(VertexAttribute attribute : attributes){
int loc = shader.getAttributeLocation(attribute.alias);
if(loc != -1) Gl.enableVertexAttribArray(loc);
}
}
}
public void endc(){
//disable all mesh attributes; TODO remove once the attribute cache bug is fixed
if(Core.gl30 == null){
for(VertexAttribute attribute : attributes){
int loc = shader.getAttributeLocation(attribute.alias);
if(loc != -1) Gl.disableVertexAttribArray(loc);
}
}
//unbind last buffer
Gl.bindBuffer(Gl.arrayBuffer, 0);
Gl.bindBuffer(Gl.elementArrayBuffer, 0);
}
public void checkChanges(){
@@ -205,6 +197,10 @@ public class FloorRenderer{
}
}
public void drawUnderwater(Runnable run){
underwaterDraw.add(run);
}
public void beginDraw(){
if(cache == null){
return;
@@ -217,14 +213,6 @@ public class FloorRenderer{
Gl.enable(Gl.blend);
}
public void endDraw(){
if(cache == null){
return;
}
endc();
}
public void drawLayer(CacheLayer layer){
if(cache == null){
return;
@@ -240,6 +228,8 @@ public class FloorRenderer{
layer.begin();
Rect bounds = camera.bounds(Tmp.r3);
for(int x = minx; x <= maxx; x++){
for(int y = miny; y <= maxy; y++){
@@ -249,33 +239,29 @@ public class FloorRenderer{
var mesh = cache[x][y][layer.id];
//this *must* be a vertexbufferobject on gles2, so cast it and render it directly
if(mesh != null && mesh.vertices instanceof VertexBufferObject vbo && mesh.indices instanceof IndexBufferObject ibo){
//bindi the buffer and update its contents, but do not unnecessarily enable all the attributes again
vbo.bind();
//set up vertex attribute pointers for this specific VBO
int offset = 0;
for(VertexAttribute attribute : attributes){
int location = shader.getAttributeLocation(attribute.alias);
int aoffset = offset;
offset += attribute.size;
if(location < 0) continue;
Gl.vertexAttribPointer(location, attribute.components, attribute.type, attribute.normalized, vertexSize * 4, aoffset);
}
ibo.bind();
mesh.vertices.render(mesh.indices, Gl.triangles, 0, mesh.getNumIndices());
}else if(mesh != null){
//TODO this should be the default branch!
mesh.bind(shader);
mesh.render(shader, Gl.triangles);
if(mesh != null && mesh.bounds.overlaps(bounds)){
mesh.render(shader, Gl.triangles, 0, mesh.getMaxVertices() * 6 / 4);
}
}
}
//every underwater object needs to be drawn once per cache layer, which sucks.
if(layer.liquid && underwaterDraw.size > 0){
Draw.blend(underwaterBlend);
var items = underwaterDraw.items;
int len = underwaterDraw.size;
for(int i = 0; i < len; i++){
items[i].run();
}
Draw.flush();
Draw.blend(Blending.normal);
Blending.normal.apply();
beginDraw();
}
layer.end();
}
@@ -298,7 +284,7 @@ public class FloorRenderer{
}
if(cache[cx][cy].length == 0){
cache[cx][cy] = new Mesh[CacheLayer.all.length];
cache[cx][cy] = new ChunkMesh[CacheLayer.all.length];
}
var meshes = cache[cx][cy];
@@ -315,7 +301,7 @@ public class FloorRenderer{
}
}
private Mesh cacheChunkLayer(int cx, int cy, CacheLayer layer){
private ChunkMesh cacheChunkLayer(int cx, int cy, CacheLayer layer){
vidx = 0;
Batch current = Core.batch;
@@ -345,13 +331,13 @@ public class FloorRenderer{
Core.batch = current;
int floats = vidx;
//every 4 vertices need 6 indices
int vertCount = floats / vertexSize, indCount = vertCount * 6/4;
ChunkMesh mesh = new ChunkMesh(true, floats / vertexSize, 0, attributes,
cx * tilesize * chunksize - tilesize/2f, cy * tilesize * chunksize - tilesize/2f,
(cx+1) * tilesize * chunksize + tilesize/2f, (cy+1) * tilesize * chunksize + tilesize/2f);
Mesh mesh = new Mesh(true, vertCount, indCount, attributes);
mesh.setVertices(vertices, 0, vidx);
mesh.setAutoBind(false);
mesh.setIndices(indices, 0, indCount);
//all vertices are shared
mesh.indices = indexData;
return mesh;
}
@@ -372,7 +358,7 @@ public class FloorRenderer{
recacheSet.clear();
int chunksx = Mathf.ceil((float)(world.width()) / chunksize), chunksy = Mathf.ceil((float)(world.height()) / chunksize);
cache = new Mesh[chunksx][chunksy][dynamic ? 0 : CacheLayer.all.length];
cache = new ChunkMesh[chunksx][chunksy][dynamic ? 0 : CacheLayer.all.length];
texture = Core.atlas.find("grass1").texture;
error = Core.atlas.find("env-error");
@@ -391,7 +377,28 @@ public class FloorRenderer{
}
}
static class ChunkMesh extends Mesh{
Rect bounds = new Rect();
ChunkMesh(boolean isStatic, int maxVertices, int maxIndices, VertexAttribute[] attributes, float minX, float minY, float maxX, float maxY){
super(isStatic, maxVertices, maxIndices, attributes);
bounds.set(minX, minY, maxX - minX, maxY - minY);
}
}
class FloorRenderBatch extends Batch{
//TODO: alternate clipping approach, can be more accurate
/*
float minX, minY, maxX, maxY;
void reset(){
minX = Float.POSITIVE_INFINITY;
minY = Float.POSITIVE_INFINITY;
maxX = 0f;
maxY = 0f;
}
*/
@Override
protected void draw(TextureRegion region, float x, float y, float originX, float originY, float width, float height, float rotation){

View File

@@ -32,6 +32,8 @@ public class LightRenderer{
public void add(float x, float y, float radius, Color color, float opacity){
if(!enabled() || radius <= 0f) return;
//TODO: clipping.
float res = Color.toFloatBits(color.r, color.g, color.b, opacity);
if(circles.size <= circleIndex) circles.add(new CircleLight());

View File

@@ -11,6 +11,7 @@ import arc.struct.*;
import arc.util.*;
import arc.util.noise.*;
import mindustry.content.*;
import mindustry.game.*;
import mindustry.type.*;
import mindustry.world.*;
@@ -239,35 +240,17 @@ public class MenuRenderer implements Disposable{
}
private void drawFlyers(){
Draw.color(0f, 0f, 0f, 0.4f);
TextureRegion icon = flyerType.fullIcon;
flyerType.sample.elevation = 1f;
flyerType.sample.team = Team.sharded;
flyerType.sample.rotation = flyerRot;
flyerType.sample.heal();
flyers((x, y) -> {
Draw.rect(icon, x - 12f, y - 13f, flyerRot - 90);
flyerType.sample.set(x, y);
flyerType.drawShadow(flyerType.sample);
flyerType.draw(flyerType.sample);
});
float size = Math.max(icon.width, icon.height) * icon.scl() * 1.6f;
flyers((x, y) -> {
Draw.rect("circle-shadow", x, y, size, size);
});
Draw.color();
flyers((x, y) -> {
float engineOffset = flyerType.engineOffset, engineSize = flyerType.engineSize, rotation = flyerRot;
Draw.color(Pal.engine);
Fill.circle(x + Angles.trnsx(rotation + 180, engineOffset), y + Angles.trnsy(rotation + 180, engineOffset),
engineSize + Mathf.absin(Time.time, 2f, engineSize / 4f));
Draw.color(Color.white);
Fill.circle(x + Angles.trnsx(rotation + 180, engineOffset - 1f), y + Angles.trnsy(rotation + 180, engineOffset - 1f),
(engineSize + Mathf.absin(Time.time, 2f, engineSize / 4f)) / 2f);
Draw.color();
Draw.rect(icon, x, y, flyerRot - 90);
});
}
private void flyers(Floatc2 cons){

View File

@@ -30,8 +30,6 @@ public class MinimapRenderer{
private Rect rect = new Rect();
private float zoom = 4;
private float lastX, lastY, lastW, lastH, lastScl;
private boolean worldSpace;
private IntSet updates = new IntSet();
private float updateCounter = 0f;
@@ -123,13 +121,7 @@ public class MinimapRenderer{
region = new TextureRegion(texture);
}
public void drawEntities(float x, float y, float w, float h, float scaling, boolean fullView){
lastX = x;
lastY = y;
lastW = w;
lastH = h;
lastScl = scaling;
worldSpace = fullView;
public void drawEntities(float x, float y, float w, float h, boolean fullView){
if(!fullView){
updateUnitArray();
@@ -150,12 +142,12 @@ public class MinimapRenderer{
float scaleFactor;
var trans = Tmp.m1.idt();
trans.translate(lastX, lastY);
if(!worldSpace){
trans.scl(Tmp.v1.set(scaleFactor = lastW / rect.width, lastH / rect.height));
trans.translate(x, y);
if(!fullView){
trans.scl(Tmp.v1.set(scaleFactor = w / rect.width, h / rect.height));
trans.translate(-rect.x, -rect.y);
}else{
trans.scl(Tmp.v1.set(scaleFactor = lastW / world.unitWidth(), lastH / world.unitHeight()));
trans.scl(Tmp.v1.set(scaleFactor = w / world.unitWidth(), h / world.unitHeight()));
}
trans.translate(tilesize / 2f, tilesize / 2f);
Draw.trans(trans);

View File

@@ -131,7 +131,7 @@ public class OverlayRenderer{
Building build = (select instanceof BlockUnitc b ? b.tile() : select instanceof Building b ? b : null);
TextureRegion region = build != null ? build.block.fullIcon : Core.atlas.white();
if(!(select instanceof Unitc)){
if(select instanceof BlockUnitc){
Draw.rect(region, select.getX(), select.getY());
}

View File

@@ -116,6 +116,8 @@ public class Pal{
neoplasm1 = Color.valueOf("f98f4a"),
neoplasmMid = Color.valueOf("e05438"),
neoplasm2 = Color.valueOf("9e172c"),
neoplasmAcid = Color.valueOf("8ead44"),
neoplasmAcidGlow = Color.valueOf("68e43e"),
logicBlocks = Color.valueOf("d4816b"),
logicControl = Color.valueOf("6bb2b2"),

View File

@@ -18,8 +18,6 @@ public class PlanetParams{
public Vec3 camUp = new Vec3(0f, 1f, 0f);
/** the unit length direction vector of the camera **/
public Vec3 camDir = new Vec3(0, 0, -1);
/** The sun/main planet of the solar system from which everything is rendered. Deprecated use planet.solarSystem instead */
public @Deprecated Planet solarSystem = Planets.sun;
/** Planet being looked at. */
public Planet planet = Planets.serpulo;