Skip to content

Commit

Permalink
Optimize block state predicate and other improvements
Browse files Browse the repository at this point in the history
- Optimize predicate returned by PropertiesParsingHelper.parseBlockStates
- Optimize predicate created by BaseCTMProperties.parseBiomes
- Avoid slow Guava immutable sets and maps when parsing properties
- Fix CustomBlockLayers not resetting disableSolidCheck on resource reload
- Do not try to retrieve custom block layer if no layer predicates were defined
- Make CTM loading and model wrapping not use static state
- Improve how sprites are added to standard overlay emitter
- Rename overlay renderer to overlay emitter
- Annotate CTMProperties implementation class members with Nullable where appropriate
- Remove ApiState.Internal annotations as they were applied to members not in the API
- Update Fabric API dependency to use new mod ID and require Fabric API >=0.59.0
- Rewrite README and mod description
  • Loading branch information
PepperCode1 committed Aug 4, 2023
1 parent e1fd483 commit c1c4940
Show file tree
Hide file tree
Showing 30 changed files with 767 additions and 642 deletions.
18 changes: 7 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
# Continuity

Continuity is a Fabric mod built around modern APIs to allow for the most efficient connected textures experience possible. It is designed to provide full Optifine parity for all resource packs that use the Optifine CTM format. Continuity also supports Optifine-format emissive textures for block and item models.
Continuity is a Fabric mod that allows resource packs that use the OptiFine connected textures format, OptiFine emissive textures format (only for blocks and item models), or OptiFine custom block layers format to work without OptiFine.

Continuity depends on the Fabric API and is client-side only. It includes two built-in resource packs - one provides default connected textures, similar to Optifine, and the other provides a fix for glass pane culling.
Continuity depends on Fabric API and is client-side only. It includes two built-in resource packs. The Default Connected Textures pack provides connected textures for glass, sandstone, and bookshelves, similar to the built-in connected textures provided by OptiFine. The Glass Pane Culling Fix pack culls faces between vertically stacked glass panes to make them look seamless with connected textures.

## Forge Version
Formally, Continuity implements the Continuity connected textures specification, Continuity emissive textures specification, and Continuity custom block layers specification. All of these are extensions of the corresponding OptiFine specification and were created to provide more features to resource pack authors. The documentation for the Continuity specifications can be found at the [Continuity wiki](https://github.com/PepperCode1/Continuity/wiki).

A Forge version of Continuity will not be made for the following reasons.

- Forge's model API: It is vastly different from Fabric's model API (FRAPI), which is readily utilized by Continuity, and switching to it would not only require an incredible amount of effort, but also result in the mod being extremely inefficient.
- Optifine: It has native support for Forge and provides the same features as Continuity.
An official Forge version of Continuity is not planned at this time due to major technical differences between the Fabric and Forge APIs. An official Forge version of Continuity may be considered if these differences are minimized, possibly via the use of libraries.

### Links

[CurseForge Page](https://www.curseforge.com/minecraft/mc-mods/continuity)

[Modrinth Page](https://modrinth.com/mod/continuity)

[CurseForge Page](https://www.curseforge.com/minecraft/mc-mods/continuity) \
[Modrinth Page](https://modrinth.com/mod/continuity) \
[Wiki](https://github.com/PepperCode1/Continuity/wiki) \
[Discord](https://discord.gg/7rnTYXu)
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ repositories {
url 'https://api.modrinth.com/maven'
}
maven {
name "VRAM"
name 'VRAM'
url 'https://maven.vram.io'
}
maven {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ public interface CTMLoader<T extends CTMProperties> {
QuadProcessorFactory<T> getProcessorFactory();

static <T extends CTMProperties> CTMLoader<T> of(CTMPropertiesFactory<T> propertiesFactory, QuadProcessorFactory<T> processorFactory) {
return new CTMLoader<T>() {
return new CTMLoader<>() {
@Override
public CTMPropertiesFactory<T> getPropertiesFactory() {
return propertiesFactory;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
import me.pepperbell.continuity.client.resource.CTMPropertiesLoader;
import me.pepperbell.continuity.client.resource.EmissiveSuffixLoader;
import me.pepperbell.continuity.client.resource.ModelWrappingHandler;
import me.pepperbell.continuity.client.resource.ResourcePackUtil;
import me.pepperbell.continuity.client.util.biome.BiomeHolderManager;
import net.minecraft.block.BlockState;
import net.minecraft.client.color.block.BlockColors;
import net.minecraft.client.render.model.ModelLoader;
Expand All @@ -40,18 +38,15 @@ public class ModelLoaderMixin {
@Final
private Map<Identifier, Pair<SpriteAtlasTexture, SpriteAtlasTexture.Data>> spriteAtlasData;

@Unique
private ModelWrappingHandler continuity$wrappingHandler;
@Unique
private BlockState continuity$currentBlockState;

@Inject(method = "<init>(Lnet/minecraft/resource/ResourceManager;Lnet/minecraft/client/color/block/BlockColors;Lnet/minecraft/util/profiler/Profiler;I)V", at = @At(value = "INVOKE_STRING", target = "Lnet/minecraft/util/profiler/Profiler;push(Ljava/lang/String;)V", args = "ldc=missing_model", shift = At.Shift.BEFORE))
private void continuity$afterStoreArgs(ResourceManager resourceManager, BlockColors blockColors, Profiler profiler, int mipmap, CallbackInfo ci) {
// TODO: move these to the very beginning of resource reload
ResourcePackUtil.setup(resourceManager);
BiomeHolderManager.clearCache();

continuity$wrappingHandler = new ModelWrappingHandler(CTMPropertiesLoader.loadAllWithState(resourceManager));
EmissiveSuffixLoader.load(resourceManager);
CTMPropertiesLoader.clearAll();
CTMPropertiesLoader.loadAll(resourceManager);
}

@Inject(method = "method_4716(Lnet/minecraft/block/BlockState;)V", at = @At("HEAD"))
Expand All @@ -62,24 +57,24 @@ public class ModelLoaderMixin {
@Inject(method = "addModel(Lnet/minecraft/client/util/ModelIdentifier;)V", at = @At(value = "TAIL"), locals = LocalCapture.CAPTURE_FAILHARD)
private void continuity$afterAddModel(ModelIdentifier id, CallbackInfo ci, UnbakedModel model) {
if (continuity$currentBlockState != null) {
ModelWrappingHandler.onAddBlockStateModel(id, continuity$currentBlockState);
if (continuity$wrappingHandler != null) {
continuity$wrappingHandler.onAddBlockStateModel(id, continuity$currentBlockState);
}
continuity$currentBlockState = null;
}
}

@Inject(method = "<init>(Lnet/minecraft/resource/ResourceManager;Lnet/minecraft/client/color/block/BlockColors;Lnet/minecraft/util/profiler/Profiler;I)V", at = @At(value = "INVOKE_STRING", target = "Lnet/minecraft/util/profiler/Profiler;swap(Ljava/lang/String;)V", args = "ldc=textures"))
private void continuity$onFinishAddingModels(ResourceManager resourceManager, BlockColors blockColors, Profiler profiler, int mipmap, CallbackInfo ci) {
ModelWrappingHandler.wrapCTMModels(unbakedModels, modelsToBake);
if (continuity$wrappingHandler != null) {
continuity$wrappingHandler.wrapCTMModels(unbakedModels, modelsToBake);
}
}

@Inject(method = "<init>(Lnet/minecraft/resource/ResourceManager;Lnet/minecraft/client/color/block/BlockColors;Lnet/minecraft/util/profiler/Profiler;I)V", at = @At("TAIL"))
private void continuity$onTailInit(ResourceManager resourceManager, BlockColors blockColors, Profiler profiler, int mipmap, CallbackInfo ci) {
ModelWrappingHandler.wrapEmissiveModels(spriteAtlasData, unbakedModels, modelsToBake);

CTMPropertiesLoader.clearAll();

// TODO: move these to the very end of resource reload
ResourcePackUtil.clear();
BiomeHolderManager.refreshHolders();
if (continuity$wrappingHandler != null) {
continuity$wrappingHandler.wrapEmissiveModels(unbakedModels, modelsToBake, spriteAtlasData);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
public class RenderLayersMixin {
@Inject(method = "getBlockLayer(Lnet/minecraft/block/BlockState;)Lnet/minecraft/client/render/RenderLayer;", at = @At("HEAD"), cancellable = true)
private static void continuity$onHeadGetBlockLayer(BlockState state, CallbackInfoReturnable<RenderLayer> cir) {
if (ContinuityConfig.INSTANCE.customBlockLayers.get()) {
if (!CustomBlockLayers.isEmpty() && ContinuityConfig.INSTANCE.customBlockLayers.get()) {
RenderLayer layer = CustomBlockLayers.getLayer(state);
if (layer != null) {
cir.setReturnValue(layer);
Expand All @@ -25,7 +25,7 @@ public class RenderLayersMixin {

@Inject(method = "getMovingBlockLayer(Lnet/minecraft/block/BlockState;)Lnet/minecraft/client/render/RenderLayer;", at = @At("HEAD"), cancellable = true)
private static void continuity$onHeadGetMovingBlockLayer(BlockState state, CallbackInfoReturnable<RenderLayer> cir) {
if (ContinuityConfig.INSTANCE.customBlockLayers.get()) {
if (!CustomBlockLayers.isEmpty() && ContinuityConfig.INSTANCE.customBlockLayers.get()) {
RenderLayer layer = CustomBlockLayers.getLayer(state);
if (layer != null) {
cir.setReturnValue(layer == RenderLayer.getTranslucent() ? RenderLayer.getTranslucentMovingBlock() : layer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,18 @@
import net.minecraft.world.biome.Biome;

public class BaseProcessingPredicate implements ProcessingPredicate {
@Nullable
protected Set<Identifier> matchTilesSet;
@Nullable
protected EnumSet<Direction> faces;
@Nullable
protected Predicate<Biome> biomePredicate;
@Nullable
protected IntPredicate heightPredicate;
@Nullable
protected Predicate<String> blockEntityNamePredicate;

public BaseProcessingPredicate(Set<Identifier> matchTilesSet, EnumSet<Direction> faces, Predicate<Biome> biomePredicate, IntPredicate heightPredicate, Predicate<String> blockEntityNamePredicate) {
public BaseProcessingPredicate(@Nullable Set<Identifier> matchTilesSet, @Nullable EnumSet<Direction> faces, @Nullable Predicate<Biome> biomePredicate, @Nullable IntPredicate heightPredicate, @Nullable Predicate<String> blockEntityNamePredicate) {
this.matchTilesSet = matchTilesSet;
this.faces = faces;
this.biomePredicate = biomePredicate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.function.Supplier;

import org.apache.commons.lang3.ArrayUtils;
import org.jetbrains.annotations.Nullable;

import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntMaps;
Expand Down Expand Up @@ -54,9 +55,10 @@ public class CompactCTMQuadProcessor extends ConnectingQuadProcessor {
ArrayUtils.shift(map[7], 1);
}

@Nullable
protected Sprite[] replacementSprites;

public CompactCTMQuadProcessor(Sprite[] sprites, ProcessingPredicate processingPredicate, ConnectionPredicate connectionPredicate, boolean innerSeams, Sprite[] replacementSprites) {
public CompactCTMQuadProcessor(Sprite[] sprites, ProcessingPredicate processingPredicate, ConnectionPredicate connectionPredicate, boolean innerSeams, @Nullable Sprite[] replacementSprites) {
super(sprites, processingPredicate, connectionPredicate, innerSeams);
this.replacementSprites = replacementSprites;
}
Expand Down Expand Up @@ -668,7 +670,7 @@ public QuadProcessor createProcessor(CompactConnectingCTMProperties properties,
return createProcessor(properties, sprites, replacementSprites);
}

public QuadProcessor createProcessor(CompactConnectingCTMProperties properties, Sprite[] sprites, Sprite[] replacementSprites) {
public QuadProcessor createProcessor(CompactConnectingCTMProperties properties, Sprite[] sprites, @Nullable Sprite[] replacementSprites) {
return new CompactCTMQuadProcessor(sprites, BaseProcessingPredicate.fromProperties(properties), properties.getConnectionPredicate(), properties.getInnerSeams(), replacementSprites);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ public final class ProcessingDataKeys {
public static final ProcessingDataKey<BaseProcessingPredicate.BiomeCache> BIOME_CACHE_KEY = create("biome_cache", BaseProcessingPredicate.BiomeCache::new, BaseProcessingPredicate.BiomeCache::reset);
public static final ProcessingDataKey<BaseProcessingPredicate.BlockEntityNameCache> BLOCK_ENTITY_NAME_CACHE_KEY = create("block_entity_name_cache", BaseProcessingPredicate.BlockEntityNameCache::new, BaseProcessingPredicate.BlockEntityNameCache::reset);
public static final ProcessingDataKey<CompactCTMQuadProcessor.VertexContainer> VERTEX_CONTAINER_KEY = create("vertex_container", CompactCTMQuadProcessor.VertexContainer::new);
public static final ProcessingDataKey<StandardOverlayQuadProcessor.OverlayRendererPool> STANDARD_OVERLAY_RENDERER_POOL_KEY = create("standard_overlay_renderer_pool", StandardOverlayQuadProcessor.OverlayRendererPool::new, StandardOverlayQuadProcessor.OverlayRendererPool::reset);
public static final ProcessingDataKey<SimpleOverlayQuadProcessor.OverlayRendererPool> SIMPLE_OVERLAY_RENDERER_POOL_KEY = create("simple_overlay_renderer_pool", SimpleOverlayQuadProcessor.OverlayRendererPool::new, SimpleOverlayQuadProcessor.OverlayRendererPool::reset);
public static final ProcessingDataKey<StandardOverlayQuadProcessor.OverlayEmitterPool> STANDARD_OVERLAY_EMITTER_POOL_KEY = create("standard_overlay_emitter_pool", StandardOverlayQuadProcessor.OverlayEmitterPool::new, StandardOverlayQuadProcessor.OverlayEmitterPool::reset);
public static final ProcessingDataKey<SimpleOverlayQuadProcessor.OverlayEmitterPool> SIMPLE_OVERLAY_EMITTER_POOL_KEY = create("simple_overlay_emitter_pool", SimpleOverlayQuadProcessor.OverlayEmitterPool::new, SimpleOverlayQuadProcessor.OverlayEmitterPool::reset);

private static <T> ProcessingDataKey<T> create(String id, Supplier<T> valueSupplier) {
return ProcessingDataKeyRegistry.get().registerKey(ContinuityClient.asId(id), valueSupplier);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import java.util.function.IntPredicate;
import java.util.function.Predicate;

import org.jetbrains.annotations.Nullable;

import me.pepperbell.continuity.api.client.ProcessingDataProvider;
import me.pepperbell.continuity.client.processor.BaseProcessingPredicate;
import me.pepperbell.continuity.client.properties.BaseCTMProperties;
Expand All @@ -19,7 +21,7 @@
import net.minecraft.world.biome.Biome;

public class OverlayProcessingPredicate extends BaseProcessingPredicate {
public OverlayProcessingPredicate(Set<Identifier> matchTilesSet, EnumSet<Direction> faces, Predicate<Biome> biomePredicate, IntPredicate heightPredicate, Predicate<String> blockEntityNamePredicate) {
public OverlayProcessingPredicate(@Nullable Set<Identifier> matchTilesSet, @Nullable EnumSet<Direction> faces, @Nullable Predicate<Biome> biomePredicate, @Nullable IntPredicate heightPredicate, @Nullable Predicate<String> blockEntityNamePredicate) {
super(matchTilesSet, faces, biomePredicate, heightPredicate, blockEntityNamePredicate);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import java.util.function.Consumer;
import java.util.function.Supplier;

import org.jetbrains.annotations.Nullable;

import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import me.pepperbell.continuity.api.client.QuadProcessor;
import me.pepperbell.continuity.client.processor.ProcessingDataKeys;
Expand All @@ -28,10 +30,11 @@

public class SimpleOverlayQuadProcessor extends SimpleQuadProcessor {
protected int tintIndex;
@Nullable
protected BlockState tintBlock;
protected RenderMaterial material;

public SimpleOverlayQuadProcessor(SpriteProvider spriteProvider, ProcessingPredicate processingPredicate, int tintIndex, BlockState tintBlock, BlendMode layer) {
public SimpleOverlayQuadProcessor(SpriteProvider spriteProvider, ProcessingPredicate processingPredicate, int tintIndex, @Nullable BlockState tintBlock, BlendMode layer) {
super(spriteProvider, processingPredicate);
this.tintIndex = tintIndex;
this.tintBlock = tintBlock;
Expand All @@ -42,16 +45,16 @@ public SimpleOverlayQuadProcessor(SpriteProvider spriteProvider, ProcessingPredi
public ProcessingResult processQuad(MutableQuadView quad, Sprite sprite, BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, int pass, int processorIndex, ProcessingContext context) {
if (processingPredicate.shouldProcessQuad(quad, sprite, blockView, state, pos, context)) {
Sprite newSprite = spriteProvider.getSprite(quad, sprite, blockView, state, pos, randomSupplier, context);
if (!TextureUtil.isMissingSprite(newSprite)) {
OverlayRenderer renderer = context.getData(ProcessingDataKeys.SIMPLE_OVERLAY_RENDERER_POOL_KEY).getRenderer();
renderer.prepare(quad.lightFace(), newSprite, RenderUtil.getTintColor(tintBlock, blockView, pos, tintIndex), material);
context.addEmitterConsumer(renderer);
if (newSprite != null && !TextureUtil.isMissingSprite(newSprite)) {
OverlayEmitter emitter = context.getData(ProcessingDataKeys.SIMPLE_OVERLAY_EMITTER_POOL_KEY).get();
emitter.prepare(quad.lightFace(), newSprite, RenderUtil.getTintColor(tintBlock, blockView, pos, tintIndex), material);
context.addEmitterConsumer(emitter);
}
}
return ProcessingResult.CONTINUE;
}

public static class OverlayRenderer implements Consumer<QuadEmitter> {
public static class OverlayEmitter implements Consumer<QuadEmitter> {
protected Direction face;
protected Sprite sprite;
protected int color;
Expand All @@ -70,17 +73,17 @@ public void prepare(Direction face, Sprite sprite, int color, RenderMaterial mat
}
}

public static class OverlayRendererPool {
protected final List<OverlayRenderer> list = new ObjectArrayList<>();
public static class OverlayEmitterPool {
protected final List<OverlayEmitter> list = new ObjectArrayList<>();
protected int nextIndex = 0;

public OverlayRenderer getRenderer() {
public OverlayEmitter get() {
if (nextIndex >= list.size()) {
list.add(new OverlayRenderer());
list.add(new OverlayEmitter());
}
OverlayRenderer renderer = list.get(nextIndex);
OverlayEmitter emitter = list.get(nextIndex);
nextIndex++;
return renderer;
return emitter;
}

public void reset() {
Expand Down
Loading

0 comments on commit c1c4940

Please sign in to comment.