Added a cloud renderer that uploads geometry to the GPU. (#4143)

This commit is contained in:
Zaggy1024 2017-10-31 13:45:46 -05:00 committed by LexManos
parent 31655b3a7d
commit 9c7538db8d
6 changed files with 536 additions and 9 deletions

View File

@ -107,20 +107,15 @@
if (this.field_72777_q.field_71441_e.field_73011_w.func_186058_p().func_186068_a() == 1)
{
this.func_180448_r();
@@ -1470,6 +1492,12 @@
@@ -1470,6 +1492,7 @@
public void func_180447_b(float p_180447_1_, int p_180447_2_, double p_180447_3_, double p_180447_5_, double p_180447_7_)
{
+ net.minecraftforge.client.IRenderHandler renderer = this.field_72777_q.field_71441_e.field_73011_w.getCloudRenderer();
+ if (renderer != null)
+ {
+ renderer.render(p_180447_1_, this.field_72777_q.field_71441_e, field_72777_q);
+ return;
+ }
+ if (net.minecraftforge.fml.client.FMLClientHandler.instance().renderClouds(this.field_72773_u, p_180447_1_)) return;
if (this.field_72777_q.field_71441_e.field_73011_w.func_76569_d())
{
if (this.field_72777_q.field_71474_y.func_181147_e() == 2)
@@ -1892,8 +1920,11 @@
@@ -1892,8 +1915,11 @@
double d7 = (double)blockpos.func_177956_o() - d4;
double d8 = (double)blockpos.func_177952_p() - d5;
Block block = this.field_72769_h.func_180495_p(blockpos).func_177230_c();
@ -133,7 +128,7 @@
{
if (d6 * d6 + d7 * d7 + d8 * d8 > 1024.0D)
{
@@ -2388,7 +2419,7 @@
@@ -2388,7 +2414,7 @@
if (block.func_176223_P().func_185904_a() != Material.field_151579_a)
{

View File

@ -0,0 +1,485 @@
/*
* Minecraft Forge
* Copyright (c) 2016.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client;
import java.nio.ByteBuffer;
import org.lwjgl.opengl.GL11;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.EntityRenderer;
import net.minecraft.client.renderer.GLAllocation;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.OpenGlHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.texture.DynamicTexture;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.renderer.vertex.VertexBuffer;
import net.minecraft.client.renderer.vertex.VertexFormat;
import net.minecraft.client.resources.IReloadableResourceManager;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.client.resources.IResourceManagerReloadListener;
import net.minecraft.entity.Entity;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraftforge.common.ForgeModContainer;
public class CloudRenderer implements IResourceManagerReloadListener
{
// Shared constants.
private static final float PX_SIZE = 1 / 256F;
// Building constants.
private static final VertexFormat FORMAT = DefaultVertexFormats.POSITION_TEX_COLOR;
private static final int TOP_SECTIONS = 12; // Number of slices a top face will span.
private static final int HEIGHT = 4;
private static final float INSET = 0.001F;
private static final float ALPHA = 0.8F;
// Debug
private static final boolean WIREFRAME = false;
// Instance fields
private final Minecraft mc = Minecraft.getMinecraft();
private final ResourceLocation texture = new ResourceLocation("textures/environment/clouds.png");
private int displayList = -1;
private VertexBuffer vbo;
private int cloudMode = -1;
private int renderDistance = -1;
private DynamicTexture COLOR_TEX = null;
private int texW;
private int texH;
public CloudRenderer()
{
// Resource manager should always be reloadable.
((IReloadableResourceManager) mc.getResourceManager()).registerReloadListener(this);
}
private int getScale()
{
return cloudMode == 2 ? 12 : 8;
}
private float ceilToScale(float value)
{
float scale = getScale();
return MathHelper.ceil(value / scale) * scale;
}
private void vertices(BufferBuilder buffer)
{
boolean fancy = cloudMode == 2; // Defines whether to hide all but the bottom.
float scale = getScale();
float CULL_DIST = 2 * scale;
float bCol = fancy ? 0.7F : 1F;
float sectEnd = ceilToScale((renderDistance * 2) * 16);
float sectStart = -sectEnd;
float sectStep = ceilToScale(sectEnd * 2 / TOP_SECTIONS);
float sectPx = PX_SIZE / scale;
buffer.begin(GL11.GL_QUADS, FORMAT);
float sectX0 = sectStart;
float sectX1 = sectX0;
while (sectX1 < sectEnd)
{
sectX1 += sectStep;
if (sectX1 > sectEnd)
sectX1 = sectEnd;
float sectZ0 = sectStart;
float sectZ1 = sectZ0;
while (sectZ1 < sectEnd)
{
sectZ1 += sectStep;
if (sectZ1 > sectEnd)
sectZ1 = sectEnd;
float u0 = sectX0 * sectPx;
float u1 = sectX1 * sectPx;
float v0 = sectZ0 * sectPx;
float v1 = sectZ1 * sectPx;
// Bottom
buffer.pos(sectX0, 0, sectZ0).tex(u0, v0).color(bCol, bCol, bCol, ALPHA).endVertex();
buffer.pos(sectX1, 0, sectZ0).tex(u1, v0).color(bCol, bCol, bCol, ALPHA).endVertex();
buffer.pos(sectX1, 0, sectZ1).tex(u1, v1).color(bCol, bCol, bCol, ALPHA).endVertex();
buffer.pos(sectX0, 0, sectZ1).tex(u0, v1).color(bCol, bCol, bCol, ALPHA).endVertex();
if (fancy)
{
// Top
buffer.pos(sectX0, HEIGHT, sectZ0).tex(u0, v0).color(1, 1, 1, ALPHA).endVertex();
buffer.pos(sectX0, HEIGHT, sectZ1).tex(u0, v1).color(1, 1, 1, ALPHA).endVertex();
buffer.pos(sectX1, HEIGHT, sectZ1).tex(u1, v1).color(1, 1, 1, ALPHA).endVertex();
buffer.pos(sectX1, HEIGHT, sectZ0).tex(u1, v0).color(1, 1, 1, ALPHA).endVertex();
float slice;
float sliceCoord0;
float sliceCoord1;
for (slice = sectX0; slice < sectX1;)
{
sliceCoord0 = slice * sectPx;
sliceCoord1 = sliceCoord0 + PX_SIZE;
// X sides
if (slice > -CULL_DIST)
{
slice += INSET;
buffer.pos(slice, 0, sectZ1).tex(sliceCoord0, v1).color(0.9F, 0.9F, 0.9F, ALPHA).endVertex();
buffer.pos(slice, HEIGHT, sectZ1).tex(sliceCoord1, v1).color(0.9F, 0.9F, 0.9F, ALPHA).endVertex();
buffer.pos(slice, HEIGHT, sectZ0).tex(sliceCoord1, v0).color(0.9F, 0.9F, 0.9F, ALPHA).endVertex();
buffer.pos(slice, 0, sectZ0).tex(sliceCoord0, v0).color(0.9F, 0.9F, 0.9F, ALPHA).endVertex();
slice -= INSET;
}
slice += scale;
if (slice <= CULL_DIST)
{
slice -= INSET;
buffer.pos(slice, 0, sectZ0).tex(sliceCoord0, v0).color(0.9F, 0.9F, 0.9F, ALPHA).endVertex();
buffer.pos(slice, HEIGHT, sectZ0).tex(sliceCoord1, v0).color(0.9F, 0.9F, 0.9F, ALPHA).endVertex();
buffer.pos(slice, HEIGHT, sectZ1).tex(sliceCoord1, v1).color(0.9F, 0.9F, 0.9F, ALPHA).endVertex();
buffer.pos(slice, 0, sectZ1).tex(sliceCoord0, v1).color(0.9F, 0.9F, 0.9F, ALPHA).endVertex();
slice += INSET;
}
}
for (slice = sectZ0; slice < sectZ1;)
{
sliceCoord0 = slice * sectPx;
sliceCoord1 = sliceCoord0 + PX_SIZE;
// Z sides
if (slice > -CULL_DIST)
{
slice += INSET;
buffer.pos(sectX0, 0, slice).tex(u0, sliceCoord0).color(0.8F, 0.8F, 0.8F, ALPHA).endVertex();
buffer.pos(sectX0, HEIGHT, slice).tex(u0, sliceCoord1).color(0.8F, 0.8F, 0.8F, ALPHA).endVertex();
buffer.pos(sectX1, HEIGHT, slice).tex(u1, sliceCoord1).color(0.8F, 0.8F, 0.8F, ALPHA).endVertex();
buffer.pos(sectX1, 0, slice).tex(u1, sliceCoord0).color(0.8F, 0.8F, 0.8F, ALPHA).endVertex();
slice -= INSET;
}
slice += scale;
if (slice <= CULL_DIST)
{
slice -= INSET;
buffer.pos(sectX1, 0, slice).tex(u1, sliceCoord0).color(0.8F, 0.8F, 0.8F, ALPHA).endVertex();
buffer.pos(sectX1, HEIGHT, slice).tex(u1, sliceCoord1).color(0.8F, 0.8F, 0.8F, ALPHA).endVertex();
buffer.pos(sectX0, HEIGHT, slice).tex(u0, sliceCoord1).color(0.8F, 0.8F, 0.8F, ALPHA).endVertex();
buffer.pos(sectX0, 0, slice).tex(u0, sliceCoord0).color(0.8F, 0.8F, 0.8F, ALPHA).endVertex();
slice += INSET;
}
}
}
sectZ0 = sectZ1;
}
sectX0 = sectX1;
}
}
private void dispose()
{
if (vbo != null)
{
vbo.deleteGlBuffers();
vbo = null;
}
if (displayList >= 0)
{
GLAllocation.deleteDisplayLists(displayList);
displayList = -1;
}
}
private void build()
{
Tessellator tess = Tessellator.getInstance();
BufferBuilder buffer = tess.getBuffer();
if (OpenGlHelper.useVbo())
vbo = new VertexBuffer(FORMAT);
else
GlStateManager.glNewList(displayList = GLAllocation.generateDisplayLists(1), GL11.GL_COMPILE);
vertices(buffer);
if (OpenGlHelper.useVbo())
{
buffer.finishDrawing();
buffer.reset();
vbo.bufferData(buffer.getByteBuffer());
}
else
{
tess.draw();
GlStateManager.glEndList();
}
}
private int fullCoord(double coord, int scale)
{ // Corrects misalignment of UV offset when on negative coords.
return ((int) coord / scale) - (coord < 0 ? 1 : 0);
}
private boolean isBuilt()
{
return OpenGlHelper.useVbo() ? vbo != null : displayList >= 0;
}
public void checkSettings()
{
boolean newEnabled = ForgeModContainer.forgeCloudsEnabled
&& mc.gameSettings.shouldRenderClouds() != 0
&& mc.world != null
&& mc.world.provider.isSurfaceWorld();
if (isBuilt()
&& (!newEnabled
|| mc.gameSettings.shouldRenderClouds() != cloudMode
|| mc.gameSettings.renderDistanceChunks != renderDistance))
{
dispose();
}
cloudMode = mc.gameSettings.shouldRenderClouds();
renderDistance = mc.gameSettings.renderDistanceChunks;
if (newEnabled && !isBuilt())
{
build();
}
}
public boolean render(int cloudTicks, float partialTicks)
{
if (!isBuilt())
return false;
Entity entity = mc.getRenderViewEntity();
double totalOffset = cloudTicks + partialTicks;
double x = entity.prevPosX + (entity.posX - entity.prevPosX) * partialTicks
+ totalOffset * 0.03;
double y = mc.world.provider.getCloudHeight()
- (entity.lastTickPosY + (entity.posY - entity.lastTickPosY) * partialTicks)
+ 0.33;
double z = entity.prevPosZ + (entity.posZ - entity.prevPosZ) * partialTicks;
int scale = getScale();
if (cloudMode == 2)
z += 0.33 * scale;
// Integer UVs to translate the texture matrix by.
int offU = fullCoord(x, scale);
int offV = fullCoord(z, scale);
GlStateManager.pushMatrix();
// Translate by the remainder after the UV offset.
GlStateManager.translate((offU * scale) - x, y, (offV * scale) - z);
// Modulo to prevent texture samples becoming inaccurate at extreme offsets.
offU = offU % texW;
offV = offV % texH;
// Translate the texture.
GlStateManager.matrixMode(GL11.GL_TEXTURE);
GlStateManager.translate(offU * PX_SIZE, offV * PX_SIZE, 0);
GlStateManager.matrixMode(GL11.GL_MODELVIEW);
GlStateManager.disableCull();
GlStateManager.enableBlend();
GlStateManager.tryBlendFuncSeparate(
GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA,
GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO);
// Color multiplier.
Vec3d color = mc.world.getCloudColour(partialTicks);
float r = (float) color.x;
float g = (float) color.y;
float b = (float) color.z;
if (mc.gameSettings.anaglyph)
{
float tempR = r * 0.3F + g * 0.59F + b * 0.11F;
float tempG = r * 0.3F + g * 0.7F;
float tempB = r * 0.3F + b * 0.7F;
r = tempR;
g = tempG;
b = tempB;
}
if (COLOR_TEX == null)
COLOR_TEX = new DynamicTexture(1, 1);
// Apply a color multiplier through a texture upload if shaders aren't supported.
COLOR_TEX.getTextureData()[0] = 255 << 24
| ((int) (r * 255)) << 16
| ((int) (g * 255)) << 8
| (int) (b * 255);
COLOR_TEX.updateDynamicTexture();
GlStateManager.setActiveTexture(OpenGlHelper.lightmapTexUnit);
GlStateManager.bindTexture(COLOR_TEX.getGlTextureId());
GlStateManager.enableTexture2D();
// Bind the clouds texture last so the shader's sampler2D is correct.
GlStateManager.setActiveTexture(OpenGlHelper.defaultTexUnit);
mc.renderEngine.bindTexture(texture);
ByteBuffer buffer = Tessellator.getInstance().getBuffer().getByteBuffer();
// Set up pointers for the display list/VBO.
if (OpenGlHelper.useVbo())
{
vbo.bindBuffer();
int stride = FORMAT.getNextOffset();
GlStateManager.glVertexPointer(3, GL11.GL_FLOAT, stride, 0);
GlStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY);
GlStateManager.glTexCoordPointer(2, GL11.GL_FLOAT, stride, 12);
GlStateManager.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
GlStateManager.glColorPointer(4, GL11.GL_UNSIGNED_BYTE, stride, 20);
GlStateManager.glEnableClientState(GL11.GL_COLOR_ARRAY);
}
else
{
buffer.limit(FORMAT.getNextOffset());
for (int i = 0; i < FORMAT.getElementCount(); i++)
FORMAT.getElements().get(i).getUsage().preDraw(FORMAT, i, FORMAT.getNextOffset(), buffer);
buffer.position(0);
}
// Depth pass to prevent insides rendering from the outside.
GlStateManager.colorMask(false, false, false, false);
if (OpenGlHelper.useVbo())
vbo.drawArrays(GL11.GL_QUADS);
else
GlStateManager.callList(displayList);
// Full render.
if (!mc.gameSettings.anaglyph)
{
GlStateManager.colorMask(true, true, true, true);
}
else
{
switch (EntityRenderer.anaglyphField)
{
case 0:
GlStateManager.colorMask(false, true, true, true);
break;
case 1:
GlStateManager.colorMask(true, false, false, true);
break;
}
}
// Wireframe for debug.
if (WIREFRAME)
{
GlStateManager.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_LINE);
GlStateManager.glLineWidth(2.0F);
GlStateManager.disableTexture2D();
GlStateManager.depthMask(false);
GlStateManager.disableFog();
if (OpenGlHelper.useVbo())
vbo.drawArrays(GL11.GL_QUADS);
else
GlStateManager.callList(displayList);
GlStateManager.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);
GlStateManager.depthMask(true);
GlStateManager.enableTexture2D();
GlStateManager.enableFog();
}
if (OpenGlHelper.useVbo())
{
vbo.drawArrays(GL11.GL_QUADS);
vbo.unbindBuffer(); // Unbind buffer and disable pointers.
}
else
{
GlStateManager.callList(displayList);
}
buffer.limit(0);
for (int i = 0; i < FORMAT.getElementCount(); i++)
FORMAT.getElements().get(i).getUsage().postDraw(FORMAT, i, FORMAT.getNextOffset(), buffer);
buffer.position(0);
// Disable our coloring.
GlStateManager.setActiveTexture(OpenGlHelper.lightmapTexUnit);
GlStateManager.disableTexture2D();
GlStateManager.setActiveTexture(OpenGlHelper.defaultTexUnit);
// Reset texture matrix.
GlStateManager.matrixMode(GL11.GL_TEXTURE);
GlStateManager.loadIdentity();
GlStateManager.matrixMode(GL11.GL_MODELVIEW);
GlStateManager.disableBlend();
GlStateManager.enableCull();
GlStateManager.popMatrix();
return true;
}
private void reloadTextures()
{
if (mc.renderEngine != null)
{
mc.renderEngine.bindTexture(texture);
texW = GlStateManager.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 0, GL11.GL_TEXTURE_WIDTH);
texH = GlStateManager.glGetTexLevelParameteri(GL11.GL_TEXTURE_2D, 0, GL11.GL_TEXTURE_HEIGHT);
}
}
@Override
public void onResourceManagerReload(IResourceManager resourceManager)
{
reloadTextures();
}
}

View File

@ -27,10 +27,13 @@ import net.minecraft.world.WorldServer;
import net.minecraftforge.common.util.FakePlayerFactory;
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
import net.minecraftforge.event.world.WorldEvent;
import net.minecraftforge.fml.client.FMLClientHandler;
import net.minecraftforge.fml.common.FMLLog;
import net.minecraftforge.fml.common.eventhandler.EventPriority;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent;
import net.minecraftforge.fml.common.gameevent.TickEvent.Phase;
import net.minecraftforge.fml.common.gameevent.TickEvent.ServerTickEvent;
public class ForgeInternalHandler
@ -86,5 +89,12 @@ public class ForgeInternalHandler
{
WorldWorkerManager.tick(event.phase == TickEvent.Phase.START);
}
@SubscribeEvent
public void checkSettings(ClientTickEvent event)
{
if (event.phase == Phase.END)
FMLClientHandler.instance().updateCloudSettings();
}
}

View File

@ -115,6 +115,7 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
@Deprecated // TODO remove in 1.13
public static boolean replaceVanillaBucketModel = true;
public static boolean zoomInMissingModelTextInGui = false;
public static boolean forgeCloudsEnabled = true;
public static boolean disableStairSlabCulling = false; // Also known as the "DontCullStairsBecauseIUseACrappyTexturePackThatBreaksBasicBlockShapesSoICantTrustBasicBlockCulling" flag
public static boolean alwaysSetupTerrainOffThread = false; // In RenderGlobal.setupTerrain, always force the chunk render updates to be queued to the thread
public static int dimensionUnloadQueueDelay = 0;
@ -323,6 +324,12 @@ public class ForgeModContainer extends DummyModContainer implements WorldAccessC
prop.setLanguageKey("forge.configgui.zoomInMissingModelTextInGui");
propOrder.add(prop.getName());
prop = config.get(Configuration.CATEGORY_CLIENT, "forgeCloudsEnabled", true,
"Enable uploading cloud geometry to the GPU for faster rendering.");
prop.setLanguageKey("forge.configgui.forgeCloudsEnabled");
forgeCloudsEnabled = prop.getBoolean();
propOrder.add(prop.getName());
prop = config.get(Configuration.CATEGORY_CLIENT, "disableStairSlabCulling", false,
"Disable culling of hidden faces next to stairs and slabs. Causes extra rendering, but may fix some resource packs that exploit this vanilla mechanic.");
disableStairSlabCulling = prop.getBoolean(false);

View File

@ -80,6 +80,8 @@ import net.minecraft.util.StringUtils;
import net.minecraft.world.WorldSettings;
import net.minecraft.world.storage.WorldSummary;
import net.minecraft.world.storage.SaveFormatOld;
import net.minecraftforge.client.CloudRenderer;
import net.minecraftforge.client.IRenderHandler;
import net.minecraftforge.client.event.ModelRegistryEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.config.ConfigManager;
@ -198,6 +200,8 @@ public class FMLClientHandler implements IFMLSidedHandler
private WeakReference<NetHandlerPlayClient> currentPlayClient;
private CloudRenderer cloudRenderer;
/**
* Called to start the whole game off
*
@ -1115,4 +1119,27 @@ public class FMLClientHandler implements IFMLSidedHandler
this.client.populateSearchTreeManager();
this.client.getSearchTreeManager().onResourceManagerReload(this.client.getResourceManager());
}
private CloudRenderer getCloudRenderer()
{
if (cloudRenderer == null)
cloudRenderer = new CloudRenderer();
return cloudRenderer;
}
public void updateCloudSettings()
{
getCloudRenderer().checkSettings();
}
public boolean renderClouds(int cloudTicks, float partialTicks)
{
IRenderHandler renderer = this.client.world.provider.getCloudRenderer();
if (renderer != null)
{
renderer.render(partialTicks, this.client.world, this.client);
return true;
}
return getCloudRenderer().render(cloudTicks, partialTicks);
}
}

View File

@ -63,6 +63,9 @@ forge.configgui.zombieBabyChance=Zombie Baby Chance
forge.configgui.zombieBaseSummonChance.tooltip=Base zombie summoning spawn chance. Allows changing the bonus zombie summoning mechanic.
forge.configgui.zombieBaseSummonChance=Zombie Summon Chance
forge.configgui.stencilbits=Enable GL Stencil Bits
forge.configgui.replaceBuckets=Use Forge's bucket model
forge.configgui.forgeCloudsEnabled=Use Forge's cloud renderer
forge.configgui.forgeCloudsEnabled.tooltip=Enable uploading cloud geometry to the GPU for faster rendering.
forge.configgui.forgeLightPipelineEnabled=Forge Light Pipeline Enabled
forge.configgui.disableStairSlabCulling=Disable Stair/Slab culling.
forge.configgui.zoomInMissingModelTextInGui=Zoom in Missing model text in the GUI