Partial 7.0 merge - API preview

This commit is contained in:
Anuken
2021-06-02 11:08:08 -04:00
parent ea75a357ca
commit 28b235ef07
531 changed files with 12356 additions and 6286 deletions

View File

@@ -6,6 +6,7 @@ import arc.graphics.Texture.*;
import arc.graphics.g2d.*;
import arc.graphics.gl.*;
import arc.math.*;
import arc.math.geom.*;
import arc.struct.*;
import arc.util.*;
import mindustry.content.*;
@@ -23,8 +24,7 @@ public class BlockRenderer{
public static final int crackRegions = 8, maxCrackSize = 9;
private static final int initialRequests = 32 * 32;
private static final int expandr = 10;
private static final Color shadowColor = new Color(0, 0, 0, 0.71f);
private static final Color shadowColor = new Color(0, 0, 0, 0.71f), blendShadowColor = Color.white.cpy().lerp(Color.black, shadowColor.a);
public final FloorRenderer floor = new FloorRenderer();
public TextureRegion[][] cracks;
@@ -38,7 +38,11 @@ public class BlockRenderer{
private FrameBuffer dark = new FrameBuffer();
private Seq<Building> outArray2 = new Seq<>();
private Seq<Tile> shadowEvents = new Seq<>();
private IntSet procEntities = new IntSet(), procLinks = new IntSet(), procLights = new IntSet();
private IntSet darkEvents = new IntSet();
private IntSet procLinks = new IntSet(), procLights = new IntSet();
private BlockQuadtree blockTree;
private FloorQuadtree floorTree;
public BlockRenderer(){
@@ -52,6 +56,8 @@ public class BlockRenderer{
});
Events.on(WorldLoadEvent.class, event -> {
blockTree = new BlockQuadtree(new Rect(0, 0, world.unitWidth(), world.unitHeight()));
floorTree = new FloorQuadtree(new Rect(0, 0, world.unitWidth(), world.unitHeight()));
shadowEvents.clear();
lastCamY = lastCamX = -99; //invalidate camera position so blocks get updated
@@ -61,7 +67,7 @@ public class BlockRenderer{
Core.graphics.clear(Color.white);
Draw.proj().setOrtho(0, 0, shadows.getWidth(), shadows.getHeight());
Draw.color(shadowColor);
Draw.color(blendShadowColor);
for(Tile tile : world.tiles){
if(tile.block().hasShadow){
@@ -73,17 +79,19 @@ public class BlockRenderer{
Draw.color();
shadows.end();
dark.getTexture().setFilter(TextureFilter.linear, TextureFilter.linear);
dark.getTexture().setFilter(TextureFilter.linear);
dark.resize(world.width(), world.height());
dark.begin();
Core.graphics.clear(Color.white);
Draw.proj().setOrtho(0, 0, dark.getWidth(), dark.getHeight());
for(Tile tile : world.tiles){
recordIndex(tile);
float darkness = world.getDarkness(tile.x, tile.y);
if(darkness > 0){
Draw.color(0f, 0f, 0f, Math.min((darkness + 0.5f) / 4f, 1f));
Draw.colorl(1f - Math.min((darkness + 0.5f) / 4f, 1f));
Fill.rect(tile.x + 0.5f, tile.y + 0.5f, 1, 1);
}
}
@@ -93,6 +101,11 @@ public class BlockRenderer{
dark.end();
});
Events.on(TilePreChangeEvent.class, event -> {
if(indexBlock(event.tile)) blockTree.remove(event.tile);
if(indexFloor(event.tile)) floorTree.remove(event.tile);
});
Events.on(TileChangeEvent.class, event -> {
shadowEvents.add(event.tile);
@@ -104,10 +117,60 @@ public class BlockRenderer{
if(Math.abs(avgx - event.tile.x) <= rangex && Math.abs(avgy - event.tile.y) <= rangey){
lastCamY = lastCamX = -99; //invalidate camera position so blocks get updated
}
recordIndex(event.tile);
});
}
boolean indexBlock(Tile tile){
var block = tile.block();
return tile.isCenter() && block != Blocks.air && block.cacheLayer == CacheLayer.normal;
}
boolean indexFloor(Tile tile){
return tile.block() == Blocks.air && tile.floor().emitLight && world.getDarkness(tile.x, tile.y) < 3;
}
void recordIndex(Tile tile){
if(indexBlock(tile)) blockTree.insert(tile);
if(indexFloor(tile)) floorTree.insert(tile);
}
public void recacheWall(Tile tile){
for(int cx = tile.x - darkRadius; cx <= tile.x + darkRadius; cx++){
for(int cy = tile.y - darkRadius; cy <= tile.y + darkRadius; cy++){
Tile other = world.tile(cx, cy);
if(other != null){
darkEvents.add(other.pos());
}
}
}
}
public void drawDarkness(){
if(!darkEvents.isEmpty()){
Draw.flush();
dark.begin();
Draw.proj().setOrtho(0, 0, dark.getWidth(), dark.getHeight());
darkEvents.each(pos -> {
var tile = world.tile(pos);
tile.data = world.getWallDarkness(tile);
float darkness = world.getDarkness(tile.x, tile.y);
//then draw the shadow
Draw.colorl(!tile.isDarkened() || darkness <= 0f ? 1f : 1f - Math.min((darkness + 0.5f) / 4f, 1f));
Fill.rect(tile.x + 0.5f, tile.y + 0.5f, 1, 1);
});
Draw.flush();
Draw.color();
dark.end();
darkEvents.clear();
Draw.proj(camera);
}
Draw.shader(Shaders.darkness);
Draw.fbo(dark, world.width(), world.height(), tilesize);
Draw.shader();
@@ -129,7 +192,7 @@ public class BlockRenderer{
Draw.alpha(0.33f * brokenFade);
Draw.mixcol(Color.white, 0.2f + Mathf.absin(Time.globalTime, 6f, 0.2f));
Draw.rect(b.icon(Cicon.full), block.x * tilesize + b.offset, block.y * tilesize + b.offset, b.rotate ? block.rotation * 90 : 0f);
Draw.rect(b.fullIcon, block.x * tilesize + b.offset, block.y * tilesize + b.offset, b.rotate ? block.rotation * 90 : 0f);
}
Draw.reset();
}
@@ -143,11 +206,8 @@ public class BlockRenderer{
Draw.proj().setOrtho(0, 0, shadows.getWidth(), shadows.getHeight());
for(Tile tile : shadowEvents){
//clear it first
Draw.color(Color.white);
Fill.rect(tile.x + 0.5f, tile.y + 0.5f, 1, 1);
//then draw the shadow
Draw.color(!tile.block().hasShadow ? Color.white : shadowColor);
//draw white/shadow color depending on blend
Draw.color(!tile.block().hasShadow ? Color.white : blendShadowColor);
Fill.rect(tile.x + 0.5f, tile.y + 0.5f, 1, 1);
}
@@ -176,12 +236,11 @@ public class BlockRenderer{
/** Process all blocks to draw. */
public void processBlocks(){
int avgx = (int)(camera.position.x / tilesize);
int avgy = (int)(camera.position.y / tilesize);
int rangex = (int)(camera.width / tilesize / 2) + 3;
int rangey = (int)(camera.height / tilesize / 2) + 3;
int rangex = (int)(camera.width / tilesize / 2);
int rangey = (int)(camera.height / tilesize / 2);
if(avgx == lastCamX && avgy == lastCamY && lastRangeX == rangex && lastRangeY == rangey){
return;
@@ -189,56 +248,32 @@ public class BlockRenderer{
tileview.clear();
lightview.clear();
procEntities.clear();
procLinks.clear();
procLights.clear();
int minx = Math.max(avgx - rangex - expandr, 0);
int miny = Math.max(avgy - rangey - expandr, 0);
int maxx = Math.min(world.width() - 1, avgx + rangex + expandr);
int maxy = Math.min(world.height() - 1, avgy + rangey + expandr);
var bounds = camera.bounds(Tmp.r3).grow(tilesize);
for(int x = minx; x <= maxx; x++){
for(int y = miny; y <= maxy; y++){
boolean expanded = (Math.abs(x - avgx) > rangex || Math.abs(y - avgy) > rangey);
Tile tile = world.rawTile(x, y);
Block block = tile.block();
//link to center
if(tile.build != null){
tile = tile.build.tile;
}
//draw floor lights
floorTree.intersect(bounds, tile -> lightview.add(tile));
if(block != Blocks.air && block.cacheLayer == CacheLayer.normal && (tile.build == null || !procEntities.contains(tile.build.id))){
if(block.expanded || !expanded){
if(tile.build == null || procLinks.add(tile.build.id)){
tileview.add(tile);
if(tile.build != null){
procEntities.add(tile.build.id);
procLinks.add(tile.build.id);
}
}
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!
tileview.add(other.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!
tileview.add(other.tile);
}
}
}
}
//special case for floors
if((block == Blocks.air && tile.floor().emitLight) && procLights.add(tile.pos())){
lightview.add(tile);
}
}
}
});
lastCamX = avgx;
lastCamY = avgy;
@@ -246,7 +281,29 @@ public class BlockRenderer{
lastRangeY = rangey;
}
//debug method for drawing block bounds
void drawTree(QuadTree<Tile> tree){
Draw.color(Color.blue);
Lines.rect(tree.bounds);
Draw.color(Color.green);
for(var tile : tree.objects){
var block = tile.block();
Tmp.r1.setCentered(tile.worldx() + block.offset, tile.worldy() + block.offset, block.clipSize, block.clipSize);
Lines.rect(Tmp.r1);
}
if(!tree.leaf){
drawTree(tree.botLeft);
drawTree(tree.botRight);
drawTree(tree.topLeft);
drawTree(tree.topRight);
}
Draw.reset();
}
public void drawBlocks(){
drawDestroyed();
//draw most tile stuff
@@ -291,13 +348,47 @@ public class BlockRenderer{
entity.drawLight();
}else if(tile.block().emitLight){
tile.block().drawEnvironmentLight(tile);
}else if(tile.floor().emitLight && !tile.block().solid && world.getDarkness(tile.x, tile.y) < 3){ //only draw floor light under non-solid blocks
}else if(tile.floor().emitLight && tile.block() == Blocks.air){ //only draw floor light under non-solid blocks
tile.floor().drawEnvironmentLight(tile);
}
}
}
}
static class BlockQuadtree extends QuadTree<Tile>{
public BlockQuadtree(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.clipSize, block.clipSize);
}
@Override
protected QuadTree<Tile> newChild(Rect rect){
return new BlockQuadtree(rect);
}
}
static class FloorQuadtree extends QuadTree<Tile>{
public FloorQuadtree(Rect bounds){
super(bounds);
}
@Override
public void hitbox(Tile tile){
var floor = tile.floor();
tmp.setCentered(tile.worldx(), tile.worldy(), floor.clipSize, floor.clipSize);
}
@Override
protected QuadTree<Tile> newChild(Rect rect){
return new FloorQuadtree(rect);
}
}
}

View File

@@ -6,92 +6,80 @@ import arc.graphics.gl.*;
import static mindustry.Vars.*;
public enum CacheLayer{
water{
@Override
public void begin(){
beginShader();
}
public class CacheLayer{
public static CacheLayer
@Override
public void end(){
endShader(Shaders.water);
}
},
mud{
@Override
public void begin(){
beginShader();
}
water, mud, tar, slag, space, normal, walls;
@Override
public void end(){
endShader(Shaders.mud);
}
},
tar{
@Override
public void begin(){
beginShader();
}
public static CacheLayer[] all = {};
@Override
public void end(){
endShader(Shaders.tar);
}
},
slag{
@Override
public void begin(){
beginShader();
}
public int id;
@Override
public void end(){
endShader(Shaders.slag);
}
},
space{
@Override
public void begin(){
beginShader();
}
/** Register a new CacheLayer. */
public static void add(CacheLayer... layers){
int newSize = all.length + layers.length;
var prev = all;
//reallocate the array and copy everything over; performance matters very little here anyway
all = new CacheLayer[newSize];
System.arraycopy(prev, 0, all, 0, prev.length);
System.arraycopy(layers, 0, all, prev.length, layers.length);
@Override
public void end(){
endShader(Shaders.space);
for(int i = 0; i < all.length; i++){
all[i].id = i;
}
},
normal,
walls;
}
public static final CacheLayer[] all = values();
/** Loads default cache layers. */
public static void init(){
add(
water = new ShaderLayer(Shaders.water),
mud = new ShaderLayer(Shaders.mud),
tar = new ShaderLayer(Shaders.tar),
slag = new ShaderLayer(Shaders.slag),
space = new ShaderLayer(Shaders.space),
normal = new CacheLayer(),
walls = new CacheLayer()
);
}
/** Called before the cache layer begins rendering. Begin FBOs here. */
public void begin(){
}
/** Called after the cache layer ends rendering. Blit FBOs here. */
public void end(){
}
void beginShader(){
if(!Core.settings.getBool("animatedwater")) return;
public static class ShaderLayer extends CacheLayer{
public Shader shader;
renderer.blocks.floor.endc();
renderer.effectBuffer.begin();
Core.graphics.clear(Color.clear);
renderer.blocks.floor.beginc();
}
public ShaderLayer(Shader shader){
//shader will be null on headless backend, but that's ok
this.shader = shader;
}
void endShader(Shader shader){
if(!Core.settings.getBool("animatedwater")) return;
@Override
public void begin(){
if(!Core.settings.getBool("animatedwater")) return;
renderer.blocks.floor.endc();
renderer.effectBuffer.end();
renderer.blocks.floor.endc();
renderer.effectBuffer.begin();
Core.graphics.clear(Color.clear);
renderer.blocks.floor.beginc();
}
renderer.effectBuffer.blit(shader);
@Override
public void end(){
if(!Core.settings.getBool("animatedwater")) return;
renderer.blocks.floor.beginc();
renderer.blocks.floor.endc();
renderer.effectBuffer.end();
renderer.effectBuffer.blit(shader);
renderer.blocks.floor.beginc();
}
}
}

View File

@@ -10,13 +10,21 @@ import mindustry.*;
import mindustry.ctype.*;
import mindustry.game.*;
import mindustry.gen.*;
import mindustry.ui.*;
import mindustry.world.*;
import static mindustry.Vars.*;
public class Drawf{
public static void dashLine(Color color, float x, float y, float x2, float y2){
int segments = (int)(Math.max(Math.abs(x - x2), Math.abs(y - y2)) / tilesize * 2);
Lines.stroke(3f, Pal.gray);
Lines.dashLine(x, y, x2, y2, segments);
Lines.stroke(1f, color);
Lines.dashLine(x, y, x2, y2, segments);
Draw.reset();
}
public static void target(float x, float y, float rad, Color color){
target(x, y, rad, 1, color);
}
@@ -96,7 +104,7 @@ public class Drawf{
public static void shadow(float x, float y, float rad, float alpha){
Draw.color(0, 0, 0, 0.4f * alpha);
Draw.rect("circle-shadow", x, y, rad, rad);
Draw.rect("circle-shadow", x, y, rad * Draw.xscl, rad * Draw.yscl);
Draw.color();
}
@@ -223,7 +231,7 @@ public class Drawf{
}
public static void construct(Building t, UnlockableContent content, float rotation, float progress, float speed, float time){
construct(t, content.icon(Cicon.full), rotation, progress, speed, time);
construct(t, content.fullIcon, rotation, progress, speed, time);
}
public static void construct(float x, float y, TextureRegion region, float rotation, float progress, float speed, float time){

View File

@@ -0,0 +1,8 @@
package mindustry.graphics;
public class EnvRenderers{
public static void init(){
}
}

View File

@@ -3,6 +3,7 @@ package mindustry.graphics;
import arc.*;
import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.graphics.gl.*;
import arc.math.*;
import arc.math.geom.*;
import arc.struct.*;
@@ -12,29 +13,93 @@ import mindustry.game.EventType.*;
import mindustry.world.*;
import mindustry.world.blocks.environment.*;
import java.util.*;
import static mindustry.Vars.*;
public class FloorRenderer implements Disposable{
private static final int chunksize = mobile ? 16 : 32, chunkunits = chunksize * tilesize;
/**
* general implementation:
*
* caching:
* 1. create fixed-size float array fpr rendering into
* 2. for each chunk, cache each layer into buffer; record layer boundary indices (alternatively, create mesh per layer for fast recache)
* 3. create mesh for this chunk based on buffer size, copy buffer into mesh
*
* rendering:
* 1. iterate through visible chunks
* 2. activate the shader vertex attributes beforehand
* 3. bind each mesh individually, draw it
*
* */
public class FloorRenderer{
private static final VertexAttribute[] attributes = {VertexAttribute.position, VertexAttribute.color, VertexAttribute.texCoords};
private static final int
chunksize = 30, //todo 32?
chunkunits = chunksize * tilesize,
vertexSize = 2 + 1 + 2,
spriteSize = vertexSize * 4,
maxSprites = chunksize * chunksize * 9;
private static final float pad = tilesize/2f;
//if true, chunks are rendered on-demand; this causes small lag spikes and is generally not needed for most maps
private static final boolean dynamic = false;
private int[][][] cache;
private MultiCacheBatch cbatch;
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 IntSet drawnLayerSet = new IntSet();
private IntSet recacheSet = new IntSet();
private IntSeq drawnLayers = new IntSeq();
private ObjectSet<CacheLayer> used = new ObjectSet<>();
public FloorRenderer(){
short j = 0;
for(int i = 0; i < indices.length; i += 6, j += 4){
indices[i] = j;
indices[i + 1] = (short)(j + 1);
indices[i + 2] = (short)(j + 2);
indices[i + 3] = (short)(j + 2);
indices[i + 4] = (short)(j + 3);
indices[i + 5] = j;
}
shader = new Shader(
"""
attribute vec4 a_position;
attribute vec4 a_color;
attribute vec2 a_texCoord0;
uniform mat4 u_projectionViewMatrix;
varying vec4 v_color;
varying vec2 v_texCoords;
void main(){
v_color = a_color;
v_color.a = v_color.a * (255.0/254.0);
v_texCoords = a_texCoord0;
gl_Position = u_projectionViewMatrix * a_position;
}
""",
"""
varying vec4 v_color;
varying vec2 v_texCoords;
uniform sampler2D u_texture;
void main(){
gl_FragColor = v_color * texture2D(u_texture, v_texCoords);
}
""");
Events.on(WorldLoadEvent.class, event -> clearTiles());
}
/**Queues up a cache change for a tile. Only runs in render loop. */
/** Queues up a cache change for a tile. Only runs in render loop. */
public void recacheTile(Tile tile){
//currently a no-op
//recacheSet.add(Point2.pack(tile.x / chunksize, tile.y / chunksize));
//TODO will be faster it the position also specified the layer to be recached
//recaching all layers may not be necessary
recacheSet.add(Point2.pack(tile.x / chunksize, tile.y / chunksize));
}
public void drawFloor(){
@@ -44,6 +109,8 @@ public class FloorRenderer implements Disposable{
Camera camera = Core.camera;
float pad = tilesize/2f;
int
minx = (int)((camera.position.x - camera.width/2f - pad) / chunkunits),
miny = (int)((camera.position.y - camera.height/2f - pad) / chunkunits),
@@ -61,11 +128,15 @@ public class FloorRenderer implements Disposable{
if(!Structs.inBounds(x, y, cache)) continue;
int[] chunk = cache[x][y];
if(cache[x][y].length == 0){
cacheChunk(x, y);
}
Mesh[] 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] != -1 && i != CacheLayer.walls.ordinal()){
if(chunk[i] != null && i != CacheLayer.walls.id){
drawnLayerSet.add(i);
}
}
@@ -92,11 +163,30 @@ public class FloorRenderer implements Disposable{
}
public void beginc(){
cbatch.beginDraw();
shader.bind();
shader.setUniformMatrix4("u_projectionViewMatrix", Core.camera.mat);
shader.setUniformi("u_texture", 0);
//only ever use the base environment texture
//TODO show error texture for anything else
texture.bind(0);
//enable all mesh attributes
for(VertexAttribute attribute : attributes){
shader.enableVertexAttribute(attribute.alias);
}
}
public void endc(){
cbatch.endDraw();
//disable all mesh attributes
for(VertexAttribute attribute : attributes){
shader.disableVertexAttribute(attribute.alias);
}
//unbind last buffer
Gl.bindBuffer(Gl.arrayBuffer, 0);
Gl.bindBuffer(Gl.elementArrayBuffer, 0);
}
public void checkChanges(){
@@ -118,8 +208,8 @@ public class FloorRenderer implements Disposable{
}
Draw.flush();
cbatch.setProjection(Core.camera.mat);
cbatch.beginDraw();
beginc();
Gl.enable(Gl.blend);
}
@@ -129,7 +219,7 @@ public class FloorRenderer implements Disposable{
return;
}
cbatch.endDraw();
endc();
}
public void drawLayer(CacheLayer layer){
@@ -150,13 +240,37 @@ public class FloorRenderer implements Disposable{
for(int x = minx; x <= maxx; x++){
for(int y = miny; y <= maxy; y++){
if(!Structs.inBounds(x, y, cache)){
if(!Structs.inBounds(x, y, cache) || cache[x][y].length == 0){
continue;
}
int[] chunk = cache[x][y];
if(chunk[layer.ordinal()] == -1) continue;
cbatch.drawCache(chunk[layer.ordinal()]);
var mesh = cache[x][y][layer.id];
if(mesh != null){
//this *must* be a vertexbufferobject, so cast it and render it directly
if(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;
shader.setVertexAttribute(location, attribute.components, attribute.type, attribute.normalized, vertexSize * 4, aoffset);
}
ibo.bind();
mesh.vertices.render(mesh.indices, Gl.triangles, 0, mesh.getNumIndices());
}else{
throw new ArcRuntimeException("Non-VBO meshes are not supported for caches.");
}
}
}
}
@@ -165,7 +279,6 @@ public class FloorRenderer implements Disposable{
private void cacheChunk(int cx, int cy){
used.clear();
int[] chunk = cache[cx][cy];
for(int tilex = cx * chunksize; tilex < (cx + 1) * chunksize && tilex < world.width(); tilex++){
for(int tiley = cy * chunksize; tiley < (cy + 1) * chunksize && tiley < world.height(); tiley++){
@@ -179,21 +292,29 @@ public class FloorRenderer implements Disposable{
}
}
if(cache[cx][cy].length == 0){
cache[cx][cy] = new Mesh[CacheLayer.all.length];
}
var meshes = cache[cx][cy];
for(CacheLayer layer : CacheLayer.all){
if(meshes[layer.id] != null){
meshes[layer.id].dispose();
}
meshes[layer.id] = null;
}
for(CacheLayer layer : used){
cacheChunkLayer(cx, cy, chunk, layer);
meshes[layer.id] = cacheChunkLayer(cx, cy, layer);
}
}
private void cacheChunkLayer(int cx, int cy, int[] chunk, CacheLayer layer){
Batch current = Core.batch;
Core.batch = cbatch;
private Mesh cacheChunkLayer(int cx, int cy, CacheLayer layer){
vidx = 0;
//begin a new cache
if(chunk[layer.ordinal()] == -1){
cbatch.beginCache();
}else{
cbatch.beginCache(chunk[layer.ordinal()]);
}
Batch current = Core.batch;
Core.batch = batch;
for(int tilex = cx * chunksize; tilex < (cx + 1) * chunksize; tilex++){
for(int tiley = cy * chunksize; tiley < (cy + 1) * chunksize; tiley++){
@@ -217,36 +338,173 @@ public class FloorRenderer implements Disposable{
}
Core.batch = current;
chunk[layer.ordinal()] = cbatch.endCache();
int floats = vidx;
//every 4 vertices need 6 indices
int vertCount = floats / vertexSize, indCount = vertCount * 6/4;
Mesh mesh = new Mesh(true, vertCount, indCount, attributes);
mesh.setAutoBind(false);
mesh.setVertices(vertices, 0, vidx);
mesh.setIndices(indices, 0, indCount);
return mesh;
}
public void clearTiles(){
if(cbatch != null) cbatch.dispose();
recacheSet.clear();
int chunksx = Mathf.ceil((float)(world.width()) / chunksize),
chunksy = Mathf.ceil((float)(world.height()) / chunksize);
cache = new int[chunksx][chunksy][CacheLayer.all.length];
cbatch = new MultiCacheBatch(chunksize * chunksize * 9);
Time.mark();
for(int x = 0; x < chunksx; x++){
for(int y = 0; y < chunksy; y++){
Arrays.fill(cache[x][y], -1);
cacheChunk(x, y);
//dispose all old meshes
if(cache != null){
for(var x : cache){
for(var y : x){
for(var mesh : y){
if(mesh != null){
mesh.dispose();
}
}
}
}
}
Log.debug("Time to cache: @", Time.elapsed());
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];
texture = Core.atlas.find("grass1").texture;
error = Core.atlas.find("env-error");
//not supported due to internal access
Mesh.useVAO = false;
//pre-cache chunks
if(!dynamic){
Time.mark();
for(int x = 0; x < chunksx; x++){
for(int y = 0; y < chunksy; y++){
cacheChunk(x, y);
}
}
Log.debug("Time to cache: @", Time.elapsed());
}
}
@Override
public void dispose(){
if(cbatch != null){
cbatch.dispose();
cbatch = null;
class FloorRenderBatch extends Batch{
@Override
protected void draw(TextureRegion region, float x, float y, float originX, float originY, float width, float height, float rotation){
//substitute invalid regions with error
if(region.texture != texture && region != error){
draw(error, x, y, originX, originY, width, height, rotation);
return;
}
float[] verts = vertices;
int idx = vidx;
vidx += spriteSize;
if(!Mathf.zero(rotation)){
//bottom left and top right corner points relative to origin
float worldOriginX = x + originX;
float worldOriginY = y + originY;
float fx = -originX;
float fy = -originY;
float fx2 = width - originX;
float fy2 = height - originY;
// rotate
float cos = Mathf.cosDeg(rotation);
float sin = Mathf.sinDeg(rotation);
float x1 = cos * fx - sin * fy + worldOriginX;
float y1 = sin * fx + cos * fy + worldOriginY;
float x2 = cos * fx - sin * fy2 + worldOriginX;
float y2 = sin * fx + cos * fy2 + worldOriginY;
float x3 = cos * fx2 - sin * fy2 + worldOriginX;
float y3 = sin * fx2 + cos * fy2 + worldOriginY;
float x4 = x1 + (x3 - x2);
float y4 = y3 - (y2 - y1);
float u = region.u;
float v = region.v2;
float u2 = region.u2;
float v2 = region.v;
float color = this.colorPacked;
verts[idx] = x1;
verts[idx + 1] = y1;
verts[idx + 2] = color;
verts[idx + 3] = u;
verts[idx + 4] = v;
verts[idx + 5] = x2;
verts[idx + 6] = y2;
verts[idx + 7] = color;
verts[idx + 8] = u;
verts[idx + 9] = v2;
verts[idx + 10] = x3;
verts[idx + 11] = y3;
verts[idx + 12] = color;
verts[idx + 13] = u2;
verts[idx + 14] = v2;
verts[idx + 15] = x4;
verts[idx + 16] = y4;
verts[idx + 17] = color;
verts[idx + 18] = u2;
verts[idx + 19] = v;
}else{
float fx2 = x + width;
float fy2 = y + height;
float u = region.u;
float v = region.v2;
float u2 = region.u2;
float v2 = region.v;
float color = this.colorPacked;
verts[idx] = x;
verts[idx + 1] = y;
verts[idx + 2] = color;
verts[idx + 3] = u;
verts[idx + 4] = v;
verts[idx + 5] = x;
verts[idx + 6] = fy2;
verts[idx + 7] = color;
verts[idx + 8] = u;
verts[idx + 9] = v2;
verts[idx + 10] = fx2;
verts[idx + 11] = fy2;
verts[idx + 12] = color;
verts[idx + 13] = u2;
verts[idx + 14] = v2;
verts[idx + 15] = fx2;
verts[idx + 16] = y;
verts[idx + 17] = color;
verts[idx + 18] = u2;
verts[idx + 19] = v;
}
}
@Override
public void flush(){
}
@Override
public void setShader(Shader shader, boolean apply){
throw new IllegalArgumentException("cache shader unsupported");
}
@Override
protected void draw(Texture texture, float[] spriteVertices, int offset, int count){
throw new IllegalArgumentException("cache vertices unsupported");
}
}
}

View File

@@ -19,6 +19,9 @@ public class LightRenderer{
private float[] vertices = new float[24];
private FrameBuffer buffer = new FrameBuffer();
private Seq<Runnable> lights = new Seq<>();
private Seq<CircleLight> circles = new Seq<>(CircleLight.class);
private int circleIndex = 0;
private TextureRegion circleRegion;
public void add(Runnable run){
if(!enabled()) return;
@@ -27,14 +30,17 @@ public class LightRenderer{
}
public void add(float x, float y, float radius, Color color, float opacity){
if(!enabled()) return;
if(!enabled() || radius <= 0f) return;
float res = color.toFloatBits();
add(() -> {
Draw.color(res);
Draw.alpha(opacity);
Draw.rect("circle-shadow", x, y, radius * 2, radius * 2);
});
float res = Color.toFloatBits(color.r, color.g, color.b, opacity);
if(circles.size <= circleIndex) circles.add(new CircleLight());
//pool circles to prevent runaway GC usage from lambda capturing
var light = circles.items[circleIndex];
light.set(x, y, res, radius);
circleIndex ++;
}
public void add(float x, float y, TextureRegion region, Color color, float opacity){
@@ -170,7 +176,7 @@ public class LightRenderer{
}
public boolean enabled(){
return state.rules.lighting && state.rules.ambientLight.a > 0.00001f;
return state.rules.lighting && state.rules.ambientLight.a > 0.0001f;
}
public void draw(){
@@ -179,6 +185,8 @@ public class LightRenderer{
return;
}
if(circleRegion == null) circleRegion = Core.atlas.find("circle-shadow");
buffer.resize(Core.graphics.getWidth()/scaling, Core.graphics.getHeight()/scaling);
Draw.color();
@@ -188,6 +196,11 @@ public class LightRenderer{
for(Runnable run : lights){
run.run();
}
for(int i = 0; i < circleIndex; i++){
var cir = circles.items[i];
Draw.color(cir.color);
Draw.rect(circleRegion, cir.x, cir.y, cir.radius * 2, cir.radius * 2);
}
Draw.reset();
buffer.end();
Gl.blendEquationSeparate(Gl.funcAdd, Gl.funcAdd);
@@ -197,5 +210,17 @@ public class LightRenderer{
buffer.blit(Shaders.light);
lights.clear();
circleIndex = 0;
}
static class CircleLight{
float x, y, color, radius;
public void set(float x, float y, float color, float radius){
this.x = x;
this.y = y;
this.color = color;
this.radius = radius;
}
}
}

View File

@@ -37,6 +37,7 @@ public class LoadRenderer implements Disposable{
private int lastLength = -1;
private FxProcessor fx;
private WindowedMean renderTimes = new WindowedMean(20);
private BloomFilter bloom;
private long lastFrameTime;
{
@@ -49,7 +50,7 @@ public class LoadRenderer implements Disposable{
//vignetting is probably too much
//fx.addEffect(new VignettingFilter(false));
fx.addEffect(new BloomFilter());
fx.addEffect(bloom = new BloomFilter());
bars = new Bar[]{
new Bar("s_proc#", OS.cores / 16f, OS.cores < 4),
@@ -69,6 +70,7 @@ public class LoadRenderer implements Disposable{
public void dispose(){
mesh.dispose();
fx.dispose();
bloom.dispose();
}
public void draw(){

View File

@@ -12,7 +12,6 @@ import arc.util.*;
import arc.util.noise.*;
import mindustry.content.*;
import mindustry.type.*;
import mindustry.ui.*;
import mindustry.world.*;
import mindustry.world.blocks.environment.*;
@@ -30,7 +29,7 @@ public class MenuRenderer implements Disposable{
private float time = 0f;
private float flyerRot = 45f;
private int flyers = Mathf.chance(0.2) ? Mathf.random(35) : Mathf.random(15);
private UnitType flyerType = Structs.select(UnitTypes.flare, UnitTypes.flare, UnitTypes.horizon, UnitTypes.mono, UnitTypes.poly, UnitTypes.mega, UnitTypes.zenith);
private UnitType flyerType = content.units().select(u -> u.hitSize <= 20f && u.flying && u.region.found()).random();
public MenuRenderer(){
Time.mark();
@@ -42,7 +41,7 @@ public class MenuRenderer implements Disposable{
private void generate(){
world.beginMapLoad();
Tiles tiles = world.resize(width, height);
Seq<Block> ores = content.blocks().select(b -> b instanceof OreBlock);
Seq<Block> ores = content.blocks().select(b -> b instanceof OreBlock && !(b instanceof WallOreBlock));
shadows = new FrameBuffer(width, height);
int offset = Mathf.random(100000);
Simplex s1 = new Simplex(offset);
@@ -239,7 +238,7 @@ public class MenuRenderer implements Disposable{
private void drawFlyers(){
Draw.color(0f, 0f, 0f, 0.4f);
TextureRegion icon = flyerType.icon(Cicon.full);
TextureRegion icon = flyerType.fullIcon;
float size = Math.max(icon.width, icon.height) * Draw.scl * 1.6f;
@@ -275,10 +274,12 @@ public class MenuRenderer implements Disposable{
float offset = -100f;
for(int i = 0; i < flyers; i++){
Tmp.v1.trns(flyerRot, time * (2f + flyerType.speed));
Tmp.v1.trns(flyerRot, time * (flyerType.speed));
cons.get((Mathf.randomSeedRange(i, range) + Tmp.v1.x + Mathf.absin(time + Mathf.randomSeedRange(i + 2, 500), 10f, 3.4f) + offset) % (tw + Mathf.randomSeed(i + 5, 0, 500)),
(Mathf.randomSeedRange(i + 1, range) + Tmp.v1.y + Mathf.absin(time + Mathf.randomSeedRange(i + 3, 500), 10f, 3.4f) + offset) % th);
cons.get(
(Mathf.randomSeedRange(i, range) + Tmp.v1.x + Mathf.absin(time + Mathf.randomSeedRange(i + 2, 500), 10f, 3.4f) + offset) % (tw + Mathf.randomSeed(i + 5, 0, 500)),
(Mathf.randomSeedRange(i + 1, range) + Tmp.v1.y + Mathf.absin(time + Mathf.randomSeedRange(i + 3, 500), 10f, 3.4f) + offset) % th
);
}
}

View File

@@ -2,7 +2,6 @@ package mindustry.graphics;
import arc.*;
import arc.graphics.*;
import arc.graphics.Pixmap.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.math.geom.*;
@@ -36,6 +35,7 @@ public class MinimapRenderer{
//make sure to call on the graphics thread
Events.on(TileChangeEvent.class, event -> {
//TODO don't update when the minimap is off?
if(!ui.editor.isShown()){
update(event.tile);
}
@@ -69,7 +69,7 @@ public class MinimapRenderer{
texture.dispose();
}
setZoom(4f);
pixmap = new Pixmap(world.width(), world.height(), Format.rgba8888);
pixmap = new Pixmap(world.width(), world.height());
texture = new Texture(pixmap);
region = new TextureRegion(texture);
}
@@ -96,7 +96,7 @@ public class MinimapRenderer{
Draw.mixcol(unit.team().color, 1f);
float scale = Scl.scl(1f) / 2f * scaling * 32f;
var region = unit.type.icon(Cicon.full);
var region = unit.type.fullIcon;
Draw.rect(region, x + rx, y + ry, scale, scale * (float)region.height / region.width, unit.rotation() - 90);
Draw.reset();
}
@@ -136,7 +136,7 @@ public class MinimapRenderer{
public void updateAll(){
for(Tile tile : world.tiles){
pixmap.draw(tile.x, pixmap.getHeight() - 1 - tile.y, colorFor(tile));
pixmap.set(tile.x, pixmap.height - 1 - tile.y, colorFor(tile));
}
texture.draw(pixmap);
}
@@ -144,10 +144,10 @@ public class MinimapRenderer{
public void update(Tile tile){
if(world.isGenerating() || !state.isGame()) return;
int color = colorFor(world.tile(tile.x, tile.y));
pixmap.draw(tile.x, pixmap.getHeight() - 1 - tile.y, color);
int color = colorFor(tile);
pixmap.set(tile.x, pixmap.height - 1 - tile.y, color);
Pixmaps.drawPixel(texture, tile.x, pixmap.getHeight() - 1 - tile.y, color);
Pixmaps.drawPixel(texture, tile.x, pixmap.height - 1 - tile.y, color);
}
public void updateUnitArray(){

View File

@@ -1,21 +1,40 @@
package mindustry.graphics;
import arc.graphics.*;
import arc.graphics.Pixmap.*;
import arc.graphics.Texture.*;
import arc.graphics.g2d.*;
import arc.util.*;
import mindustry.*;
public class MultiPacker implements Disposable{
private PixmapPacker[] packers = new PixmapPacker[PageType.all.length];
public MultiPacker(){
for(int i = 0; i < packers.length; i++){
int pageSize = 2048;
packers[i] = new PixmapPacker(pageSize, pageSize, Format.rgba8888, 2, true);
packers[i] = new PixmapPacker(Math.min(Vars.maxTextureSize, PageType.all[i].width), Math.min(Vars.maxTextureSize, PageType.all[i].height), 2, true);
}
}
@Nullable
public PixmapRegion get(String name){
for(var packer : packers){
var region = packer.getRegion(name);
if(region != null){
return region;
}
}
return null;
}
public boolean has(String name){
for(var page : PageType.all){
if(packers[page.ordinal()].getRect(name) != null){
return true;
}
}
return false;
}
public boolean has(PageType type, String name){
return packers[type.ordinal()].getRect(name) != null;
}
@@ -24,6 +43,10 @@ public class MultiPacker implements Disposable{
packers[type.ordinal()].pack(name, region);
}
public void add(PageType type, String name, PixmapRegion region, int[] splits, int[] pads){
packers[type.ordinal()].pack(name, region, splits, pads);
}
public void add(PageType type, String name, Pixmap pix){
packers[type.ordinal()].pack(name, pix);
}
@@ -50,11 +73,27 @@ public class MultiPacker implements Disposable{
//zone page (sprites4.png) - zone previews
//ui page (sprites5.png) - content icons, white icons and UI elements
public enum PageType{
main,
main(4096),
environment,
editor,
ui;
editor(4096, 2048),
rubble,
ui(4096);
public static final PageType[] all = values();
public int width = 2048, height = 2048;
PageType(int defaultSize){
this.width = this.height = defaultSize;
}
PageType(int width, int height){
this.width = width;
this.height = height;
}
PageType(){
}
}
}

View File

@@ -8,6 +8,7 @@ import arc.math.geom.*;
import arc.util.*;
import mindustry.*;
import mindustry.ai.types.*;
import mindustry.entities.*;
import mindustry.gen.*;
import mindustry.input.*;
import mindustry.ui.*;
@@ -21,7 +22,7 @@ public class OverlayRenderer{
private static final Rect rect = new Rect();
private float buildFade, unitFade;
private Unit lastSelect;
private Sized lastSelect;
public void drawBottom(){
InputHandler input = control.input;
@@ -72,27 +73,31 @@ public class OverlayRenderer{
InputHandler input = control.input;
Unit select = input.selectedUnit();
Sized select = input.selectedUnit();
if(select == null) select = input.selectedControlBuild();
if(!Core.input.keyDown(Binding.control)) select = null;
unitFade = Mathf.lerpDelta(unitFade, Mathf.num(select != null), 0.1f);
if(select != null) lastSelect = select;
if(select == null) select = lastSelect;
if(select != null && select.isAI()){
if(select != null && (!(select instanceof Unitc u) || u.isAI())){
Draw.mixcol(Pal.accent, 1f);
Draw.alpha(unitFade);
Building build = (select instanceof BlockUnitc b ? b.tile() : select instanceof Building b ? b : null);
if(select instanceof BlockUnitc){
if(build != null){
//special selection for block "units"
Fill.square(select.x, select.y, ((BlockUnitc)select).tile().block.size * tilesize/2f);
}else{
Draw.rect(select.type.icon(Cicon.full), select.x(), select.y(), select.rotation() - 90);
Fill.square(build.x, build.y, build.block.size * tilesize/2f);
}else if(select instanceof Unit u){
Draw.rect(u.type.fullIcon, u.x, u.y, u.rotation - 90);
}
for(int i = 0; i < 4; i++){
float rot = i * 90f + 45f + (-Time.time) % 360f;
float length = select.hitSize() * 1.5f + (unitFade * 2.5f);
Draw.rect("select-arrow", select.x + Angles.trnsx(rot, length), select.y + Angles.trnsy(rot, length), length / 1.9f, length / 1.9f, rot - 135f);
Draw.rect("select-arrow", select.getX() + Angles.trnsx(rot, length), select.getY() + Angles.trnsy(rot, length), length / 1.9f, length / 1.9f, rot - 135f);
}
Draw.reset();
@@ -159,7 +164,8 @@ public class OverlayRenderer{
input.drawOverSelect();
if(ui.hudfrag.blockfrag.hover() instanceof Unit unit && unit.controller() instanceof LogicAI ai && ai.controller instanceof Building build && build.isValid()){
if(ui.hudfrag.blockfrag.hover() instanceof Unit unit && unit.controller() instanceof LogicAI ai && ai.controller != null && ai.controller.isValid()){
var build = ai.controller;
Drawf.square(build.x, build.y, build.block.size * tilesize/2f + 2f);
if(!unit.within(build, unit.hitSize * 2f)){
Drawf.arrow(unit.x, unit.y, build.x, build.y, unit.hitSize *2f, 4f);
@@ -170,7 +176,7 @@ public class OverlayRenderer{
if(input.isDroppingItem()){
Vec2 v = Core.input.mouseWorld(input.getMouseX(), input.getMouseY());
float size = 8;
Draw.rect(player.unit().item().icon(Cicon.medium), v.x, v.y, size, size);
Draw.rect(player.unit().item().fullIcon, v.x, v.y, size, size);
Draw.color(Pal.accent);
Lines.circle(v.x, v.y, 6 + Mathf.absin(Time.time, 5f, 1f));
Draw.reset();

View File

@@ -14,10 +14,12 @@ public class Pal{
sapBullet = Color.valueOf("bf92f9"),
sapBulletBack = Color.valueOf("6d56bf"),
reactorPurple = Color.valueOf("bf92f9"),
reactorPurple2 = Color.valueOf("8a73c6"),
spore = Color.valueOf("7457ce"),
shield = Color.valueOf("ffd37f").a(0.7f),
shieldIn = Color.black.cpy().a(0f),
bulletYellow = Color.valueOf("fff8e8"),
bulletYellowBack = Color.valueOf("f9c27a"),

View File

@@ -1,31 +0,0 @@
package mindustry.graphics;
import arc.graphics.*;
import arc.math.*;
import arc.util.noise.*;
/** Generates a scorch pixmap based on parameters. Thread safe, unless multiple scorch generators are running in parallel. */
public class ScorchGenerator{
private static final Simplex sim = new Simplex();
public int size = 80, seed = 0, color = Color.white.rgba();
public double scale = 18, pow = 2, octaves = 4, pers = 0.4, add = 2, nscl = 4.5f;
public Pixmap generate(){
Pixmap pix = new Pixmap(size, size);
sim.setSeed(seed);
pix.each((x, y) -> {
double dst = Mathf.dst(x, y, size/2, size/2) / (size / 2f);
double scaled = Math.abs(dst - 0.5f) * 5f + add;
scaled -= noise(Angles.angle(x, y, size/2, size/2))*nscl;
if(scaled < 1.5f) pix.draw(x, y, color);
});
return pix;
}
private double noise(float angle){
return Math.pow(sim.octaveNoise2D(octaves, pers, 1 / scale, Angles.trnsx(angle, size/2f) + size/2f, Angles.trnsy(angle, size/2f) + size/2f), pow);
}
}

View File

@@ -21,7 +21,7 @@ public class Shaders{
public static UnitBuildShader build;
public static DarknessShader darkness;
public static LightShader light;
public static SurfaceShader water, mud, tar, slag, space;
public static SurfaceShader water, mud, tar, slag, space, caustics;
public static PlanetShader planet;
public static PlanetGridShader planetGrid;
public static AtmosphereShader atmosphere;
@@ -48,6 +48,12 @@ public class Shaders{
tar = new SurfaceShader("tar");
slag = new SurfaceShader("slag");
space = new SpaceShader("space");
caustics = new SurfaceShader("caustics"){
@Override
public String textureName(){
return "caustics";
}
};
planet = new PlanetShader();
planetGrid = new PlanetGridShader();
atmosphere = new AtmosphereShader();
@@ -249,6 +255,8 @@ public class Shaders{
}
public static class SurfaceShader extends Shader{
Texture noiseTex;
public SurfaceShader(String frag){
super(getShaderFi("screenspace.vert"), getShaderFi(frag + ".frag"));
loadNoise();
@@ -259,8 +267,12 @@ public class Shaders{
loadNoise();
}
public String textureName(){
return "noise";
}
public void loadNoise(){
Core.assets.load("sprites/noise.png", Texture.class).loaded = t -> {
Core.assets.load("sprites/" + textureName() + ".png", Texture.class).loaded = t -> {
((Texture)t).setFilter(TextureFilter.linear);
((Texture)t).setWrap(TextureWrap.repeat);
};
@@ -273,7 +285,11 @@ public class Shaders{
setUniformf("u_time", Time.time);
if(hasUniform("u_noise")){
Core.assets.get("sprites/noise.png", Texture.class).bind(1);
if(noiseTex == null){
noiseTex = Core.assets.get("sprites/" + textureName() + ".png", Texture.class);
}
noiseTex.bind(1);
renderer.effectBuffer.getTexture().bind(0);
setUniformi("u_noise", 1);

View File

@@ -3,51 +3,101 @@ package mindustry.graphics;
import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.math.geom.*;
import arc.struct.*;
import arc.util.pooling.*;
import arc.util.*;
public class Trail{
public int length;
private final Seq<Vec3> points;
private float lastX = -1, lastY = -1;
private final FloatSeq points;
private float lastX = -1, lastY = -1, lastAngle = -1, counter = 0f;
public Trail(int length){
this.length = length;
points = new Seq<>(length);
points = new FloatSeq(length*3);
}
public Trail copy(){
Trail out = new Trail(length);
out.points.addAll(points);
out.lastX = lastX;
out.lastY = lastY;
out.lastAngle = lastAngle;
return out;
}
public void clear(){
points.clear();
}
public int size(){
return points.size/3;
}
public void drawCap(Color color, float width){
if(points.size > 0){
Draw.color(color);
float[] items = points.items;
int i = points.size - 3;
float x1 = items[i], y1 = items[i + 1], w1 = items[i + 2], w = w1 * width / (points.size/3) * i/3f * 2f;
Draw.rect("hcircle", x1, y1, w, w, -Mathf.radDeg * lastAngle + 180f);
Draw.reset();
}
}
public void draw(Color color, float width){
Draw.color(color);
float[] items = points.items;
float lx = lastX, ly = lastY, lastAngle = this.lastAngle;
for(int i = 0; i < points.size - 1; i++){
Vec3 c = points.get(i);
Vec3 n = points.get(i + 1);
float size = width / length;
for(int i = 0; i < points.size - 3; i+= 3){
float x1 = items[i], y1 = items[i + 1], w1 = items[i + 2],
x2 = items[i + 3], y2 = items[i + 4], w2 = items[i + 5];
float size = width / (points.size/3);
float z1 = lastAngle;
float z2 = -Angles.angleRad(x2, y2, lx, ly);
float cx = Mathf.sin(c.z) * i * size, cy = Mathf.cos(c.z) * i * size, nx = Mathf.sin(n.z) * (i + 1) * size, ny = Mathf.cos(n.z) * (i + 1) * size;
Fill.quad(c.x - cx, c.y - cy, c.x + cx, c.y + cy, n.x + nx, n.y + ny, n.x - nx, n.y - ny);
float cx = Mathf.sin(z1) * i/3f * size * w1, cy = Mathf.cos(z1) * i/3f * size * w1,
nx = Mathf.sin(z2) * (i/3f + 1) * size * w2, ny = Mathf.cos(z2) * (i/3f + 1) * size * w2;
Fill.quad(x1 - cx, y1 - cy, x1 + cx, y1 + cy, x2 + nx, y2 + ny, x2 - nx, y2 - ny);
lastAngle = z2;
lx = x2;
ly = y2;
}
Draw.reset();
}
public void update(float x, float y){
if(points.size > length){
Pools.free(points.first());
points.remove(0);
/** Removes the last point from the trail at intervals. */
public void shorten(){
if((counter += Time.delta) >= 0.99f){
if(points.size >= 3){
points.removeRange(0, 2);
}
}
}
float angle = -Angles.angle(x, y, lastX, lastY);
/** Adds a new point to the trail at intervals. */
public void update(float x, float y){
update(x, y, 1f);
}
points.add(Pools.obtain(Vec3.class, Vec3::new).set(x, y, (angle) * Mathf.degRad));
/** Adds a new point to the trail at intervals. */
public void update(float x, float y, float width){
if((counter += Time.delta) >= 0.99f){
if(points.size > length*3){
points.removeRange(0, 2);
}
lastX = x;
lastY = y;
lastAngle = -Angles.angleRad(x, y, lastX, lastY);
points.add(x, y, width);
lastX = x;
lastY = y;
counter = 0f;
}
}
}

View File

@@ -94,8 +94,12 @@ public class PlanetRenderer implements Disposable{
cam.position.setZero();
cam.update();
Gl.depthMask(false);
skybox.render(cam.combined);
Gl.depthMask(true);
cam.position.set(lastPos);
cam.update();
@@ -172,7 +176,7 @@ public class PlanetRenderer implements Disposable{
}
public void renderOrbit(Planet planet){
if(planet.parent == null || !planet.visible()) return;
if(planet.parent == null || !planet.visible() || orbitAlpha <= 0.02f) return;
Vec3 center = planet.parent.position;
float radius = planet.orbitRadius;
@@ -182,6 +186,8 @@ public class PlanetRenderer implements Disposable{
}
public void renderSectors(Planet planet){
if(orbitAlpha <= 0.02f) return;
//apply transformed position
batch.proj().mul(planet.getTransform(mat));
@@ -203,6 +209,7 @@ public class PlanetRenderer implements Disposable{
public void drawArc(Planet planet, Vec3 a, Vec3 b){
drawArc(planet, a, b, Pal.accent, Color.clear, 1f);
}
public void drawArc(Planet planet, Vec3 a, Vec3 b, Color from, Color to, float length){
drawArc(planet, a, b, from, to, length, 80f, 25);
}