fix (with bad hacks!) some lighting issues. rewrite some comments

One of these fixes unfortunately removes the "free" smooth lighting from
before, but that was actually mostly the result of a side-effect from a
bug in the way we checked for light-source tiles. And it wasn't really
fully correct smooth lighting anyway.
This commit is contained in:
Gered 2013-07-21 13:13:37 -04:00
parent 1a18049b27
commit 0482491c2a

View file

@ -4,6 +4,7 @@ import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g3d.utils.MeshBuilder;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.math.collision.BoundingBox;
import com.blarg.gdx.graphics.Vertices;
import com.blarg.gdx.tilemap3d.ChunkVertexGenerator;
import com.blarg.gdx.tilemap3d.Tile;
@ -13,8 +14,8 @@ import com.blarg.gdx.tilemap3d.tilemesh.TileMesh;
public class LitChunkVertexGenerator extends ChunkVertexGenerator {
final Vector3 tmpOffset = new Vector3();
final Vector3 tmpLightSource = new Vector3();
final Color finalLightingColor = new Color();
final Vector3 tmpLightSourcePos = new Vector3();
final BoundingBox tmpBounds = new BoundingBox();
@Override
protected void addMesh(MeshBuilder builder, Tile tile, TileMesh sourceMesh, TileChunk chunk, TileCoord position, Matrix4 transform, Color color, int firstVertex, int numVertices) {
@ -28,6 +29,8 @@ public class LitChunkVertexGenerator extends ChunkVertexGenerator {
tmpOffset.y += (float)position.y;
tmpOffset.z += (float)position.z;
chunk.getBoundingBoxFor(position.x, position.y, position.z, tmpBounds);
// figure out what the default lighting value is for this chunk
byte defaultLightValue = chunk.tileMap.skyLightValue;
if (chunk.tileMap.ambientLightValue > defaultLightValue)
@ -49,46 +52,85 @@ public class LitChunkVertexGenerator extends ChunkVertexGenerator {
// translate vertex into "world/tilemap space"
vertex.position.add(tmpOffset);
// the color we set to the chunk mesh determines the brightness (in other words: it is the lighting value)
// now we need to find the appropriate light source for this vertex. this light source could be either
// this very tile itself, or an adjacent tile. we'll check using the vertex normal as a direction to
// "look in" for the light source ...
// use the tile that's adjacent to this one in the direction that
// this vertex's normal is pointing as the light source
tmpLightSource.set(vertex.position).add(vertex.normal);
// get the exact "world/tilemap space" position to grab a potential light source tile from
tmpLightSourcePos.set(vertex.position).add(vertex.normal);
// HACK: if the light source position we just got lies exactly on the max x/y/z boundaries of this tile
// then casting it to int and using it as a tilemap grid coord to get the tile will fetch us the
// _next_ tile which we probably don't want when the position is exactly at max x/y/z ...
// (this was resulting in some incorrect dark edges along certain tile layouts)
if (tmpLightSourcePos.x == tmpBounds.max.x)
tmpLightSourcePos.x -= 0.01f;
if (tmpLightSourcePos.y == tmpBounds.max.y)
tmpLightSourcePos.y -= 0.01f;
if (tmpLightSourcePos.z == tmpBounds.max.z)
tmpLightSourcePos.z -= 0.01f;
// if the light source position is off the bounds of the entire world
// then use the default light value.
// the below call to TileChunk.getWithinSelfOrNeighbour() actually does
// do bounds checking, but we would need to cast from float to int
// first. this causes some issues when the one or more of the
// lightSource x/y/z values are between 0 and -1 (rounds up to 0 when
// using a cast). rather then do some weird custom rounding, we just
// check for negatives to ensure we catch them and handle it properly
// NOTE: this is only a problem currently because world coords are
// always >= 0. this will need to be adjusted if that changes
float brightness;
if (tmpLightSource.x < 0.0f || tmpLightSource.y < 0.0f || tmpLightSource.z < 0.0f)
// if the light source position is off the bounds of the entire world then use the default light value.
// the below call to TileChunk.getWithinSelfOrNeighbour() actually does do bounds checking, but we would
// need to cast from float to int first. this causes some issues when the one or more of the lightSource
// x/y/z values are between 0 and -1 (rounds up to 0 when using a cast). rather then do some weird custom
// rounding, we just check for negatives to ensure we catch them and handle it properly
// NOTE: this is _only_ a problem currently because world coords right now are always >= 0
if (tmpLightSourcePos.x < 0.0f || tmpLightSourcePos.y < 0.0f || tmpLightSourcePos.z < 0.0f)
brightness = Tile.getBrightness(defaultLightValue);
else {
// light source is within the boundaries of the world, get the
// actual tile (may or may not be in a neighbouring chunk)
int lightX = (int)tmpLightSource.x - chunk.getMinX();
int lightY = (int)tmpLightSource.y - chunk.getMinY();
int lightZ = (int)tmpLightSource.z - chunk.getMinZ();
int lightX = (int)tmpLightSourcePos.x - chunk.getMinX();
int lightY = (int)tmpLightSourcePos.y - chunk.getMinY();
int lightZ = (int)tmpLightSourcePos.z - chunk.getMinZ();
Tile lightSourceTile = chunk.getWithinSelfOrNeighbourSafe(lightX, lightY, lightZ);
if (lightSourceTile == null)
// out of bounds of the map
brightness = Tile.getBrightness(defaultLightValue);
else
else if (lightSourceTile.isEmptySpace())
// this tile is getting it's light from another tile that is empty
// just use the other tile's light value as-is
brightness = lightSourceTile.getBrightness();
else {
// this tile is getting it's light from another tile that is not empty
// check if the direction we went in to find the other tile passes through any
// of the other tile's opaque sides. if so, we cannot use its light value and
// should instead just use whatever this tile's light value is
// TODO: i'm pretty sure this is going to produce poor results at some point in the future... fix!
TileMesh lightSourceTileMesh = chunk.tileMap.tileMeshes.get(lightSourceTile);
// collect a list of the sides to check for opaqueness with the light source tile .. we check
// the sides of the light source mesh opposite to the direction of the vertex normal (direction we
// are "moving" in)
// TODO: is it better to check each side individually? how would that work if the normal moves us
// in a non-orthogonal direction and we need to check 2 sides ... ?
byte sides = 0;
if (vertex.normal.y < 0.0f) sides |= TileMesh.SIDE_TOP;
if (vertex.normal.y > 0.0f) sides |= TileMesh.SIDE_BOTTOM;
if (vertex.normal.x < 0.0f) sides |= TileMesh.SIDE_RIGHT;
if (vertex.normal.x > 0.0f) sides |= TileMesh.SIDE_LEFT;
if (vertex.normal.z < 0.0f) sides |= TileMesh.SIDE_BACK;
if (vertex.normal.z > 0.0f) sides |= TileMesh.SIDE_FRONT;
if (lightSourceTileMesh.isOpaque(sides))
brightness = tile.tileLight;
else
brightness = lightSourceTile.getBrightness();
}
}
finalLightingColor.set(
// TODO: need to play with vertex/mesh color combinations a bit more to see if this is really correct
vertex.color.set(
vertex.color.r * color.r * brightness,
vertex.color.g * color.g * brightness,
vertex.color.b * color.b * brightness,
vertex.color.a * color.a
);
vertex.color.set(finalLightingColor);
builder.vertex(vertex);
sourceVertices.moveNext();