diff --git a/build.gradle b/build.gradle
index 1fcd3303..ee90f77d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -17,7 +17,7 @@ buildscript {
}
plugins {
id 'net.franz-becker.gradle-lombok' version '1.5'
- id 'com.matthewprenger.cursegradle' version '1.0.9'
+ id 'com.matthewprenger.cursegradle' version '1.4.0'
}
apply plugin: 'net.minecraftforge.gradle.forge'
@@ -75,6 +75,9 @@ eclipse.classpath.file {
}
}
+compileJava.options.encoding = 'UTF-8'
+javadoc.options.encoding = 'UTF-8'
+
tasks.eclipse.dependsOn installLombok
processResources {
@@ -170,11 +173,9 @@ curseforge {
changelog = System.getenv('CHANGELOG') == null || System.getenv('CHANGELOG').equals('none') ? getChangelogText() : System.getenv('CHANGELOG')
changelogType = 'html'
releaseType = project.curse_type
- addGameVersion '1.12.1'
addGameVersion '1.12.2'
mainArtifact(jar) {
displayName = "CTM - ${version}"
}
- addArtifact(apiJar)
}
}
diff --git a/changelog.txt b/changelog.txt
index 9d34dc7d..92988bff 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,3 +1,27 @@
+1.0.2:
+Fixes
+- #115 Fix CTM models using the no-layer cache on the incorrect model, should fix the iChisel's preview mode
+
+1.0.1:
+Changes
+- If no layer is specified, the default will now be null (same as textures with no metadata) rather than SOLID
+- Now requires forge 2807 at a minimum (for item lighting fixes)
+Fixes
+- Eliminated unnecessary memory overhead from model loading (asiekierka)
+- Fix ctm models having duplicate quads when rendered as an item and containing null-layer quads
+- Cache quad subsets for no-layer cases a bit, should speed up item model rendering marginally
+
+1.0.0:
+New
+- Added 'use_actual_state' flag to allow connected textures to compare contextual states (such as glass panes)
+Changes
+- Remove deprecated APIs
+Fixes
+- Fix models with CTM overrides not inheriting from texture data
+- Fix disableCTM config not doing anything
+- Fix CTM models not working properly with forge blockstate features (retexture, uvlock, etc.)
+- #89 Fix CTM models not considering item overrides
+
0.3.3:
New
- Models with light data will now render properly in item form. NOTE: This requires Forge 2781 or higher!
diff --git a/gradle.properties b/gradle.properties
index 3329bd98..f46c0022 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,7 +1,7 @@
-mod_version=0.4.0
+mod_version=1.0.2
minecraft_version=1.12.2
-forge_version=14.23.4.2705
+forge_version=14.23.5.2807
-curse_type=beta
+curse_type=release
projectId=267602
github_project=Chisel-Team/ConnectedTexturesMod
\ No newline at end of file
diff --git a/src/main/java/team/chisel/ctm/CTM.java b/src/main/java/team/chisel/ctm/CTM.java
index 22bc6807..371af557 100644
--- a/src/main/java/team/chisel/ctm/CTM.java
+++ b/src/main/java/team/chisel/ctm/CTM.java
@@ -20,7 +20,7 @@
import team.chisel.ctm.client.util.CTMPackReloadListener;
import team.chisel.ctm.client.util.TextureMetadataHandler;
-@Mod(name = MOD_NAME, modid = MOD_ID, version = VERSION, dependencies = "before:chisel;after:forge@[14.23,)", clientSideOnly = true)
+@Mod(name = MOD_NAME, modid = MOD_ID, version = VERSION, dependencies = "before:chisel;after:forge@[14.23.5.2807,)", clientSideOnly = true)
public class CTM {
public static final String MOD_ID = "ctm";
@@ -38,6 +38,7 @@ public void preInit(FMLPreInitializationEvent event) {
TextureTypeRegistry.preInit(event);
ModelLoaderRegistry.registerLoader(ModelLoaderCTM.INSTANCE);
+ MinecraftForge.EVENT_BUS.register(ModelLoaderCTM.INSTANCE);
Minecraft.getMinecraft().metadataSerializer_.registerMetadataSectionType(new IMetadataSectionCTM.Serializer(), IMetadataSectionCTM.class);
MinecraftForge.EVENT_BUS.register(TextureMetadataHandler.INSTANCE);
diff --git a/src/main/java/team/chisel/ctm/Configurations.java b/src/main/java/team/chisel/ctm/Configurations.java
index 4364f3d4..530a36cc 100644
--- a/src/main/java/team/chisel/ctm/Configurations.java
+++ b/src/main/java/team/chisel/ctm/Configurations.java
@@ -1,8 +1,16 @@
package team.chisel.ctm;
+import net.minecraft.client.Minecraft;
import net.minecraftforge.common.config.Config;
+import net.minecraftforge.common.config.Config.Type;
+import net.minecraftforge.common.config.ConfigManager;
+import net.minecraftforge.fml.client.event.ConfigChangedEvent;
+import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import team.chisel.ctm.client.model.AbstractCTMBakedModel;
@Config(modid = CTM.MOD_ID)
+@EventBusSubscriber(modid = CTM.MOD_ID)
public class Configurations {
@Config.Comment("Disable connected textures entirely.")
@@ -11,4 +19,12 @@ public class Configurations {
@Config.Comment("Choose whether the inside corner is disconnected on a CTM block - http://imgur.com/eUywLZ4")
public static boolean connectInsideCTM = false;
+ @SubscribeEvent
+ public static void onConfigChange(ConfigChangedEvent event) {
+ if (event.getModID().equals(CTM.MOD_ID)) {
+ ConfigManager.sync(CTM.MOD_ID, Type.INSTANCE);
+ AbstractCTMBakedModel.invalidateCaches();
+ Minecraft.getMinecraft().renderGlobal.loadRenderers();
+ }
+ }
}
diff --git a/src/main/java/team/chisel/ctm/api/IFacade.java b/src/main/java/team/chisel/ctm/api/IFacade.java
index b1aa17d5..8e098e51 100644
--- a/src/main/java/team/chisel/ctm/api/IFacade.java
+++ b/src/main/java/team/chisel/ctm/api/IFacade.java
@@ -30,7 +30,7 @@ public interface IFacade {
* The Blocks position
* @param side
* The side being rendered, NOT the side being connected from.
- *
+ *
* This value can be null if no side is specified. Please handle this appropriately.
* @param connection
* The position of the block being connected to.
diff --git a/src/main/java/team/chisel/ctm/api/model/IModelCTM.java b/src/main/java/team/chisel/ctm/api/model/IModelCTM.java
index 5d23baab..3918e51b 100644
--- a/src/main/java/team/chisel/ctm/api/model/IModelCTM.java
+++ b/src/main/java/team/chisel/ctm/api/model/IModelCTM.java
@@ -7,8 +7,10 @@
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.util.BlockRenderLayer;
+import net.minecraft.util.EnumFacing;
import net.minecraftforge.client.model.IModel;
import team.chisel.ctm.api.texture.ICTMTexture;
+import team.chisel.ctm.api.texture.IChiselFace;
public interface IModelCTM extends IModel {
@@ -16,9 +18,24 @@ public interface IModelCTM extends IModel {
void load();
- Collection> getCTMTextures();
+ @Deprecated
+ Collection> getChiselTextures();
+
+ default Collection> getCTMTextures() {
+ return getChiselTextures();
+ }
ICTMTexture> getTexture(String iconName);
+
+ @Deprecated
+ default IChiselFace getFace(EnumFacing facing) {
+ return null;
+ }
+
+ @Deprecated
+ default IChiselFace getDefaultFace() {
+ return null;
+ }
boolean canRenderInLayer(IBlockState state, BlockRenderLayer layer);
diff --git a/src/main/java/team/chisel/ctm/api/texture/ICTMTexture.java b/src/main/java/team/chisel/ctm/api/texture/ICTMTexture.java
index 2b913473..3daadf4b 100644
--- a/src/main/java/team/chisel/ctm/api/texture/ICTMTexture.java
+++ b/src/main/java/team/chisel/ctm/api/texture/ICTMTexture.java
@@ -48,11 +48,12 @@ public interface ICTMTexture {
/**
* The layer this texture requires. The layers will be prioritized for a face in the order:
- *
- * {@link BlockRenderLayer#TRANSLUCENT}
- * {@link BlockRenderLayer#CUTOUT}
- * {@link BlockRenderLayer#SOLID}
- *
+ *
+ * - {@link BlockRenderLayer#TRANSLUCENT}
+ * - {@link BlockRenderLayer#CUTOUT}
+ * - {@link BlockRenderLayer#SOLID}
+ *
+ *
* @return The layer of this texture.
*/
@Nullable
diff --git a/src/main/java/team/chisel/ctm/api/texture/IChiselFace.java b/src/main/java/team/chisel/ctm/api/texture/IChiselFace.java
new file mode 100644
index 00000000..25e933ae
--- /dev/null
+++ b/src/main/java/team/chisel/ctm/api/texture/IChiselFace.java
@@ -0,0 +1,15 @@
+package team.chisel.ctm.api.texture;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+import net.minecraft.client.renderer.texture.TextureAtlasSprite;
+
+@Deprecated
+public interface IChiselFace {
+
+ List> getTextureList();
+
+ @Nonnull TextureAtlasSprite getParticle();
+}
\ No newline at end of file
diff --git a/src/main/java/team/chisel/ctm/client/model/AbstractCTMBakedModel.java b/src/main/java/team/chisel/ctm/client/model/AbstractCTMBakedModel.java
index bb299193..66110d6c 100644
--- a/src/main/java/team/chisel/ctm/client/model/AbstractCTMBakedModel.java
+++ b/src/main/java/team/chisel/ctm/client/model/AbstractCTMBakedModel.java
@@ -1,8 +1,11 @@
package team.chisel.ctm.client.model;
+import java.util.Collection;
+import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
@@ -15,6 +18,7 @@
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
@@ -44,11 +48,14 @@
import net.minecraft.item.ItemStack;
import net.minecraft.util.BlockRenderLayer;
import net.minecraft.util.EnumFacing;
+import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraftforge.client.MinecraftForgeClient;
+import net.minecraftforge.client.model.ModelLoader;
import net.minecraftforge.common.model.TRSRTransformation;
import team.chisel.ctm.api.model.IModelCTM;
import team.chisel.ctm.api.texture.ICTMTexture;
+import team.chisel.ctm.api.texture.IChiselFace;
import team.chisel.ctm.api.util.RenderContextList;
import team.chisel.ctm.client.asm.CTMCoreMethods;
import team.chisel.ctm.client.state.CTMExtendedState;
@@ -81,6 +88,16 @@ public IBakedModel handleItemState(IBakedModel originalModel, ItemStack stack, W
block = ((ItemBlock) stack.getItem()).getBlock();
}
final IBlockState state = block == null ? null : block.getDefaultState();
+ if (!stack.isEmpty() && stack.getItem().hasCustomProperties()) { // Handle parent model's overrides
+ @SuppressWarnings("deprecation") // Duplicate super logic, but called on the parent model overrides
+ ResourceLocation location = parent.getOverrides().applyOverride(stack, world, entity);
+ if (location != null) {
+ // Use the override's location as cache key
+ ModelResourceLocation overrideLoc = ModelLoader.getInventoryVariant(location.toString());
+ IBakedModel newParent = Minecraft.getMinecraft().getRenderItem().getItemModelMesher().getModelManager().getModel(overrideLoc);
+ return itemcache.get(overrideLoc, () -> withNewParent(newParent).createModel(state, model, null, 0));
+ }
+ }
ModelResourceLocation mrl = ModelUtil.getMesh(stack);
if (mrl == null) {
// this must be a missing/invalid model
@@ -145,6 +162,9 @@ public int hashCode() {
protected final ListMultimap genQuads = MultimapBuilder.enumKeys(BlockRenderLayer.class).arrayListValues().build();
protected final Table> faceQuads = Tables.newCustomTable(Maps.newEnumMap(BlockRenderLayer.class), () -> Maps.newEnumMap(EnumFacing.class));
+
+ private final EnumMap> noLayerCache = new EnumMap<>(EnumFacing.class);
+ private ImmutableList noSideNoLayerCache;
@Override
@SneakyThrows
@@ -163,7 +183,7 @@ public int hashCode() {
if (Minecraft.getMinecraft().world != null && state instanceof CTMExtendedState) {
ProfileUtil.start("state_creation");
CTMExtendedState ext = (CTMExtendedState) state;
- RenderContextList ctxList = ext.getContextList(ext.getClean(), model);
+ RenderContextList ctxList = ext.getContextList(ext.getClean(), baked);
Object2LongMap> serialized = ctxList.serialized();
ProfileUtil.endAndStart("model_creation");
@@ -180,11 +200,22 @@ public int hashCode() {
if (side != null && layer != null) {
ret = baked.faceQuads.get(layer, side);
} else if (side != null) {
- ret = baked.faceQuads.column(side).values().stream().flatMap(List::stream).collect(Collectors.toList());
+ final AbstractCTMBakedModel _baked = baked;
+ ret = baked.noLayerCache.computeIfAbsent(side, f -> ImmutableList.copyOf(_baked.faceQuads.column(f).values()
+ .stream()
+ .flatMap(List::stream)
+ .distinct()
+ .collect(Collectors.toList())));
} else if (layer != null) {
ret = baked.genQuads.get(layer);
} else {
- ret = Lists.newArrayList(baked.genQuads.values());
+ ret = baked.noSideNoLayerCache;
+ if (ret == null) {
+ ret = baked.noSideNoLayerCache = ImmutableList.copyOf(baked.genQuads.values()
+ .stream()
+ .distinct()
+ .collect(Collectors.toList()));
+ }
}
ProfileUtil.end();
@@ -223,9 +254,11 @@ public boolean isBuiltInRenderer() {
return false;
}
+ @SuppressWarnings("deprecation")
@Override
public @Nonnull TextureAtlasSprite getParticleTexture() {
- return this.parent.getParticleTexture();
+ IChiselFace face = this.model.getDefaultFace();
+ return face != null ? face.getParticle() : this.parent.getParticleTexture();
}
@Override
@@ -261,5 +294,49 @@ public Pair extends IBakedModel, Matrix4f> handlePerspective(ItemCameraTransfo
protected static final BlockRenderLayer[] LAYERS = BlockRenderLayer.values();
protected abstract AbstractCTMBakedModel createModel(IBlockState state, @Nonnull IModelCTM model, RenderContextList ctx, long rand);
+
+ protected /* abstract */ AbstractCTMBakedModel withNewParent(@Nonnull IBakedModel parent) {
+ return new ModelBakedCTM(getModel(), parent);
+ }
+
+ private T applyToParent(long rand, Function func) {
+ IBakedModel parent = getParent(rand);
+ if (parent instanceof AbstractCTMBakedModel) {
+ return func.apply((AbstractCTMBakedModel) parent);
+ }
+ return null;
+ }
+
+ protected ICTMTexture> getOverrideTexture(long rand, int tintIndex, String iconName) {
+ ICTMTexture> ret = getModel().getOverrideTexture(tintIndex, iconName);
+ if (ret == null) {
+ ret = applyToParent(rand, parent -> parent.getOverrideTexture(rand, tintIndex, iconName));
+ }
+ return ret;
+ }
+
+ protected ICTMTexture> getTexture(long rand, String iconName) {
+ ICTMTexture> ret = getModel().getTexture(iconName);
+ if (ret == null) {
+ ret = applyToParent(rand, parent -> parent.getTexture(rand, iconName));
+ }
+ return ret;
+ }
+
+ protected TextureAtlasSprite getOverrideSprite(long rand, int tintIndex) {
+ TextureAtlasSprite ret = getModel().getOverrideSprite(tintIndex);
+ if (ret == null) {
+ ret = applyToParent(rand, parent -> parent.getOverrideSprite(rand, tintIndex));
+ }
+ return ret;
+ }
+ public Collection> getCTMTextures() {
+ ImmutableList.Builder> builder = ImmutableList.builder();
+ builder.addAll(getModel().getCTMTextures());
+ if (getParent() instanceof AbstractCTMBakedModel) {
+ builder.addAll(((AbstractCTMBakedModel)getParent()).getCTMTextures());
+ }
+ return builder.build();
+ }
}
diff --git a/src/main/java/team/chisel/ctm/client/model/ModelBakedCTM.java b/src/main/java/team/chisel/ctm/client/model/ModelBakedCTM.java
index fa60bbf4..a2b82dc0 100644
--- a/src/main/java/team/chisel/ctm/client/model/ModelBakedCTM.java
+++ b/src/main/java/team/chisel/ctm/client/model/ModelBakedCTM.java
@@ -61,13 +61,13 @@ protected AbstractCTMBakedModel createModel(@Nullable IBlockState state, IModelC
// Gather all quads and map them to their textures
// All quads should have an associated ICTMTexture, so ignore any that do not
for (BakedQuad q : parentQuads) {
- ICTMTexture> tex = this.getModel().getOverrideTexture(q.getTintIndex(), q.getSprite().getIconName());
+ ICTMTexture> tex = this.getOverrideTexture(rand, q.getTintIndex(), q.getSprite().getIconName());
if (tex == null) {
- tex = this.getModel().getTexture(q.getSprite().getIconName());
+ tex = this.getTexture(rand, q.getSprite().getIconName());
}
if (tex != null) {
- TextureAtlasSprite spriteReplacement = getModel().getOverrideSprite(q.getTintIndex());
+ TextureAtlasSprite spriteReplacement = this.getOverrideSprite(rand, q.getTintIndex());
if (spriteReplacement != null) {
q = new BakedQuadRetextured(q, spriteReplacement);
}
@@ -91,7 +91,7 @@ protected AbstractCTMBakedModel createModel(@Nullable IBlockState state, IModelC
}
return ret;
}
-
+
@Override
public @Nonnull TextureAtlasSprite getParticleTexture() {
return Optional.ofNullable(getModel().getTexture(getParent().getParticleTexture().getIconName()))
diff --git a/src/main/java/team/chisel/ctm/client/model/ModelCTM.java b/src/main/java/team/chisel/ctm/client/model/ModelCTM.java
index 35d03fff..20debdce 100644
--- a/src/main/java/team/chisel/ctm/client/model/ModelCTM.java
+++ b/src/main/java/team/chisel/ctm/client/model/ModelCTM.java
@@ -47,7 +47,7 @@
import net.minecraftforge.client.model.IModel;
import net.minecraftforge.client.model.ModelLoaderRegistry;
import net.minecraftforge.common.model.IModelState;
-import net.minecraftforge.common.model.TRSRTransformation;
+import net.minecraftforge.common.model.animation.IClip;
import team.chisel.ctm.api.model.IModelCTM;
import team.chisel.ctm.api.texture.ICTMTexture;
import team.chisel.ctm.api.util.TextureInfo;
@@ -62,6 +62,7 @@ public class ModelCTM implements IModelCTM {
private final ModelBlock modelinfo;
private IModel vanillamodel;
+ private Boolean uvlock;
// Populated from overrides data during construction
private final Int2ObjectMap overrides;
@@ -96,6 +97,10 @@ public ModelCTM(ModelBlock modelinfo, IModel vanillamodel, Int2ObjectMap getClip(String name) {
+ return getVanillaParent().getClip(name);
}
@Override
public void load() {}
-
+
@Override
- public Collection> getCTMTextures() {
+ public Collection> getChiselTextures() {
return ImmutableList.>builder().addAll(textures.values()).addAll(textureOverrides.values()).build();
}
@@ -249,50 +263,101 @@ public ICTMTexture> getOverrideTexture(int tintIndex, String sprite) {
@Override
public IModel retexture(ImmutableMap textures) {
try {
- return retexture(this, textures);
+ ModelCTM ret = deepCopy(getVanillaParent().retexture(textures), null, null);
+
+ ret.modelinfo.textures.putAll(textures);
+ for (Entry e : ret.metaOverrides.entrySet()) {
+ ResourceLocation[] additionals = e.getValue().getAdditionalTextures();
+ for (int i = 0; i < additionals.length; i++) {
+ ResourceLocation res = additionals[i];
+ if (res.getResourcePath().startsWith("#")) {
+ String newTexture = textures.get(res.getResourcePath().substring(1));
+ if (newTexture != null) {
+ additionals[i] = new ResourceLocation(newTexture);
+ ret.textureDependencies.add(additionals[i]);
+ }
+ }
+ }
+ }
+ for (int i : ret.overrides.keySet()) {
+ ret.overrides.compute(i, (idx, ele) -> {
+ if (ele.isJsonPrimitive() && ele.getAsJsonPrimitive().isString()) {
+ String newTexture = textures.get(ele.getAsString().substring(1));
+ if (newTexture != null) {
+ ele = new JsonPrimitive(newTexture);
+ ret.textureDependencies.add(new ResourceLocation(ele.getAsString()));
+ }
+ }
+ return ele;
+ });
+ }
+ return ret;
} catch (IOException e) {
e.printStackTrace();
return ModelLoaderRegistry.getMissingModel();
}
}
+
+ @Override
+ public IModel uvlock(boolean value) {
+ if (uvlock == null || uvlock.booleanValue() != value) {
+ IModel newParent = getVanillaParent().uvlock(value);
+ if (newParent != getVanillaParent()) {
+ IModel ret = deepCopyOrMissing(newParent, null, null);
+ if (ret instanceof ModelCTM) {
+ ((ModelCTM) ret).uvlock = value;
+ }
+ return ret;
+ }
+ }
+ return this;
+ }
- private static ModelCTM retexture(ModelCTM current, ImmutableMap textures) throws IOException {
- IModel vanillamodel = current.getVanillaParent().retexture(textures);
+ /**
+ * Allows the model to process custom data from the variant definition.
+ * If unknown data is encountered it should be skipped.
+ * @return a new model, with data applied.
+ */
+ public IModel process(ImmutableMap customData) {
+ return deepCopyOrMissing(getVanillaParent().process(customData), null, null);
+ }
+ public IModel smoothLighting(boolean value) {
+ if (modelinfo.isAmbientOcclusion() != value) {
+ return deepCopyOrMissing(getVanillaParent().smoothLighting(value), value, null);
+ }
+ return this;
+ }
+
+ public IModel gui3d(boolean value) {
+ if (modelinfo.isGui3d() != value) {
+ return deepCopyOrMissing(getVanillaParent().gui3d(value), null, value);
+ }
+ return this;
+ }
+
+ private IModel deepCopyOrMissing(IModel newParent, Boolean ao, Boolean gui3d) {
+ try {
+ return deepCopy(newParent, ao, gui3d);
+ } catch (IOException e) {
+ e.printStackTrace();
+ return ModelLoaderRegistry.getMissingModel();
+ }
+ }
+
+ private ModelCTM deepCopy(IModel newParent, Boolean ao, Boolean gui3d) throws IOException {
// Deep copy logic taken from ModelLoader$VanillaModelWrapper
List parts = new ArrayList<>();
- for (BlockPart part : current.modelinfo.getElements()) {
+ for (BlockPart part : modelinfo.getElements()) {
parts.add(new BlockPart(part.positionFrom, part.positionTo, Maps.newHashMap(part.mapFaces), part.partRotation, part.shade));
}
- ModelBlock newModel = new ModelBlock(current.modelinfo.getParentLocation(), parts,
- Maps.newHashMap(current.modelinfo.textures), current.modelinfo.isAmbientOcclusion(), current.modelinfo.isGui3d(),
- current.modelinfo.getAllTransforms(), Lists.newArrayList(current.modelinfo.getOverrides()));
+ ModelBlock newModel = new ModelBlock(modelinfo.getParentLocation(), parts,
+ Maps.newHashMap(modelinfo.textures), ao == null ? modelinfo.isAmbientOcclusion() : ao, gui3d == null ? modelinfo.isGui3d() : gui3d,
+ modelinfo.getAllTransforms(), Lists.newArrayList(modelinfo.getOverrides()));
- newModel.name = current.modelinfo.name;
- newModel.parent = current.modelinfo.parent;
- ModelCTM ret = new ModelCTM(newModel, vanillamodel, new Int2ObjectArrayMap<>(current.overrides));
-
- ret.modelinfo.textures.putAll(textures);
- for (Entry e : ret.metaOverrides.entrySet()) {
- ResourceLocation[] additionals = e.getValue().getAdditionalTextures();
- for (int i = 0; i < additionals.length; i++) {
- ResourceLocation res = additionals[i];
- if (res.getResourcePath().startsWith("#")) {
- additionals[i] = new ResourceLocation(textures.get(res.getResourcePath().substring(1)));
- ret.textureDependencies.add(additionals[i]);
- }
- }
- }
- for (int i : ret.overrides.keySet()) {
- ret.overrides.compute(i, (idx, ele) -> {
- if (ele.isJsonPrimitive() && ele.getAsJsonPrimitive().isString()) {
- ele = new JsonPrimitive(textures.get(ele.getAsString().substring(1)));
- ret.textureDependencies.add(new ResourceLocation(ele.getAsString()));
- }
- return ele;
- });
- }
- return ret;
+ newModel.name = modelinfo.name;
+ newModel.parent = modelinfo.parent;
+ return new ModelCTM(newModel, newParent, new Int2ObjectArrayMap<>(overrides));
}
}
diff --git a/src/main/java/team/chisel/ctm/client/model/parsing/ModelLoaderCTM.java b/src/main/java/team/chisel/ctm/client/model/parsing/ModelLoaderCTM.java
index 7ade6a1e..aa9de02d 100644
--- a/src/main/java/team/chisel/ctm/client/model/parsing/ModelLoaderCTM.java
+++ b/src/main/java/team/chisel/ctm/client/model/parsing/ModelLoaderCTM.java
@@ -1,6 +1,7 @@
package team.chisel.ctm.client.model.parsing;
import java.io.IOException;
+import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.Map;
@@ -8,7 +9,12 @@
import javax.annotation.Nonnull;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.MapMaker;
import com.google.common.collect.Maps;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
@@ -19,8 +25,11 @@
import net.minecraft.client.resources.IResource;
import net.minecraft.client.resources.IResourceManager;
import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.client.event.ModelBakeEvent;
import net.minecraftforge.client.model.ICustomModelLoader;
import net.minecraftforge.client.model.IModel;
+import net.minecraftforge.fml.common.eventhandler.EventPriority;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import team.chisel.ctm.api.model.IModelCTM;
import team.chisel.ctm.api.model.IModelParser;
@@ -31,13 +40,42 @@ public enum ModelLoaderCTM implements ICustomModelLoader {
private static final Map parserVersions = ImmutableMap.of(1, new ModelParserV1());
private IResourceManager manager;
- private Map jsonCache = Maps.newHashMap();
private Map loadedModels = Maps.newHashMap();
-
+
+ private LoadingCache jsonCache = CacheBuilder.newBuilder().maximumSize(128).build(
+ new CacheLoader() {
+ @Override
+ @SuppressWarnings("null")
+ public JsonElement load(ResourceLocation modelLocation) throws Exception {
+ String path = modelLocation.getResourcePath() + ".json";
+ if (!path.startsWith("models/")) {
+ path = "models/" + path;
+ }
+ ResourceLocation absolute = new ResourceLocation(modelLocation.getResourceDomain(), path);
+
+ try (IResource resource = manager.getResource(absolute);
+ InputStream resourceInputStream = resource.getInputStream();
+ InputStreamReader resourceInputStreamReader = new InputStreamReader(resourceInputStream)) {
+ JsonElement ele = new JsonParser().parse(resourceInputStreamReader);
+ if (ele != null) {
+ return ele;
+ }
+ } catch (Exception e) {}
+
+ return JsonNull.INSTANCE;
+ }
+ }
+ );
+
+ @SubscribeEvent(priority = EventPriority.LOWEST)
+ public void afterModelBaking(ModelBakeEvent event) {
+ jsonCache.invalidateAll();
+ }
+
@Override
public void onResourceManagerReload(@Nonnull IResourceManager resourceManager) {
this.manager = resourceManager;
- jsonCache.clear();
+ jsonCache.invalidateAll();
loadedModels.clear();
}
@@ -47,7 +85,7 @@ public boolean accepts(ResourceLocation modelLocation) {
modelLocation = new ResourceLocation(modelLocation.getResourceDomain(), modelLocation.getResourcePath());
}
- JsonElement json = getJSON(modelLocation);
+ JsonElement json = jsonCache.getUnchecked(modelLocation);
return json.isJsonObject() && json.getAsJsonObject().has("ctm_version");
}
@@ -60,27 +98,7 @@ public IModel loadModel(ResourceLocation modelLocation) throws IOException {
}
return model;
}
-
- @SuppressWarnings("null")
- public @Nonnull JsonElement getJSON(ResourceLocation modelLocation) {
- return jsonCache.computeIfAbsent(modelLocation, res -> {
- String path = modelLocation.getResourcePath() + ".json";
- if (!path.startsWith("models/")) {
- path = "models/" + path;
- }
- ResourceLocation absolute = new ResourceLocation(modelLocation.getResourceDomain(), path);
- try (IResource resource = manager.getResource(absolute)) {
- JsonElement ele = new JsonParser().parse(new InputStreamReader(resource.getInputStream()));
- if (ele != null) {
- return ele;
- }
- } catch (Exception e) {}
-
- return JsonNull.INSTANCE;
- });
- }
-
public static final Set parsedLocations = new HashSet<>();
private IModelCTM loadFromFile(ResourceLocation res, boolean forLoad) {
@@ -88,7 +106,7 @@ private IModelCTM loadFromFile(ResourceLocation res, boolean forLoad) {
parsedLocations.add(new ResourceLocation(res.getResourceDomain(), res.getResourcePath().replace("models/", "")));
}
- JsonObject json = getJSON(res).getAsJsonObject();
+ JsonObject json = jsonCache.getUnchecked(res).getAsJsonObject();
IModelParser parser = parserVersions.get(json.get("ctm_version").getAsInt());
if (parser == null) {
diff --git a/src/main/java/team/chisel/ctm/client/state/CTMExtendedState.java b/src/main/java/team/chisel/ctm/client/state/CTMExtendedState.java
index 609c79f1..e7603f91 100644
--- a/src/main/java/team/chisel/ctm/client/state/CTMExtendedState.java
+++ b/src/main/java/team/chisel/ctm/client/state/CTMExtendedState.java
@@ -20,6 +20,7 @@
import net.minecraftforge.common.property.IUnlistedProperty;
import team.chisel.ctm.api.model.IModelCTM;
import team.chisel.ctm.api.util.RenderContextList;
+import team.chisel.ctm.client.model.AbstractCTMBakedModel;
import team.chisel.ctm.client.util.ProfileUtil;
@ParametersAreNonnullByDefault
@@ -69,7 +70,7 @@ public CTMExtendedState(IBlockState state, CTMExtendedState parent) {
this(state, parent.world, parent.pos);
}
- public RenderContextList getContextList(IBlockState state, IModelCTM model) {
+ public RenderContextList getContextList(IBlockState state, AbstractCTMBakedModel model) {
if (ctxCache == null) {
ctxCache = new RenderContextList(state, model.getCTMTextures(), world, pos);
}
diff --git a/src/main/java/team/chisel/ctm/client/texture/IMetadataSectionCTM.java b/src/main/java/team/chisel/ctm/client/texture/IMetadataSectionCTM.java
index 37f62806..c3bd824a 100644
--- a/src/main/java/team/chisel/ctm/client/texture/IMetadataSectionCTM.java
+++ b/src/main/java/team/chisel/ctm/client/texture/IMetadataSectionCTM.java
@@ -75,7 +75,7 @@ default ICTMTexture> makeTexture(TextureAtlasSprite sprite, Function textureCoords = new EnumMap<>(EnumFacing.class);
private final long serialized;
+ protected BlockPos origin;
@SuppressWarnings("null")
public TextureContextGrid(BlockPos pos, TextureMap tex, boolean applyOffset) {
+ this(pos, pos, tex, applyOffset);
+ }
+
+ @SuppressWarnings("null")
+ public TextureContextGrid(BlockPos pos, BlockPos origin, TextureMap tex, boolean applyOffset) {
super(pos);
+ this.origin = origin;
// Since we can only return a long, we must limit to 10 bits of data per face = 60 bits
Preconditions.checkArgument(tex.getXSize() * tex.getYSize() < 1024, "V* Texture size too large for texture %s", tex.getParticle());
if (applyOffset) {
- applyOffset();
+ BlockPos offset = OffsetProviderRegistry.INSTANCE.getOffset(Minecraft.getMinecraft().world, position);
+ this.position = position.add(offset);
+ this.origin = origin.add(offset);
}
long serialized = 0;
diff --git a/src/main/java/team/chisel/ctm/client/texture/render/TextureCTM.java b/src/main/java/team/chisel/ctm/client/texture/render/TextureCTM.java
index 9e387457..aa412dbb 100644
--- a/src/main/java/team/chisel/ctm/client/texture/render/TextureCTM.java
+++ b/src/main/java/team/chisel/ctm/client/texture/render/TextureCTM.java
@@ -23,6 +23,7 @@
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.JsonUtils;
+import team.chisel.ctm.Configurations;
import team.chisel.ctm.api.texture.ITextureContext;
import team.chisel.ctm.api.util.TextureInfo;
import team.chisel.ctm.client.texture.ctx.TextureContextCTM;
@@ -110,7 +111,7 @@ public boolean connectTo(CTMLogic ctm, IBlockState from, IBlockState to, EnumFac
@Override
public List transformQuad(BakedQuad bq, ITextureContext context, int quadGoal) {
Quad quad = makeQuad(bq, context);
- if (context == null) {
+ if (context == null || Configurations.disableCTM) {
return Collections.singletonList(quad.transformUVs(sprites[0]).rebake());
}
diff --git a/src/main/java/team/chisel/ctm/client/texture/render/TextureEdges.java b/src/main/java/team/chisel/ctm/client/texture/render/TextureEdges.java
index eb9f827b..f48831be 100644
--- a/src/main/java/team/chisel/ctm/client/texture/render/TextureEdges.java
+++ b/src/main/java/team/chisel/ctm/client/texture/render/TextureEdges.java
@@ -7,6 +7,7 @@
import java.util.stream.Collectors;
import net.minecraft.client.renderer.block.model.BakedQuad;
+import team.chisel.ctm.Configurations;
import team.chisel.ctm.api.texture.ITextureContext;
import team.chisel.ctm.api.util.TextureInfo;
import team.chisel.ctm.client.texture.ctx.TextureContextCTM;
@@ -23,7 +24,7 @@ public TextureEdges(TextureTypeEdges type, TextureInfo info) {
@Override
public List transformQuad(BakedQuad bq, ITextureContext context, int quadGoal) {
Quad quad = makeQuad(bq, context);
- if (context == null) {
+ if (context == null || Configurations.disableCTM) {
return Collections.singletonList(quad.transformUVs(sprites[0]).rebake());
}
diff --git a/src/main/java/team/chisel/ctm/client/texture/render/TextureMap.java b/src/main/java/team/chisel/ctm/client/texture/render/TextureMap.java
index 98f8312c..9fc142a7 100644
--- a/src/main/java/team/chisel/ctm/client/texture/render/TextureMap.java
+++ b/src/main/java/team/chisel/ctm/client/texture/render/TextureMap.java
@@ -3,8 +3,11 @@
import com.google.common.base.Preconditions;
import com.google.gson.JsonObject;
import lombok.Getter;
+import net.minecraft.block.state.IBlockState;
import net.minecraft.client.renderer.block.model.BakedQuad;
+import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.IBlockAccess;
import team.chisel.ctm.api.texture.ISubmap;
import team.chisel.ctm.api.texture.ITextureContext;
import team.chisel.ctm.api.util.TextureInfo;
@@ -58,7 +61,7 @@ protected List transformQuad(TextureMap tex, BakedQuad quad, @Nullabl
}
@Override
- public ITextureContext getContext(@Nonnull BlockPos pos, @Nonnull TextureMap tex) {
+ public ITextureContext getContext(@Nonnull IBlockState state, @Nonnull IBlockAccess world, @Nonnull BlockPos pos, @Nonnull TextureMap tex) {
return new TextureContextGrid.Random(pos, tex, true);
}
},
@@ -94,19 +97,60 @@ protected List transformQuad(TextureMap tex, BakedQuad quad, @Nullabl
}
@Override
- public ITextureContext getContext(@Nonnull BlockPos pos, @Nonnull TextureMap tex) {
- return new TextureContextGrid.Patterned(pos, tex, true);
+ public ITextureContext getContext(@Nonnull IBlockState state, @Nonnull IBlockAccess world, @Nonnull BlockPos pos, @Nonnull TextureMap tex) {
+ return new TextureContextGrid.Patterned(pos, findPatternOrigin(state, world, pos), tex, true);
}
};
protected abstract List transformQuad(TextureMap tex, BakedQuad quad, @Nullable ITextureContext context, int quadGoal);
@Nonnull
- public ITextureContext getContext(@Nonnull BlockPos pos, @Nonnull TextureMap tex) {
+ public ITextureContext getContext(@Nonnull IBlockState state, @Nonnull IBlockAccess world, @Nonnull BlockPos pos, @Nonnull TextureMap tex) {
return new TextureContextPosition(pos);
}
}
+ private static BlockPos findPatternOrigin(IBlockState state, IBlockAccess world, BlockPos start) {
+ java.util.ArrayDeque pending = new java.util.ArrayDeque<>();
+ java.util.HashSet visited = new java.util.HashSet<>();
+ BlockPos.MutableBlockPos cursor = new BlockPos.MutableBlockPos();
+ BlockPos origin = start;
+
+ pending.add(start);
+ visited.add(start);
+
+ while (!pending.isEmpty()) {
+ BlockPos current = pending.removeFirst();
+ if (compareForPatternOrigin(current, origin) < 0) {
+ origin = current;
+ }
+
+ for (EnumFacing facing : EnumFacing.VALUES) {
+ cursor.setPos(current).move(facing);
+ BlockPos next = cursor.toImmutable();
+ if (visited.add(next) && world.getBlockState(next) == state) {
+ pending.addLast(next);
+ }
+ }
+ }
+
+ return origin;
+ }
+
+ private static int compareForPatternOrigin(BlockPos left, BlockPos right) {
+ int yCompare = Integer.compare(left.getY(), right.getY());
+ if (yCompare != 0) {
+ return yCompare;
+ }
+
+ int zCompare = Integer.compare(left.getZ(), right.getZ());
+ if (zCompare != 0) {
+ return zCompare;
+ }
+
+ return Integer.compare(left.getX(), right.getX());
+ }
+
@Getter
private final int xSize;
@Getter
diff --git a/src/main/java/team/chisel/ctm/client/texture/type/TextureTypeCTM.java b/src/main/java/team/chisel/ctm/client/texture/type/TextureTypeCTM.java
index c22fb924..8b16df79 100644
--- a/src/main/java/team/chisel/ctm/client/texture/type/TextureTypeCTM.java
+++ b/src/main/java/team/chisel/ctm/client/texture/type/TextureTypeCTM.java
@@ -3,6 +3,7 @@
import net.minecraft.block.state.IBlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
+import team.chisel.ctm.Configurations;
import team.chisel.ctm.api.texture.ICTMTexture;
import team.chisel.ctm.api.texture.ITextureContext;
import team.chisel.ctm.api.texture.ITextureType;
@@ -26,7 +27,7 @@ public TextureContextCTM getBlockRenderContext(IBlockState state, IBlockAccess w
@Override
public int getQuadsPerSide() {
- return 4;
+ return Configurations.disableCTM ? 1 : 4;
}
@Override
diff --git a/src/main/java/team/chisel/ctm/client/texture/type/TextureTypeMap.java b/src/main/java/team/chisel/ctm/client/texture/type/TextureTypeMap.java
index 85f9ce4e..73ff38b1 100644
--- a/src/main/java/team/chisel/ctm/client/texture/type/TextureTypeMap.java
+++ b/src/main/java/team/chisel/ctm/client/texture/type/TextureTypeMap.java
@@ -28,7 +28,7 @@ public TextureMap makeTexture(TextureInfo info) {
@Override
public ITextureContext getBlockRenderContext(IBlockState state, IBlockAccess world, @Nonnull BlockPos pos, ICTMTexture> tex) {
- return type.getContext(pos, (TextureMap) tex);
+ return type.getContext(state, world, pos, (TextureMap) tex);
}
@Override
diff --git a/src/main/java/team/chisel/ctm/client/util/CTMLogic.java b/src/main/java/team/chisel/ctm/client/util/CTMLogic.java
index 7fd29eca..2d68f0d2 100644
--- a/src/main/java/team/chisel/ctm/client/util/CTMLogic.java
+++ b/src/main/java/team/chisel/ctm/client/util/CTMLogic.java
@@ -335,7 +335,7 @@ public final boolean isConnected(IBlockAccess world, BlockPos current, BlockPos
* @param world
* @param current
* The position of your block.
- * @param y
+ * @param connection
* The position of the block to check against.
* @param dir
* The {@link EnumFacing side} of the block to check for connection status. This is not the direction to check in.