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

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