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

View File

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

View File

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

View File

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

View File

@ -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){

View File

@ -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){

View File

@ -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{

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.*;
/** Defines color and height for a planet mesh. */
public interface PlanetMesher{
public interface HexMesher{
float getHeight(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;
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();
}
}

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;
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){

View File

@ -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<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. */
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<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);
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. */

View File

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

View File

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

View File

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