From 7a01719816af1dd9a22313405ab2a92f10bee5cf Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 29 Feb 2020 11:58:10 -0500 Subject: [PATCH] Real-time lighting + solar system rendering --- core/assets/shaders/planet.vertex.glsl | 10 +- core/assets/shaders/planetgrid.vertex.glsl | 6 +- core/assets/shaders/sun.vertex.glsl | 11 +- core/src/mindustry/content/Planets.java | 27 ++- core/src/mindustry/core/Logic.java | 5 +- core/src/mindustry/game/Universe.java | 28 +++ core/src/mindustry/graphics/Shaders.java | 19 +- .../mindustry/graphics/g3d/GenericMesh.java | 61 ----- core/src/mindustry/graphics/g3d/HexMesh.java | 18 ++ .../g3d/{PlanetMesher.java => HexMesher.java} | 2 +- .../mindustry/graphics/g3d/MeshBuilder.java | 131 +++++++++++ .../mindustry/graphics/g3d/PlanetMesh.java | 112 ++------- .../graphics/g3d/ShaderSphereMesh.java | 11 + .../mindustry/graphics/g3d/SphereMesh.java | 24 -- core/src/mindustry/graphics/g3d/SunMesh.java | 41 ++++ .../maps/generators/PlanetGenerator.java | 4 +- core/src/mindustry/type/Planet.java | 114 ++++++---- core/src/mindustry/type/Sector.java | 8 + .../mindustry/ui/dialogs/PlanetDialog.java | 213 +++++++++--------- gradle.properties | 2 +- 20 files changed, 491 insertions(+), 356 deletions(-) delete mode 100644 core/src/mindustry/graphics/g3d/GenericMesh.java create mode 100644 core/src/mindustry/graphics/g3d/HexMesh.java rename core/src/mindustry/graphics/g3d/{PlanetMesher.java => HexMesher.java} (86%) create mode 100644 core/src/mindustry/graphics/g3d/MeshBuilder.java create mode 100644 core/src/mindustry/graphics/g3d/ShaderSphereMesh.java delete mode 100644 core/src/mindustry/graphics/g3d/SphereMesh.java create mode 100644 core/src/mindustry/graphics/g3d/SunMesh.java diff --git a/core/assets/shaders/planet.vertex.glsl b/core/assets/shaders/planet.vertex.glsl index ca58a98367..e173b4d57e 100755 --- a/core/assets/shaders/planet.vertex.glsl +++ b/core/assets/shaders/planet.vertex.glsl @@ -2,20 +2,22 @@ attribute vec4 a_position; attribute vec3 a_normal; attribute vec4 a_color; -uniform mat4 u_projModelView; +uniform mat4 u_proj; +uniform mat4 u_trans; uniform vec3 u_lightdir; +uniform vec3 u_ambientColor; varying vec4 v_col; const vec3 ambientColor = vec3(1.0); const vec3 ambientDir = normalize(vec3(1.0, 1.0, 1.0)); -const vec3 diffuse = vec3(0.3); +const vec3 diffuse = vec3(0.2); const vec3 v1 = vec3(1.0, 0.0, 1.0); const vec3 v2 = vec3(1.0, 0.5, 0.0); void main(){ - vec3 norc = ambientColor * (diffuse + vec3(clamp((dot(a_normal, u_lightdir) + 1.0) / 2.0, 0.0, 1.0))); + vec3 norc = u_ambientColor * (diffuse + vec3(clamp((dot(a_normal, u_lightdir) + 1.0) / 2.0, 0.0, 1.0))); v_col = a_color * vec4(norc, 1.0); - gl_Position = u_projModelView * a_position; + gl_Position = u_proj * u_trans * a_position; } diff --git a/core/assets/shaders/planetgrid.vertex.glsl b/core/assets/shaders/planetgrid.vertex.glsl index 34e7c73a2e..5dd54ffebc 100644 --- a/core/assets/shaders/planetgrid.vertex.glsl +++ b/core/assets/shaders/planetgrid.vertex.glsl @@ -1,12 +1,14 @@ attribute vec4 a_position; attribute vec4 a_color; -uniform mat4 u_projModelView; +uniform mat4 u_proj; +uniform mat4 u_trans; + varying vec4 v_col; varying vec4 v_position; void main() { - gl_Position = u_projModelView * a_position; + gl_Position = u_proj * u_trans * a_position; v_col = a_color; v_position = a_position; } diff --git a/core/assets/shaders/sun.vertex.glsl b/core/assets/shaders/sun.vertex.glsl index e4866a3308..c2880dee60 100755 --- a/core/assets/shaders/sun.vertex.glsl +++ b/core/assets/shaders/sun.vertex.glsl @@ -1,9 +1,9 @@ attribute vec4 a_position; attribute vec3 a_normal; -uniform mat4 u_projModelView; +uniform mat4 u_proj; +uniform mat4 u_trans; -uniform vec3 u_center; uniform float u_time; uniform int u_octaves; uniform float u_falloff; @@ -100,8 +100,7 @@ float onoise(vec4 pos, int octaves, float falloff, float scl, float po){ } void main(){ - vec3 center = u_center; - vec4 pos = u_projModelView * (a_position); + vec4 pos = a_position; float height = onoise(vec4(a_position.xyz, u_time + u_seed), u_octaves, u_falloff, u_scale, u_power); @@ -111,7 +110,5 @@ void main(){ v_height = (height + (onoise(vec4(a_position.xyz, u_time + u_seed*2.0), u_octaves, u_falloff, u_scale, u_power) - 0.5) / 6.0 - 0.5) * u_spread + 0.5; - vec3 rel = (-vec3(pos) + ((vec3(pos) - center) * dst + center)); - - gl_Position = u_projModelView * (a_position + vec4(center.xyz, 0.0)) + vec4(rel, 0.0); + gl_Position = u_proj * u_trans * a_position; //u_proj * (a_position + vec4(pos.xyz * (dst - 1.0), 0.0)); } \ No newline at end of file diff --git a/core/src/mindustry/content/Planets.java b/core/src/mindustry/content/Planets.java index 5a91bf0eab..a7c33bfb94 100644 --- a/core/src/mindustry/content/Planets.java +++ b/core/src/mindustry/content/Planets.java @@ -1,6 +1,8 @@ package mindustry.content; +import arc.graphics.*; import mindustry.ctype.*; +import mindustry.graphics.g3d.*; import mindustry.maps.planet.*; import mindustry.type.*; @@ -11,14 +13,31 @@ public class Planets implements ContentList{ @Override public void load(){ - sun = new Planet("sun", null, 3, 1){{ - detail = 6; - generator = new TestPlanetGenerator(); + sun = new Planet("sun", null, 0, 2){{ + bloom = true; + //lightColor = Color.valueOf("f4ee8e"); + meshLoader = () -> new SunMesh(this, 3){{ + setColors( + Color.valueOf("ff7a38"), + Color.valueOf("ff9638"), + Color.valueOf("ffc64c"), + Color.valueOf("ffc64c"), + Color.valueOf("ffe371"), + Color.valueOf("f4ee8e") + ); + + scale = 1f; + speed = 1000f; + falloff = 0.3f; + octaves = 4; + spread = 1.2f; + magnitude = 0f; + }}; }}; starter = new Planet("TODO", sun, 3, 1){{ - detail = 6; generator = new TestPlanetGenerator(); + meshLoader = () -> new HexMesh(this, 6); }}; } } diff --git a/core/src/mindustry/core/Logic.java b/core/src/mindustry/core/Logic.java index 9ca3244c8a..812f9f6d6e 100644 --- a/core/src/mindustry/core/Logic.java +++ b/core/src/mindustry/core/Logic.java @@ -204,6 +204,7 @@ public class Logic implements ApplicationListener{ @Override public void update(){ Events.fire(Trigger.update); + universe.updateGlobal(); if(!state.is(State.menu)){ if(!net.client()){ @@ -211,7 +212,9 @@ public class Logic implements ApplicationListener{ } if(!state.isPaused()){ - universe.update(); + if(world.isCampaign()){ + universe.update(); + } Time.update(); if(state.rules.waves && state.rules.waveTimer && !state.gameOver){ diff --git a/core/src/mindustry/game/Universe.java b/core/src/mindustry/game/Universe.java index 0347124d35..e14c75c757 100644 --- a/core/src/mindustry/game/Universe.java +++ b/core/src/mindustry/game/Universe.java @@ -1,7 +1,12 @@ package mindustry.game; import arc.*; +import arc.math.*; import arc.util.*; +import mindustry.content.*; +import mindustry.type.*; + +import static mindustry.Vars.*; public class Universe{ private long seconds; @@ -11,6 +16,22 @@ public class Universe{ load(); } + public void updateGlobal(){ + //currently only updates one solar system + updatePlanet(Planets.sun); + } + + private void updatePlanet(Planet planet){ + planet.position.setZero(); + planet.addParentOffset(planet.position); + if(planet.parent != null){ + planet.position.add(planet.parent.position); + } + for(Planet child : planet.children){ + updatePlanet(child); + } + } + public void update(){ secondCounter += Time.delta() / 60f; if(secondCounter >= 1){ @@ -22,6 +43,13 @@ public class Universe{ save(); } } + + //update sector light + float light = world.getSector().getLight(); + float alpha = Mathf.clamp(Mathf.map(light, 0f, 0.8f, 0.1f, 1f)); + //assign and map so darkness is not 100% dark + state.rules.ambientLight.a = 1f - alpha; + state.rules.lighting = !Mathf.equal(alpha, 1f); } public float secondsMod(float mod, float scale){ diff --git a/core/src/mindustry/graphics/Shaders.java b/core/src/mindustry/graphics/Shaders.java index 8baae23442..0ee0b3ab69 100644 --- a/core/src/mindustry/graphics/Shaders.java +++ b/core/src/mindustry/graphics/Shaders.java @@ -47,6 +47,7 @@ public class Shaders{ public static class PlanetShader extends LoadShader{ public Vec3 lightDir = new Vec3(1, 1, 1).nor(); + public Color ambientColor = Color.white.cpy(); public PlanetShader(){ super("planet", "planet"); @@ -55,6 +56,7 @@ public class Shaders{ @Override public void apply(){ setUniformf("u_lightdir", lightDir); + setUniformf("u_ambientColor", ambientColor.r, ambientColor.g, ambientColor.b); } } @@ -62,11 +64,8 @@ public class Shaders{ public int octaves = 5; public float falloff = 0.5f, scale = 1f, power = 1.3f, magnitude = 0.6f, speed = 99999999999f, spread = 1.3f, seed = Mathf.random(9999f); - public Color[] colors; public float[] colorValues; - public Vec3 center = new Vec3(); - public SunShader(){ super("sun", "sun"); } @@ -81,22 +80,10 @@ public class Shaders{ setUniformf("u_time", Time.globalTime() / speed); setUniformf("u_seed", seed); setUniformf("u_spread", spread); - setUniformf("u_center", center); - setUniformi("u_colornum", colors.length); + setUniformi("u_colornum", colorValues.length / 4); setUniform4fv("u_colors[0]", colorValues, 0, colorValues.length); } - - public void updateColors(){ - colorValues = new float[colors.length*4]; - - for(int i = 0; i < colors.length; i ++){ - colorValues[i*4] = colors[i].r; - colorValues[i*4 + 1] = colors[i].g; - colorValues[i*4 + 2] = colors[i].b; - colorValues[i*4 + 3] = colors[i].a; - } - } } public static class PlanetGridShader extends LoadShader{ diff --git a/core/src/mindustry/graphics/g3d/GenericMesh.java b/core/src/mindustry/graphics/g3d/GenericMesh.java deleted file mode 100644 index 854d3810c8..0000000000 --- a/core/src/mindustry/graphics/g3d/GenericMesh.java +++ /dev/null @@ -1,61 +0,0 @@ -package mindustry.graphics.g3d; - -import arc.graphics.*; -import arc.graphics.VertexAttributes.*; -import arc.graphics.gl.*; -import arc.math.geom.*; -import arc.util.*; -import mindustry.graphics.*; - -public class GenericMesh{ - protected final float[] floats = new float[3 + 3 + 1]; - protected final int primitiveType; - protected final Mesh mesh; - - public GenericMesh(int vertices, int primitiveType){ - this.primitiveType = primitiveType; - mesh = new Mesh(true, vertices, 0, - new VertexAttribute(Usage.position, 3, Shader.positionAttribute), - new VertexAttribute(Usage.normal, 3, Shader.normalAttribute), - new VertexAttribute(Usage.colorPacked, 4, Shader.colorAttribute) - ); - - mesh.getVerticesBuffer().limit(mesh.getMaxVertices()); - mesh.getVerticesBuffer().position(0); - } - - public void render(Mat3D mat){ - render(mat, Shaders.planet); - } - - public void render(Mat3D mat, Shader shader){ - shader.begin(); - shader.setUniformMatrix4("u_projModelView", mat.val); - shader.apply(); - mesh.render(shader, primitiveType); - shader.end(); - } - - protected Vec3 normal(Vec3 v1, Vec3 v2, Vec3 v3){ - return Tmp.v32.set(v2).sub(v1).crs(v3.x - v1.x, v3.y - v1.y, v3.z - v1.z).nor(); - } - - protected void verts(Vec3 a, Vec3 b, Vec3 c, Vec3 normal, Color color){ - vert(a, normal, color); - vert(b, normal, color); - vert(c, normal, color); - } - - protected void vert(Vec3 a, Vec3 normal, Color color){ - floats[0] = a.x; - floats[1] = a.y; - floats[2] = a.z; - - floats[3] = normal.x; - floats[4] = normal.y; - floats[5] = normal.z; - - floats[6] = color.toFloatBits(); - mesh.getVerticesBuffer().put(floats); - } -} diff --git a/core/src/mindustry/graphics/g3d/HexMesh.java b/core/src/mindustry/graphics/g3d/HexMesh.java new file mode 100644 index 0000000000..eff0c53101 --- /dev/null +++ b/core/src/mindustry/graphics/g3d/HexMesh.java @@ -0,0 +1,18 @@ +package mindustry.graphics.g3d; + +import arc.math.geom.*; +import mindustry.graphics.*; +import mindustry.type.*; + +public class HexMesh extends PlanetMesh{ + + public HexMesh(Planet planet, int divisions){ + super(planet, MeshBuilder.buildHex(planet.generator, divisions, false, planet.radius, 0.2f), Shaders.planet); + } + + @Override + public void preRender(){ + Shaders.planet.lightDir.set(planet.solarSystem.position).sub(planet.position).rotate(Vec3.Y, planet.getRotation()).nor(); + Shaders.planet.ambientColor.set(planet.solarSystem.lightColor); + } +} diff --git a/core/src/mindustry/graphics/g3d/PlanetMesher.java b/core/src/mindustry/graphics/g3d/HexMesher.java similarity index 86% rename from core/src/mindustry/graphics/g3d/PlanetMesher.java rename to core/src/mindustry/graphics/g3d/HexMesher.java index 39543c72a0..459c414b89 100644 --- a/core/src/mindustry/graphics/g3d/PlanetMesher.java +++ b/core/src/mindustry/graphics/g3d/HexMesher.java @@ -4,7 +4,7 @@ import arc.graphics.*; import arc.math.geom.*; /** Defines color and height for a planet mesh. */ -public interface PlanetMesher{ +public interface HexMesher{ float getHeight(Vec3 position); Color getColor(Vec3 position); } diff --git a/core/src/mindustry/graphics/g3d/MeshBuilder.java b/core/src/mindustry/graphics/g3d/MeshBuilder.java new file mode 100644 index 0000000000..b51acea09b --- /dev/null +++ b/core/src/mindustry/graphics/g3d/MeshBuilder.java @@ -0,0 +1,131 @@ +package mindustry.graphics.g3d; + +import arc.graphics.*; +import arc.graphics.VertexAttributes.*; +import arc.graphics.gl.*; +import arc.math.geom.*; +import arc.util.*; +import mindustry.graphics.g3d.PlanetGrid.*; + +public class MeshBuilder{ + private static final Vec3 v1 = new Vec3(), v2 = new Vec3(), v3 = new Vec3(); + private static final float[] floats = new float[3 + 3 + 1]; + private static Mesh mesh; + + public static Mesh buildIcosphere(int divisions, float radius){ + begin(20 * (2 << (2 * divisions - 1)) * 7 * 3); + + MeshResult result = Icosphere.create(divisions); + for(int i = 0; i < result.indices.size; i+= 3){ + v1.set(result.vertices.items, result.indices.items[i] * 3).setLength(radius); + v2.set(result.vertices.items, result.indices.items[i + 1] * 3).setLength(radius); + v3.set(result.vertices.items, result.indices.items[i + 2] * 3).setLength(radius); + + verts(v1, v3, v2, normal(v1, v2, v3).scl(-1f), Color.white); + } + + return end(); + } + + public static Mesh buildHex(HexMesher mesher, int divisions, boolean lines, float radius, float intensity){ + PlanetGrid grid = PlanetGrid.create(divisions); + + begin(grid.tiles.length * 12 * (3 + 3 + 1)); + + for(Ptile tile : grid.tiles){ + + Vec3 nor = v1.setZero(); + Corner[] c = tile.corners; + + for(Corner corner : c){ + corner.bv.set(corner.v).setLength(radius); + } + + for(Corner corner : c){ + corner.v.setLength(radius + hexElevation(corner.bv, mesher, radius)*intensity); + } + + for(Corner corner : c){ + nor.add(corner.v); + } + nor.nor(); + + Vec3 realNormal = normal(c[0].v, c[2].v, c[4].v); + nor.set(realNormal); + + Color color = hexColor(tile.v, mesher, radius); + + if(lines){ + nor.set(1f, 1f, 1f); + + for(int i = 0; i < c.length; i++){ + Vec3 v1 = c[i].v; + Vec3 v2 = c[(i + 1) % c.length].v; + + vert(v1, nor, color); + vert(v2, nor, color); + } + }else{ + verts(c[0].v, c[1].v, c[2].v, nor, color); + verts(c[0].v, c[2].v, c[3].v, nor, color); + verts(c[0].v, c[3].v, c[4].v, nor, color); + + if(c.length > 5){ + verts(c[0].v, c[4].v, c[5].v, nor, color); + }else{ + verts(c[0].v, c[3].v, c[4].v, nor, color); + } + } + } + + return end(); + } + + private static float hexElevation(Vec3 v, HexMesher mesher, float radius){ + return mesher.getHeight(v2.set(v).scl(1f / radius)); + } + + private static Color hexColor(Vec3 v, HexMesher mesher, float radius){ + return mesher.getColor(v2.set(v).scl(1f / radius)); + } + + private static void begin(int count){ + mesh = new Mesh(true, count, 0, + new VertexAttribute(Usage.position, 3, Shader.positionAttribute), + new VertexAttribute(Usage.normal, 3, Shader.normalAttribute), + new VertexAttribute(Usage.colorPacked, 4, Shader.colorAttribute) + ); + + mesh.getVerticesBuffer().limit(mesh.getMaxVertices()); + mesh.getVerticesBuffer().position(0); + } + + private static Mesh end(){ + Mesh last = mesh; + mesh = null; + return last; + } + + private static Vec3 normal(Vec3 v1, Vec3 v2, Vec3 v3){ + return Tmp.v32.set(v2).sub(v1).crs(v3.x - v1.x, v3.y - v1.y, v3.z - v1.z).nor(); + } + + private static void verts(Vec3 a, Vec3 b, Vec3 c, Vec3 normal, Color color){ + vert(a, normal, color); + vert(b, normal, color); + vert(c, normal, color); + } + + private static void vert(Vec3 a, Vec3 normal, Color color){ + floats[0] = a.x; + floats[1] = a.y; + floats[2] = a.z; + + floats[3] = normal.x; + floats[4] = normal.y; + floats[5] = normal.z; + + floats[6] = color.toFloatBits(); + mesh.getVerticesBuffer().put(floats); + } +} diff --git a/core/src/mindustry/graphics/g3d/PlanetMesh.java b/core/src/mindustry/graphics/g3d/PlanetMesh.java index 976806de3f..0af251c51a 100644 --- a/core/src/mindustry/graphics/g3d/PlanetMesh.java +++ b/core/src/mindustry/graphics/g3d/PlanetMesh.java @@ -1,102 +1,32 @@ package mindustry.graphics.g3d; import arc.graphics.*; +import arc.graphics.gl.*; import arc.math.geom.*; -import arc.util.ArcAnnotate.*; -import arc.util.*; -import mindustry.graphics.g3d.PlanetGrid.*; +import mindustry.type.*; -public class PlanetMesh extends GenericMesh{ - private Vec3 vec = new Vec3(); - private PlanetGrid grid; - private Vec3 center = new Vec3(); +/** Defines a mesh that is rendered for a planet. Subclasses provide a mesh and a shader. */ +public abstract class PlanetMesh{ + protected final Mesh mesh; + protected final Planet planet; + protected final Shader shader; - private boolean lines; - private float radius, intensity = 0.2f; - - private final PlanetMesher gen; - - public PlanetMesh(int divisions, PlanetMesher gen){ - this(divisions, gen, 1f, false); + public PlanetMesh(Planet planet, Mesh mesh, Shader shader){ + this.planet = planet; + this.mesh = mesh; + this.shader = shader; } - public PlanetMesh(int divisions, PlanetMesher gen, float radius, boolean lines){ - super(PlanetGrid.create(divisions).tiles.length * 12 * (3 + 3 + 1), lines ? Gl.lines : Gl.triangles); + /** Should be overridden to set up any shader parameters such as planet position, normals, etc. */ + public abstract void preRender(); - this.gen = gen; - this.radius = radius; - this.grid = PlanetGrid.create(divisions); - this.lines = lines; - - generateMesh(); - } - - public @Nullable Vec3 intersect(Ray ray){ - boolean found = Intersector3D.intersectRaySphere(ray, center, radius, Tmp.v33); - if(!found) return null; - return Tmp.v33; - } - - /** @return the sector that is hit by this ray, or null if nothing intersects it. */ - public @Nullable Ptile getTile(Ray ray){ - boolean found = Intersector3D.intersectRaySphere(ray, center, radius, Tmp.v33); - if(!found) return null; - return Structs.findMin(grid.tiles, t -> t.v.dst(Tmp.v33)); - } - - private void generateMesh(){ - for(Ptile tile : grid.tiles){ - - Vec3 nor = Tmp.v31.setZero(); - Corner[] c = tile.corners; - - for(Corner corner : c){ - corner.bv.set(corner.v).setLength(radius); - } - - for(Corner corner : c){ - corner.v.setLength(radius + elevation(corner.bv)*intensity); - } - - for(Corner corner : c){ - nor.add(corner.v); - } - nor.nor(); - - Vec3 realNormal = normal(c[0].v, c[2].v, c[4].v); - nor.set(realNormal); - - Color color = color(tile.v); - - if(lines){ - nor.set(1f, 1f, 1f); - - for(int i = 0; i < c.length; i++){ - Vec3 v1 = c[i].v; - Vec3 v2 = c[(i + 1) % c.length].v; - - vert(v1, nor, color); - vert(v2, nor, color); - } - }else{ - verts(c[0].v, c[1].v, c[2].v, nor, color); - verts(c[0].v, c[2].v, c[3].v, nor, color); - verts(c[0].v, c[3].v, c[4].v, nor, color); - - if(c.length > 5){ - verts(c[0].v, c[4].v, c[5].v, nor, color); - }else{ - verts(c[0].v, c[3].v, c[4].v, nor, color); - } - } - } - } - - private float elevation(Vec3 v){ - return gen.getHeight(vec.set(v).scl(1f / radius)); - } - - private Color color(Vec3 v){ - return gen.getColor(vec.set(v).scl(1f / radius)); + public void render(Mat3D projection, Mat3D transform){ + preRender(); + shader.begin(); + shader.setUniformMatrix4("u_proj", projection.val); + shader.setUniformMatrix4("u_trans", transform.val); + shader.apply(); + mesh.render(shader, Gl.triangles); + shader.end(); } } diff --git a/core/src/mindustry/graphics/g3d/ShaderSphereMesh.java b/core/src/mindustry/graphics/g3d/ShaderSphereMesh.java new file mode 100644 index 0000000000..229cf7f710 --- /dev/null +++ b/core/src/mindustry/graphics/g3d/ShaderSphereMesh.java @@ -0,0 +1,11 @@ +package mindustry.graphics.g3d; + +import arc.graphics.gl.*; +import mindustry.type.*; + +public abstract class ShaderSphereMesh extends PlanetMesh{ + + public ShaderSphereMesh(Planet planet, Shader shader, int divisions){ + super(planet, MeshBuilder.buildIcosphere(divisions, planet.radius), shader); + } +} diff --git a/core/src/mindustry/graphics/g3d/SphereMesh.java b/core/src/mindustry/graphics/g3d/SphereMesh.java deleted file mode 100644 index af2e285ed5..0000000000 --- a/core/src/mindustry/graphics/g3d/SphereMesh.java +++ /dev/null @@ -1,24 +0,0 @@ -package mindustry.graphics.g3d; - -import arc.graphics.*; -import arc.math.geom.*; - -public class SphereMesh extends GenericMesh{ - private static final Vec3 v1 = new Vec3(), v2 = new Vec3(), v3 = new Vec3(), v4 = new Vec3(); - - protected final float radius; - - public SphereMesh(int divisions, float radius){ - super(20 * (2 << (2 * divisions - 1)) * 7 * 3, Gl.triangles); - this.radius = radius; - - MeshResult result = Icosphere.create(divisions); - for(int i = 0; i < result.indices.size; i+= 3){ - v1.set(result.vertices.items, result.indices.items[i] * 3).setLength(radius); - v2.set(result.vertices.items, result.indices.items[i + 1] * 3).setLength(radius); - v3.set(result.vertices.items, result.indices.items[i + 2] * 3).setLength(radius); - - verts(v1, v3, v2, normal(v1, v2, v3).scl(-1f), Color.white); - } - } -} diff --git a/core/src/mindustry/graphics/g3d/SunMesh.java b/core/src/mindustry/graphics/g3d/SunMesh.java new file mode 100644 index 0000000000..a01328bcd2 --- /dev/null +++ b/core/src/mindustry/graphics/g3d/SunMesh.java @@ -0,0 +1,41 @@ +package mindustry.graphics.g3d; + +import arc.graphics.*; +import arc.math.*; +import mindustry.graphics.*; +import mindustry.graphics.Shaders.*; +import mindustry.type.*; + +public class SunMesh extends ShaderSphereMesh{ + public int octaves = 5; + public float falloff = 0.5f, scale = 1f, power = 1.3f, magnitude = 0.6f, speed = 99999999999f, spread = 1.3f, seed = Mathf.random(9999f); + public float[] colorValues; + + public SunMesh(Planet planet, int divisions){ + super(planet, Shaders.sun, divisions); + } + + public void setColors(Color... colors){ + colorValues = new float[colors.length*4]; + + for(int i = 0; i < colors.length; i ++){ + colorValues[i*4] = colors[i].r; + colorValues[i*4 + 1] = colors[i].g; + colorValues[i*4 + 2] = colors[i].b; + colorValues[i*4 + 3] = colors[i].a; + } + } + + @Override + public void preRender(){ + SunShader s = (SunShader)shader; + s.octaves = octaves; + s.falloff = falloff; + s.scale = scale; + s.power = power; + s.magnitude = magnitude; + s.speed = speed; + s.seed = seed; + s.colorValues = colorValues; + } +} diff --git a/core/src/mindustry/maps/generators/PlanetGenerator.java b/core/src/mindustry/maps/generators/PlanetGenerator.java index e2a624da50..8dfd297197 100644 --- a/core/src/mindustry/maps/generators/PlanetGenerator.java +++ b/core/src/mindustry/maps/generators/PlanetGenerator.java @@ -1,11 +1,11 @@ package mindustry.maps.generators; import arc.math.geom.*; -import mindustry.graphics.g3d.PlanetMesher; +import mindustry.graphics.g3d.HexMesher; import mindustry.type.*; import mindustry.world.*; -public abstract class PlanetGenerator extends BasicGenerator implements PlanetMesher{ +public abstract class PlanetGenerator extends BasicGenerator implements HexMesher{ protected Sector sector; protected void genTile(Vec3 position, TileGen tile){ diff --git a/core/src/mindustry/type/Planet.java b/core/src/mindustry/type/Planet.java index 20e1a048d0..4d1328c2b5 100644 --- a/core/src/mindustry/type/Planet.java +++ b/core/src/mindustry/type/Planet.java @@ -1,12 +1,13 @@ package mindustry.type; import arc.files.*; +import arc.func.*; +import arc.graphics.*; import arc.math.*; import arc.math.geom.*; import arc.scene.ui.layout.*; import arc.struct.*; import arc.util.ArcAnnotate.*; -import arc.util.*; import arc.util.io.*; import mindustry.*; import mindustry.ctype.*; @@ -19,17 +20,19 @@ import static mindustry.Vars.universe; public class Planet extends UnlockableContent{ /** Default spacing between planet orbits in world units. */ - private static final float orbitSpacing = 6f; + private static final float orbitSpacing = 5f; + /** intersect() temp var. */ + private static final Vec3 intersectResult = new Vec3(); /** Mesh used for rendering. Created on load() - will be null on the server! */ - public GenericMesh mesh; - /** Grid used for the sectors on the planet. */ - public @NonNull PlanetGrid grid; - /** Generator that will make the planet. */ - public @NonNull PlanetGenerator generator; + public PlanetMesh mesh; + /** Position in global coordinates. Will be 0,0,0 until the Universe updates it. */ + public Vec3 position = new Vec3(); + /** Grid used for the sectors on the planet. Null if this planet can't be landed on. */ + public @Nullable PlanetGrid grid; + /** Generator that will make the planet. Can be null for planets that don't need to be landed on. */ + public @Nullable PlanetGenerator generator; /** Array of sectors; directly maps to tiles in the grid. */ public @NonNull Array sectors; - /** Detail in divisions. Must be between 1 and 10. 6 is a good number for this.*/ - public int detail = 3; /** Radius of this planet's sphere. Does not take into account sattelites. */ public float radius; /** Orbital radius around the sun. Do not change unless you know exactly what you are doing.*/ @@ -42,26 +45,52 @@ public class Planet extends UnlockableContent{ public float rotateTime = 24f * 60f; /** Whether this planet is tidally locked relative to its parent - see https://en.wikipedia.org/wiki/Tidal_locking */ public boolean tidalLock = false; + /** Whether the bloom render effect is enabled. */ + public boolean bloom = false; + /** For suns, this is the color that shines on other planets. Does nothing for children. */ + public Color lightColor = Color.white.cpy(); /** Parent body that this planet orbits around. If null, this planet is considered to be in the middle of the solar system.*/ public @Nullable Planet parent; + /** The root parent of the whole solar system this planet is in. */ + public @NonNull Planet solarSystem; /** All planets orbiting this one, in ascending order of radius. */ public Array children = new Array<>(); + /** Loads the mesh. Clientside only. Defaults to a boring sphere mesh. */ + protected Prov meshLoader = () -> new SunMesh(this, 2); - public Planet(String name, Planet parent, int size, float radius){ + public Planet(String name, Planet parent, int sectorSize, float radius){ super(name); this.radius = radius; this.parent = parent; - grid = PlanetGrid.create(size); + if(sectorSize > 0){ + grid = PlanetGrid.create(sectorSize); - sectors = new Array<>(grid.tiles.length); - for(int i = 0; i < grid.tiles.length; i++){ - sectors.add(new Sector(this, grid.tiles[i], new SectorData())); + sectors = new Array<>(grid.tiles.length); + for(int i = 0; i < grid.tiles.length; i++){ + sectors.add(new Sector(this, grid.tiles[i], new SectorData())); + } + + //read data for sectors + Fi data = Vars.tree.get("planets/" + name + ".dat"); + if(data.exists()){ + try(Reads read = data.reads()){ + short dsize = read.s(); + for(int i = 0; i < dsize; i++){ + sectors.get(i).data.read(read); + } + } + } + }else{ + sectors = new Array<>(); } + //total radius is initially just the radius + totalRadius += radius; + //get orbit radius by extending past the parent's total radius - orbitRadius = parent == null ? 0f : (parent.totalRadius + orbitSpacing + radius); + orbitRadius = parent == null ? 0f : (parent.totalRadius + orbitSpacing + totalRadius); //orbit time is based on radius [kepler's third law] orbitTime = Mathf.pow(orbitRadius, 1.5f) * 1000; @@ -72,19 +101,12 @@ public class Planet extends UnlockableContent{ parent.updateTotalRadius(); } - //read data - Fi data = Vars.tree.get("planets/" + name + ".dat"); - if(data.exists()){ - try(Reads read = data.reads()){ - short dsize = read.s(); - for(int i = 0; i < dsize; i++){ - sectors.get(i).data.read(read); - } - } - }else{ - //TODO crash instead - this is a critical error! - Log.err("Planet {0} is missing its data file.", name); - } + //calculate solar system + for(solarSystem = this; solarSystem.parent != null; solarSystem = solarSystem.parent); + } + + public boolean isLandable(){ + return grid != null && generator != null && sectors.size > 0; } public void updateTotalRadius(){ @@ -99,7 +121,7 @@ public class Planet extends UnlockableContent{ public float getOrbitAngle(){ //applies random offset to prevent planets from starting out in a line float offset = Mathf.randomSeed(id, 360); - return (offset + universe.seconds() / (orbitTime / 360f)) % 360f; + return (offset + universe.secondsf() / (orbitTime / 360f)) % 360f; } /** Calulates rotation on own axis based on universe time.*/ @@ -110,7 +132,7 @@ public class Planet extends UnlockableContent{ } //random offset for more variability float offset = Mathf.randomSeed(id+1, 360); - return (offset + universe.seconds() / (rotateTime / 360f)) % 360f; + return (offset + universe.secondsf() / (rotateTime / 360f)) % 360f; } /** Adds this planet's offset relative to its parent to the vector. Used for calculating world positions. */ @@ -121,7 +143,7 @@ public class Planet extends UnlockableContent{ } float angle = getOrbitAngle(); - return in.add(Angles.trnsx(angle, orbitRadius), Angles.trnsy(angle, orbitRadius), 0f); + return in.add(Angles.trnsx(angle, orbitRadius), 0, Angles.trnsy(angle, orbitRadius)); } /** Gets the absolute world position of this planet, taking into account all parents. O(n) complexity.*/ @@ -133,9 +155,14 @@ public class Planet extends UnlockableContent{ return in; } + /** @return the supplied matrix with transformation applied. */ + public Mat3D getTransform(Mat3D mat){ + return mat.setToTranslation(position).rotate(Vec3.Y, getRotation()); + } + @Override public void load(){ - mesh = new PlanetMesh(detail, generator); + mesh = meshLoader.get(); } /** Gets a sector a tile position. */ @@ -143,13 +170,24 @@ public class Planet extends UnlockableContent{ return sectors.get(tile.id); } - /** @return the sector that is hit by this ray, or null if nothing intersects it. - * @param center the center of this planet in 3D space, usually (0,0,0). */ - public @Nullable Sector getSector(Vec3 center, Ray ray){ - boolean found = Intersector3D.intersectRaySphere(ray, center, radius, Tmp.v33); + /** @return the sector that is hit by this ray, or null if nothing intersects it. */ + public @Nullable Sector getSector(Ray ray){ + return getSector(ray, radius); + } + + /** @return the sector that is hit by this ray, or null if nothing intersects it. */ + public @Nullable Sector getSector(Ray ray, float radius){ + Vec3 vec = intersect(ray, radius); + if(vec == null) return null; + vec.sub(position).rotate(Vec3.Y, getRotation()); + return sectors.min(t -> t.tile.v.dst2(vec)); + } + + /** @return the sector that is hit by this ray, or null if nothing intersects it. */ + public @Nullable Vec3 intersect(Ray ray, float radius){ + boolean found = Intersector3D.intersectRaySphere(ray, position, radius, intersectResult); if(!found) return null; - //TODO fix O(N) search - return sectors.min(t -> t.tile.v.dst(Tmp.v33)); + return intersectResult; } /** Planets cannot be viewed in the database dialog. */ diff --git a/core/src/mindustry/type/Sector.java b/core/src/mindustry/type/Sector.java index ded308c0e8..d8834228df 100644 --- a/core/src/mindustry/type/Sector.java +++ b/core/src/mindustry/type/Sector.java @@ -34,6 +34,14 @@ public class Sector{ this.data = data; } + /** @return light dot product in the range [0, 1]. */ + public float getLight(){ + Vec3 normal = Tmp.v31.set(tile.v).rotate(Vec3.Y, -planet.getRotation()).nor(); + Vec3 light = Tmp.v32.set(planet.solarSystem.position).sub(planet.position).nor(); + //lightness in [0, 1] + return (normal.dot(light) + 1f) / 2f; + } + public int getSize(){ return (int)(rect.radius * 3200); } diff --git a/core/src/mindustry/ui/dialogs/PlanetDialog.java b/core/src/mindustry/ui/dialogs/PlanetDialog.java index 4485939205..ccdda8221c 100644 --- a/core/src/mindustry/ui/dialogs/PlanetDialog.java +++ b/core/src/mindustry/ui/dialogs/PlanetDialog.java @@ -4,6 +4,7 @@ import arc.*; import arc.graphics.*; import arc.graphics.g2d.*; import arc.graphics.g3d.*; +import arc.graphics.gl.*; import arc.input.*; import arc.math.*; import arc.math.geom.*; @@ -34,16 +35,16 @@ public class PlanetDialog extends FloatingDialog{ //the base planet that's being rendered private final Planet solarSystem = Planets.sun; - private final PlanetMesh[] outlines = new PlanetMesh[10]; + private final Mesh[] outlines = new Mesh[10]; private final Camera3D cam = new Camera3D(); private final VertexBatch3D batch = new VertexBatch3D(false, true, 0); private final PlaneBatch3D projector = new PlaneBatch3D(); private final Mat3D mat = new Mat3D(); + private final Vec3 camRelative = new Vec3(); - - private final SphereMesh sun = new SphereMesh(3, 1.2f); - private final Bloom bloom = new Bloom(false){{ + private final Bloom bloom = new Bloom(Core.graphics.getWidth()/4, Core.graphics.getHeight()/4, true, false, true){{ setClearColor(0, 0, 0, 0); + blurPasses = 6; }}; private Planet planet = Planets.starter; @@ -57,31 +58,34 @@ public class PlanetDialog extends FloatingDialog{ addCloseButton(); buttons.addImageTextButton("$techtree", Icon.tree, () -> ui.tech.show()).size(230f, 64f); - Tmp.v1.trns(0, camLength); - cam.position.set(Tmp.v1.x, 0f, Tmp.v1.y); + camRelative.set(0, 0f, camLength); projector.setScaling(1f / 300f); update(() -> { - Ptile tile = outline(planet.grid.size).getTile(cam.getPickRay(Core.input.mouseX(), Core.input.mouseY())); - hovered = tile == null ? null : planet.getSector(tile); - Vec3 v = Tmp.v33.set(Core.input.mouseX(), Core.input.mouseY(), 0); - if(Core.input.keyDown(KeyCode.MOUSE_LEFT)){ - float upV = cam.position.angle(Vec3.Y); - float xscale = 9f, yscale = 10f; - float margin = 1; + if(planet.isLandable()){ + hovered = planet.getSector(cam.getMouseRay(), outlineRad); - //scale X speed depending on polar coordinate - float speed = 1f - Math.abs(upV - 90) / 90f; + if(Core.input.keyDown(KeyCode.MOUSE_LEFT)){ + float upV = camRelative.angle(Vec3.Y); + float xscale = 9f, yscale = 10f; + float margin = 1; - cam.position.rotate(cam.up, (v.x - lastX) / xscale * speed); + //scale X speed depending on polar coordinate + float speed = 1f - Math.abs(upV - 90) / 90f; - //prevent user from scrolling all the way up and glitching it out - float amount = (v.y - lastY) / yscale; - amount = Mathf.clamp(upV + amount, margin, 180f - margin) - upV; + camRelative.rotate(cam.up, (v.x - lastX) / xscale * speed); - cam.position.rotate(Tmp.v31.set(cam.up).rotate(cam.direction, 90), amount); + //prevent user from scrolling all the way up and glitching it out + float amount = (v.y - lastY) / yscale; + amount = Mathf.clamp(upV + amount, margin, 180f - margin) - upV; + + camRelative.rotate(Tmp.v31.set(cam.up).rotate(cam.direction, 90), amount); + } + + }else{ + hovered = selected = null; } lastX = v.x; @@ -110,23 +114,6 @@ public class PlanetDialog extends FloatingDialog{ stable.pack(); stable.setPosition(0, 0, Align.center); - Shaders.sun.colors = new Color[]{ - Color.valueOf("ff7a38"), - Color.valueOf("ff9638"), - Color.valueOf("ffc64c"), - Color.valueOf("ffc64c"), - Color.valueOf("ffe371"), - Color.valueOf("f4ee8e"), - }; - - Shaders.sun.updateColors(); - Shaders.sun.scale = 1f; - Shaders.sun.speed = 1000f; - Shaders.sun.falloff = 0.3f; - Shaders.sun.octaves = 4; - Shaders.sun.spread = 1.2f; - Shaders.sun.magnitude = 0f; - shown(this::setup); } @@ -149,16 +136,80 @@ public class PlanetDialog extends FloatingDialog{ cam.up.set(Vec3.Y); cam.resize(Core.graphics.getWidth(), Core.graphics.getHeight()); - cam.lookAt(0, 0, 0); + cam.position.set(planet.position).add(camRelative); + cam.lookAt(planet.position); cam.update(); projector.proj(cam.combined()); batch.proj(cam.combined()); - renderPlanet(solarSystem); + bloom.capture(); - //renderSun(); - //renderPlanet(); + renderPlanet(solarSystem); + if(planet.isLandable()){ + //TODO + renderSectors(planet); + } + + if(false) + Draw.batch(projector, () -> { + if(selected != null){ + setPlane(selected); + stable.draw(); + } + }); + + bloom.render(); + + Gl.disable(Gl.cullFace); + //Gl.disable(Gl.depthTest); + + if(false && selected != null){ + Vec3 pos = cam.project(Tmp.v31.set(selected.tile.v).setLength(outlineRad)); + stable.setPosition(pos.x, pos.y, Align.center); + stable.draw(); + } + + cam.update(); + } + + private void renderPlanet(Planet planet){ + //render planet at offsetted position in the world + + if(false){ + bloom.capture(); + } + + planet.mesh.render(cam.combined(), planet.getTransform(mat)); + + if(false){ + bloom.render(); + + Gl.enable(Gl.depthTest); + Gl.enable(Gl.blend); + Gl.depthMask(true); + } + + renderOrbit(planet); + + for(Planet child : planet.children){ + renderPlanet(child); + } + } + + private void renderOrbit(Planet planet){ + if(planet.parent == null) return; + + Vec3 center = planet.parent.position; + float radius = planet.orbitRadius; + int points = (int)(radius * 50); + Angles.circleVectors(points, radius, (cx, cy) -> batch.vertex(Tmp.v32.set(center).add(cx, 0, cy), Pal.gray)); + batch.flush(Gl.lineLoop); + } + + private void renderSectors(Planet planet){ + //apply transformed position + batch.proj().mul(planet.getTransform(mat)); for(Sector sec : planet.sectors){ if(sec.save == null){ @@ -184,67 +235,19 @@ public class PlanetDialog extends FloatingDialog{ batch.flush(Gl.triangles); - if(true) - Draw.batch(projector, () -> { - if(selected != null){ - setPlane(selected); - stable.draw(); - } - }); + //render sector grid + Mesh mesh = outline(planet.grid.size); + Shader shader = Shaders.planetGrid; + Vec3 tile = planet.intersect(cam.getMouseRay(), outlineRad); + //Log.info(tile); + Shaders.planetGrid.mouse.lerp(tile == null ? Vec3.Zero : tile.sub(planet.position).rotate(Vec3.Y, planet.getRotation()), 0.2f); - //3D aligned table - - Gl.disable(Gl.cullFace); - Gl.disable(Gl.depthTest); - - if(false && selected != null){ - Vec3 pos = cam.project(Tmp.v31.set(selected.tile.v).setLength(outlineRad)); - stable.setPosition(pos.x, pos.y, Align.center); - stable.draw(); - } - } - - private void renderPlanet(Planet planet){ - //render planet at offsetted position in the world - Vec3 position = planet.getWorldPosition(Tmp.v33); - mat.set(cam.combined()).trn(position); //TODO this probably won't give the desired result - planet.mesh.render(mat); - - renderOrbit(planet); - - for(Planet child : planet.children){ - renderPlanet(child); - } - } - - private void renderOrbit(Planet planet){ - if(planet.parent == null) return; - - Vec3 center = planet.parent.getWorldPosition(Tmp.v31); - float radius = planet.radius; - int points = (int)(radius * 10); - Angles.circleVectors(points, radius, (cx, cy) -> batch.vertex(Tmp.v32.set(center).add(cx, 0, cy), Pal.gray)); - batch.flush(Gl.lineLoop); - } - - private void renderPlanet(){ - PlanetMesh outline = outline(planet.grid.size); - Vec3 tile = outline.intersect(cam.getPickRay(Core.input.mouseX(), Core.input.mouseY())); - Shaders.planetGrid.mouse.lerp(tile == null ? Vec3.Zero : tile, 0.2f); - Shaders.planet.lightDir.set(Shaders.sun.center).nor(); - - planet.mesh.render(cam.combined()); - outline.render(cam.combined(), Shaders.planetGrid); - } - - private void renderSun(){ - bloom.capture(); - Shaders.sun.center.set(-3f, 0f, 0).rotate(Vec3.Y, Time.time() / 3f); - sun.render(cam.combined(), Shaders.sun); - bloom.render(); - - Gl.enable(Gl.depthTest); - Gl.enable(Gl.blend); + shader.begin(); + shader.setUniformMatrix4("u_proj", cam.combined().val); + shader.setUniformMatrix4("u_trans", planet.getTransform(mat).val); + shader.apply(); + mesh.render(shader, Gl.lines); + shader.end(); } private void drawBorders(Sector sector, Color base){ @@ -305,6 +308,8 @@ public class PlanetDialog extends FloatingDialog{ } private void setPlane(Sector sector){ + float rotation = planet.getRotation(); + projector.setPlane( //origin on sector position Tmp.v33.set(sector.tile.v).setLength(outlineRad + 0.1f), @@ -340,9 +345,9 @@ public class PlanetDialog extends FloatingDialog{ } } - private PlanetMesh outline(int size){ + private Mesh outline(int size){ if(outlines[size] == null){ - outlines[size] = new PlanetMesh(size, new PlanetMesher(){ + outlines[size] = MeshBuilder.buildHex(new HexMesher(){ @Override public float getHeight(Vec3 position){ return 0; @@ -352,7 +357,7 @@ public class PlanetDialog extends FloatingDialog{ public Color getColor(Vec3 position){ return outlineColor; } - }, outlineRad, true); + }, size, true, outlineRad, 0.2f); } return outlines[size]; } diff --git a/gradle.properties b/gradle.properties index c20f31f7e3..9141b274a4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=true org.gradle.jvmargs=-Xms256m -Xmx1024m -archash=bff072e2d671c74a32b41353125c2aa6ba8c0314 +archash=17caa50a75e98000d6e7bd1568a932a087dc09c3