Merge branch 'render_test' into 'main'

Merge the Render test branch with improvements

See merge request jeseibel/distant-horizons-core!4
This commit is contained in:
TomTheFurry
2022-02-17 14:20:12 +00:00
21 changed files with 975 additions and 1655 deletions
@@ -19,148 +19,46 @@
package com.seibel.lod.core.builders.bufferBuilding;
import java.util.Map;
import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.enums.rendering.DebugMode;
import com.seibel.lod.core.objects.VertexOptimizer;
import com.seibel.lod.core.objects.opengl.LodBufferBuilder;
import com.seibel.lod.core.util.ColorUtil;
import com.seibel.lod.core.objects.opengl.LodBox;
import com.seibel.lod.core.objects.opengl.LodQuadBuilder;
import com.seibel.lod.core.util.DataPointUtil;
import com.seibel.lod.core.util.LevelPosUtil;
import com.seibel.lod.core.util.LodUtil;
import static com.seibel.lod.core.builders.lodBuilding.LodBuilder.MIN_WORLD_HEIGHT;
/**
* Builds LODs as rectangular prisms.
*
* @author James Seibel
* @version 12-8-2021
*/
public class CubicLodTemplate
{
public static void addLodToBuffer(LodBufferBuilder buffer, int playerX, int playerZ, long data, Map<LodDirection, long[]> adjData,
byte detailLevel, int posX, int posZ, VertexOptimizer vertexOptimizer, DebugMode debugging, boolean[] adjShadeDisabled
)//, int cullingRangeX, int cullingRangeZ)
{
if (vertexOptimizer == null)
public class CubicLodTemplate {
public static void addLodToBuffer(long data, long topData, long botData, long[][] adjData, byte detailLevel,
int offsetPosX, int offsetOosZ, LodQuadBuilder quadBuilder, DebugMode debugging) {
short width = (short) (1 << detailLevel);
short x = (short) LevelPosUtil.convert(detailLevel, offsetPosX, LodUtil.BLOCK_DETAIL_LEVEL);
short y = DataPointUtil.getDepth(data);
short z = (short) LevelPosUtil.convert(detailLevel, offsetOosZ, LodUtil.BLOCK_DETAIL_LEVEL);
short dy = (short) (DataPointUtil.getHeight(data) - y);
if (dy == 0)
return;
// equivalent to 2^detailLevel
int blockWidth = 1 << detailLevel;
int color;
if (debugging != DebugMode.OFF && debugging != DebugMode.SHOW_WIREFRAME)
{
if (debugging != DebugMode.OFF && debugging != DebugMode.SHOW_WIREFRAME) {
if (debugging == DebugMode.SHOW_DETAIL || debugging == DebugMode.SHOW_DETAIL_WIREFRAME)
color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[detailLevel].getRGB();
else ///if (debugging == DebugMode.SHOW_GENMODE || debugging == DebugMode.SHOW_GENMODE_WIREFRAME)
else /// if (debugging == DebugMode.SHOW_GENMODE || debugging ==
/// DebugMode.SHOW_GENMODE_WIREFRAME)
color = LodUtil.DEBUG_DETAIL_LEVEL_COLORS[DataPointUtil.getGenerationMode(data)].getRGB();
}
else
} else
color = DataPointUtil.getColor(data);
generateBoundingBox(
vertexOptimizer,
DataPointUtil.getHeight(data),
DataPointUtil.getDepth(data),
blockWidth,
posX * blockWidth, 0, posZ * blockWidth, // x, y, z offset
playerX,
playerZ,
adjData,
color,
DataPointUtil.getLightSky(data),
DataPointUtil.getLightBlock(data),
adjShadeDisabled);
addBoundingBoxToBuffer(buffer, vertexOptimizer);//, cullingRangeX, cullingRangeZ);
LodBox.addBoxQuadsToBuilder(quadBuilder, // buffer
width, dy, width, // setWidth
x, y, z, // setOffset
color, // setColor
DataPointUtil.getLightSky(data), DataPointUtil.getLightBlock(data), // setLights
topData, botData, adjData); // setAdjData
}
/** add the given position and color to the buffer */
public static void addPosAndColor(LodBufferBuilder buffer,
float x, float y, float z,
int color, byte skyLightValue, byte blockLightValue)
{
// TODO transparency re-add by replacing the color 255 with "ColorUtil.getAlpha(color)"
buffer.position(x, y, z)
.color(ColorUtil.getRed(color), ColorUtil.getGreen(color), ColorUtil.getBlue(color), 255)
.minecraftLightValue(skyLightValue).minecraftLightValue(blockLightValue)
.endVertex();
}
private static void generateBoundingBox(VertexOptimizer vertexOptimizer,
int height, int depth, int width,
double xOffset, double yOffset, double zOffset,
int playerX, int playerZ,
Map<LodDirection, long[]> adjData,
int color, byte skyLight, byte blockLight,
boolean[] adjShadeDisabled)
{
// don't add an LOD if it is empty
if (height == -1 && depth == -1)
return;
if (depth == height)
// if the top and bottom points are at the same height
// render this LOD as 1 block thick
height++;
// offset the AABB by its x/z position in the world since
// it uses doubles to specify its location, unlike the model view matrix
// which only uses floats
double x = -playerX;
double z = -playerZ;
vertexOptimizer.reset();
vertexOptimizer.setColor(color, adjShadeDisabled);
vertexOptimizer.setLights(skyLight, blockLight);
vertexOptimizer.setWidth(width, height - depth, width);
vertexOptimizer.setOffset((int) (xOffset + x), (int) (depth + yOffset), (int) (zOffset + z));
vertexOptimizer.setAdjData(adjData);
}
private static void addBoundingBoxToBuffer(LodBufferBuilder buffer, VertexOptimizer vertexOptimizer)//, int cullingRangeX, int cullingRangeZ)
{
int color;
byte skyLight;
byte blockLight;
for (LodDirection lodDirection : VertexOptimizer.DIRECTIONS)
{
//if(vertexOptimizer.isCulled(lodDirection))
// continue;
// culling
// FIXME: Reimpl backface culling
/*
if (lodDirection == LodDirection.NORTH && vertexOptimizer.getZ(lodDirection, 0) < -cullingRangeZ
|| lodDirection == LodDirection.EAST && vertexOptimizer.getX(lodDirection, 0) > cullingRangeX
|| lodDirection == LodDirection.SOUTH && vertexOptimizer.getZ(lodDirection, 0) > cullingRangeZ
|| lodDirection == LodDirection.WEST && vertexOptimizer.getX(lodDirection, 0) < -cullingRangeX)
continue;
*/
int verticalFaceIndex = 0;
while (vertexOptimizer.shouldRenderFace(lodDirection, verticalFaceIndex))
{
for (int vertexIndex = 0; vertexIndex < 6; vertexIndex++)
{
skyLight = vertexOptimizer.getSkyLight(lodDirection, verticalFaceIndex);
blockLight = (byte) vertexOptimizer.getBlockLight();
color = vertexOptimizer.getColor(lodDirection);
addPosAndColor(buffer,
vertexOptimizer.getX(lodDirection, vertexIndex),
vertexOptimizer.getY(lodDirection, vertexIndex, verticalFaceIndex) + MIN_WORLD_HEIGHT,
vertexOptimizer.getZ(lodDirection, vertexIndex),
color, skyLight, blockLight );
}
verticalFaceIndex++;
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -23,7 +23,20 @@ public enum LodDirection
SOUTH(3, 2, 0, "south", LodDirection.AxisDirection.POSITIVE, LodDirection.Axis.Z, new Vec3i(0, 0, 1)),
WEST(4, 5, 1, "west", LodDirection.AxisDirection.NEGATIVE, LodDirection.Axis.X, new Vec3i(-1, 0, 0)),
EAST(5, 4, 3, "east", LodDirection.AxisDirection.POSITIVE, LodDirection.Axis.X, new Vec3i(1, 0, 0));
public static final LodDirection[] DIRECTIONS = new LodDirection[] {
LodDirection.UP,
LodDirection.DOWN,
LodDirection.WEST,
LodDirection.EAST,
LodDirection.NORTH,
LodDirection.SOUTH };
/** North, South, East, West */
public static final LodDirection[] ADJ_DIRECTIONS = new LodDirection[] {
LodDirection.EAST,
LodDirection.WEST,
LodDirection.SOUTH,
LodDirection.NORTH };
// private final int data3d;
// private final int oppositeIndex;
// private final int data2d;
@@ -1,591 +0,0 @@
/*
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.core.objects;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.enums.rendering.DebugMode;
import com.seibel.lod.core.objects.math.Vec3i;
import com.seibel.lod.core.util.ColorUtil;
import com.seibel.lod.core.util.DataPointUtil;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.util.SingletonHandler;
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
/**
* This class handles all the vertex optimization that's needed for a column of lods. W
* @author Leonardo Amato
* @version 10-2-2021
*/
public class VertexOptimizer
{
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
public static final int ADJACENT_HEIGHT_INDEX = 0;
public static final int ADJACENT_DEPTH_INDEX = 1;
public static final int X = 0;
public static final int Y = 1;
public static final int Z = 2;
public static final int MIN = 0;
public static final int MAX = 1;
public static final int VOID_FACE = 0;
/** The six cardinal directions */
public static final LodDirection[] DIRECTIONS = new LodDirection[] {
LodDirection.UP,
LodDirection.DOWN,
LodDirection.WEST,
LodDirection.EAST,
LodDirection.NORTH,
LodDirection.SOUTH };
/** North, South, East, West */
public static final LodDirection[] ADJ_DIRECTIONS = new LodDirection[] {
LodDirection.EAST,
LodDirection.WEST,
LodDirection.SOUTH,
LodDirection.NORTH };
/** All the faces and vertices of a cube. This is used to extract the vertex from the column */
@SuppressWarnings("serial")
public static final Map<LodDirection, int[][]> DIRECTION_VERTEX_MAP = new HashMap<LodDirection, int[][]>()
{{
put(LodDirection.UP, new int[][] {
{ 0, 1, 0 }, // 0
{ 0, 1, 1 }, // 1
{ 1, 1, 1 }, // 2
{ 0, 1, 0 }, // 0
{ 1, 1, 1 }, // 2
{ 1, 1, 0 } // 3
});
put(LodDirection.DOWN, new int[][] {
{ 1, 0, 0 }, // 0
{ 1, 0, 1 }, // 1
{ 0, 0, 1 }, // 2
{ 1, 0, 0 }, // 0
{ 0, 0, 1 }, // 2
{ 0, 0, 0 } // 3
});
put(LodDirection.EAST, new int[][] {
{ 1, 1, 0 }, // 0
{ 1, 1, 1 }, // 1
{ 1, 0, 1 }, // 2
{ 1, 1, 0 }, // 0
{ 1, 0, 1 }, // 2
{ 1, 0, 0 } }); // 3
put(LodDirection.WEST, new int[][] {
{ 0, 0, 0 }, // 0
{ 0, 0, 1 }, // 1
{ 0, 1, 1 }, // 2
{ 0, 0, 0 }, // 0
{ 0, 1, 1 }, // 2
{ 0, 1, 0 } // 3
});
put(LodDirection.SOUTH, new int[][] {
{ 1, 0, 1 }, // 0
{ 1, 1, 1 }, // 1
{ 0, 1, 1 }, // 2
{ 1, 0, 1 }, // 0
{ 0, 1, 1 }, // 2
{ 0, 0, 1 } // 3
});
put(LodDirection.NORTH, new int[][] {
{ 0, 0, 0 }, // 0
{ 0, 1, 0 }, // 1
{ 1, 1, 0 }, // 2
{ 0, 0, 0 }, // 0
{ 1, 1, 0 }, // 2
{ 1, 0, 0 } // 3
});
}};
/**
* This indicates which position is invariable in the DIRECTION_VERTEX_MAP.
* Is used to extract the vertex
*/
@SuppressWarnings("serial")
public static final Map<LodDirection, int[]> FACE_DIRECTION = new HashMap<LodDirection, int[]>()
{{
put(LodDirection.UP, new int[] { Y, MAX });
put(LodDirection.DOWN, new int[] { Y, MIN });
put(LodDirection.EAST, new int[] { X, MAX });
put(LodDirection.WEST, new int[] { X, MIN });
put(LodDirection.SOUTH, new int[] { Z, MAX });
put(LodDirection.NORTH, new int[] { Z, MIN });
}};
/**
* This is a map from Direction to the relative normal vector
* we are using this since I'm not sure if the getNormal create new object at every call
*/
// FIXME: No. It doesn't. Just remove this.
@SuppressWarnings("serial")
public static final Map<LodDirection, Vec3i> DIRECTION_NORMAL_MAP = new HashMap<LodDirection, Vec3i>()
{{
put(LodDirection.UP, LodDirection.UP.getNormal());
put(LodDirection.DOWN, LodDirection.DOWN.getNormal());
put(LodDirection.EAST, LodDirection.EAST.getNormal());
put(LodDirection.WEST, LodDirection.WEST.getNormal());
put(LodDirection.SOUTH, LodDirection.SOUTH.getNormal());
put(LodDirection.NORTH, LodDirection.NORTH.getNormal());
}};
/** We use this index for all array that are going to */
@SuppressWarnings("serial")
public static final Map<LodDirection, Integer> DIRECTION_INDEX = new HashMap<LodDirection, Integer>()
{{
put(LodDirection.UP, 0);
put(LodDirection.DOWN, 1);
put(LodDirection.EAST, 2);
put(LodDirection.WEST, 3);
put(LodDirection.SOUTH, 4);
put(LodDirection.NORTH, 5);
}};
@SuppressWarnings("serial")
public static final Map<LodDirection, Integer> ADJ_DIRECTION_INDEX = new HashMap<LodDirection, Integer>()
{{
put(LodDirection.EAST, 0);
put(LodDirection.WEST, 1);
put(LodDirection.SOUTH, 2);
put(LodDirection.NORTH, 3);
}};
/** holds the box's x, y, z offset */
public final int[] boxOffset;
/** holds the box's x, y, z width */
public final int[] boxWidth;
/** Holds each direction's color */
public final int[] colorMap;
/** The original color (before shading) of this box */
public int color;
/**
*
*/
public final Map<LodDirection, int[]> adjHeight;
public final Map<LodDirection, int[]> adjDepth;
public final Map<LodDirection, byte[]> skyLights;
public byte blockLight;
boolean skipTop;
boolean skipBot;
/** creates an empty box */
@SuppressWarnings("serial")
public VertexOptimizer()
{
boxOffset = new int[3];
boxWidth = new int[3];
colorMap = new int[6];
skyLights = new HashMap<LodDirection, byte[]>()
{{
put(LodDirection.UP, new byte[1]);
put(LodDirection.DOWN, new byte[1]);
put(LodDirection.EAST, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
put(LodDirection.WEST, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
put(LodDirection.SOUTH, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
put(LodDirection.NORTH, new byte[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
}};
adjHeight = new HashMap<LodDirection, int[]>()
{{
put(LodDirection.EAST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
put(LodDirection.WEST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
put(LodDirection.SOUTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
put(LodDirection.NORTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
}};
adjDepth = new HashMap<LodDirection, int[]>()
{{
put(LodDirection.EAST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
put(LodDirection.WEST, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
put(LodDirection.SOUTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
put(LodDirection.NORTH, new int[LodUtil.MAX_NUMBER_OF_VERTICAL_LODS]);
}};
}
/** Set the light of the columns */
public void setLights(int skyLight, int blockLight)
{
this.blockLight = (byte) blockLight;
skyLights.get(LodDirection.UP)[0] = (byte) skyLight;
}
/**
* Set the color of the columns
* @param color color to add
* @param adjShadeDisabled this array indicates which face does not need shading
*/
public void setColor(int color, boolean[] adjShadeDisabled)
{
this.color = color;
for (LodDirection lodDirection : DIRECTIONS)
{
if (!adjShadeDisabled[DIRECTION_INDEX.get(lodDirection)])
colorMap[DIRECTION_INDEX.get(lodDirection)] = ColorUtil.applyShade(color, MC.getShade(lodDirection));
else
colorMap[DIRECTION_INDEX.get(lodDirection)] = color;
}
}
/**
* @param lodDirection of the face of which we want to get the color
* @return color of the face
*/
public int getColor(LodDirection lodDirection)
{
if (CONFIG.client().advanced().debugging().getDebugMode() != DebugMode.SHOW_DETAIL &&
CONFIG.client().advanced().debugging().getDebugMode() != DebugMode.SHOW_WIREFRAME)
return colorMap[DIRECTION_INDEX.get(lodDirection)];
else
return ColorUtil.applyShade(color, MC.getShade(lodDirection));
}
/**
*/
public byte getSkyLight(LodDirection lodDirection, int verticalIndex)
{
if(lodDirection == LodDirection.UP || lodDirection == LodDirection.DOWN)
return skyLights.get(lodDirection)[0];
else
return skyLights.get(lodDirection)[verticalIndex];
}
/**
*/
public int getBlockLight()
{
return blockLight;
}
/** clears this box, resetting everything to default values */
public void reset()
{
Arrays.fill(boxWidth, 0);
Arrays.fill(boxOffset, 0);
Arrays.fill(colorMap, 0);
blockLight = 0;
for (LodDirection lodDirection : ADJ_DIRECTIONS)
{
for (int i = 0; i < adjHeight.get(lodDirection).length; i++)
{
adjHeight.get(lodDirection)[i] = VOID_FACE;
adjDepth.get(lodDirection)[i] = VOID_FACE;
skyLights.get(lodDirection)[i] = 0;
}
}
}
/**
* This method create all the shared face culling based on the adjacent data
* @param adjData data adjacent to the column we are going to render
*/
public void setAdjData(Map<LodDirection, long[]> adjData)
{
int height;
int depth;
int minY = getMinY();
int maxY = getMaxY();
long singleAdjDataPoint;
// TODO transparency uncomment final condition to see ocean floor
//Up direction case
singleAdjDataPoint = adjData.get(LodDirection.UP)[0];
skipTop = DataPointUtil.doesItExist(singleAdjDataPoint) && DataPointUtil.getDepth(singleAdjDataPoint) == maxY;// && DataPointUtil.getAlpha(singleAdjDataPoint) == 255;
//Down direction case
singleAdjDataPoint = adjData.get(LodDirection.DOWN)[0];
skipBot = DataPointUtil.doesItExist(singleAdjDataPoint) && DataPointUtil.getHeight(singleAdjDataPoint) == minY;// && DataPointUtil.getAlpha(singleAdjDataPoint) == 255;
if(DataPointUtil.doesItExist(singleAdjDataPoint))
skyLights.get(LodDirection.DOWN)[0] = DataPointUtil.getLightSky(singleAdjDataPoint);
else
skyLights.get(LodDirection.DOWN)[0] = skyLights.get(LodDirection.UP)[0];
//other sided
//TODO clean some similar cases
for (LodDirection lodDirection : ADJ_DIRECTIONS)
{
long[] dataPoint = adjData.get(lodDirection);
if (dataPoint == null || DataPointUtil.isVoid(dataPoint[0]))
{
adjHeight.get(lodDirection)[0] = maxY;
adjDepth.get(lodDirection)[0] = minY;
adjHeight.get(lodDirection)[1] = VOID_FACE;
adjDepth.get(lodDirection)[1] = VOID_FACE;
skyLights.get(lodDirection)[0] = 15; //in void set full skylight
continue;
}
int i;
int faceToDraw = 0;
boolean firstFace = true;
boolean toFinish = false;
int toFinishIndex = 0;
boolean allAbove = true;
// TODO transparency ocean floor fix
//boolean isOpaque = ((colorMap[0] >> 24) & 0xFF) == 255;
for (i = 0; i < dataPoint.length; i++)
{
singleAdjDataPoint = dataPoint[i];
if (DataPointUtil.isVoid(singleAdjDataPoint) || !DataPointUtil.doesItExist(singleAdjDataPoint))
break;
// TODO transparency ocean floor fix
//if (isOpaque && DataPointUtil.getAlpha(singleAdjDataPoint) != 255)
// continue;
height = DataPointUtil.getHeight(singleAdjDataPoint);
depth = DataPointUtil.getDepth(singleAdjDataPoint);
if (depth < maxY)
{
allAbove = false;
if (height < minY)
{
// the adj data is lower than the current data
if (firstFace)
{
adjHeight.get(lodDirection)[0] = getMaxY();
adjDepth.get(lodDirection)[0] = getMinY();
skyLights.get(lodDirection)[0] = DataPointUtil.getLightSky(singleAdjDataPoint); //skyLights.get(Direction.UP)[0];
}
else
{
adjDepth.get(lodDirection)[faceToDraw] = getMinY();
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSky(singleAdjDataPoint);
}
faceToDraw++;
toFinish = false;
// break since all the other data will be lower
break;
}
else if (depth <= minY)
{
if (height >= maxY)
{
// the adj data is inside the current data
// don't draw the face
adjHeight.get(lodDirection)[0] = VOID_FACE;
adjDepth.get(lodDirection)[0] = VOID_FACE;
}
else // height < maxY
{
// the adj data intersects the lower part of the current data
// if this is the only face, use the maxY and break,
// if there was another face we finish the last one and break
if (firstFace)
{
adjHeight.get(lodDirection)[0] = getMaxY();
adjDepth.get(lodDirection)[0] = height;
skyLights.get(lodDirection)[0] = DataPointUtil.getLightSky(singleAdjDataPoint); //skyLights.get(Direction.UP)[0];
}
else
{
adjDepth.get(lodDirection)[faceToDraw] = height;
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSky(singleAdjDataPoint);
}
toFinish = false;
faceToDraw++;
}
break;
}
else if (height >= maxY)// && depth > minY
{
// the adj data intersects the higher part of the current data
// we start the creation of a new face
adjHeight.get(lodDirection)[faceToDraw] = depth;
//skyLights.get(direction)[faceToDraw] = (byte) DataPointUtil.getLightSkyAlt(singleAdjDataPoint);
firstFace = false;
toFinish = true;
toFinishIndex = i + 1;
}
else
{
// if (depth > minY && height < maxY)
// the adj data is contained in the current data
if (firstFace)
{
adjHeight.get(lodDirection)[0] = getMaxY();
}
adjDepth.get(lodDirection)[faceToDraw] = height;
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSky(singleAdjDataPoint);
faceToDraw++;
adjHeight.get(lodDirection)[faceToDraw] = depth;
firstFace = false;
toFinish = true;
toFinishIndex = i + 1;
}
}
}
if (allAbove)
{
adjHeight.get(lodDirection)[0] = getMaxY();
adjDepth.get(lodDirection)[0] = getMinY();
skyLights.get(lodDirection)[0] = skyLights.get(LodDirection.UP)[0];
faceToDraw++;
}
else if (toFinish)
{
adjDepth.get(lodDirection)[faceToDraw] = minY;
if(toFinishIndex < dataPoint.length)
{
singleAdjDataPoint = dataPoint[toFinishIndex];
if (DataPointUtil.doesItExist(singleAdjDataPoint))
skyLights.get(lodDirection)[faceToDraw] = DataPointUtil.getLightSky(singleAdjDataPoint);
else
skyLights.get(lodDirection)[faceToDraw] = skyLights.get(LodDirection.UP)[0];
}
faceToDraw++;
}
adjHeight.get(lodDirection)[faceToDraw] = VOID_FACE;
adjDepth.get(lodDirection)[faceToDraw] = VOID_FACE;
}
}
/** We use this method to set the various width of the column */
public void setWidth(int xWidth, int yWidth, int zWidth)
{
boxWidth[X] = xWidth;
boxWidth[Y] = yWidth;
boxWidth[Z] = zWidth;
}
/** We use this method to set the various offset of the column */
public void setOffset(int xOffset, int yOffset, int zOffset)
{
boxOffset[X] = xOffset;
boxOffset[Y] = yOffset;
boxOffset[Z] = zOffset;
}
/** returns true if the given direction should be rendered. */
public boolean shouldRenderFace(LodDirection lodDirection, int adjIndex)
{
if (lodDirection == LodDirection.UP)
return adjIndex == 0 && !skipTop;
if (lodDirection == LodDirection.DOWN)
return adjIndex == 0 && !skipBot;
return !(adjHeight.get(lodDirection)[adjIndex] == VOID_FACE && adjDepth.get(lodDirection)[adjIndex] == VOID_FACE);
}
/**
* @param lodDirection direction of the face we want to render
* @param vertexIndex index of the vertex of the face (0-1-2-3)
* @return position x of the relative vertex
*/
public int getX(LodDirection lodDirection, int vertexIndex)
{
return boxOffset[X] + boxWidth[X] * DIRECTION_VERTEX_MAP.get(lodDirection)[vertexIndex][X];
}
/**
* @param lodDirection direction of the face we want to render
* @param vertexIndex index of the vertex of the face (0-1-2-3)
* @return position y of the relative vertex
*/
public int getY(LodDirection lodDirection, int vertexIndex)
{
return boxOffset[Y] + boxWidth[Y] * DIRECTION_VERTEX_MAP.get(lodDirection)[vertexIndex][Y];
}
/**
* @param lodDirection direction of the face we want to render
* @param vertexIndex index of the vertex of the face (0-1-2-3)
* @param adjIndex, index of the n-th culled face of this direction
* @return position x of the relative vertex
*/
public int getY(LodDirection lodDirection, int vertexIndex, int adjIndex)
{
if (lodDirection == LodDirection.DOWN || lodDirection == LodDirection.UP)
return boxOffset[Y] + boxWidth[Y] * DIRECTION_VERTEX_MAP.get(lodDirection)[vertexIndex][Y];
else
{
// this could probably be cleaned up more,
// but it still works
if (1 - DIRECTION_VERTEX_MAP.get(lodDirection)[vertexIndex][Y] == ADJACENT_HEIGHT_INDEX)
return adjHeight.get(lodDirection)[adjIndex];
else
return adjDepth.get(lodDirection)[adjIndex];
}
}
/**
* @param lodDirection direction of the face we want to render
* @param vertexIndex index of the vertex of the face (0-1-2-3)
* @return position z of the relative vertex
*/
public int getZ(LodDirection lodDirection, int vertexIndex)
{
return boxOffset[Z] + boxWidth[Z] * DIRECTION_VERTEX_MAP.get(lodDirection)[vertexIndex][Z];
}
public int getMinX()
{
return boxOffset[X];
}
public int getMaxX()
{
return boxOffset[X] + boxWidth[X];
}
public int getMinY()
{
return boxOffset[Y];
}
public int getMaxY()
{
return boxOffset[Y] + boxWidth[Y];
}
public int getMinZ()
{
return boxOffset[Z];
}
public int getMaxZ()
{
return boxOffset[Z] + boxWidth[Z];
}
}
@@ -68,6 +68,14 @@ public interface LevelContainer
* @return the data in long array format
*/
long getData(int posX, int posZ, int index);
/**
* With this you can get data from the level container
* @param posX x position in the detail level
* @param posZ z position in the detail level
* @return the data in long array format
*/
long[] getAllData(int posX, int posZ);
/**
* With this you can get data from the level container
@@ -550,6 +550,25 @@ public class LodDimension
return region.getData(detailLevel, posX, posZ, verticalIndex);
}
/**
* Get the data point at the given X and Z coordinates
* in this dimension.
* <br>
* Returns null if the LodChunk doesn't exist or
* is outside the loaded area.
*/
public long[] getAllData(byte detailLevel, int posX, int posZ)
{
if (detailLevel > LodUtil.REGION_DETAIL_LEVEL)
throw new IllegalArgumentException("getLodFromCoordinates given a level of \"" + detailLevel + "\" when \"" + LodUtil.REGION_DETAIL_LEVEL + "\" is the max.");
LodRegion region = getRegion(detailLevel, posX, posZ);
if (region == null)
return null;
return region.getAllData(detailLevel, posX, posZ);
}
/**
* Get the data point at the given X and Z coordinates
@@ -190,6 +190,18 @@ public class LodRegion {
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
return dataContainer[detailLevel].getData(posX, posZ, verticalIndex);
}
/**
* Get the dataPoint at the given relative position.
*
* @return the data at the relative pos and detail level, 0 if the data doesn't
* exist.
*/
public long[] getAllData(byte detailLevel, int posX, int posZ) {
posX = LevelPosUtil.getRegionModule(detailLevel, posX);
posZ = LevelPosUtil.getRegionModule(detailLevel, posZ);
return dataContainer[detailLevel].getAllData(posX, posZ);
}
/**
* Get the dataPoint at the given relative position.
@@ -26,8 +26,6 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import com.seibel.lod.core.dataFormat.*;
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
import com.seibel.lod.core.util.*;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
@@ -188,6 +186,15 @@ public class VerticalLevelContainer implements LevelContainer
return dataContainer[posX * size * verticalSize + posZ * verticalSize];
}
@Override
public long[] getAllData(int posX, int posZ)
{
long[] result = new long[verticalSize];
int index = posX * size * verticalSize + posZ * verticalSize;
System.arraycopy(dataContainer, index, result, 0, verticalSize);
return result;
}
@Override
public int getVerticalSize()
{
@@ -397,6 +397,7 @@ public class Mat4f
/**
* TODO: what kind of translation is this?
* and how is this different from "multiplyTranslationMatrix"?
* Answer: This is faster and direct (but only if this is pure translation matrix without rotate)
*/
public void translate(Vec3f vec)
{
@@ -30,14 +30,14 @@ import com.google.common.collect.ImmutableList;
*/
public class DefaultLodVertexFormats
{
public static final LodVertexFormatElement ELEMENT_POSITION = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.FLOAT, 3, false);
public static final LodVertexFormatElement ELEMENT_POSITION = new LodVertexFormatElement(3, LodVertexFormatElement.DataType.USHORT, 3, false);
public static final LodVertexFormatElement ELEMENT_COLOR = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.UBYTE, 4, false);
public static final LodVertexFormatElement ELEMENT_UV = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.FLOAT, 2, false);
public static final LodVertexFormatElement ELEMENT_LIGHT_MAP_UV = new LodVertexFormatElement(1, LodVertexFormatElement.DataType.SHORT, 2, false);
public static final LodVertexFormatElement ELEMENT_NORMAL = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.BYTE, 3, false);
public static final LodVertexFormatElement ELEMENT_PADDING = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.BYTE, 1, true);
public static final LodVertexFormatElement ELEMENT_BLOCK_LIGHT = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.UBYTE, 1, false);
public static final LodVertexFormatElement ELEMENT_LIGHT = new LodVertexFormatElement(0, LodVertexFormatElement.DataType.UBYTE, 1, false);
public static final LodVertexFormat POSITION = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).build());
@@ -47,5 +47,7 @@ public class DefaultLodVertexFormats
public static final LodVertexFormat POSITION_COLOR_TEX = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).add(ELEMENT_COLOR).add(ELEMENT_UV).build());
public static final LodVertexFormat POSITION_COLOR_TEX_LIGHTMAP = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).add(ELEMENT_COLOR).add(ELEMENT_UV).add(ELEMENT_LIGHT_MAP_UV).build());
public static final LodVertexFormat POSITION_COLOR_BLOCK_LIGHT_SKY_LIGHT = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder().add(ELEMENT_POSITION).add(ELEMENT_COLOR).add(ELEMENT_BLOCK_LIGHT).add(ELEMENT_BLOCK_LIGHT).add(ELEMENT_PADDING).add(ELEMENT_PADDING).build());
public static final LodVertexFormat POSITION_COLOR_BLOCK_LIGHT_SKY_LIGHT = new LodVertexFormat(ImmutableList.<LodVertexFormatElement>builder()
.add(ELEMENT_POSITION).add(ELEMENT_PADDING).add(ELEMENT_LIGHT)
.add(ELEMENT_COLOR).build());
}
@@ -0,0 +1,161 @@
package com.seibel.lod.core.objects.opengl;
import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.util.ColorUtil;
import com.seibel.lod.core.util.DataPointUtil;
import com.seibel.lod.core.util.SingletonHandler;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
public class LodBox {
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
public static void addBoxQuadsToBuilder(LodQuadBuilder builder, short xSize, short ySize, short zSize, short x,
short y, short z, int color, byte skyLight, byte blockLight, long topData, long botData, long[][] adjData) {
short maxX = (short) (x + xSize);
short maxY = (short) (y + ySize);
short maxZ = (short) (z + zSize);
byte skyLightTop = skyLight;
byte skyLightBot = DataPointUtil.doesItExist(botData) ? DataPointUtil.getLightSky(botData) : 0;
// Up direction case
boolean skipTop = DataPointUtil.doesItExist(topData) && DataPointUtil.getDepth(topData) == maxY;// &&
// DataPointUtil.getAlpha(singleAdjDataPoint)
// == 255;
boolean skipBot = DataPointUtil.doesItExist(botData) && DataPointUtil.getHeight(botData) == y;// &&
// DataPointUtil.getAlpha(singleAdjDataPoint)
// == 255;
if (!skipTop)
builder.addQuadUp(x, maxY, z, xSize, zSize, color, skyLightTop, blockLight);
if (!skipBot)
builder.addQuadDown(x, y, z, xSize, zSize, color, skyLightBot, blockLight);
makeAdjQuads(builder, adjData[LodDirection.NORTH.ordinal() - 2], LodDirection.NORTH, x, y, z, xSize, ySize,
color, skyLightTop, blockLight);
makeAdjQuads(builder, adjData[LodDirection.SOUTH.ordinal() - 2], LodDirection.SOUTH, x, y, maxZ, xSize, ySize,
color, skyLightTop, blockLight);
makeAdjQuads(builder, adjData[LodDirection.WEST.ordinal() - 2], LodDirection.WEST, x, y, z, zSize, ySize, color,
skyLightTop, blockLight);
makeAdjQuads(builder, adjData[LodDirection.EAST.ordinal() - 2], LodDirection.EAST, maxX, y, z, zSize, ySize,
color, skyLightTop, blockLight);
}
private static void makeAdjQuads(LodQuadBuilder builder, long[] adjData, LodDirection direction, short x, short y,
short z, short w0, short wy, int color, byte upSkyLight, byte blockLight) {
color = ColorUtil.applyShade(color, MC.getShade(direction));
long[] dataPoint = adjData;
if (dataPoint == null || DataPointUtil.isVoid(dataPoint[0])) {
builder.addQuadAdj(direction, x, y, z, w0, wy, color, (byte) 15, blockLight);
return;
}
int i;
boolean firstFace = true;
boolean allAbove = true;
short nextStartingHeight = -1;
byte nextSkyLight = upSkyLight;
// TODO transparency ocean floor fix
// boolean isOpaque = ((colorMap[0] >> 24) & 0xFF) == 255;
for (i = 0; i < dataPoint.length && DataPointUtil.doesItExist(adjData[i])
&& !DataPointUtil.isVoid(adjData[i]); i++) {
long adjPoint = adjData[i];
// TODO transparency ocean floor fix
// if (isOpaque && DataPointUtil.getAlpha(singleAdjDataPoint) != 255)
// continue;
short height = DataPointUtil.getHeight(adjPoint);
short depth = DataPointUtil.getDepth(adjPoint);
// If the depth of said block is higher then our max Y, continue
// Basically: y < maxY <= _____ height
// _______&&: y < maxY <= depth
if (y + wy <= depth)
continue;
// Now: depth < maxY
allAbove = false;
if (height < y) {
// Basically: _____ height < y < maxY
// _______&&: depth ______ < y < maxY
if (firstFace) {
builder.addQuadAdj(direction, x, y, z, w0, wy, color, DataPointUtil.getLightSky(adjPoint),
blockLight);
} else {
// Now: depth < height < y < previousDepth < maxY
if (nextStartingHeight == -1)
throw new RuntimeException("Loop error");
builder.addQuadAdj(direction, x, y, z, w0, (short) (nextStartingHeight - y), color,
DataPointUtil.getLightSky(adjPoint), blockLight);
nextStartingHeight = -1;
}
break;
}
if (depth <= y) { // AND y <= height
if (y + wy <= height) {
// Basically: ________ y < maxY <= height
// _______&&: depth <= y < maxY
// The face is inside adj face completely. Don't draw.
break;
}
// Otherwise: ________ y <= Height < maxY
// _______&&: depth <= y _________ < maxY
// the adj data intersects the lower part of the current data
// if this is the only face, use the maxY and break,
// if there was another face we finish the last one and break
if (firstFace) {
builder.addQuadAdj(direction, x, height, z, w0, (short) (y + wy - height), color,
DataPointUtil.getLightSky(adjPoint), blockLight);
} else {
// Now: depth <= y <= height < previousDepth < maxY
if (nextStartingHeight == -1)
throw new RuntimeException("Loop error");
builder.addQuadAdj(direction, x, height, z, w0, (short) (nextStartingHeight - height), color,
DataPointUtil.getLightSky(adjPoint), blockLight);
nextStartingHeight = -1;
}
break;
}
// In here always true: y < depth < maxY
// _________________&&: y < _____ (height and maxY)
if (y + wy <= height) {
// Basically: y _______ < maxY <= height
// _______&&: y < depth < maxY
// the adj data intersects the higher part of the current data
// we start the creation of a new face
} else {
// Otherwise: y < _____ height < maxY
// _______&&: y < depth ______ < maxY
if (firstFace) {
builder.addQuadAdj(direction, x, height, z, w0, (short) (y + wy - height), color,
DataPointUtil.getLightSky(adjPoint), blockLight);
} else {
// Now: y < depth < height < previousDepth < maxY
if (nextStartingHeight == -1)
throw new RuntimeException("Loop error");
builder.addQuadAdj(direction, x, height, z, w0, (short) (nextStartingHeight - height), color,
DataPointUtil.getLightSky(adjPoint), blockLight);
nextStartingHeight = -1;
}
}
// set next top as current depth
nextStartingHeight = depth;
firstFace = false;
nextSkyLight = upSkyLight;
if (i + 1 < adjData.length && DataPointUtil.doesItExist(adjData[i + 1]))
nextSkyLight = DataPointUtil.getLightSky(adjData[i + 1]);
}
if (allAbove) {
builder.addQuadAdj(direction, x, y, z, w0, wy, color, upSkyLight, blockLight);
} else if (nextStartingHeight != -1) {
// We need to finish the last quad.
builder.addQuadAdj(direction, x, y, z, w0, (short) (nextStartingHeight - y), color, nextSkyLight,
blockLight);
}
}
}
@@ -1,320 +0,0 @@
/*
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
* licensed under the GNU GPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.lod.core.objects.opengl;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import com.google.common.collect.ImmutableList;
/**
* A (almost) exact copy of Minecraft's
* BufferBuilder object. <br>
* Which allows for creating and filling
* OpenGL buffers.
*
* @author James Seibel
* @version 12-9-2021
*/
public class LodBufferBuilder
{
public ByteBuffer buffer;
private int nextElementByte = 0;
private int vertices;
private LodVertexFormatElement currentElement;
private int elementIndex;
private LodVertexFormat format;
private boolean building;
public LodBufferBuilder(int bufferSizeInBytes)
{
this.buffer = allocateByteBuffer(bufferSizeInBytes);
}
public int getMemUsage() {return buffer.capacity();}
/** originally from MC's GLAllocation class */
private ByteBuffer allocateByteBuffer(int bufferSizeInBytes)
{
return ByteBuffer.allocateDirect(bufferSizeInBytes).order(ByteOrder.nativeOrder());
}
/** make sure the buffer doesn't overflow when inserting new elements */
private void ensureVertexCapacity()
{
this.ensureCapacity(this.format.getByteSize());
}
private void ensureCapacity(int vertexSizeInBytes)
{
if (this.nextElementByte + vertexSizeInBytes > this.buffer.capacity())
{
int i = this.buffer.capacity();
int j = roundUp((int) ((i + vertexSizeInBytes)*2));
//LOGGER.debug("Needed to grow BufferBuilder buffer: Old size {} bytes, new size {} bytes.", i, j);
ByteBuffer bytebuffer = allocateByteBuffer(j);
this.buffer.position(0);
bytebuffer.put(this.buffer);
bytebuffer.rewind();
this.buffer = bytebuffer;
}
}
private void packBuffer() {
int cap = this.buffer.capacity();
int filled = this.format.getByteSize() * this.vertices;
this.buffer.position(0);
this.buffer.limit(filled);
if (cap - filled > 4096) {
ByteBuffer bytebuffer = allocateByteBuffer(filled);
bytebuffer.put(this.buffer);
bytebuffer.rewind();
this.buffer = bytebuffer;
}
}
private static int roundUp(int vertexSizeInBytes)
{
int i = 4096; // 4 KB (1 page)
if (vertexSizeInBytes == 0)
{
return i;
}
else
{
if (vertexSizeInBytes < 0)
{
i *= -1;
}
int j = vertexSizeInBytes % i;
return j == 0 ? vertexSizeInBytes : vertexSizeInBytes + i - j;
}
}
private void switchFormat(LodVertexFormat newFormat)
{
format = newFormat;
}
//========================================//
// methods for actually building a buffer //
//========================================//
/**
* @param openGlLodVertexFormat GL11.GL_QUADS, GL11.GL_TRIANGLES, etc.
* @param LodVertexFormat
*/
public void begin(int openGlLodVertexFormat, LodVertexFormat LodVertexFormat)
{
if (this.building)
{
throw new IllegalStateException("Already building!");
}
else
{
this.building = true;
this.switchFormat(LodVertexFormat);
this.currentElement = LodVertexFormat.getElements().get(0);
this.elementIndex = 0;
this.buffer.clear();
this.vertices = 0;
}
}
public void end()
{
if (!this.building)
{
return;
} else {
this.building = false;
this.currentElement = null;
this.elementIndex = 0;
packBuffer();
}
}
public void putByte(int index, byte newByte)
{
this.buffer.put(this.nextElementByte + index, newByte);
}
public void putShort(int index, short newShort)
{
this.buffer.putShort(this.nextElementByte + index, newShort);
}
public void putFloat(int index, float newFloat)
{
this.buffer.putFloat(this.nextElementByte + index, newFloat);
}
public void endVertex()
{
if (this.elementIndex != 0)
{
throw new IllegalStateException("Not filled all elements of the vertex");
}
else
{
++this.vertices;
this.ensureVertexCapacity();
}
}
public void nextElement()
{
ImmutableList<LodVertexFormatElement> immutablelist = this.format.getElements();
this.elementIndex = (this.elementIndex + 1) % immutablelist.size();
this.nextElementByte += this.currentElement.getByteSize();
this.currentElement = immutablelist.get(this.elementIndex);
if (currentElement.getIsPadding())
{
this.nextElement();
}
// if (this.defaultColorSet && this.currentElement.getUsage() == LodVertexFormatElement.Usage.COLOR)
// {
// color(this.defaultR, this.defaultG, this.defaultB, this.defaultA);
// }
}
public LodBufferBuilder color(int red, int green, int blue, int alpha)
{
LodVertexFormatElement LodVertexFormatelement = this.currentElement();
if (LodVertexFormatelement.getType() != LodVertexFormatElement.DataType.UBYTE)
{
throw new IllegalStateException("Color must be stored as a UBYTE");
}
else
{
this.putByte(0, (byte) red);
this.putByte(1, (byte) green);
this.putByte(2, (byte) blue);
this.putByte(3, (byte) alpha);
this.nextElement();
return this;
}
}
public LodBufferBuilder minecraftLightValue(byte lightValue)
{
LodVertexFormatElement LodVertexFormatelement = this.currentElement();
if (LodVertexFormatelement.getType() != LodVertexFormatElement.DataType.UBYTE)
{
throw new IllegalStateException("Light Color must be stored as a UBYTE");
}
else
{
this.putByte(0, lightValue);
this.nextElement();
return this;
}
}
public LodBufferBuilder position(float x, float y, float z)
{
if (this.currentElement().getType() != LodVertexFormatElement.DataType.FLOAT)
{
throw new IllegalStateException("Position verticies must be stored as a FLOAT");
}
else
{
this.putFloat(0, x);
this.putFloat(4, y);
this.putFloat(8, z);
this.nextElement();
return this;
}
}
public ByteBuffer getCleanedByteBuffer()
{
return this.buffer;
}
public void reset()
{
this.nextElementByte = 0;
this.vertices = 0;
this.buffer.clear();
}
public LodVertexFormatElement currentElement()
{
if (this.currentElement == null)
{
throw new IllegalStateException("BufferBuilder not started");
}
else
{
return this.currentElement;
}
}
public boolean building()
{
return this.building;
}
//==================//
// internal classes //
//==================//
public static final class DrawState
{
private final LodVertexFormat format;
private final int vertexCount;
private final int mode;
private DrawState(LodVertexFormat p_i225905_1_, int p_i225905_2_, int p_i225905_3_)
{
this.format = p_i225905_1_;
this.vertexCount = p_i225905_2_;
this.mode = p_i225905_3_;
}
public LodVertexFormat format()
{
return this.format;
}
public int vertexCount()
{
return this.vertexCount;
}
public int mode()
{
return this.mode;
}
}
public LodVertexFormat getLodVertexFormat()
{
return this.format;
}
}
@@ -0,0 +1,228 @@
package com.seibel.lod.core.objects.opengl;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Iterator;
import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.enums.LodDirection.Axis;
import com.seibel.lod.core.util.ColorUtil;
public class LodQuadBuilder {
static final int MAX_BUFFER_SIZE = (1024 * 1024 * 1);
static final int QUAD_BYTE_SIZE = (12 * 6);
static final int MAX_QUADS_PER_BUFFER = MAX_BUFFER_SIZE / QUAD_BYTE_SIZE;
static class Quad {
final short x;
final short y;
final short z;
final short w0;
final short w1;
final int color;
final byte skylight;
final byte blocklight;
final LodDirection dir;
Quad(short x, short y, short z, short w0, short w1, int color, byte skylight, byte blocklight,
LodDirection dir) {
this.x = x;
this.y = y;
this.z = z;
this.w0 = w0;
this.w1 = w1;
this.color = color;
this.skylight = skylight;
this.blocklight = blocklight;
this.dir = dir;
}
}
final ArrayList<Quad> quads;
public LodQuadBuilder(int initialSize) {
quads = new ArrayList<Quad>(initialSize);
}
public void addQuadAdj(LodDirection dir, short x, short y, short z, short w0, short wy, int color, byte skylight,
byte blocklight) {
if (dir.ordinal() <= LodDirection.DOWN.ordinal())
throw new IllegalArgumentException("addQuadAdj() is only for adj direction! Not UP or Down!");
quads.add(new Quad(x, y, z, w0, wy, color, skylight, blocklight, dir));
}
// XZ
public void addQuadUp(short x, short y, short z, short wx, short wz, int color, byte skylight, byte blocklight) {
quads.add(new Quad(x, y, z, wx, wz, color, skylight, blocklight, LodDirection.UP));
}
public void addQuadDown(short x, short y, short z, short wx, short wz, int color, byte skylight, byte blocklight) {
quads.add(new Quad(x, y, z, wx, wz, color, skylight, blocklight, LodDirection.DOWN));
}
// XY
public void addQuadN(short x, short y, short z, short wx, short wy, int color, byte skylight, byte blocklight) {
quads.add(new Quad(x, y, z, wx, wy, color, skylight, blocklight, LodDirection.NORTH));
}
public void addQuadS(short x, short y, short z, short wx, short wy, int color, byte skylight, byte blocklight) {
quads.add(new Quad(x, y, z, wx, wy, color, skylight, blocklight, LodDirection.SOUTH));
}
// ZY
public void addQuadW(short x, short y, short z, short wz, short wy, int color, byte skylight, byte blocklight) {
quads.add(new Quad(x, y, z, wz, wy, color, skylight, blocklight, LodDirection.WEST));
}
public void addQuadE(short x, short y, short z, short wz, short wy, int color, byte skylight, byte blocklight) {
quads.add(new Quad(x, y, z, wz, wy, color, skylight, blocklight, LodDirection.EAST));
}
private static void putVertex(ByteBuffer bb, short x, short y, short z, int color, byte skylight, byte blocklight) {
skylight %= 16;
blocklight %= 16;
bb.putShort(x);
bb.putShort(y);
bb.putShort(z);
bb.putShort((short) (skylight | (blocklight << 4)));
byte r = (byte) ColorUtil.getRed(color);
byte g = (byte) ColorUtil.getGreen(color);
byte b = (byte) ColorUtil.getBlue(color);
byte a = (byte) ColorUtil.getAlpha(color);
bb.put(r);
bb.put(g);
bb.put(b);
bb.put(a);
}
private static void putQuad(ByteBuffer bb, Quad quad) {
int[][] quadBase = DIRECTION_VERTEX_QUAD[quad.dir.ordinal()];
short d0 = quad.w0;
short d1 = quad.w1;
Axis axis = quad.dir.getAxis();
for (int i = 0; i < quadBase.length; i++) {
short dx, dy, dz;
switch (axis) {
case X: // ZY
dx = 0;
dz = quadBase[i][0] == 1 ? d0 : 0;
dy = quadBase[i][1] == 1 ? d1 : 0;
break;
case Y: // XZ
dy = 0;
dx = quadBase[i][0] == 1 ? d0 : 0;
dz = quadBase[i][1] == 1 ? d1 : 0;
break;
case Z: // XY
dz = 0;
dx = quadBase[i][0] == 1 ? d0 : 0;
dy = quadBase[i][1] == 1 ? d1 : 0;
break;
default:
throw new IllegalArgumentException("Invalid Axis enum: " + axis);
}
putVertex(bb, (short) (quad.x + dx), (short) (quad.y + dy), (short) (quad.z + dz), quad.color,
quad.skylight, quad.blocklight);
}
}
private ByteBuffer writeVertexData(ByteBuffer bb, int quadsStart, int quadsCount) {
if (quadsStart + quadsCount > quads.size())
quadsCount = quads.size() - quadsStart;
bb.clear();
bb.limit(quadsCount * QUAD_BYTE_SIZE);
for (Quad quad : quads.subList(quadsStart, quadsStart + quadsCount)) {
putQuad(bb, quad);
}
if (bb.hasRemaining())
throw new RuntimeException();
bb.rewind();
return bb;
}
public Iterator<ByteBuffer> makeVertexBuffers() {
int numOfBuffers = getCurrentNeededVertexBuffers();
return new Iterator<ByteBuffer>() {
int counter = 0;
ByteBuffer bb = ByteBuffer.allocateDirect(MAX_QUADS_PER_BUFFER * QUAD_BYTE_SIZE)
.order(ByteOrder.nativeOrder());
@Override
public boolean hasNext() {
return counter < numOfBuffers;
}
@Override
public ByteBuffer next() {
if (counter >= numOfBuffers) {
return null;
}
return writeVertexData(bb, MAX_QUADS_PER_BUFFER * counter++, MAX_QUADS_PER_BUFFER);
}
};
}
public int getCurrentNeededVertexBuffers() {
return quads.size() / MAX_QUADS_PER_BUFFER + 1;
}
public static final int[][][] DIRECTION_VERTEX_QUAD = new int[][][] {
// X,Z
{ // UP
{ 1, 0 }, // 0
{ 1, 1 }, // 1
{ 0, 1 }, // 2
{ 1, 0 }, // 0
{ 0, 1 }, // 2
{ 0, 0 }, // 3
}, { // DOWN
{ 0, 0 }, // 0
{ 0, 1 }, // 1
{ 1, 1 }, // 2
{ 0, 0 }, // 0
{ 1, 1 }, // 2
{ 1, 0 }, // 3
},
// X,Y
{ // NORTH
{ 0, 0 }, // 0
{ 0, 1 }, // 1
{ 1, 1 }, // 2
{ 0, 0 }, // 0
{ 1, 1 }, // 2
{ 1, 0 }, // 3
}, { // SOUTH
{ 1, 0 }, // 0
{ 1, 1 }, // 1
{ 0, 1 }, // 2
{ 1, 0 }, // 0
{ 0, 1 }, // 2
{ 0, 0 }, // 3
},
// Z,Y
{ // WEST
{ 0, 0 }, // 0
{ 1, 0 }, // 1
{ 1, 1 }, // 2
{ 0, 0 }, // 0
{ 1, 1 }, // 2
{ 0, 1 }, // 3
}, { // EAST
{ 0, 1 }, // 0
{ 1, 1 }, // 1
{ 1, 0 }, // 2
{ 0, 1 }, // 0
{ 1, 0 }, // 2
{ 0, 0 }, // 3
}, };
}
@@ -0,0 +1,50 @@
package com.seibel.lod.core.objects.opengl;
public class RenderRegion implements AutoCloseable {
LodVertexBuffer[] vbos;
public RenderRegion(int size) {
vbos = new LodVertexBuffer[size];
}
public void resize(int size) {
if (vbos.length != size) {
LodVertexBuffer[] newVbos = new LodVertexBuffer[size];
if (vbos.length > size) {
for (int i=size; i<vbos.length; i++) {
vbos[i].close();
vbos[i] = null;
}
}
for (int i=0; i<newVbos.length && i<vbos.length; i++) {
newVbos[i] = vbos[i];
vbos[i] = null;
}
for (LodVertexBuffer b : vbos) {
if (b != null) throw new RuntimeException("LEAKING VBO!");
}
vbos = newVbos;
}
}
public LodVertexBuffer[] debugGetBuffers() {
return vbos;
}
@Override
public void close() {
}
public LodVertexBuffer getOrMakeVbo(int iIndex, boolean useBuffStorage) {
if (vbos[iIndex] == null) {
vbos[iIndex] = new LodVertexBuffer(useBuffStorage);
} else if (vbos[iIndex].isBufferStorage != useBuffStorage) {
vbos[iIndex].close();
vbos[iIndex] = new LodVertexBuffer(useBuffStorage);
}
return vbos[iIndex];
}
}
@@ -24,7 +24,6 @@ import java.awt.Color;
import com.seibel.lod.core.enums.rendering.FogDistance;
import com.seibel.lod.core.enums.rendering.FogDrawMode;
import com.seibel.lod.core.objects.math.Mat4f;
import com.seibel.lod.core.objects.math.Vec3f;
import com.seibel.lod.core.render.objects.ShaderProgram;
import com.seibel.lod.core.render.objects.VertexAttribute;
import com.seibel.lod.core.render.objects.VertexAttributePostGL43;
@@ -40,11 +39,11 @@ public class LodRenderProgram extends ShaderProgram {
// Attributes
public final int posAttrib;
public final int colAttrib;
public final int lightAttrib; //Sky light then block light
//public final int lightAttrib; //Sky light then block light
// Uniforms
public final int mvmUniform;
public final int projUniform;
public final int cameraUniform;
//public final int cameraUniform;
public final int fogColorUniform;
// public final int skyLightUniform; worldSkyLight is currently not used
public final int lightMapUniform;
@@ -63,11 +62,11 @@ public class LodRenderProgram extends ShaderProgram {
posAttrib = getAttributeLocation("vPosition");
colAttrib = getAttributeLocation("color");
lightAttrib = getAttributeLocation("light");
//lightAttrib = getAttributeLocation("light");
mvmUniform = getUniformLocation("modelViewMatrix");
projUniform = getUniformLocation("projectionMatrix");
cameraUniform = getUniformLocation("cameraPos");
//cameraUniform = getUniformLocation("cameraPos");
fogColorUniform = getUniformLocation("fogColor");
// skyLightUniform = getUniformLocation("worldSkyLight");
lightMapUniform = getUniformLocation("lightMap");
@@ -90,9 +89,11 @@ public class LodRenderProgram extends ShaderProgram {
else
vao = new VertexAttributePreGL43(); // also binds VertexAttribute
//vao.bind();
vao.setVertexAttribute(0, posAttrib, VertexAttribute.VertexPointer.addVec3Pointer(false)); // 4+4+4
// Now a pos+light.
vao.setVertexAttribute(0, posAttrib, VertexAttribute.VertexPointer.addUnsignedShortsPointer(4, false)); // 2+2+2+2
//vao.setVertexAttribute(0, posAttrib, VertexAttribute.VertexPointer.addVec3Pointer(false)); // 4+4+4
vao.setVertexAttribute(0, colAttrib, VertexAttribute.VertexPointer.addUnsignedBytesPointer(4, true)); // +4
vao.setVertexAttribute(0, lightAttrib, VertexAttribute.VertexPointer.addUnsignedBytesPointer(2, false)); // +4 due to how it aligns
//vao.setVertexAttribute(0, lightAttrib, VertexAttribute.VertexPointer.addUnsignedBytesPointer(2, false)); // +4 due to how it aligns
try {
vao.completeAndCheck(vertexByteCount);
} catch (RuntimeException e) {
@@ -126,17 +127,20 @@ public class LodRenderProgram extends ShaderProgram {
vao.unbindBuffersFromAllBindingPoint();
}
public void fillUniformData(Mat4f modelViewMatrix, Mat4f projectionMatrix, Vec3f cameraPos, Color fogColor, int skyLight, int lightmapBindPoint) {
public void fillUniformData(Mat4f projectionMatrix, Color fogColor, int skyLight, int lightmapBindPoint) {
super.bind();
// uniforms
setUniform(mvmUniform, modelViewMatrix);
setUniform(projUniform, projectionMatrix);
setUniform(cameraUniform, cameraPos);
setUniform(fogColorUniform, fogColor);
// setUniform(skyLightUniform, skyLight);
setUniform(lightMapUniform, lightmapBindPoint);
}
public void fillUniformModelMatrix(Mat4f modelViewMatrix) {
super.bind();
setUniform(mvmUniform, modelViewMatrix);
}
public void fillUniformDataForFog(LodFogConfig fogSettings, boolean allFogMode) {
super.bind();
if (allFogMode) {
@@ -29,6 +29,7 @@ import org.lwjgl.opengl.GL32;
import com.seibel.lod.core.api.ApiShared;
import com.seibel.lod.core.api.ClientApi;
import com.seibel.lod.core.builders.bufferBuilding.LodBufferBuilderFactory;
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
import com.seibel.lod.core.enums.rendering.DebugMode;
import com.seibel.lod.core.enums.rendering.FogColorMode;
import com.seibel.lod.core.enums.rendering.FogDistance;
@@ -38,8 +39,10 @@ import com.seibel.lod.core.objects.math.Mat4f;
import com.seibel.lod.core.objects.math.Vec3d;
import com.seibel.lod.core.objects.math.Vec3f;
import com.seibel.lod.core.objects.opengl.LodVertexBuffer;
import com.seibel.lod.core.objects.opengl.RenderRegion;
import com.seibel.lod.core.render.objects.LightmapTexture;
import com.seibel.lod.core.util.DetailDistanceUtil;
import com.seibel.lod.core.util.LevelPosUtil;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.util.MovableGridList;
import com.seibel.lod.core.util.SingletonHandler;
@@ -145,7 +148,7 @@ public class LodRenderer
* @param mcProjectionMatrix
* @param partialTicks how far into the current tick this method was called.
*/
public void drawLODs(LodDimension lodDim, Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks, IProfilerWrapper profiler)
public void drawLODs(LodDimension lodDim, Mat4f baseModelViewMatrix, Mat4f baseProjectionMatrix, float partialTicks, IProfilerWrapper profiler)
{
//=================================//
// determine if LODs should render //
@@ -216,11 +219,11 @@ public class LodRenderer
}
swapBuffer.end("SwapBuffer");
// Get the front buffers to draw
MovableGridList<LodVertexBuffer[]> vbos = lodBufferBuilderFactory.getFrontBuffers();
MovableGridList<RenderRegion> regions = lodBufferBuilderFactory.getFrontBuffers();
int vbosCenterX = lodBufferBuilderFactory.getFrontBuffersCenterX();
int vbosCenterZ = lodBufferBuilderFactory.getFrontBuffersCenterZ();
if (vbos == null) {
if (regions == null) {
// There is no vbos, which means nothing needs to be drawn. So skip rendering
return;
}
@@ -282,7 +285,6 @@ public class LodRenderer
/*---------Get required data--------*/
// Get the matrixs for rendering
Mat4f modelViewMatrix = translateModelViewMatrix(mcModelViewMatrix, partialTicks, vbosCenterX, vbosCenterZ);
int vanillaBlockRenderedDistance = MC_RENDER.getRenderDistance() * LodUtil.CHUNK_WIDTH;
int farPlaneBlockDistance;
// required for setupFog and setupProjectionMatrix
@@ -290,15 +292,16 @@ public class LodRenderer
farPlaneBlockDistance = Math.min(CONFIG.client().graphics().quality().getLodChunkRenderDistance(), LodUtil.CEILED_DIMENSION_MAX_RENDER_DISTANCE) * LodUtil.CHUNK_WIDTH;
else
farPlaneBlockDistance = CONFIG.client().graphics().quality().getLodChunkRenderDistance() * LodUtil.CHUNK_WIDTH;
Mat4f projectionMatrix = createProjectionMatrix(mcProjectionMatrix, vanillaBlockRenderedDistance, farPlaneBlockDistance);
LodFogConfig fogSettings = new LodFogConfig(CONFIG, REFLECTION_HANDLER, farPlaneBlockDistance, vanillaBlockRenderedDistance);
drawCalculateParams.end("drawCalculateParams");
Mat4f projectionMatrix = createProjectionMatrix(baseProjectionMatrix, vanillaBlockRenderedDistance, farPlaneBlockDistance);
/*---------Fill uniform data--------*/
LagSpikeCatcher drawFillData = new LagSpikeCatcher();
// Fill the uniform data. Note: GL33.GL_TEXTURE0 == texture bindpoint 0
shaderProgram.fillUniformData(modelViewMatrix, projectionMatrix, getTranslatedCameraPos(vbosCenterX, vbosCenterZ),
MC_RENDER.isFogStateSpecial() ? getSpecialFogColor(partialTicks) : getFogColor(partialTicks), (int) (MC.getSkyDarken(partialTicks) * 15), 0);
shaderProgram.fillUniformData(projectionMatrix,
MC_RENDER.isFogStateSpecial() ? getSpecialFogColor(partialTicks) : getFogColor(partialTicks),
(int) (MC.getSkyDarken(partialTicks) * 15), 0);
// Previous guy said fog setting may be different from region to region, but the fogSettings never changed... soooooo...
shaderProgram.fillUniformDataForFog(fogSettings, MC_RENDER.isFogStateSpecial());
// Note: Since lightmapTexture is changing every frame, it's faster to recreate it than to reuse the old one.
@@ -317,16 +320,28 @@ public class LodRenderer
// where the center of the buffers is (needed when culling regions)
// render each of the buffers
int lowRegionX = vbos.getCenterX() - vbos.gridCentreToEdge;
int lowRegionZ = vbos.getCenterY() - vbos.gridCentreToEdge;
int lowRegionX = regions.getCenterX() - regions.gridCentreToEdge;
int lowRegionZ = regions.getCenterY() - regions.gridCentreToEdge;
int drawCall = 0;
int vCount0 = 0;
for (int regionX=lowRegionX; regionX<lowRegionX+vbos.gridSize; regionX++) {
for (int regionZ=lowRegionZ; regionZ<lowRegionZ+vbos.gridSize; regionZ++) {
if (vbos.get(regionX, regionZ) == null) continue;
for (int regionX=lowRegionX; regionX<lowRegionX+regions.gridSize; regionX++) {
for (int regionZ=lowRegionZ; regionZ<lowRegionZ+regions.gridSize; regionZ++) {
if (regions.get(regionX, regionZ) == null) continue;
if (cullingDisabled || RenderUtil.isRegionInViewFrustum(MC_RENDER.getCameraBlockPosition(),
MC_RENDER.getLookAtVector(), regionX, regionZ)) {
for (LodVertexBuffer vbo : vbos.get(regionX, regionZ)) {
RenderRegion region = regions.get(regionX, regionZ);
//TODO improve this
Vec3d cameraPos = MC_RENDER.getCameraExactPosition();
Mat4f localModelViewMatrix = baseModelViewMatrix.copy();
localModelViewMatrix.multiplyTranslationMatrix(
(regionX * LodUtil.REGION_WIDTH) - cameraPos.x,
LodBuilder.MIN_WORLD_HEIGHT - cameraPos.y,
(regionZ * LodUtil.REGION_WIDTH) - cameraPos.z);
shaderProgram.fillUniformModelMatrix(localModelViewMatrix);
for (LodVertexBuffer vbo : region.debugGetBuffers()) {
if (vbo == null) continue;
if (vbo.vertexCount == 0) {
vCount0++;
@@ -419,42 +434,6 @@ public class LodRenderer
{
return MC_RENDER.getSpecialFogColor(partialTicks);
}
/**
* Translate the camera relative to the LodDimension's center,
* this is done since all LOD buffers are created in world space
* instead of object space.
* (since AxisAlignedBoundingBoxes (LODs) use doubles and thus have a higher
* accuracy vs the model view matrix, which only uses floats)
*/
private Mat4f translateModelViewMatrix(Mat4f mcModelViewMatrix, float partialTicks, int vbosCenterX, int vbosCenterZ)
{
// get all relevant camera info
Vec3d projectedView = MC_RENDER.getCameraExactPosition();
// translate the camera relative to the regions' center
// (AxisAlignedBoundingBoxes (LODs) use doubles and thus have a higher
// accuracy vs the model view matrix, which only uses floats)
//int bufferPosX = LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, vbosCenterX, LodUtil.BLOCK_DETAIL_LEVEL);
//int bufferPosZ = LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, vbosCenterZ, LodUtil.BLOCK_DETAIL_LEVEL);
double xDiff = projectedView.x - vbosCenterX;
double zDiff = projectedView.z - vbosCenterZ;
mcModelViewMatrix.multiplyTranslationMatrix(-xDiff, -projectedView.y, -zDiff);
return mcModelViewMatrix;
}
/**
* Similar to translateModelViewMatrix (above),
* but for the camera position
*/
private Vec3f getTranslatedCameraPos(int vbosCenterX, int vbosCenterZ)
{
//int worldCenterX = LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, vbosCenterX, LodUtil.BLOCK_DETAIL_LEVEL);
//int worldCenterZ = LevelPosUtil.convert(LodUtil.CHUNK_DETAIL_LEVEL, vbosCenterZ, LodUtil.BLOCK_DETAIL_LEVEL);
Vec3d cameraPos = MC_RENDER.getCameraExactPosition();
return new Vec3f((float)cameraPos.x - vbosCenterX, (float)cameraPos.y, (float)cameraPos.z - vbosCenterZ);
}
/**
* create and return a new projection matrix based on MC's projection matrix
@@ -48,8 +48,8 @@ public class LightmapTexture {
MC.sendChatMessage(same + " " + badIndex);
*/
// comment this line out to prevent uploading the new lightmap
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RGBA, lightMapWidth,
lightMapHeight, 0, GL32.GL_RGBA, GL32.GL_UNSIGNED_BYTE, pixels);
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RGBA, lightMapWidth*lightMapHeight,
1, 0, GL32.GL_RGBA, GL32.GL_UNSIGNED_BYTE, pixels);
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_WRAP_S, GL32.GL_CLAMP_TO_BORDER);
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_WRAP_T, GL32.GL_CLAMP_TO_BORDER);
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_NEAREST);
@@ -34,6 +34,10 @@ public abstract class VertexAttribute {
this.normalized = normalized;
this.byteSize = byteSize;
}
private static int _align(int bytes) {
return (-Math.floorDiv(-bytes, 4))*4;
}
public static VertexPointer addFloatPointer(boolean normalized) {
return new VertexPointer(1, GL32.GL_FLOAT, normalized, 4);
}
@@ -50,7 +54,10 @@ public abstract class VertexAttribute {
return new VertexPointer(1, GL32.GL_UNSIGNED_BYTE, normalized, 4); // Always aligned to 4 bytes
}
public static VertexPointer addUnsignedBytesPointer(int elementCount, boolean normalized) {
return new VertexPointer(elementCount, GL32.GL_UNSIGNED_BYTE, normalized, (-Math.floorDiv(-elementCount, 4))*4); // aligned to 4 bytes
return new VertexPointer(elementCount, GL32.GL_UNSIGNED_BYTE, normalized, _align(elementCount)); // aligned to 4 bytes
}
public static VertexPointer addUnsignedShortsPointer(int elementCount, boolean normalized) {
return new VertexPointer(elementCount, GL32.GL_UNSIGNED_SHORT, normalized, _align(elementCount*2));
}
public static VertexPointer addIntPointer(boolean normalized) {
return new VertexPointer(1, GL32.GL_INT, normalized, 4);
@@ -27,7 +27,6 @@ import com.seibel.lod.core.enums.LodDirection;
import com.seibel.lod.core.enums.config.HorizontalResolution;
import com.seibel.lod.core.enums.config.VanillaOverdraw;
import com.seibel.lod.core.handlers.IReflectionHandler;
import com.seibel.lod.core.objects.VertexOptimizer;
import com.seibel.lod.core.objects.lod.LodDimension;
import com.seibel.lod.core.objects.lod.RegionPos;
import com.seibel.lod.core.objects.opengl.DefaultLodVertexFormats;
@@ -398,10 +397,10 @@ public class LodUtil
return false;
int tempX;
int tempZ;
for (LodDirection lodDirection : VertexOptimizer.ADJ_DIRECTIONS)
for (LodDirection lodDirection : LodDirection.ADJ_DIRECTIONS)
{
tempX = x + VertexOptimizer.DIRECTION_NORMAL_MAP.get(lodDirection).x;
tempZ = z + VertexOptimizer.DIRECTION_NORMAL_MAP.get(lodDirection).z;
tempX = x + lodDirection.getNormal().x;
tempZ = z + lodDirection.getNormal().z;
if (vanillaRenderedChunks[x][z] || (!(tempX < 0 || tempZ < 0 || tempX >= vanillaRenderedChunks.length || tempZ >= vanillaRenderedChunks[0].length)
&& !vanillaRenderedChunks[tempX][tempZ]))
return true;
@@ -410,7 +409,7 @@ public class LodUtil
}
public static boolean isBorderChunk(MovableGridList<Boolean> vanillaRenderedChunks, int chunkX, int chunkZ)
{
for (LodDirection lodDirection : VertexOptimizer.ADJ_DIRECTIONS)
for (LodDirection lodDirection : LodDirection.ADJ_DIRECTIONS)
{
int tempX = chunkX + lodDirection.getNormal().x;
int tempZ = chunkZ + lodDirection.getNormal().z;
+1 -7
View File
@@ -1,15 +1,10 @@
#version 150 core
in vec4 vertexColor;
in vec4 vertexWorldPos;
in float dist;
out vec4 fragColor;
uniform vec3 cameraPos;
uniform bool fogEnabled;
uniform bool nearFogEnabled;
uniform bool farFogEnabled;
@@ -44,7 +39,6 @@ void main()
{
// add fog
float dist = distance(vertexWorldPos, vec4(cameraPos,1));
// no fog by default
float fogAlpha = 0;
+10 -26
View File
@@ -1,13 +1,10 @@
#version 150 core
in vec3 vPosition;
in vec4 vPosition;
in vec4 color;
in vec2 light;
out vec4 vertexColor;
out vec4 vertexWorldPos;
out float depth;
out float dist;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
@@ -21,31 +18,18 @@ uniform sampler2D lightMap;
*
* author: James Seibel
* version: 12-8-2021
*
* updated: TomTheFurry
* version: 15-2-2022
*/
void main()
{
float blockSkyLight = light[0];
float blockLight = light[1];
vec4 worldSpacePos = modelViewMatrix * vec4(vPosition.xyz,1);
float light = (vPosition.a+0.5) / 256.0;
// just skylight
// good for sanity checks; but will cause OpenGL errors since we are binding unused data
// vertexColor = vec4(color.xyz * worldSkyLight / 16.0, color.w);
vertexColor = color * texture(lightMap, vec2(light,0.5));
float blockLightTex = blockLight / 16.0;
float skyLightTex = blockSkyLight / 16.0;
dist = length(worldSpacePos.xyz);
// we don't really need alpha in the lightmap
// vertexColor = color * vec4(texture(lightMap, vec2(skyLightTex, blockLightTex)).xyz, 1);
vertexColor = color * texture(lightMap, vec2(skyLightTex, blockLightTex));
// TODO: add a simple white texture to support Optifine shaders
//textureCoord = textureCoord;
vertexWorldPos = vec4(vPosition, 1);
// the vPosition needs to be converted to a vec4 so it can be multiplied
// by the 4x4 matrices
gl_Position = projectionMatrix * modelViewMatrix * vec4(vPosition, 1);
gl_Position = projectionMatrix * worldSpacePos;
}