Little things.

- Quark magnets don't crash (already fixed).
 - Fix quark magnet leaving behind ghost instances.
 - Fix crash with belt lighting after being placed by contraptions.
 - Simplify tile add/remove mixins and avoid conflict with Performant.
 - Avoid FloatBuffer detour when uploading matrix uniforms.
 - InstancedTileRenderer no longer has to clean up.
 - Properly let go of tickable instances.
This commit is contained in:
JozsefA 2021-03-24 15:48:15 -07:00
parent 7eafbe5757
commit b18993ed26
13 changed files with 300 additions and 290 deletions

View file

@ -548,18 +548,20 @@ public class BeltTileEntity extends KineticTileEntity implements LightUpdateList
} }
private void initializeLight() { private void initializeLight() {
light = new byte[beltLength * 2]; if (beltLength > 0) {
light = new byte[beltLength * 2];
Vec3i vec = getBeltFacing().getDirectionVec(); Vec3i vec = getBeltFacing().getDirectionVec();
BeltSlope slope = getBlockState().get(BeltBlock.SLOPE); BeltSlope slope = getBlockState().get(BeltBlock.SLOPE);
int verticality = slope == BeltSlope.DOWNWARD ? -1 : slope == BeltSlope.UPWARD ? 1 : 0; int verticality = slope == BeltSlope.DOWNWARD ? -1 : slope == BeltSlope.UPWARD ? 1 : 0;
BlockPos.Mutable pos = new BlockPos.Mutable(controller); BlockPos.Mutable pos = new BlockPos.Mutable(controller);
for (int i = 0; i < beltLength * 2; i += 2) { for (int i = 0; i < beltLength * 2; i += 2) {
light[i] = (byte) world.getLightLevel(LightType.BLOCK, pos); light[i] = (byte) world.getLightLevel(LightType.BLOCK, pos);
light[i + 1] = (byte) world.getLightLevel(LightType.SKY, pos); light[i + 1] = (byte) world.getLightLevel(LightType.SKY, pos);
pos.move(vec.getX(), verticality, vec.getZ()); pos.move(vec.getX(), verticality, vec.getZ());
}
} }
} }

View file

@ -1,72 +0,0 @@
package com.simibubi.create.foundation.mixin;
import com.simibubi.create.foundation.render.KineticRenderer;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import com.simibubi.create.CreateClient;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import java.util.Set;
@OnlyIn(Dist.CLIENT)
@Mixin(value = World.class, priority = 1042)
public class AddRemoveTileMixin {
@Shadow @Final public boolean isRemote;
@Shadow @Final protected Set<TileEntity> tileEntitiesToBeRemoved;
/**
* JUSTIFICATION: This method is called whenever a tile entity is removed due
* to a change in block state, even on the client. By hooking into this method,
* we gain easy access to the information while having no impact on performance.
*/
@Inject(at = @At(
value = "INVOKE_ASSIGN",
target = "Lnet/minecraft/world/World;getTileEntity(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/tileentity/TileEntity;"
),
method = "removeTileEntity",
locals = LocalCapture.CAPTURE_FAILHARD
)
private void onRemoveTile(BlockPos pos, CallbackInfo ci, TileEntity te) {
if (isRemote) {
World thi = (World)(Object) this;
CreateClient.kineticRenderer.get(thi).remove(te);
}
}
@Inject(at = @At("TAIL"), method = "addTileEntity")
private void onAddTile(TileEntity te, CallbackInfoReturnable<Boolean> cir) {
if (isRemote) {
World thi = (World)(Object) this;
CreateClient.kineticRenderer.get(thi).queueAdd(te);
}
}
@Inject(at = @At(
value = "INVOKE",
target = "Ljava/util/Set;clear()V", ordinal = 0
),
method = "tickBlockEntities")
private void onChunkUnload(CallbackInfo ci) {
if (isRemote) {
World thi = (World)(Object) this;
KineticRenderer kineticRenderer = CreateClient.kineticRenderer.get(thi);
for (TileEntity tile : tileEntitiesToBeRemoved) {
kineticRenderer.remove(tile);
}
}
}
}

View file

@ -0,0 +1,29 @@
package com.simibubi.create.foundation.mixin;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.simibubi.create.CreateClient;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT)
@Mixin(World.class)
public class TileAddMixin {
@Shadow @Final public boolean isRemote;
@Inject(at = @At("TAIL"), method = "addTileEntity")
private void onAddTile(TileEntity te, CallbackInfoReturnable<Boolean> cir) {
if (isRemote) {
CreateClient.kineticRenderer.get((World)(Object) this).queueAdd(te);
}
}
}

View file

@ -0,0 +1,25 @@
package com.simibubi.create.foundation.mixin;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.world.World;
import javax.annotation.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.simibubi.create.CreateClient;
@Mixin(TileEntity.class)
public class TileRemoveMixin {
@Shadow @Nullable protected World world;
@Inject(at = @At("TAIL"), method = "remove")
private void onRemove(CallbackInfo ci) {
if (world instanceof ClientWorld)
CreateClient.kineticRenderer.get(this.world).remove((TileEntity) (Object) this);
}
}

View file

@ -11,6 +11,7 @@ import com.simibubi.create.foundation.render.backend.instancing.IFlywheelWorld;
import net.minecraft.world.World; import net.minecraft.world.World;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GLCapabilities; import org.lwjgl.opengl.GLCapabilities;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
@ -23,82 +24,85 @@ import net.minecraft.util.ResourceLocation;
import net.minecraftforge.resource.ISelectiveResourceReloadListener; import net.minecraftforge.resource.ISelectiveResourceReloadListener;
public class Backend { public class Backend {
public static final Boolean SHADER_DEBUG_OUTPUT = true; public static final Boolean SHADER_DEBUG_OUTPUT = true;
public static final Logger log = LogManager.getLogger(Backend.class); public static final Logger log = LogManager.getLogger(Backend.class);
public static final FloatBuffer MATRIX_BUFFER = MemoryUtil.memAllocFloat(16); public static GLCapabilities capabilities;
public static GlFeatureCompat compat;
static final Map<ResourceLocation, ProgramSpec<?>> registry = new HashMap<>(); private static boolean instancingAvailable;
static final Map<ProgramSpec<?>, ProgramGroup<?>> programs = new HashMap<>(); private static boolean enabled;
private static boolean enabled; static final Map<ResourceLocation, ProgramSpec<?>> registry = new HashMap<>();
static final Map<ProgramSpec<?>, ProgramGroup<?>> programs = new HashMap<>();
public static GLCapabilities capabilities; public Backend() {
public static GlFeatureCompat compat; throw new IllegalStateException();
}
public Backend() { /**
throw new IllegalStateException(); * Register a shader program. TODO: replace with forge registry?
} */
public static <P extends GlProgram, S extends ProgramSpec<P>> S register(S spec) {
ResourceLocation name = spec.name;
if (registry.containsKey(name)) {
throw new IllegalStateException("Program spec '" + name + "' already registered.");
}
registry.put(name, spec);
return spec;
}
/** @SuppressWarnings("unchecked")
* Register a shader program. TODO: replace with forge registry? public static <P extends GlProgram, S extends ProgramSpec<P>> P getProgram(S spec) {
*/ return (P) programs.get(spec).get(GlFog.getFogMode());
public static <P extends GlProgram, S extends ProgramSpec<P>> S register(S spec) { }
ResourceLocation name = spec.name;
if (registry.containsKey(name)) {
throw new IllegalStateException("Program spec '" + name + "' already registered.");
}
registry.put(name, spec);
return spec;
}
@SuppressWarnings("unchecked") public static boolean isFlywheelWorld(World world) {
public static <P extends GlProgram, S extends ProgramSpec<P>> P getProgram(S spec) { return world == Minecraft.getInstance().world || (world instanceof IFlywheelWorld && ((IFlywheelWorld) world).supportsFlywheel());
return (P) programs.get(spec).get(GlFog.getFogMode()); }
}
public static boolean isFlywheelWorld(World world) { public static boolean available() {
return world == Minecraft.getInstance().world || (world instanceof IFlywheelWorld && ((IFlywheelWorld) world).supportsFlywheel()); return canUseVBOs();
} }
public static boolean available() { public static boolean canUseInstancing() {
return canUseVBOs(); return enabled && instancingAvailable;
} }
public static boolean canUseInstancing() { public static boolean canUseVBOs() {
return enabled && return enabled && gl20();
compat.vertexArrayObjectsSupported() && }
compat.drawInstancedSupported() &&
compat.instancedArraysSupported();
}
public static boolean canUseVBOs() { public static boolean gl33() {
return enabled && gl20(); return capabilities.OpenGL33;
} }
public static boolean gl33() { public static boolean gl20() {
return capabilities.OpenGL33; return capabilities.OpenGL20;
} }
public static boolean gl20() { public static void init() {
return capabilities.OpenGL20; // Can be null when running datagenerators due to the unfortunate time we call this
} Minecraft mc = Minecraft.getInstance();
if (mc == null) return;
public static void init() { IResourceManager manager = mc.getResourceManager();
// Can be null when running datagenerators due to the unfortunate time we call this
Minecraft mc = Minecraft.getInstance();
if (mc == null) return;
IResourceManager manager = mc.getResourceManager(); if (manager instanceof IReloadableResourceManager) {
ISelectiveResourceReloadListener listener = ShaderLoader::onResourceManagerReload;
((IReloadableResourceManager) manager).addReloadListener(listener);
}
}
if (manager instanceof IReloadableResourceManager) { public static void refresh() {
ISelectiveResourceReloadListener listener = ShaderLoader::onResourceManagerReload; capabilities = GL.createCapabilities();
((IReloadableResourceManager) manager).addReloadListener(listener);
}
}
public static void refresh() { compat = new GlFeatureCompat(capabilities);
enabled = AllConfigs.CLIENT.experimentalRendering.get() && !OptifineHandler.usingShaders();
}
instancingAvailable = compat.vertexArrayObjectsSupported() &&
compat.drawInstancedSupported() &&
compat.instancedArraysSupported();
enabled = AllConfigs.CLIENT.experimentalRendering.get() && !OptifineHandler.usingShaders();
}
} }

View file

@ -3,8 +3,7 @@ package com.simibubi.create.foundation.render.backend;
import net.minecraft.client.renderer.Matrix3f; import net.minecraft.client.renderer.Matrix3f;
import net.minecraft.client.renderer.Matrix4f; import net.minecraft.client.renderer.Matrix4f;
import java.nio.ByteBuffer; import com.mojang.blaze3d.matrix.MatrixStack;
import java.nio.FloatBuffer;
public class RenderUtil { public class RenderUtil {
public static int nextPowerOf2(int a) { public static int nextPowerOf2(int a) {
@ -17,9 +16,12 @@ public class RenderUtil {
return b == 0 && n != 0; return b == 0 && n != 0;
} }
// GPUs want matrices in column major order. public static float[] writeMatrixStack(MatrixStack stack) {
return writeMatrixStack(stack.peek().getModel(), stack.peek().getNormal());
}
public static float[] bufferMatrices(Matrix4f model, Matrix3f normal) { // GPUs want matrices in column major order.
public static float[] writeMatrixStack(Matrix4f model, Matrix3f normal) {
return new float[] { return new float[] {
model.a00, model.a00,
model.a10, model.a10,
@ -48,4 +50,25 @@ public class RenderUtil {
normal.a22, normal.a22,
}; };
} }
public static float[] writeMatrix(Matrix4f model) {
return new float[]{
model.a00,
model.a10,
model.a20,
model.a30,
model.a01,
model.a11,
model.a21,
model.a31,
model.a02,
model.a12,
model.a22,
model.a32,
model.a03,
model.a13,
model.a23,
model.a33,
};
}
} }

View file

@ -28,175 +28,172 @@ import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
public class ShaderLoader { public class ShaderLoader {
public static final String SHADER_DIR = "flywheel/shaders/"; public static final String SHADER_DIR = "flywheel/shaders/";
public static final ArrayList<String> EXTENSIONS = Lists.newArrayList(".vert", ".vsh", ".frag", ".fsh", ".glsl"); public static final ArrayList<String> EXTENSIONS = Lists.newArrayList(".vert", ".vsh", ".frag", ".fsh", ".glsl");
static final Map<ResourceLocation, String> shaderSource = new HashMap<>(); static final Map<ResourceLocation, String> shaderSource = new HashMap<>();
static void onResourceManagerReload(IResourceManager manager, Predicate<IResourceType> predicate) { static void onResourceManagerReload(IResourceManager manager, Predicate<IResourceType> predicate) {
if (predicate.test(VanillaResourceType.SHADERS)) { if (predicate.test(VanillaResourceType.SHADERS)) {
Backend.capabilities = GL.createCapabilities(); OptifineHandler.refresh();
Backend.compat = new GlFeatureCompat(Backend.capabilities); Backend.refresh();
OptifineHandler.refresh(); if (Backend.gl20()) {
Backend.refresh(); shaderSource.clear();
loadShaderSources(manager);
if (Backend.gl20()) { Backend.programs.values().forEach(ProgramGroup::delete);
shaderSource.clear(); Backend.programs.clear();
loadShaderSources(manager); Backend.registry.values().forEach(ShaderLoader::loadProgram);
Backend.programs.values().forEach(ProgramGroup::delete); Backend.log.info("Loaded all shader programs.");
Backend.programs.clear(); }
Backend.registry.values().forEach(ShaderLoader::loadProgram); }
}
Backend.log.info("Loaded all shader programs."); private static void loadShaderSources(IResourceManager manager){
} Collection<ResourceLocation> allShaders = manager.getAllResourceLocations(SHADER_DIR, s -> {
} for (String ext : EXTENSIONS) {
} if (s.endsWith(ext)) return true;
}
return false;
});
private static void loadShaderSources(IResourceManager manager){ for (ResourceLocation location : allShaders) {
Collection<ResourceLocation> allShaders = manager.getAllResourceLocations(SHADER_DIR, s -> { try {
for (String ext : EXTENSIONS) { IResource resource = manager.getResource(location);
if (s.endsWith(ext)) return true;
}
return false;
});
for (ResourceLocation location : allShaders) { String file = readToString(resource.getInputStream());
try {
IResource resource = manager.getResource(location);
String file = readToString(resource.getInputStream()); ResourceLocation name = new ResourceLocation(location.getNamespace(),
location.getPath().substring(SHADER_DIR.length()));
ResourceLocation name = new ResourceLocation(location.getNamespace(), shaderSource.put(name, file);
location.getPath().substring(SHADER_DIR.length())); } catch (IOException e) {
shaderSource.put(name, file); }
} catch (IOException e) { }
}
} static <P extends GlProgram, S extends ProgramSpec<P>> void loadProgram(S programSpec) {
} Map<GlFogMode, P> programGroup = new EnumMap<>(GlFogMode.class);
}
static <P extends GlProgram, S extends ProgramSpec<P>> void loadProgram(S programSpec) { for (GlFogMode fogMode : GlFogMode.values()) {
Map<GlFogMode, P> programGroup = new EnumMap<>(GlFogMode.class); programGroup.put(fogMode, loadProgram(programSpec, fogMode));
}
for (GlFogMode fogMode : GlFogMode.values()) { Backend.programs.put(programSpec, new ProgramGroup<>(programGroup));
programGroup.put(fogMode, loadProgram(programSpec, fogMode));
}
Backend.programs.put(programSpec, new ProgramGroup<>(programGroup)); Backend.log.debug("Loaded program {}", programSpec.name);
}
Backend.log.debug("Loaded program {}", programSpec.name); private static <P extends GlProgram, S extends ProgramSpec<P>> P loadProgram(S programSpec, GlFogMode fogMode) {
} GlShader vert = null;
GlShader frag = null;
try {
ShaderConstants defines = new ShaderConstants(programSpec.defines);
private static <P extends GlProgram, S extends ProgramSpec<P>> P loadProgram(S programSpec, GlFogMode fogMode) { defines.defineAll(fogMode.getDefines());
GlShader vert = null;
GlShader frag = null;
try {
ShaderConstants defines = new ShaderConstants(programSpec.defines);
defines.defineAll(fogMode.getDefines()); vert = loadShader(programSpec.getVert(), ShaderType.VERTEX, defines);
frag = loadShader(programSpec.getFrag(), ShaderType.FRAGMENT, defines);
vert = loadShader(programSpec.getVert(), ShaderType.VERTEX, defines); GlProgram.Builder builder = GlProgram.builder(programSpec.name, fogMode).attachShader(vert).attachShader(frag);
frag = loadShader(programSpec.getFrag(), ShaderType.FRAGMENT, defines);
GlProgram.Builder builder = GlProgram.builder(programSpec.name, fogMode).attachShader(vert).attachShader(frag); programSpec.attributes.forEach(builder::addAttribute);
programSpec.attributes.forEach(builder::addAttribute); return builder.build(programSpec.factory);
return builder.build(programSpec.factory); } finally {
if (vert != null) vert.delete();
if (frag != null) frag.delete();
}
}
} finally { private static final Pattern includePattern = Pattern.compile("#flwinclude <\"([\\w\\d_]+:[\\w\\d_./]+)\">");
if (vert != null) vert.delete();
if (frag != null) frag.delete();
}
}
private static final Pattern includePattern = Pattern.compile("#flwinclude <\"([\\w\\d_]+:[\\w\\d_./]+)\">"); private static String processIncludes(ResourceLocation baseName, String source) {
HashSet<ResourceLocation> seen = new HashSet<>();
seen.add(baseName);
private static String processIncludes(ResourceLocation baseName, String source) { return includeRecursive(source, seen).collect(Collectors.joining("\n"));
HashSet<ResourceLocation> seen = new HashSet<>(); }
seen.add(baseName);
return includeRecursive(source, seen).collect(Collectors.joining("\n")); private static Stream<String> includeRecursive(String source, Set<ResourceLocation> seen) {
} return new BufferedReader(new StringReader(source)).lines().flatMap(line -> {
private static Stream<String> includeRecursive(String source, Set<ResourceLocation> seen) { Matcher matcher = includePattern.matcher(line);
return new BufferedReader(new StringReader(source)).lines().flatMap(line -> {
Matcher matcher = includePattern.matcher(line); if (matcher.find()) {
String includeName = matcher.group(1);
if (matcher.find()) { ResourceLocation include = new ResourceLocation(includeName);
String includeName = matcher.group(1);
ResourceLocation include = new ResourceLocation(includeName); if (seen.add(include)) {
String includeSource = shaderSource.get(include);
if (seen.add(include)) { if (includeSource != null) {
String includeSource = shaderSource.get(include); return includeRecursive(includeSource, seen);
}
}
}
if (includeSource != null) { return Stream.of(line);
return includeRecursive(includeSource, seen); });
} }
}
}
return Stream.of(line); private static GlShader loadShader(ResourceLocation name, ShaderType type, ShaderConstants defines) {
}); String source = shaderSource.get(name);
}
private static GlShader loadShader(ResourceLocation name, ShaderType type, ShaderConstants defines) { source = processIncludes(name, source);
String source = shaderSource.get(name);
source = processIncludes(name, source); if (defines != null)
source = defines.process(source);
if (defines != null)
source = defines.process(source);
return new GlShader(type, name, source); return new GlShader(type, name, source);
} }
public static String readToString(InputStream is) { public static String readToString(InputStream is) {
RenderSystem.assertThread(RenderSystem::isOnRenderThread); RenderSystem.assertThread(RenderSystem::isOnRenderThread);
ByteBuffer bytebuffer = null; ByteBuffer bytebuffer = null;
try { try {
bytebuffer = readToBuffer(is); bytebuffer = readToBuffer(is);
int i = bytebuffer.position(); int i = bytebuffer.position();
((Buffer)bytebuffer).rewind(); ((Buffer)bytebuffer).rewind();
return MemoryUtil.memASCII(bytebuffer, i); return MemoryUtil.memASCII(bytebuffer, i);
} catch (IOException e) { } catch (IOException e) {
} finally { } finally {
if (bytebuffer != null) { if (bytebuffer != null) {
MemoryUtil.memFree(bytebuffer); MemoryUtil.memFree(bytebuffer);
} }
} }
return null; return null;
} }
public static ByteBuffer readToBuffer(InputStream is) throws IOException { public static ByteBuffer readToBuffer(InputStream is) throws IOException {
ByteBuffer bytebuffer; ByteBuffer bytebuffer;
if (is instanceof FileInputStream) { if (is instanceof FileInputStream) {
FileInputStream fileinputstream = (FileInputStream)is; FileInputStream fileinputstream = (FileInputStream)is;
FileChannel filechannel = fileinputstream.getChannel(); FileChannel filechannel = fileinputstream.getChannel();
bytebuffer = MemoryUtil.memAlloc((int)filechannel.size() + 1); bytebuffer = MemoryUtil.memAlloc((int)filechannel.size() + 1);
while (filechannel.read(bytebuffer) != -1) { } while (filechannel.read(bytebuffer) != -1) { }
} else { } else {
bytebuffer = MemoryUtil.memAlloc(8192); bytebuffer = MemoryUtil.memAlloc(8192);
ReadableByteChannel readablebytechannel = Channels.newChannel(is); ReadableByteChannel readablebytechannel = Channels.newChannel(is);
while (readablebytechannel.read(bytebuffer) != -1) { while (readablebytechannel.read(bytebuffer) != -1) {
if (bytebuffer.remaining() == 0) { if (bytebuffer.remaining() == 0) {
bytebuffer = MemoryUtil.memRealloc(bytebuffer, bytebuffer.capacity() * 2); bytebuffer = MemoryUtil.memRealloc(bytebuffer, bytebuffer.capacity() * 2);
} }
} }
} }
return bytebuffer; return bytebuffer;
} }
} }

View file

@ -1,5 +1,6 @@
package com.simibubi.create.foundation.render.backend.gl; package com.simibubi.create.foundation.render.backend.gl;
import com.simibubi.create.foundation.render.backend.RenderUtil;
import com.simibubi.create.foundation.render.backend.gl.shader.ProgramFogMode; import com.simibubi.create.foundation.render.backend.gl.shader.ProgramFogMode;
import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL20;
@ -53,9 +54,6 @@ public class BasicProgram extends GlProgram {
} }
protected static void uploadMatrixUniform(int uniform, Matrix4f mat) { protected static void uploadMatrixUniform(int uniform, Matrix4f mat) {
Backend.MATRIX_BUFFER.position(0); GL20.glUniformMatrix4fv(uniform, false, RenderUtil.writeMatrix(mat));
mat.write(Backend.MATRIX_BUFFER);
Backend.MATRIX_BUFFER.rewind();
GL20.glUniformMatrix4fv(uniform, false, Backend.MATRIX_BUFFER);
} }
} }

View file

@ -2,7 +2,8 @@ package com.simibubi.create.foundation.render.backend.instancing;
public interface IDynamicInstance { public interface IDynamicInstance {
/** /**
* Called every frame, this can be used to make more dynamic animations. * Called every frame. This can be used to smoothly change instance data
* to allow for fancy animations that could not be achieved on the GPU alone.
*/ */
void beginFrame(); void beginFrame();
} }

View file

@ -2,5 +2,9 @@ package com.simibubi.create.foundation.render.backend.instancing;
public interface ITickableInstance { public interface ITickableInstance {
/**
* Called every tick. This is useful for things that don't have to be smooth,
* or to recalculate something that would only change after a game tick.
*/
void tick(); void tick();
} }

View file

@ -39,14 +39,6 @@ public abstract class InstancedTileRenderer<P extends BasicProgram> {
public abstract void registerMaterials(); public abstract void registerMaterials();
public void tick() { public void tick() {
int ticks = AnimationTickHolder.getTicks();
// Clean up twice a second. This doesn't have to happen every tick,
// but this does need to be run to ensure we don't miss anything.
if (ticks % 10 == 0) {
clean();
}
if (tickableInstances.size() > 0) if (tickableInstances.size() > 0)
tickableInstances.values().forEach(ITickableInstance::tick); tickableInstances.values().forEach(ITickableInstance::tick);
} }
@ -187,16 +179,13 @@ public abstract class InstancedTileRenderer<P extends BasicProgram> {
return renderer; return renderer;
} }
private void clean() {
instances.keySet().removeIf(TileEntity::isRemoved);
}
public void invalidate() { public void invalidate() {
for (RenderMaterial<?, ?> material : materials.values()) { for (RenderMaterial<?, ?> material : materials.values()) {
material.delete(); material.delete();
} }
instances.clear(); instances.clear();
dynamicInstances.clear(); dynamicInstances.clear();
tickableInstances.clear();
} }
public boolean canCreateInstance(TileEntity tile) { public boolean canCreateInstance(TileEntity tile) {

View file

@ -16,7 +16,7 @@ public class ModelData extends BasicData {
} }
public ModelData setTransform(MatrixStack stack) { public ModelData setTransform(MatrixStack stack) {
matrices = RenderUtil.bufferMatrices(stack.peek().getModel(), stack.peek().getNormal()); matrices = RenderUtil.writeMatrixStack(stack);
return this; return this;
} }

View file

@ -3,9 +3,19 @@
"package": "com.simibubi.create.foundation.mixin", "package": "com.simibubi.create.foundation.mixin",
"compatibilityLevel": "JAVA_8", "compatibilityLevel": "JAVA_8",
"refmap": "create.refmap.json", "refmap": "create.refmap.json",
"client": ["AddRemoveTileMixin", "CancelTileEntityRenderMixin", "FogColorTrackerMixin", "LightUpdateMixin", "NetworkLightUpdateMixin", "RenderHooksMixin", "ShaderCloseMixin"], "mixins": ["StepSoundMixin"],
"client": [
"TileAddMixin",
"CancelTileEntityRenderMixin",
"FogColorTrackerMixin",
"LightUpdateMixin",
"NetworkLightUpdateMixin",
"RenderHooksMixin",
"ShaderCloseMixin",
"TileRemoveMixin"
],
"injectors": { "injectors": {
"defaultRequire": 1 "defaultRequire": 1
}, },
"minVersion": "0.8", "mixins": ["StepSoundMixin"] "minVersion": "0.8"
} }