Real-time lighting + solar system rendering

This commit is contained in:
Anuken
2020-02-29 11:58:10 -05:00
parent f0857fa22d
commit 7a01719816
20 changed files with 491 additions and 356 deletions

View File

@@ -2,20 +2,22 @@ attribute vec4 a_position;
attribute vec3 a_normal; attribute vec3 a_normal;
attribute vec4 a_color; attribute vec4 a_color;
uniform mat4 u_projModelView; uniform mat4 u_proj;
uniform mat4 u_trans;
uniform vec3 u_lightdir; uniform vec3 u_lightdir;
uniform vec3 u_ambientColor;
varying vec4 v_col; varying vec4 v_col;
const vec3 ambientColor = vec3(1.0); const vec3 ambientColor = vec3(1.0);
const vec3 ambientDir = normalize(vec3(1.0, 1.0, 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 v1 = vec3(1.0, 0.0, 1.0);
const vec3 v2 = vec3(1.0, 0.5, 0.0); const vec3 v2 = vec3(1.0, 0.5, 0.0);
void main(){ 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); v_col = a_color * vec4(norc, 1.0);
gl_Position = u_projModelView * a_position; gl_Position = u_proj * u_trans * a_position;
} }

View File

@@ -1,12 +1,14 @@
attribute vec4 a_position; attribute vec4 a_position;
attribute vec4 a_color; attribute vec4 a_color;
uniform mat4 u_projModelView; uniform mat4 u_proj;
uniform mat4 u_trans;
varying vec4 v_col; varying vec4 v_col;
varying vec4 v_position; varying vec4 v_position;
void main() { void main() {
gl_Position = u_projModelView * a_position; gl_Position = u_proj * u_trans * a_position;
v_col = a_color; v_col = a_color;
v_position = a_position; v_position = a_position;
} }

View File

@@ -1,9 +1,9 @@
attribute vec4 a_position; attribute vec4 a_position;
attribute vec3 a_normal; 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 float u_time;
uniform int u_octaves; uniform int u_octaves;
uniform float u_falloff; uniform float u_falloff;
@@ -100,8 +100,7 @@ float onoise(vec4 pos, int octaves, float falloff, float scl, float po){
} }
void main(){ void main(){
vec3 center = u_center; vec4 pos = a_position;
vec4 pos = u_projModelView * (a_position);
float height = onoise(vec4(a_position.xyz, u_time + u_seed), u_octaves, u_falloff, u_scale, u_power); 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; 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_proj * u_trans * a_position; //u_proj * (a_position + vec4(pos.xyz * (dst - 1.0), 0.0));
gl_Position = u_projModelView * (a_position + vec4(center.xyz, 0.0)) + vec4(rel, 0.0);
} }

View File

@@ -1,6 +1,8 @@
package mindustry.content; package mindustry.content;
import arc.graphics.*;
import mindustry.ctype.*; import mindustry.ctype.*;
import mindustry.graphics.g3d.*;
import mindustry.maps.planet.*; import mindustry.maps.planet.*;
import mindustry.type.*; import mindustry.type.*;
@@ -11,14 +13,31 @@ public class Planets implements ContentList{
@Override @Override
public void load(){ public void load(){
sun = new Planet("sun", null, 3, 1){{ sun = new Planet("sun", null, 0, 2){{
detail = 6; bloom = true;
generator = new TestPlanetGenerator(); //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){{ starter = new Planet("TODO", sun, 3, 1){{
detail = 6;
generator = new TestPlanetGenerator(); generator = new TestPlanetGenerator();
meshLoader = () -> new HexMesh(this, 6);
}}; }};
} }
} }

View File

@@ -204,6 +204,7 @@ public class Logic implements ApplicationListener{
@Override @Override
public void update(){ public void update(){
Events.fire(Trigger.update); Events.fire(Trigger.update);
universe.updateGlobal();
if(!state.is(State.menu)){ if(!state.is(State.menu)){
if(!net.client()){ if(!net.client()){
@@ -211,7 +212,9 @@ public class Logic implements ApplicationListener{
} }
if(!state.isPaused()){ if(!state.isPaused()){
universe.update(); if(world.isCampaign()){
universe.update();
}
Time.update(); Time.update();
if(state.rules.waves && state.rules.waveTimer && !state.gameOver){ if(state.rules.waves && state.rules.waveTimer && !state.gameOver){

View File

@@ -1,7 +1,12 @@
package mindustry.game; package mindustry.game;
import arc.*; import arc.*;
import arc.math.*;
import arc.util.*; import arc.util.*;
import mindustry.content.*;
import mindustry.type.*;
import static mindustry.Vars.*;
public class Universe{ public class Universe{
private long seconds; private long seconds;
@@ -11,6 +16,22 @@ public class Universe{
load(); 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(){ public void update(){
secondCounter += Time.delta() / 60f; secondCounter += Time.delta() / 60f;
if(secondCounter >= 1){ if(secondCounter >= 1){
@@ -22,6 +43,13 @@ public class Universe{
save(); 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){ public float secondsMod(float mod, float scale){

View File

@@ -47,6 +47,7 @@ public class Shaders{
public static class PlanetShader extends LoadShader{ public static class PlanetShader extends LoadShader{
public Vec3 lightDir = new Vec3(1, 1, 1).nor(); public Vec3 lightDir = new Vec3(1, 1, 1).nor();
public Color ambientColor = Color.white.cpy();
public PlanetShader(){ public PlanetShader(){
super("planet", "planet"); super("planet", "planet");
@@ -55,6 +56,7 @@ public class Shaders{
@Override @Override
public void apply(){ public void apply(){
setUniformf("u_lightdir", lightDir); 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 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 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 float[] colorValues;
public Vec3 center = new Vec3();
public SunShader(){ public SunShader(){
super("sun", "sun"); super("sun", "sun");
} }
@@ -81,22 +80,10 @@ public class Shaders{
setUniformf("u_time", Time.globalTime() / speed); setUniformf("u_time", Time.globalTime() / speed);
setUniformf("u_seed", seed); setUniformf("u_seed", seed);
setUniformf("u_spread", spread); 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); 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{ public static class PlanetGridShader extends LoadShader{

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -4,7 +4,7 @@ import arc.graphics.*;
import arc.math.geom.*; import arc.math.geom.*;
/** Defines color and height for a planet mesh. */ /** Defines color and height for a planet mesh. */
public interface PlanetMesher{ public interface HexMesher{
float getHeight(Vec3 position); float getHeight(Vec3 position);
Color getColor(Vec3 position); Color getColor(Vec3 position);
} }

View File

@@ -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);
}
}

View File

@@ -1,102 +1,32 @@
package mindustry.graphics.g3d; package mindustry.graphics.g3d;
import arc.graphics.*; import arc.graphics.*;
import arc.graphics.gl.*;
import arc.math.geom.*; import arc.math.geom.*;
import arc.util.ArcAnnotate.*; import mindustry.type.*;
import arc.util.*;
import mindustry.graphics.g3d.PlanetGrid.*;
public class PlanetMesh extends GenericMesh{ /** Defines a mesh that is rendered for a planet. Subclasses provide a mesh and a shader. */
private Vec3 vec = new Vec3(); public abstract class PlanetMesh{
private PlanetGrid grid; protected final Mesh mesh;
private Vec3 center = new Vec3(); protected final Planet planet;
protected final Shader shader;
private boolean lines; public PlanetMesh(Planet planet, Mesh mesh, Shader shader){
private float radius, intensity = 0.2f; this.planet = planet;
this.mesh = mesh;
private final PlanetMesher gen; this.shader = shader;
public PlanetMesh(int divisions, PlanetMesher gen){
this(divisions, gen, 1f, false);
} }
public PlanetMesh(int divisions, PlanetMesher gen, float radius, boolean lines){ /** Should be overridden to set up any shader parameters such as planet position, normals, etc. */
super(PlanetGrid.create(divisions).tiles.length * 12 * (3 + 3 + 1), lines ? Gl.lines : Gl.triangles); public abstract void preRender();
this.gen = gen; public void render(Mat3D projection, Mat3D transform){
this.radius = radius; preRender();
this.grid = PlanetGrid.create(divisions); shader.begin();
this.lines = lines; shader.setUniformMatrix4("u_proj", projection.val);
shader.setUniformMatrix4("u_trans", transform.val);
generateMesh(); shader.apply();
} mesh.render(shader, Gl.triangles);
shader.end();
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));
} }
} }

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -1,11 +1,11 @@
package mindustry.maps.generators; package mindustry.maps.generators;
import arc.math.geom.*; import arc.math.geom.*;
import mindustry.graphics.g3d.PlanetMesher; import mindustry.graphics.g3d.HexMesher;
import mindustry.type.*; import mindustry.type.*;
import mindustry.world.*; import mindustry.world.*;
public abstract class PlanetGenerator extends BasicGenerator implements PlanetMesher{ public abstract class PlanetGenerator extends BasicGenerator implements HexMesher{
protected Sector sector; protected Sector sector;
protected void genTile(Vec3 position, TileGen tile){ protected void genTile(Vec3 position, TileGen tile){

View File

@@ -1,12 +1,13 @@
package mindustry.type; package mindustry.type;
import arc.files.*; import arc.files.*;
import arc.func.*;
import arc.graphics.*;
import arc.math.*; import arc.math.*;
import arc.math.geom.*; import arc.math.geom.*;
import arc.scene.ui.layout.*; import arc.scene.ui.layout.*;
import arc.struct.*; import arc.struct.*;
import arc.util.ArcAnnotate.*; import arc.util.ArcAnnotate.*;
import arc.util.*;
import arc.util.io.*; import arc.util.io.*;
import mindustry.*; import mindustry.*;
import mindustry.ctype.*; import mindustry.ctype.*;
@@ -19,17 +20,19 @@ import static mindustry.Vars.universe;
public class Planet extends UnlockableContent{ public class Planet extends UnlockableContent{
/** Default spacing between planet orbits in world units. */ /** 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! */ /** Mesh used for rendering. Created on load() - will be null on the server! */
public GenericMesh mesh; public PlanetMesh mesh;
/** Grid used for the sectors on the planet. */ /** Position in global coordinates. Will be 0,0,0 until the Universe updates it. */
public @NonNull PlanetGrid grid; public Vec3 position = new Vec3();
/** Generator that will make the planet. */ /** Grid used for the sectors on the planet. Null if this planet can't be landed on. */
public @NonNull PlanetGenerator generator; 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. */ /** Array of sectors; directly maps to tiles in the grid. */
public @NonNull Array<Sector> sectors; public @NonNull Array<Sector> 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. */ /** Radius of this planet's sphere. Does not take into account sattelites. */
public float radius; public float radius;
/** Orbital radius around the sun. Do not change unless you know exactly what you are doing.*/ /** 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; public float rotateTime = 24f * 60f;
/** Whether this planet is tidally locked relative to its parent - see https://en.wikipedia.org/wiki/Tidal_locking */ /** Whether this planet is tidally locked relative to its parent - see https://en.wikipedia.org/wiki/Tidal_locking */
public boolean tidalLock = false; 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.*/ /** 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; 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. */ /** All planets orbiting this one, in ascending order of radius. */
public Array<Planet> children = new Array<>(); public Array<Planet> children = new Array<>();
/** Loads the mesh. Clientside only. Defaults to a boring sphere mesh. */
protected Prov<PlanetMesh> 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); super(name);
this.radius = radius; this.radius = radius;
this.parent = parent; this.parent = parent;
grid = PlanetGrid.create(size); if(sectorSize > 0){
grid = PlanetGrid.create(sectorSize);
sectors = new Array<>(grid.tiles.length); sectors = new Array<>(grid.tiles.length);
for(int i = 0; i < grid.tiles.length; i++){ for(int i = 0; i < grid.tiles.length; i++){
sectors.add(new Sector(this, grid.tiles[i], new SectorData())); 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 //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] //orbit time is based on radius [kepler's third law]
orbitTime = Mathf.pow(orbitRadius, 1.5f) * 1000; orbitTime = Mathf.pow(orbitRadius, 1.5f) * 1000;
@@ -72,19 +101,12 @@ public class Planet extends UnlockableContent{
parent.updateTotalRadius(); parent.updateTotalRadius();
} }
//read data //calculate solar system
Fi data = Vars.tree.get("planets/" + name + ".dat"); for(solarSystem = this; solarSystem.parent != null; solarSystem = solarSystem.parent);
if(data.exists()){ }
try(Reads read = data.reads()){
short dsize = read.s(); public boolean isLandable(){
for(int i = 0; i < dsize; i++){ return grid != null && generator != null && sectors.size > 0;
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);
}
} }
public void updateTotalRadius(){ public void updateTotalRadius(){
@@ -99,7 +121,7 @@ public class Planet extends UnlockableContent{
public float getOrbitAngle(){ public float getOrbitAngle(){
//applies random offset to prevent planets from starting out in a line //applies random offset to prevent planets from starting out in a line
float offset = Mathf.randomSeed(id, 360); 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.*/ /** Calulates rotation on own axis based on universe time.*/
@@ -110,7 +132,7 @@ public class Planet extends UnlockableContent{
} }
//random offset for more variability //random offset for more variability
float offset = Mathf.randomSeed(id+1, 360); 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. */ /** 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(); 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.*/ /** 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 in;
} }
/** @return the supplied matrix with transformation applied. */
public Mat3D getTransform(Mat3D mat){
return mat.setToTranslation(position).rotate(Vec3.Y, getRotation());
}
@Override @Override
public void load(){ public void load(){
mesh = new PlanetMesh(detail, generator); mesh = meshLoader.get();
} }
/** Gets a sector a tile position. */ /** Gets a sector a tile position. */
@@ -143,13 +170,24 @@ public class Planet extends UnlockableContent{
return sectors.get(tile.id); return sectors.get(tile.id);
} }
/** @return the sector that is hit by this ray, or null if nothing intersects it. /** @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(Ray ray){
public @Nullable Sector getSector(Vec3 center, Ray ray){ return getSector(ray, radius);
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, 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; if(!found) return null;
//TODO fix O(N) search return intersectResult;
return sectors.min(t -> t.tile.v.dst(Tmp.v33));
} }
/** Planets cannot be viewed in the database dialog. */ /** Planets cannot be viewed in the database dialog. */

View File

@@ -34,6 +34,14 @@ public class Sector{
this.data = data; 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(){ public int getSize(){
return (int)(rect.radius * 3200); return (int)(rect.radius * 3200);
} }

View File

@@ -4,6 +4,7 @@ import arc.*;
import arc.graphics.*; import arc.graphics.*;
import arc.graphics.g2d.*; import arc.graphics.g2d.*;
import arc.graphics.g3d.*; import arc.graphics.g3d.*;
import arc.graphics.gl.*;
import arc.input.*; import arc.input.*;
import arc.math.*; import arc.math.*;
import arc.math.geom.*; import arc.math.geom.*;
@@ -34,16 +35,16 @@ public class PlanetDialog extends FloatingDialog{
//the base planet that's being rendered //the base planet that's being rendered
private final Planet solarSystem = Planets.sun; 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 Camera3D cam = new Camera3D();
private final VertexBatch3D batch = new VertexBatch3D(false, true, 0); private final VertexBatch3D batch = new VertexBatch3D(false, true, 0);
private final PlaneBatch3D projector = new PlaneBatch3D(); private final PlaneBatch3D projector = new PlaneBatch3D();
private final Mat3D mat = new Mat3D(); private final Mat3D mat = new Mat3D();
private final Vec3 camRelative = new Vec3();
private final Bloom bloom = new Bloom(Core.graphics.getWidth()/4, Core.graphics.getHeight()/4, true, false, true){{
private final SphereMesh sun = new SphereMesh(3, 1.2f);
private final Bloom bloom = new Bloom(false){{
setClearColor(0, 0, 0, 0); setClearColor(0, 0, 0, 0);
blurPasses = 6;
}}; }};
private Planet planet = Planets.starter; private Planet planet = Planets.starter;
@@ -57,31 +58,34 @@ public class PlanetDialog extends FloatingDialog{
addCloseButton(); addCloseButton();
buttons.addImageTextButton("$techtree", Icon.tree, () -> ui.tech.show()).size(230f, 64f); buttons.addImageTextButton("$techtree", Icon.tree, () -> ui.tech.show()).size(230f, 64f);
Tmp.v1.trns(0, camLength); camRelative.set(0, 0f, camLength);
cam.position.set(Tmp.v1.x, 0f, Tmp.v1.y);
projector.setScaling(1f / 300f); projector.setScaling(1f / 300f);
update(() -> { 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); Vec3 v = Tmp.v33.set(Core.input.mouseX(), Core.input.mouseY(), 0);
if(Core.input.keyDown(KeyCode.MOUSE_LEFT)){ if(planet.isLandable()){
float upV = cam.position.angle(Vec3.Y); hovered = planet.getSector(cam.getMouseRay(), outlineRad);
float xscale = 9f, yscale = 10f;
float margin = 1;
//scale X speed depending on polar coordinate if(Core.input.keyDown(KeyCode.MOUSE_LEFT)){
float speed = 1f - Math.abs(upV - 90) / 90f; 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 camRelative.rotate(cam.up, (v.x - lastX) / xscale * speed);
float amount = (v.y - lastY) / yscale;
amount = Mathf.clamp(upV + amount, margin, 180f - margin) - upV;
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; lastX = v.x;
@@ -110,23 +114,6 @@ public class PlanetDialog extends FloatingDialog{
stable.pack(); stable.pack();
stable.setPosition(0, 0, Align.center); 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); shown(this::setup);
} }
@@ -149,16 +136,80 @@ public class PlanetDialog extends FloatingDialog{
cam.up.set(Vec3.Y); cam.up.set(Vec3.Y);
cam.resize(Core.graphics.getWidth(), Core.graphics.getHeight()); 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(); cam.update();
projector.proj(cam.combined()); projector.proj(cam.combined());
batch.proj(cam.combined()); batch.proj(cam.combined());
renderPlanet(solarSystem); bloom.capture();
//renderSun(); renderPlanet(solarSystem);
//renderPlanet(); 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){ for(Sector sec : planet.sectors){
if(sec.save == null){ if(sec.save == null){
@@ -184,67 +235,19 @@ public class PlanetDialog extends FloatingDialog{
batch.flush(Gl.triangles); batch.flush(Gl.triangles);
if(true) //render sector grid
Draw.batch(projector, () -> { Mesh mesh = outline(planet.grid.size);
if(selected != null){ Shader shader = Shaders.planetGrid;
setPlane(selected); Vec3 tile = planet.intersect(cam.getMouseRay(), outlineRad);
stable.draw(); //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 shader.begin();
shader.setUniformMatrix4("u_proj", cam.combined().val);
Gl.disable(Gl.cullFace); shader.setUniformMatrix4("u_trans", planet.getTransform(mat).val);
Gl.disable(Gl.depthTest); shader.apply();
mesh.render(shader, Gl.lines);
if(false && selected != null){ shader.end();
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);
} }
private void drawBorders(Sector sector, Color base){ private void drawBorders(Sector sector, Color base){
@@ -305,6 +308,8 @@ public class PlanetDialog extends FloatingDialog{
} }
private void setPlane(Sector sector){ private void setPlane(Sector sector){
float rotation = planet.getRotation();
projector.setPlane( projector.setPlane(
//origin on sector position //origin on sector position
Tmp.v33.set(sector.tile.v).setLength(outlineRad + 0.1f), 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){ if(outlines[size] == null){
outlines[size] = new PlanetMesh(size, new PlanetMesher(){ outlines[size] = MeshBuilder.buildHex(new HexMesher(){
@Override @Override
public float getHeight(Vec3 position){ public float getHeight(Vec3 position){
return 0; return 0;
@@ -352,7 +357,7 @@ public class PlanetDialog extends FloatingDialog{
public Color getColor(Vec3 position){ public Color getColor(Vec3 position){
return outlineColor; return outlineColor;
} }
}, outlineRad, true); }, size, true, outlineRad, 0.2f);
} }
return outlines[size]; return outlines[size];
} }

View File

@@ -1,3 +1,3 @@
org.gradle.daemon=true org.gradle.daemon=true
org.gradle.jvmargs=-Xms256m -Xmx1024m org.gradle.jvmargs=-Xms256m -Xmx1024m
archash=bff072e2d671c74a32b41353125c2aa6ba8c0314 archash=17caa50a75e98000d6e7bd1568a932a087dc09c3