diff --git a/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/api/client/rendering/v1/ColorResolverRegistry.java b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/api/client/rendering/v1/ColorResolverRegistry.java new file mode 100644 index 0000000000..4bdddc86ec --- /dev/null +++ b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/api/client/rendering/v1/ColorResolverRegistry.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.api.client.rendering.v1; + +import java.util.Set; + +import org.jetbrains.annotations.UnmodifiableView; + +import net.minecraft.client.color.world.BiomeColors; +import net.minecraft.world.BlockRenderView; +import net.minecraft.world.biome.ColorResolver; + +import net.fabricmc.fabric.impl.client.rendering.ColorResolverRegistryImpl; + +/** + * The registry for custom {@link ColorResolver}s. Custom resolvers must be registered during client initialization for + * them to be usable in {@link BlockRenderView#getColor}. Calling this method may throw an exception if the passed + * resolver is not registered with this class. Vanilla resolvers found in {@link BiomeColors} are automatically + * registered. + * + *

Other mods may also require custom resolvers to be registered if they provide additional functionality related to + * color resolvers. + */ +public final class ColorResolverRegistry { + private ColorResolverRegistry() { + } + + /** + * Registers a custom {@link ColorResolver} for use in {@link BlockRenderView#getColor}. This method should be + * called during client initialization. + * + * @param resolver the resolver to register + */ + public static void register(ColorResolver resolver) { + ColorResolverRegistryImpl.register(resolver); + } + + /** + * Gets a view of all registered {@link ColorResolver}s, including all vanilla resolvers. + * + * @return a view of all registered resolvers + */ + @UnmodifiableView + public static Set getAllResolvers() { + return ColorResolverRegistryImpl.getAllResolvers(); + } + + /** + * Gets a view of all registered {@link ColorResolver}s, not including vanilla resolvers. + * + * @return a view of all registered custom resolvers + */ + @UnmodifiableView + public static Set getCustomResolvers() { + return ColorResolverRegistryImpl.getCustomResolvers(); + } + + /** + * Checks whether the given {@link ColorResolver} is registered. Vanilla resolvers are always registered. + * + * @param resolver the resolver + * @return whether the given resolver is registered + */ + public static boolean isRegistered(ColorResolver resolver) { + return getAllResolvers().contains(resolver); + } +} diff --git a/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/impl/client/rendering/ColorResolverRegistryImpl.java b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/impl/client/rendering/ColorResolverRegistryImpl.java new file mode 100644 index 0000000000..e49c231d68 --- /dev/null +++ b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/impl/client/rendering/ColorResolverRegistryImpl.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.impl.client.rendering; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.function.Function; + +import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap; +import org.jetbrains.annotations.UnmodifiableView; + +import net.minecraft.client.color.world.BiomeColors; +import net.minecraft.client.world.BiomeColorCache; +import net.minecraft.world.biome.ColorResolver; + +public final class ColorResolverRegistryImpl { + // Includes vanilla resolvers + private static final Set ALL_RESOLVERS = new HashSet<>(); + // Does not include vanilla resolvers + private static final Set CUSTOM_RESOLVERS = new HashSet<>(); + private static final Set ALL_RESOLVERS_VIEW = Collections.unmodifiableSet(ALL_RESOLVERS); + private static final Set CUSTOM_RESOLVERS_VIEW = Collections.unmodifiableSet(CUSTOM_RESOLVERS); + + static { + ALL_RESOLVERS.add(BiomeColors.GRASS_COLOR); + ALL_RESOLVERS.add(BiomeColors.FOLIAGE_COLOR); + ALL_RESOLVERS.add(BiomeColors.WATER_COLOR); + } + + private ColorResolverRegistryImpl() { + } + + public static void register(ColorResolver resolver) { + ALL_RESOLVERS.add(resolver); + CUSTOM_RESOLVERS.add(resolver); + } + + @UnmodifiableView + public static Set getAllResolvers() { + return ALL_RESOLVERS_VIEW; + } + + @UnmodifiableView + public static Set getCustomResolvers() { + return CUSTOM_RESOLVERS_VIEW; + } + + public static Reference2ReferenceMap createCustomCacheMap(Function cacheFactory) { + Reference2ReferenceOpenHashMap map = new Reference2ReferenceOpenHashMap<>(); + + for (ColorResolver resolver : CUSTOM_RESOLVERS) { + map.put(resolver, cacheFactory.apply(resolver)); + } + + map.trim(); + return map; + } +} diff --git a/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/ClientWorldMixin.java b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/ClientWorldMixin.java new file mode 100644 index 0000000000..8249e6c3f5 --- /dev/null +++ b/fabric-rendering-v1/src/client/java/net/fabricmc/fabric/mixin/client/rendering/ClientWorldMixin.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.mixin.client.rendering; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import it.unimi.dsi.fastutil.objects.Reference2ReferenceMap; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.client.world.BiomeColorCache; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.world.biome.ColorResolver; + +import net.fabricmc.fabric.impl.client.rendering.ColorResolverRegistryImpl; + +@Mixin(ClientWorld.class) +public abstract class ClientWorldMixin { + // Do not use the vanilla map because it is an Object2ObjectArrayMap. Array maps have O(n) retrievals compared to + // hash maps' O(1) retrievals. If many custom ColorResolvers are registered, this may have a non-negligible + // performance impact. + @Unique + private final Reference2ReferenceMap customColorCache = ColorResolverRegistryImpl.createCustomCacheMap(resolver -> new BiomeColorCache(pos -> calculateColor(pos, resolver))); + + @Shadow + public abstract int calculateColor(BlockPos pos, ColorResolver colorResolver); + + @Inject(method = "resetChunkColor(Lnet/minecraft/util/math/ChunkPos;)V", at = @At("RETURN")) + private void onResetChunkColor(ChunkPos chunkPos, CallbackInfo ci) { + for (BiomeColorCache cache : customColorCache.values()) { + cache.reset(chunkPos.x, chunkPos.z); + } + } + + @Inject(method = "reloadColor()V", at = @At("RETURN")) + private void onReloadColor(CallbackInfo ci) { + for (BiomeColorCache cache : customColorCache.values()) { + cache.reset(); + } + } + + @ModifyExpressionValue(method = "getColor(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/biome/ColorResolver;)I", at = @At(value = "INVOKE", target = "it/unimi/dsi/fastutil/objects/Object2ObjectArrayMap.get(Ljava/lang/Object;)Ljava/lang/Object;")) + private Object modifyNullCache(/* BiomeColorCache */ Object cache, BlockPos pos, ColorResolver resolver) { + if (cache == null) { + cache = customColorCache.get(resolver); + + if (cache == null) { + throw new UnsupportedOperationException("ClientWorld.getColor called with unregistered ColorResolver " + resolver); + } + } + + return cache; + } +} diff --git a/fabric-rendering-v1/src/client/resources/fabric-rendering-v1.mixins.json b/fabric-rendering-v1/src/client/resources/fabric-rendering-v1.mixins.json index fd802ffc65..0ec5218d1c 100644 --- a/fabric-rendering-v1/src/client/resources/fabric-rendering-v1.mixins.json +++ b/fabric-rendering-v1/src/client/resources/fabric-rendering-v1.mixins.json @@ -9,6 +9,7 @@ "BlockEntityRendererFactoriesMixin", "BuiltinModelItemRendererMixin", "CapeFeatureRendererMixin", + "ClientWorldMixin", "DimensionEffectsAccessor", "EntityModelLayersAccessor", "EntityModelsMixin", diff --git a/fabric-rendering-v1/src/testmod/java/net/fabricmc/fabric/test/rendering/CustomColorResolverTestInit.java b/fabric-rendering-v1/src/testmod/java/net/fabricmc/fabric/test/rendering/CustomColorResolverTestInit.java new file mode 100644 index 0000000000..926740eed3 --- /dev/null +++ b/fabric-rendering-v1/src/testmod/java/net/fabricmc/fabric/test/rendering/CustomColorResolverTestInit.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.test.rendering; + +import net.minecraft.block.AbstractBlock; +import net.minecraft.block.Block; +import net.minecraft.item.BlockItem; +import net.minecraft.item.Item; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; +import net.minecraft.util.Identifier; + +import net.fabricmc.api.ModInitializer; + +public class CustomColorResolverTestInit implements ModInitializer { + public static final Block CUSTOM_COLOR_BLOCK = new Block(AbstractBlock.Settings.create()); + public static final Item CUSTOM_COLOR_BLOCK_ITEM = new BlockItem(CUSTOM_COLOR_BLOCK, new Item.Settings()); + + @Override + public void onInitialize() { + Registry.register(Registries.BLOCK, new Identifier("fabric-rendering-v1-testmod", "custom_color_block"), CUSTOM_COLOR_BLOCK); + Registry.register(Registries.ITEM, new Identifier("fabric-rendering-v1-testmod", "custom_color_block"), CUSTOM_COLOR_BLOCK_ITEM); + } +} diff --git a/fabric-rendering-v1/src/testmod/resources/fabric.mod.json b/fabric-rendering-v1/src/testmod/resources/fabric.mod.json index 3c5f3fe0a6..57107b39f9 100644 --- a/fabric-rendering-v1/src/testmod/resources/fabric.mod.json +++ b/fabric-rendering-v1/src/testmod/resources/fabric.mod.json @@ -8,11 +8,13 @@ "entrypoints": { "main": [ "net.fabricmc.fabric.test.rendering.CustomAtlasSourcesTestInit", + "net.fabricmc.fabric.test.rendering.CustomColorResolverTestInit", "net.fabricmc.fabric.test.rendering.TooltipComponentTestInit" ], "client": [ "net.fabricmc.fabric.test.rendering.client.ArmorRenderingTests", "net.fabricmc.fabric.test.rendering.client.CustomAtlasSourcesTest", + "net.fabricmc.fabric.test.rendering.client.CustomColorResolverTest", "net.fabricmc.fabric.test.rendering.client.DimensionalRenderingTest", "net.fabricmc.fabric.test.rendering.client.FeatureRendererTest", "net.fabricmc.fabric.test.rendering.client.HudAndShaderTest", diff --git a/fabric-rendering-v1/src/testmodClient/java/net/fabricmc/fabric/test/rendering/client/CustomColorResolverTest.java b/fabric-rendering-v1/src/testmodClient/java/net/fabricmc/fabric/test/rendering/client/CustomColorResolverTest.java new file mode 100644 index 0000000000..3d7cd88b10 --- /dev/null +++ b/fabric-rendering-v1/src/testmodClient/java/net/fabricmc/fabric/test/rendering/client/CustomColorResolverTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2016, 2017, 2018, 2019 FabricMC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.fabricmc.fabric.test.rendering.client; + +import net.minecraft.world.biome.ColorResolver; + +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.rendering.v1.ColorProviderRegistry; +import net.fabricmc.fabric.api.client.rendering.v1.ColorResolverRegistry; +import net.fabricmc.fabric.test.rendering.CustomColorResolverTestInit; + +public class CustomColorResolverTest implements ClientModInitializer { + public static final ColorResolver TEST_COLOR_RESOLVER = (biome, x, z) -> { + if (biome.hasPrecipitation()) { + return 0xFFFF00FF; + } else { + return 0xFFFFFF00; + } + }; + + @Override + public void onInitializeClient() { + ColorResolverRegistry.register(TEST_COLOR_RESOLVER); + + ColorProviderRegistry.BLOCK.register((state, world, pos, tintIndex) -> { + if (world != null && pos != null) { + return world.getColor(pos, TEST_COLOR_RESOLVER); + } else { + return -1; + } + }, CustomColorResolverTestInit.CUSTOM_COLOR_BLOCK); + } +} diff --git a/fabric-rendering-v1/src/testmodClient/resources/assets/fabric-rendering-v1-testmod/blockstates/custom_color_block.json b/fabric-rendering-v1/src/testmodClient/resources/assets/fabric-rendering-v1-testmod/blockstates/custom_color_block.json new file mode 100644 index 0000000000..2b12403da0 --- /dev/null +++ b/fabric-rendering-v1/src/testmodClient/resources/assets/fabric-rendering-v1-testmod/blockstates/custom_color_block.json @@ -0,0 +1,5 @@ +{ + "variants": { + "": { "model": "fabric-rendering-v1-testmod:block/custom_color_block" } + } +} diff --git a/fabric-rendering-v1/src/testmodClient/resources/assets/fabric-rendering-v1-testmod/models/block/custom_color_block.json b/fabric-rendering-v1/src/testmodClient/resources/assets/fabric-rendering-v1-testmod/models/block/custom_color_block.json new file mode 100644 index 0000000000..62703835e6 --- /dev/null +++ b/fabric-rendering-v1/src/testmodClient/resources/assets/fabric-rendering-v1-testmod/models/block/custom_color_block.json @@ -0,0 +1,20 @@ +{ + "parent": "block/block", + "textures": { + "all": "fabric-rendering-v1-testmod:block/blank", + "particle": "#all" + }, + "elements": [ + { "from": [ 0, 0, 0 ], + "to": [ 16, 16, 16 ], + "faces": { + "down": { "uv": [ 0, 0, 16, 16 ], "texture": "#all", "cullface": "down" }, + "up": { "uv": [ 0, 0, 16, 16 ], "texture": "#all", "tintindex": 0, "cullface": "up" }, + "north": { "uv": [ 0, 0, 16, 16 ], "texture": "#all", "cullface": "north" }, + "south": { "uv": [ 0, 0, 16, 16 ], "texture": "#all", "cullface": "south" }, + "west": { "uv": [ 0, 0, 16, 16 ], "texture": "#all", "cullface": "west" }, + "east": { "uv": [ 0, 0, 16, 16 ], "texture": "#all", "cullface": "east" } + } + } + ] +} diff --git a/fabric-rendering-v1/src/testmodClient/resources/assets/fabric-rendering-v1-testmod/textures/block/blank.png b/fabric-rendering-v1/src/testmodClient/resources/assets/fabric-rendering-v1-testmod/textures/block/blank.png new file mode 100644 index 0000000000..3869a61f64 Binary files /dev/null and b/fabric-rendering-v1/src/testmodClient/resources/assets/fabric-rendering-v1-testmod/textures/block/blank.png differ