diff --git a/build.gradle b/build.gradle index bdeaae955..2c70eab48 100644 --- a/build.gradle +++ b/build.gradle @@ -12,7 +12,7 @@ plugins { def ENV = System.getenv() class Globals { - static def baseVersion = "0.4.1" + static def baseVersion = "0.4.2" static def mcVersion = "19w39a" static def yarnVersion = "+build.1" } diff --git a/fabric-networking-blockentity-v0/build.gradle b/fabric-networking-blockentity-v0/build.gradle index a41271b05..e03e5a7b7 100644 --- a/fabric-networking-blockentity-v0/build.gradle +++ b/fabric-networking-blockentity-v0/build.gradle @@ -1,5 +1,5 @@ archivesBaseName = "fabric-networking-blockentity-v0" -version = getSubprojectVersion(project, "0.1.1") +version = getSubprojectVersion(project, "0.2.0") dependencies { compile project(path: ':fabric-api-base', configuration: 'dev') diff --git a/fabric-networking-blockentity-v0/src/main/java/net/fabricmc/fabric/api/block/entity/BlockEntityClientSerializable.java b/fabric-networking-blockentity-v0/src/main/java/net/fabricmc/fabric/api/block/entity/BlockEntityClientSerializable.java index f500f5b2a..99db278e0 100644 --- a/fabric-networking-blockentity-v0/src/main/java/net/fabricmc/fabric/api/block/entity/BlockEntityClientSerializable.java +++ b/fabric-networking-blockentity-v0/src/main/java/net/fabricmc/fabric/api/block/entity/BlockEntityClientSerializable.java @@ -16,14 +16,37 @@ package net.fabricmc.fabric.api.block.entity; +import com.google.common.base.Preconditions; + +import net.minecraft.block.entity.BlockEntity; import net.minecraft.nbt.CompoundTag; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.world.World; /** - * Implement this interace on a BlockEntity which you would like to be + * Implement this interface on a BlockEntity which you would like to be * synchronized with the client side using the built-in engine methods. */ public interface BlockEntityClientSerializable { void fromClientTag(CompoundTag tag); CompoundTag toClientTag(CompoundTag tag); + + /** + * When called on the server, schedules a BlockEntity sync to client. + * This will cause {@link #toClientTag(CompoundTag)} to be called on the + * server to generate the packet data, and then + * {@link #fromClientTag(CompoundTag)} on the client to decode that data. + * + *

This is preferable to + * {@link World#updateListeners(net.minecraft.util.math.BlockPos, net.minecraft.block.BlockState, net.minecraft.block.BlockState, int)} + * because it does not cause entities to update their pathing as a side effect. + */ + default void sync() { + World world = ((BlockEntity) this).getWorld(); + Preconditions.checkNotNull(world); //Maintain distinct failure case from below + if (!(world instanceof ServerWorld)) throw new IllegalStateException("Cannot call sync() on the logical client! Did you check world.isClient first?"); + + ((ServerWorld) world).method_14178().markForUpdate(((BlockEntity) this).getPos()); + } } diff --git a/fabric-particles-v1/build.gradle b/fabric-particles-v1/build.gradle new file mode 100644 index 000000000..aaaa3e9a6 --- /dev/null +++ b/fabric-particles-v1/build.gradle @@ -0,0 +1,7 @@ +archivesBaseName = "fabric-particles-v1" +version = getSubprojectVersion(project, "0.1.1") + +dependencies { + compile project(path: ':fabric-api-base', configuration: 'dev') + compile project(path: ':fabric-textures-v0', configuration: 'dev') +} diff --git a/fabric-particles-v1/src/main/java/net/fabricmc/fabric/api/client/particle/v1/FabricSpriteProvider.java b/fabric-particles-v1/src/main/java/net/fabricmc/fabric/api/client/particle/v1/FabricSpriteProvider.java new file mode 100644 index 000000000..5d6897886 --- /dev/null +++ b/fabric-particles-v1/src/main/java/net/fabricmc/fabric/api/client/particle/v1/FabricSpriteProvider.java @@ -0,0 +1,48 @@ +/* + * 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.particle.v1; + +import java.util.List; + +import net.minecraft.client.particle.SpriteProvider; +import net.minecraft.client.texture.Sprite; +import net.minecraft.client.texture.SpriteAtlasTexture; + +/* + * FabricSpriteProvider. It does the same thing as vanilla's SpriteProvider, + * but in a way that's accessible to mods, and that exposes the atlas as well. + * + * Custom sprites registered using ParticleFactoryRegistry have the options + * to supply a particle factory which will recieve an instance of this + * interface containing the sprites set loaded for their particle from the + * active resourcepacks. + * + * @see ParticleFactoryRegistry.register(type, constructor) + * @see ParticleFactoryRegistry.PendingParticleFactory + */ +public interface FabricSpriteProvider extends SpriteProvider { + /** + * Returns the entire particles texture atlas. + */ + public SpriteAtlasTexture getAtlas(); + + /** + * Gets the list of all sprites available for this particle to use. + * This is defined in your resourcepack. + */ + public List getSprites(); +} diff --git a/fabric-particles-v1/src/main/java/net/fabricmc/fabric/api/client/particle/v1/ParticleFactoryRegistry.java b/fabric-particles-v1/src/main/java/net/fabricmc/fabric/api/client/particle/v1/ParticleFactoryRegistry.java new file mode 100644 index 000000000..9c5a58756 --- /dev/null +++ b/fabric-particles-v1/src/main/java/net/fabricmc/fabric/api/client/particle/v1/ParticleFactoryRegistry.java @@ -0,0 +1,67 @@ +/* + * 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.particle.v1; + +import net.fabricmc.fabric.impl.client.particle.ParticleFactoryRegistryImpl; +import net.minecraft.client.particle.ParticleFactory; +import net.minecraft.particle.ParticleEffect; +import net.minecraft.particle.ParticleType; + +/** + * Registry for adding particle factories on the client for + * particle types created using FabricParticleTypes (or otherwise). + * + * @see FabricParticleTypes + */ +public interface ParticleFactoryRegistry { + public static ParticleFactoryRegistry getInstance() { + return ParticleFactoryRegistryImpl.INSTANCE; + } + + /** + * Registers a factory for constructing particles of the given type. + */ + public void register(ParticleType type, ParticleFactory factory); + + /** + * Registers a delayed factory for constructing particles of the given type. + * + * The factory method will be called with a sprite provider to use for that particle when it comes time. + * + * Particle sprites will be loaded from domain:/particles/particle_name.json as per vanilla minecraft behaviour. + */ + public void register(ParticleType type, PendingParticleFactory constructor); + + /** + * A pending particle factory. + * + * @param The type of particle effects this factory deals with. + */ + @FunctionalInterface + public interface PendingParticleFactory { + /** + * Called to create a new particle factory. + * + * Particle sprites will be loaded from domain:/particles/particle_name.json as per vanilla minecraft behaviour. + * + * @param provider The sprite provider used to supply sprite textures when drawing the mod's particle. + * + * @return A new particle factory. + */ + public ParticleFactory create(FabricSpriteProvider provider); + } +} diff --git a/fabric-particles-v1/src/main/java/net/fabricmc/fabric/api/particle/v1/FabricParticleTypes.java b/fabric-particles-v1/src/main/java/net/fabricmc/fabric/api/particle/v1/FabricParticleTypes.java new file mode 100644 index 000000000..ba0d211fd --- /dev/null +++ b/fabric-particles-v1/src/main/java/net/fabricmc/fabric/api/particle/v1/FabricParticleTypes.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.particle.v1; + +import net.minecraft.particle.DefaultParticleType; +import net.minecraft.particle.ParticleEffect; +import net.minecraft.particle.ParticleType; + +/** + * Methods for creating particle types, both simple and using an existing attribute factory. + * + * Usage: + * + * public static final DefaultParticleType SIMPLE_TEST_PARTICLE = FabricParticleTypes.simple(); + * public static final DefaultParticleType CUSTOM_TEST_PARTICLE = FabricParticleTypes.simple(); + * + * @Override + * public void onInitialize() { + * Registry.register(Registry.PARTICLE_TYPE, new Identifier("testmod", "simple"), SIMPLE_TEST_PARTICLE); + * Registry.register(Registry.PARTICLE_TYPE, new Identifier("testmod", "custom"), CUSTOM_TEST_PARTICLE); + * } + * + * @see ParticleModClient in the fabric example mods for a more complete usage. + */ +public final class FabricParticleTypes { + private FabricParticleTypes() { } + + /** + * Creates a new, default particle type for the given id. + * + * @param id The particle id. + */ + public static DefaultParticleType simple() { + return simple(false); + } + + /** + * Creates a new, default particle type for the given id. + * + * @param id The particle id. + * @param alwaysSpawn True to always spawn the particle regardless of distance. + */ + public static DefaultParticleType simple(boolean alwaysSpawn) { + return new DefaultParticleType(alwaysSpawn) {}; + } + + /** + * Creates a new particle type with a custom factory for packet/data serialization. + * + * @param id The particle id. + * @param factory A factory for serializing packet data and string command parameters into a particle effect. + */ + public static ParticleType complex(ParticleEffect.Factory factory) { + return complex(false, factory); + } + + /** + * Creates a new particle type with a custom factory for packet/data serialization. + * + * @param id The particle id. + * @param alwaysSpawn True to always spawn the particle regardless of distance. + * @param factory A factory for serializing packet data and string command parameters into a particle effect. + */ + public static ParticleType complex(boolean alwaysSpawn, ParticleEffect.Factory factory) { + return new ParticleType(alwaysSpawn, factory) {}; + } +} diff --git a/fabric-particles-v1/src/main/java/net/fabricmc/fabric/impl/client/particle/FabricParticleManager.java b/fabric-particles-v1/src/main/java/net/fabricmc/fabric/impl/client/particle/FabricParticleManager.java new file mode 100644 index 000000000..37b3f5f91 --- /dev/null +++ b/fabric-particles-v1/src/main/java/net/fabricmc/fabric/impl/client/particle/FabricParticleManager.java @@ -0,0 +1,125 @@ +/* + * 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.particle; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; + +import com.google.common.collect.ImmutableList; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import net.fabricmc.fabric.api.client.particle.v1.FabricSpriteProvider; +import net.minecraft.client.particle.ParticleTextureData; +import net.minecraft.client.texture.Sprite; +import net.minecraft.client.texture.SpriteAtlasTexture; +import net.minecraft.resource.ResourceManager; +import net.minecraft.util.Identifier; +import net.minecraft.util.JsonHelper; + +public final class FabricParticleManager { + private final VanillaParticleManager manager; + + private final Int2ObjectMap providers = new Int2ObjectOpenHashMap<>(); + + public FabricParticleManager(VanillaParticleManager manager) { + this.manager = manager; + } + + public void injectValues() { + manager.getFactories().putAll(ParticleFactoryRegistryImpl.INSTANCE.factories); + ParticleFactoryRegistryImpl.INSTANCE.constructors.forEach((id, factory) -> { + FabricSpriteProviderImpl provider = new FabricSpriteProviderImpl(); + + providers.put((int)id, provider); + manager.getFactories().put((int)id, factory.create(provider)); + }); + } + + private FabricSpriteProviderImpl getProvider(Identifier id) { + if (!ParticleFactoryRegistryImpl.INSTANCE.constructorsIdsMap.containsKey(id)) { + return null; + } + + return providers.get((int)ParticleFactoryRegistryImpl.INSTANCE.constructorsIdsMap.get(id)); + } + + public boolean loadParticle(ResourceManager manager, Identifier id) { + FabricSpriteProviderImpl provider = getProvider(id); + + if (provider == null) { + return false; // preserve vanilla behaviour (i don't got dis) + } + + Identifier file = new Identifier(id.getNamespace(), "particles/" + id.getPath() + ".json"); + + try (Reader reader = new InputStreamReader(manager.getResource(file).getInputStream(), StandardCharsets.UTF_8)) { + List spriteIds = ParticleTextureData.load(JsonHelper.deserialize(reader)).getTextureList(); + + if (spriteIds == null) { + // Particles should have a list of picks, even if it's just empty. + throw new IllegalStateException("(Fabric) Missing texture list for particle " + id); + } + + provider.setSprites(spriteIds); + } catch (IOException e) { + throw new IllegalStateException("Failed to load description for particle " + id, e); + } + + return true; // i got dis + } + + private final class FabricSpriteProviderImpl implements FabricSpriteProvider { + private List spriteIds; + + // @Nullable + private List sprites; + + @Override + public Sprite getSprite(int min, int max) { + return getSprites().get(min * (getSprites().size() - 1) / max); + } + + @Override + public Sprite getSprite(Random random_1) { + return getSprites().get(random_1.nextInt(getSprites().size())); + } + + @Override + public SpriteAtlasTexture getAtlas() { + return manager.getAtlas(); + } + + @Override + public List getSprites() { + if (sprites == null) { + sprites = spriteIds.stream().map(getAtlas()::getSprite).collect(Collectors.toList()); + } + return sprites; + } + + public void setSprites(List sprites) { + this.sprites = null; + this.spriteIds = ImmutableList.copyOf(sprites); + } + } +} diff --git a/fabric-particles-v1/src/main/java/net/fabricmc/fabric/impl/client/particle/ParticleFactoryRegistryImpl.java b/fabric-particles-v1/src/main/java/net/fabricmc/fabric/impl/client/particle/ParticleFactoryRegistryImpl.java new file mode 100644 index 000000000..9c5e1383d --- /dev/null +++ b/fabric-particles-v1/src/main/java/net/fabricmc/fabric/impl/client/particle/ParticleFactoryRegistryImpl.java @@ -0,0 +1,50 @@ +/* + * 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.particle; + +import java.util.HashMap; +import java.util.Map; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import net.fabricmc.fabric.api.client.particle.v1.ParticleFactoryRegistry; +import net.minecraft.client.particle.ParticleFactory; +import net.minecraft.particle.ParticleEffect; +import net.minecraft.particle.ParticleType; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +public final class ParticleFactoryRegistryImpl implements ParticleFactoryRegistry { + public static final ParticleFactoryRegistryImpl INSTANCE = new ParticleFactoryRegistryImpl(); + + final Int2ObjectMap> factories = new Int2ObjectOpenHashMap<>(); + final Int2ObjectMap> constructors = new Int2ObjectOpenHashMap<>(); + final Map constructorsIdsMap = new HashMap<>(); + + private ParticleFactoryRegistryImpl() { } + + @Override + public void register(ParticleType type, ParticleFactory factory) { + factories.put(Registry.PARTICLE_TYPE.getRawId(type), factory); + } + + @Override + public void register(ParticleType type, PendingParticleFactory factory) { + constructors.put(Registry.PARTICLE_TYPE.getRawId(type), factory); + constructorsIdsMap.put(Registry.PARTICLE_TYPE.getId(type), Registry.PARTICLE_TYPE.getRawId(type)); + } +} diff --git a/fabric-particles-v1/src/main/java/net/fabricmc/fabric/impl/client/particle/VanillaParticleManager.java b/fabric-particles-v1/src/main/java/net/fabricmc/fabric/impl/client/particle/VanillaParticleManager.java new file mode 100644 index 000000000..88852bba8 --- /dev/null +++ b/fabric-particles-v1/src/main/java/net/fabricmc/fabric/impl/client/particle/VanillaParticleManager.java @@ -0,0 +1,33 @@ +/* + * 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.particle; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import net.minecraft.client.particle.ParticleFactory; +import net.minecraft.client.texture.SpriteAtlasTexture; + +/** + * Accessors for some private members of ParticleManager. + * + * Usage: + * SpriteAtlasTexture atlas = ((VanillaParticleManager)MinecraftClient.getInstance().particleManager).getAtlas() + */ +public interface VanillaParticleManager { + public SpriteAtlasTexture getAtlas(); + + public Int2ObjectMap> getFactories(); +} diff --git a/fabric-particles-v1/src/main/java/net/fabricmc/fabric/mixin/client/particle/MixinParticleManager.java b/fabric-particles-v1/src/main/java/net/fabricmc/fabric/mixin/client/particle/MixinParticleManager.java new file mode 100644 index 000000000..77eadddc3 --- /dev/null +++ b/fabric-particles-v1/src/main/java/net/fabricmc/fabric/mixin/client/particle/MixinParticleManager.java @@ -0,0 +1,62 @@ +/* + * 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.particle; + +import java.util.List; +import java.util.Map; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import net.fabricmc.fabric.impl.client.particle.FabricParticleManager; +import net.fabricmc.fabric.impl.client.particle.VanillaParticleManager; +import net.minecraft.client.particle.ParticleFactory; +import net.minecraft.client.particle.ParticleManager; +import net.minecraft.client.texture.SpriteAtlasTexture; +import net.minecraft.resource.ResourceManager; +import net.minecraft.util.Identifier; + +@Mixin(ParticleManager.class) +public abstract class MixinParticleManager implements VanillaParticleManager { + private final FabricParticleManager fabricParticleManager = new FabricParticleManager(this); + + @Override + @Accessor("particleAtlasTexture") + public abstract SpriteAtlasTexture getAtlas(); + + @Override + @Accessor("factories") + public abstract Int2ObjectMap> getFactories(); + + @Inject(method = "registerDefaultFactories()V", at = @At("RETURN")) + private void onRegisterDefaultFactories(CallbackInfo info) { + fabricParticleManager.injectValues(); + } + + @Inject(method = "loadTextureList(Lnet/minecraft/resource/ResourceManager;Lnet/minecraft/util/Identifier;Ljava/util/Map;)V", + at = @At("HEAD"), + cancellable = true) + private void onLoadTextureList(ResourceManager manager, Identifier id, Map> output, CallbackInfo info) { + if (fabricParticleManager.loadParticle(manager, id)) { + info.cancel(); + } + } +} diff --git a/fabric-particles-v1/src/main/resources/fabric-particles-v1.mixins.json b/fabric-particles-v1/src/main/resources/fabric-particles-v1.mixins.json new file mode 100644 index 000000000..c12763163 --- /dev/null +++ b/fabric-particles-v1/src/main/resources/fabric-particles-v1.mixins.json @@ -0,0 +1,11 @@ +{ + "required": true, + "package": "net.fabricmc.fabric.mixin.client.particle", + "compatibilityLevel": "JAVA_8", + "client": [ + "MixinParticleManager" + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/fabric-particles-v1/src/main/resources/fabric.mod.json b/fabric-particles-v1/src/main/resources/fabric.mod.json new file mode 100644 index 000000000..49c85e272 --- /dev/null +++ b/fabric-particles-v1/src/main/resources/fabric.mod.json @@ -0,0 +1,12 @@ +{ + "schemaVersion": 1, + "id": "fabric-particles-v1", + "version": "${version}", + "license": "Apache-2.0", + "depends": { + "fabricloader": ">=0.4.0" + }, + "mixins": [ + "fabric-particles-v1.mixins.json" + ] +} diff --git a/fabric-renderer-api-v1/build.gradle b/fabric-renderer-api-v1/build.gradle index 6c8d0f9e4..052a343c5 100644 --- a/fabric-renderer-api-v1/build.gradle +++ b/fabric-renderer-api-v1/build.gradle @@ -1,5 +1,5 @@ archivesBaseName = "fabric-renderer-api-v1" -version = getSubprojectVersion(project, "0.2.2") +version = getSubprojectVersion(project, "0.2.3") dependencies { compile project(path: ':fabric-api-base', configuration: 'dev') diff --git a/fabric-renderer-api-v1/src/main/java/net/fabricmc/fabric/api/renderer/v1/model/ModelHelper.java b/fabric-renderer-api-v1/src/main/java/net/fabricmc/fabric/api/renderer/v1/model/ModelHelper.java index bee076ec4..39da668df 100644 --- a/fabric-renderer-api-v1/src/main/java/net/fabricmc/fabric/api/renderer/v1/model/ModelHelper.java +++ b/fabric-renderer-api-v1/src/main/java/net/fabricmc/fabric/api/renderer/v1/model/ModelHelper.java @@ -80,19 +80,17 @@ public abstract class ModelHelper { builders[i] = ImmutableList.builder(); } - if (mesh == null) { - return null; + if (mesh != null) { + mesh.forEach(q -> { + final int limit = q.material().spriteDepth(); + + for (int l = 0; l < limit; l++) { + Direction face = q.cullFace(); + builders[face == null ? 6 : face.getId()].add(q.toBakedQuad(l, finder.find(q, l), false)); + } + }); } - mesh.forEach(q -> { - final int limit = q.material().spriteDepth(); - - for (int l = 0; l < limit; l++) { - Direction face = q.cullFace(); - builders[face == null ? 6 : face.getId()].add(q.toBakedQuad(l, finder.find(q, l), false)); - } - }); - @SuppressWarnings("unchecked") List[] result = new List[7]; for (int i = 0; i < 7; i++) { diff --git a/fabric-renderer-api-v1/src/main/java/net/fabricmc/fabric/mixin/renderer/client/MixinBlockRenderManager.java b/fabric-renderer-api-v1/src/main/java/net/fabricmc/fabric/mixin/renderer/client/MixinBlockRenderManager.java deleted file mode 100644 index 82766e612..000000000 --- a/fabric-renderer-api-v1/src/main/java/net/fabricmc/fabric/mixin/renderer/client/MixinBlockRenderManager.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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.renderer.client; - -import java.util.Random; - -import org.apache.commons.lang3.tuple.MutablePair; -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.ModifyVariable; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel; -import net.fabricmc.fabric.impl.renderer.DamageModel; -import net.minecraft.class_4587; -import net.minecraft.class_4588; -import net.minecraft.block.BlockState; -import net.minecraft.client.render.BufferBuilder; -import net.minecraft.client.render.block.BlockModelRenderer; -import net.minecraft.client.render.block.BlockRenderManager; -import net.minecraft.client.render.model.BakedModel; -import net.minecraft.client.texture.Sprite; -import net.minecraft.util.math.BlockPos; -import net.minecraft.world.BlockRenderView; - -/** - * Implements hook for block-breaking render. - */ -@Mixin(BlockRenderManager.class) -public abstract class MixinBlockRenderManager { -// TODO: temporarily disabled - may no longer be needed -// @Shadow -// private BlockModelRenderer renderer; -// @Shadow -// private Random random; - -// private static final ThreadLocal> DAMAGE_STATE = ThreadLocal.withInitial(() -> MutablePair.of(new DamageModel(), null)); - -// /** -// * Intercept the model assignment from getModel() - simpler than capturing entire LVT. -// */ -// @ModifyVariable(method = "tesselateDamage", at = @At(value = "STORE", ordinal = 0), allow = 1, require = 1) -// private BakedModel hookTesselateDamageModel(BakedModel modelIn) { -// DAMAGE_STATE.get().right = modelIn; -// return modelIn; -// } - -// /** -// * If the model we just captured is a fabric model, render it using a specialized -// * damage render context and cancel rest of the logic. Avoids creating a bunch of -// * vanilla quads for complex meshes and honors dynamic model geometry. -// */ -// @Inject(method = "tesselateDamage", cancellable = true, at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/client/render/block/BlockModels;getModel(Lnet/minecraft/block/BlockState;)Lnet/minecraft/client/render/model/BakedModel;")) -// private void hookTesselateDamage(BlockState blockState, BlockPos blockPos, BlockRenderView blockView, class_4587 matrix, class_4588 buffer, CallbackInfo ci) { -// MutablePair damageState = DAMAGE_STATE.get(); -// -// if (damageState.right != null && !((FabricBakedModel) damageState.right).isVanillaAdapter()) { -// damageState.left.prepare(damageState.right, sprite, blockState, blockPos); -// this.renderer.tesselate(blockView, damageState.left, blockState, blockPos, matrix, buffer, true, this.random, blockState.getRenderingSeed(blockPos)); -// ci.cancel(); -// } -// } -} diff --git a/fabric-renderer-api-v1/src/main/resources/fabric-renderer-api-v1.mixins.json b/fabric-renderer-api-v1/src/main/resources/fabric-renderer-api-v1.mixins.json index 1a13a1de5..4c5b7f3e2 100644 --- a/fabric-renderer-api-v1/src/main/resources/fabric-renderer-api-v1.mixins.json +++ b/fabric-renderer-api-v1/src/main/resources/fabric-renderer-api-v1.mixins.json @@ -4,7 +4,6 @@ "compatibilityLevel": "JAVA_8", "client": [ "client.MixinBakedModel", - "client.MixinBlockRenderManager", "client.MixinSpriteAtlasTexture" ], "injectors": { diff --git a/fabric-renderer-indigo/build.gradle b/fabric-renderer-indigo/build.gradle index 7942cab86..d33f495f7 100644 --- a/fabric-renderer-indigo/build.gradle +++ b/fabric-renderer-indigo/build.gradle @@ -1,5 +1,5 @@ archivesBaseName = "fabric-renderer-indigo" -version = getSubprojectVersion(project, "0.2.2") +version = getSubprojectVersion(project, "0.2.3") dependencies { compile project(path: ':fabric-api-base', configuration: 'dev') diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/mesh/EncodingFormat.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/mesh/EncodingFormat.java index 6522a3ef3..d785790e5 100644 --- a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/mesh/EncodingFormat.java +++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/mesh/EncodingFormat.java @@ -59,8 +59,8 @@ public abstract class EncodingFormat { VERTEX_X = HEADER_STRIDE + 0; VERTEX_Y = HEADER_STRIDE + 1; VERTEX_Z = HEADER_STRIDE + 2; - VERTEX_COLOR = HEADER_STRIDE + 4; - VERTEX_U = HEADER_STRIDE + 5; + VERTEX_COLOR = HEADER_STRIDE + 3; + VERTEX_U = HEADER_STRIDE + 4; VERTEX_V = VERTEX_U + 1; VERTEX_LIGHTMAP = HEADER_STRIDE + 6; VERTEX_NORMAL = HEADER_STRIDE + 7; diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/mesh/MutableQuadViewImpl.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/mesh/MutableQuadViewImpl.java index ee2f7b304..9ecb5fbe4 100644 --- a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/mesh/MutableQuadViewImpl.java +++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/mesh/MutableQuadViewImpl.java @@ -70,7 +70,7 @@ public abstract class MutableQuadViewImpl extends QuadViewImpl implements QuadEm if (material == null) { material = IndigoRenderer.MATERIAL_STANDARD; } - + data[baseIndex + HEADER_BITS] = EncodingFormat.material(data[baseIndex + HEADER_BITS], (Value) material); return this; } @@ -147,20 +147,21 @@ public abstract class MutableQuadViewImpl extends QuadViewImpl implements QuadEm /** * Internal helper method. Copies face normals to vertex normals lacking one. - * Does not set flags that indicate a vertex normal was input. */ public final void populateMissingNormals() { final int normalFlags = this.normalFlags(); - + if (normalFlags == 0b1111) return; - + final int packedFaceNormal = NormalHelper.packNormal(faceNormal(), 0); - + for (int v = 0; v < 4; v++) { if ((normalFlags & (1 << v)) == 0) { data[baseIndex + v * VERTEX_STRIDE + VERTEX_NORMAL] = packedFaceNormal; } } + + normalFlags(0b1111); } @Override diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/mixin/MixinChunkRebuildTask.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/mixin/MixinChunkRebuildTask.java index f78e66611..a3f7c19fe 100644 --- a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/mixin/MixinChunkRebuildTask.java +++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/mixin/MixinChunkRebuildTask.java @@ -96,14 +96,13 @@ public class MixinChunkRebuildTask { @Redirect(method = "method_22785", require = 1, at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/block/BlockRenderManager;tesselateBlock(Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/BlockRenderView;Lnet/minecraft/class_4587;Lnet/minecraft/class_4588;ZLjava/util/Random;)Z")) private boolean hookChunkBuildTesselate(BlockRenderManager renderManager, BlockState blockState, BlockPos blockPos, BlockRenderView blockView, class_4587 matrix, class_4588 bufferBuilder, boolean checkSides, Random random) { - // TODO: temporarily disabled - // if (blockState.getRenderType() == BlockRenderType.MODEL) { - // final BakedModel model = renderManager.getModel(blockState); - // - // if (Indigo.ALWAYS_TESSELATE_INDIGO || !((FabricBakedModel) model).isVanillaAdapter()) { - // return ((AccessChunkRendererRegion) blockView).fabric_getRenderer().tesselateBlock(blockState, blockPos, model, matrix); - // } - // } + if (blockState.getRenderType() == BlockRenderType.MODEL) { + final BakedModel model = renderManager.getModel(blockState); + + if (Indigo.ALWAYS_TESSELATE_INDIGO || !((FabricBakedModel) model).isVanillaAdapter()) { + return ((AccessChunkRendererRegion) blockView).fabric_getRenderer().tesselateBlock(blockState, blockPos, model, matrix); + } + } return renderManager.tesselateBlock(blockState, blockPos, blockView, matrix, bufferBuilder, checkSides, random); } diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/mixin/MixinItemRenderer.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/mixin/MixinItemRenderer.java index bb47086db..5dac74dbd 100644 --- a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/mixin/MixinItemRenderer.java +++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/mixin/MixinItemRenderer.java @@ -17,37 +17,40 @@ package net.fabricmc.indigo.renderer.mixin; 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 net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel; +import net.fabricmc.indigo.renderer.render.ItemRenderContext; +import net.fabricmc.indigo.renderer.render.ItemRenderContext.VanillaQuadHandler; +import net.minecraft.class_4587; +import net.minecraft.class_4588; +import net.minecraft.client.color.item.ItemColors; import net.minecraft.client.render.item.ItemRenderer; +import net.minecraft.client.render.model.BakedModel; +import net.minecraft.item.ItemStack; @Mixin(ItemRenderer.class) public abstract class MixinItemRenderer { - // TODO: temporarily disabled - // @Shadow - // protected abstract void renderQuads(BufferBuilder bufferBuilder, List quads, int color, ItemStack stack); - // - // @Shadow - // protected ItemColors colorMap; - // private final ThreadLocal CONTEXTS = ThreadLocal.withInitial(() -> new ItemRenderContext(colorMap)); - // - // /** - // * Save stack for enchantment glint renders - we won't otherwise have access to it - // * during the glint render because it receives an empty stack. - // */ - // @Inject(at = @At("HEAD"), method = "renderItemAndGlow") - // private void hookRenderItemAndGlow(ItemStack stack, BakedModel model, CallbackInfo ci) { - // if (stack.hasEnchantmentGlint() && !((FabricBakedModel) model).isVanillaAdapter()) { - // CONTEXTS.get().enchantmentStack = stack; - // } - // } - // - // @Inject(at = @At("HEAD"), method = "renderModel", cancellable = true) - // private void hookRenderModel(BakedModel model, int color, ItemStack stack, CallbackInfo ci) { - // final FabricBakedModel fabricModel = (FabricBakedModel) model; - // - // if (!fabricModel.isVanillaAdapter()) { - // CONTEXTS.get().renderModel(fabricModel, color, stack, this::renderQuads); - // ci.cancel(); - // } - // } + @Shadow + protected abstract void method_23182(BakedModel model, ItemStack stack, int color, class_4587 matrixStack, class_4588 buffer); + + @Shadow + protected ItemColors colorMap; + + private final VanillaQuadHandler vanillaHandler = this::method_23182; + + private final ThreadLocal CONTEXTS = ThreadLocal.withInitial(() -> new ItemRenderContext(colorMap)); + + @Inject(at = @At("HEAD"), method = "method_23182", cancellable = true) + private void hook_method_23182(BakedModel model, ItemStack stack, int lightmap, class_4587 matrixStack, class_4588 buffer, CallbackInfo ci) { + final FabricBakedModel fabricModel = (FabricBakedModel) model; + + if (!fabricModel.isVanillaAdapter()) { + CONTEXTS.get().renderModel(fabricModel, stack, lightmap, matrixStack, buffer, vanillaHandler); + ci.cancel(); + } + } } diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/AbstractMeshConsumer.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/AbstractMeshConsumer.java index 1478f7e49..7b8bf49b9 100644 --- a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/AbstractMeshConsumer.java +++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/AbstractMeshConsumer.java @@ -30,16 +30,16 @@ import net.fabricmc.indigo.renderer.helper.GeometryHelper; import net.fabricmc.indigo.renderer.mesh.EncodingFormat; import net.fabricmc.indigo.renderer.mesh.MeshImpl; import net.fabricmc.indigo.renderer.mesh.MutableQuadViewImpl; +import net.minecraft.class_4588; import net.minecraft.block.BlockRenderLayer; import net.minecraft.client.MinecraftClient; -import net.minecraft.client.render.BufferBuilder; /** * Consumer for pre-baked meshes. Works by copying the mesh data to a * "editor" quad held in the instance, where all transformations are applied before buffering. */ public abstract class AbstractMeshConsumer extends AbstractQuadRenderer implements Consumer { - protected AbstractMeshConsumer(BlockRenderInfo blockInfo, Function bufferFunc, AoCalculator aoCalc, QuadTransform transform) { + protected AbstractMeshConsumer(BlockRenderInfo blockInfo, Function bufferFunc, AoCalculator aoCalc, QuadTransform transform) { super(blockInfo, bufferFunc, aoCalc, transform); } diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/AbstractQuadRenderer.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/AbstractQuadRenderer.java index c25bd143d..8bc93a61d 100644 --- a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/AbstractQuadRenderer.java +++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/AbstractQuadRenderer.java @@ -24,10 +24,10 @@ import net.fabricmc.fabric.api.renderer.v1.render.RenderContext.QuadTransform; import net.fabricmc.indigo.renderer.aocalc.AoCalculator; import net.fabricmc.indigo.renderer.helper.ColorHelper; import net.fabricmc.indigo.renderer.mesh.MutableQuadViewImpl; +import net.minecraft.class_4588; import net.minecraft.block.Block; import net.minecraft.block.BlockRenderLayer; import net.minecraft.block.BlockState; -import net.minecraft.client.render.BufferBuilder; import net.minecraft.client.util.math.Matrix4f; import net.minecraft.util.math.BlockPos; @@ -38,14 +38,14 @@ import net.minecraft.util.math.BlockPos; public abstract class AbstractQuadRenderer { static final int FULL_BRIGHTNESS = 0xF000F0; - protected final Function bufferFunc; + protected final Function bufferFunc; protected final BlockRenderInfo blockInfo; protected final AoCalculator aoCalc; protected final QuadTransform transform; protected abstract Matrix4f matrix(); - AbstractQuadRenderer(BlockRenderInfo blockInfo, Function bufferFunc, AoCalculator aoCalc, QuadTransform transform) { + AbstractQuadRenderer(BlockRenderInfo blockInfo, Function bufferFunc, AoCalculator aoCalc, QuadTransform transform) { this.blockInfo = blockInfo; this.bufferFunc = bufferFunc; this.aoCalc = aoCalc; @@ -60,6 +60,7 @@ public abstract class AbstractQuadRenderer { } } else { final int blockColor = blockInfo.blockColor(blockColorIndex); + for (int i = 0; i < 4; i++) { q.spriteColor(i, 0, ColorHelper.swapRedBlueIfNeeded(ColorHelper.multiplyColor(blockColor, q.spriteColor(i, 0)))); } @@ -71,14 +72,14 @@ public abstract class AbstractQuadRenderer { bufferQuad(bufferFunc.apply(renderLayer), quad, matrix()); } - public static void bufferQuad(BufferBuilder buff, MutableQuadViewImpl quad, Matrix4f matrix) { + public static void bufferQuad(class_4588 buff, MutableQuadViewImpl quad, Matrix4f matrix) { for (int i = 0; i < 4; i++) { buff.method_22918(matrix, quad.x(i), quad.y(i), quad.z(i)); final int color = quad.spriteColor(i, 0); buff.color(color & 0xFF, (color >> 8) & 0xFF, (color >> 16) & 0xFF, (color >> 24) & 0xFF); buff.method_22913(quad.spriteU(i, 0), quad.spriteV(i, 0)); buff.method_22916(quad.lightmap(i)); - buff.method_22914(quad.normalX(i),quad.normalY(i), quad.normalZ(i)); + buff.method_22914(quad.normalX(i), quad.normalY(i), quad.normalZ(i)); buff.next(); } } diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/BlockRenderContext.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/BlockRenderContext.java index 7760ab117..7467b32f6 100644 --- a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/BlockRenderContext.java +++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/BlockRenderContext.java @@ -30,7 +30,6 @@ import net.minecraft.class_4587; import net.minecraft.class_4588; import net.minecraft.block.BlockRenderLayer; import net.minecraft.block.BlockState; -import net.minecraft.client.render.BufferBuilder; import net.minecraft.client.render.block.BlockModelRenderer; import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.util.math.Matrix4f; @@ -46,7 +45,7 @@ public class BlockRenderContext extends AbstractRenderContext implements RenderC private final MeshConsumer meshConsumer = new MeshConsumer(blockInfo, this::outputBuffer, aoCalc, this::transform); private final Random random = new Random(); private BlockModelRenderer vanillaRenderer; - private BufferBuilder bufferBuilder; + private class_4588 bufferBuilder; private long seed; private boolean isCallingVanilla = false; private boolean didOutput = false; @@ -68,14 +67,14 @@ public class BlockRenderContext extends AbstractRenderContext implements RenderC return blockView == null ? 1f : AoLuminanceFix.INSTANCE.apply(blockView, pos); } - private BufferBuilder outputBuffer(BlockRenderLayer renderLayer) { + private class_4588 outputBuffer(BlockRenderLayer renderLayer) { didOutput = true; return bufferBuilder; } public boolean tesselate(BlockModelRenderer vanillaRenderer, BlockRenderView blockView, BakedModel model, BlockState state, BlockPos pos, class_4587 matrixStack, class_4588 buffer, boolean checkSides, long seed) { this.vanillaRenderer = vanillaRenderer; - this.bufferBuilder = (BufferBuilder) buffer; + this.bufferBuilder = buffer; this.prepareMatrix(state, pos, blockView, matrixStack); this.seed = seed; @@ -97,12 +96,12 @@ public class BlockRenderContext extends AbstractRenderContext implements RenderC protected void acceptVanillaModel(BakedModel model) { isCallingVanilla = true; - didOutput = didOutput && vanillaRenderer.tesselate(blockInfo.blockView, model, blockInfo.blockState, blockInfo.blockPos, matrixStack, (class_4588) bufferBuilder, false, random, seed); + didOutput = didOutput && vanillaRenderer.tesselate(blockInfo.blockView, model, blockInfo.blockState, blockInfo.blockPos, matrixStack, bufferBuilder, false, random, seed); isCallingVanilla = false; } private class MeshConsumer extends AbstractMeshConsumer { - MeshConsumer(BlockRenderInfo blockInfo, Function bufferFunc, AoCalculator aoCalc, QuadTransform transform) { + MeshConsumer(BlockRenderInfo blockInfo, Function bufferFunc, AoCalculator aoCalc, QuadTransform transform) { super(blockInfo, bufferFunc, aoCalc, transform); } diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/ChunkRenderInfo.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/ChunkRenderInfo.java index fe9edf280..ca8d47486 100644 --- a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/ChunkRenderInfo.java +++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/ChunkRenderInfo.java @@ -19,12 +19,9 @@ package net.fabricmc.indigo.renderer.render; import it.unimi.dsi.fastutil.longs.Long2FloatOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; -import net.fabricmc.indigo.Indigo; import net.fabricmc.indigo.renderer.accessor.AccessChunkRenderer; import net.fabricmc.indigo.renderer.accessor.AccessChunkRendererData; import net.fabricmc.indigo.renderer.aocalc.AoLuminanceFix; -import net.fabricmc.indigo.renderer.mesh.MutableQuadViewImpl; -import net.minecraft.block.Block.OffsetType; import net.minecraft.block.BlockRenderLayer; import net.minecraft.block.BlockState; import net.minecraft.client.render.BufferBuilder; @@ -33,7 +30,6 @@ import net.minecraft.client.render.chunk.ChunkBatcher.ChunkRenderData; import net.minecraft.client.render.chunk.ChunkBatcher.ChunkRenderer; import net.minecraft.client.render.chunk.ChunkRendererRegion; import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Vec3d; import net.minecraft.world.BlockRenderView; /** @@ -69,7 +65,6 @@ public class ChunkRenderInfo { private final Long2IntOpenHashMap brightnessCache; private final Long2FloatOpenHashMap aoLevelCache; - private final BlockRenderInfo blockInfo; private final BlockPos.Mutable chunkOrigin = new BlockPos.Mutable(); AccessChunkRendererData chunkData; ChunkRenderer chunkRenderer; @@ -78,17 +73,7 @@ public class ChunkRenderInfo { private final Object2ObjectOpenHashMap buffers = new Object2ObjectOpenHashMap<>(); - private double chunkOffsetX; - private double chunkOffsetY; - private double chunkOffsetZ; - - // chunk offset + block pos offset + model offsets for plants, etc. - private float offsetX = 0; - private float offsetY = 0; - private float offsetZ = 0; - - ChunkRenderInfo(BlockRenderInfo blockInfo) { - this.blockInfo = blockInfo; + ChunkRenderInfo() { brightnessCache = new Long2IntOpenHashMap(); brightnessCache.defaultReturnValue(Integer.MAX_VALUE); aoLevelCache = new Long2FloatOpenHashMap(); @@ -102,9 +87,6 @@ public class ChunkRenderInfo { this.chunkRenderer = chunkRenderer; this.builders = builders; buffers.clear(); - chunkOffsetX = -chunkOrigin.getX(); - chunkOffsetY = -chunkOrigin.getY(); - chunkOffsetZ = -chunkOrigin.getZ(); brightnessCache.clear(); aoLevelCache.clear(); } @@ -115,30 +97,6 @@ public class ChunkRenderInfo { buffers.clear(); } - void beginBlock() { - final BlockState blockState = blockInfo.blockState; - final BlockPos blockPos = blockInfo.blockPos; - - // When we are using the BufferBuilder input methods, the builder will - // add the chunk offset for us, so we should only apply the block offset. - if (Indigo.ENSURE_VERTEX_FORMAT_COMPATIBILITY) { - offsetX = (float) (blockPos.getX()); - offsetY = (float) (blockPos.getY()); - offsetZ = (float) (blockPos.getZ()); - } else { - offsetX = (float) (chunkOffsetX + blockPos.getX()); - offsetY = (float) (chunkOffsetY + blockPos.getY()); - offsetZ = (float) (chunkOffsetZ + blockPos.getZ()); - } - - if (blockState.getBlock().getOffsetType() != OffsetType.NONE) { - Vec3d offset = blockState.getOffsetPos(blockInfo.blockView, blockPos); - offsetX += (float) offset.x; - offsetY += (float) offset.y; - offsetZ += (float) offset.z; - } - } - /** Lazily retrieves output buffer for given layer, initializing as needed. */ public BufferBuilder getInitializedBuffer(BlockRenderLayer renderLayer) { BufferBuilder result = buffers.get(renderLayer); @@ -156,15 +114,6 @@ public class ChunkRenderInfo { return result; } - /** - * Applies position offset for chunk and, if present, block random offset. - */ - void applyOffsets(MutableQuadViewImpl q) { - for (int i = 0; i < 4; i++) { - q.pos(i, q.x(i) + offsetX, q.y(i) + offsetY, q.z(i) + offsetZ); - } - } - /** * Cached values for {@link BlockState#getBlockBrightness(BlockRenderView, BlockPos)}. * See also the comments for {@link #brightnessCache}. diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/ItemRenderContext.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/ItemRenderContext.java index 3c70351ca..f5c3e25fc 100644 --- a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/ItemRenderContext.java +++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/ItemRenderContext.java @@ -16,16 +16,11 @@ package net.fabricmc.indigo.renderer.render; -import static net.fabricmc.indigo.renderer.render.AbstractQuadRenderer.FULL_BRIGHTNESS; - import java.util.List; import java.util.Random; import java.util.function.Consumer; import java.util.function.Supplier; -import com.mojang.blaze3d.systems.RenderSystem; -import org.lwjgl.opengl.GL11; - import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh; import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel; @@ -37,11 +32,10 @@ import net.fabricmc.indigo.renderer.helper.GeometryHelper; import net.fabricmc.indigo.renderer.mesh.EncodingFormat; import net.fabricmc.indigo.renderer.mesh.MeshImpl; import net.fabricmc.indigo.renderer.mesh.MutableQuadViewImpl; +import net.minecraft.class_4587; +import net.minecraft.class_4588; import net.minecraft.block.BlockState; import net.minecraft.client.color.item.ItemColors; -import net.minecraft.client.render.BufferBuilder; -import net.minecraft.client.render.Tessellator; -import net.minecraft.client.render.VertexFormats; import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedQuad; import net.minecraft.client.util.math.Matrix4f; @@ -60,18 +54,18 @@ public class ItemRenderContext extends AbstractRenderContext implements RenderCo /** used to accept a method reference from the ItemRenderer */ @FunctionalInterface public static interface VanillaQuadHandler { - void accept(BufferBuilder bufferBuilder, List quads, int color, ItemStack stack); + void accept(BakedModel model, ItemStack stack, int color, class_4587 matrixStack, class_4588 buffer); } private final ItemColors colorMap; private final Random random = new Random(); private final Consumer fallbackConsumer; - BufferBuilder bufferBuilder; - private int color; + class_4588 bufferBuilder; + class_4587 matrixStack; + Matrix4f matrix; + private int lightmap; private ItemStack itemStack; private VanillaQuadHandler vanillaHandler; - private boolean smoothShading = false; - private boolean enchantment = false; private final Supplier randomSupplier = () -> { Random result = random; @@ -79,13 +73,6 @@ public class ItemRenderContext extends AbstractRenderContext implements RenderCo return random; }; - /** - * When rendering an enchanted item, input stack will be empty. - * This value is populated earlier in the call tree when this is the case - * so that we can render correct geometry and only a single texture. - */ - public ItemStack enchantmentStack; - private final int[] quadData = new int[EncodingFormat.TOTAL_STRIDE];; public ItemRenderContext(ItemColors colorMap) { @@ -93,33 +80,18 @@ public class ItemRenderContext extends AbstractRenderContext implements RenderCo this.fallbackConsumer = this::fallbackConsumer; } - public void renderModel(FabricBakedModel model, int color, ItemStack stack, VanillaQuadHandler vanillaHandler) { - this.color = color; - - if (stack.isEmpty() && enchantmentStack != null) { - enchantment = true; - this.itemStack = enchantmentStack; - enchantmentStack = null; - } else { - enchantment = false; - this.itemStack = stack; - } + public void renderModel(FabricBakedModel model, ItemStack stack, int lightmap, class_4587 matrixStack, class_4588 buffer, VanillaQuadHandler vanillaHandler) { + this.lightmap = lightmap; + this.itemStack = stack; + this.bufferBuilder = buffer; + this.matrixStack = matrixStack; + this.matrix = matrixStack.method_22910(); this.vanillaHandler = vanillaHandler; - Tessellator tessellator = Tessellator.getInstance(); - bufferBuilder = tessellator.getBufferBuilder(); - - bufferBuilder.begin(7, VertexFormats.POSITION_COLOR_UV_NORMAL); model.emitItemQuads(stack, randomSupplier, this); - tessellator.draw(); - if (smoothShading) { - RenderSystem.shadeModel(GL11.GL_FLAT); - smoothShading = false; - } - - bufferBuilder = null; - tessellator = null; + this.bufferBuilder = null; + this.matrixStack = null; this.itemStack = null; this.vanillaHandler = null; } @@ -134,8 +106,7 @@ public class ItemRenderContext extends AbstractRenderContext implements RenderCo public Maker emit() { lightFace(GeometryHelper.lightFace(this)); ColorHelper.applyDiffuseShading(this, false); - //TODO: populate - renderQuad(null); + renderQuad(); clear(); return this; } @@ -153,50 +124,16 @@ public class ItemRenderContext extends AbstractRenderContext implements RenderCo System.arraycopy(data, index, editorQuad.data(), 0, EncodingFormat.TOTAL_STRIDE); editorQuad.load(); index += EncodingFormat.TOTAL_STRIDE; - //TODO: populate - renderQuad(null); + renderQuad(); } }; - /** - * Vanilla normally renders items with flat shading - meaning only - * the last vertex normal is applied for lighting purposes. We - * support non-cube vertex normals so we need to change this to smooth - * for models that use them. We don't change it unless needed because - * OpenGL state changes always impose a performance cost and this happens - * for every item, every frame. - */ - private void handleShading() { - if (!smoothShading && editorQuad.hasVertexNormals()) { - smoothShading = true; - RenderSystem.shadeModel(GL11.GL_SMOOTH); - } - } - - private int quadColor() { + private int indexColor() { final int colorIndex = editorQuad.colorIndex(); - int quadColor = color; - - if (!enchantment && quadColor == -1 && colorIndex != -1) { - quadColor = colorMap.getColorMultiplier(itemStack, colorIndex); - quadColor |= 0xFF000000; - } - return quadColor; + return colorIndex == -1 ? -1 : (colorMap.getColorMultiplier(itemStack, colorIndex) | 0xFF000000); } - private void colorizeAndOutput(int quadColor, Matrix4f matrix) { - final MutableQuadViewImpl q = editorQuad; - - for (int i = 0; i < 4; i++) { - int c = q.spriteColor(i, 0); - c = ColorHelper.multiplyColor(quadColor, c); - q.spriteColor(i, 0, ColorHelper.swapRedBlueIfNeeded(c)); - } - - AbstractQuadRenderer.bufferQuad(bufferBuilder, q, matrix); - } - - private void renderQuad(Matrix4f matrix) { + private void renderQuad() { final MutableQuadViewImpl quad = editorQuad; if (!transform(editorQuad)) { @@ -204,14 +141,19 @@ public class ItemRenderContext extends AbstractRenderContext implements RenderCo } RenderMaterialImpl.Value mat = quad.material(); - final int quadColor = quadColor(); - - handleShading(); + final int quadColor = mat.disableColorIndex(0) ? -1 : indexColor(); + final int lightmap = mat.emissive(0) ? AbstractQuadRenderer.FULL_BRIGHTNESS : this.lightmap; quad.populateMissingNormals(); - quad.lightmap(FULL_BRIGHTNESS, FULL_BRIGHTNESS, FULL_BRIGHTNESS, FULL_BRIGHTNESS); - colorizeAndOutput(!enchantment && mat.disableColorIndex(0) ? -1 : quadColor, matrix); + for (int i = 0; i < 4; i++) { + int c = quad.spriteColor(i, 0); + c = ColorHelper.multiplyColor(quadColor, c); + quad.spriteColor(i, 0, ColorHelper.swapRedBlueIfNeeded(c)); + quad.lightmap(i, ColorHelper.maxBrightness(quad.lightmap(i), lightmap)); + } + + AbstractQuadRenderer.bufferQuad(bufferBuilder, quad, matrix); } @Override @@ -225,37 +167,31 @@ public class ItemRenderContext extends AbstractRenderContext implements RenderCo for (int i = 0; i <= ModelHelper.NULL_FACE_ID; i++) { random.setSeed(ITEM_RANDOM_SEED); final Direction cullFace = ModelHelper.faceFromIndex(i); - renderFallbackWithTransform(bufferBuilder, model.getQuads((BlockState) null, cullFace, random), color, itemStack, cullFace); + renderFallbackWithTransform(bufferBuilder, model.getQuads((BlockState) null, cullFace, random), lightmap, itemStack, cullFace); } } else { for (int i = 0; i <= ModelHelper.NULL_FACE_ID; i++) { - random.setSeed(ITEM_RANDOM_SEED); - vanillaHandler.accept(bufferBuilder, model.getQuads((BlockState) null, ModelHelper.faceFromIndex(i), random), color, itemStack); + vanillaHandler.accept(model, itemStack, lightmap, matrixStack, bufferBuilder); } } }; - private void renderFallbackWithTransform(BufferBuilder bufferBuilder, List quads, int color, ItemStack stack, Direction cullFace) { + private void renderFallbackWithTransform(class_4588 bufferBuilder, List quads, int color, ItemStack stack, Direction cullFace) { if (quads.isEmpty()) { return; } - if (CompatibilityHelper.canRender(quads.get(0).getVertexData())) { - Maker editorQuad = this.editorQuad; + Maker editorQuad = this.editorQuad; - for (BakedQuad q : quads) { - editorQuad.clear(); - editorQuad.fromVanilla(q.getVertexData(), 0, false); - editorQuad.cullFace(cullFace); - final Direction lightFace = q.getFace(); - editorQuad.lightFace(lightFace); - editorQuad.nominalFace(lightFace); - editorQuad.colorIndex(q.getColorIndex()); - //TODO: populate - renderQuad(null); - } - } else { - vanillaHandler.accept(bufferBuilder, quads, color, stack); + for (BakedQuad q : quads) { + editorQuad.clear(); + editorQuad.fromVanilla(q.getVertexData(), 0, false); + editorQuad.cullFace(cullFace); + final Direction lightFace = q.getFace(); + editorQuad.lightFace(lightFace); + editorQuad.nominalFace(lightFace); + editorQuad.colorIndex(q.getColorIndex()); + renderQuad(); } } diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/TerrainFallbackConsumer.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/TerrainFallbackConsumer.java index 1464dcfad..25cf923e5 100644 --- a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/TerrainFallbackConsumer.java +++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/TerrainFallbackConsumer.java @@ -60,12 +60,10 @@ public class TerrainFallbackConsumer extends AbstractQuadRenderer implements Con private static Value MATERIAL_SHADED = (Value) IndigoRenderer.INSTANCE.materialFinder().find(); private final int[] editorBuffer = new int[EncodingFormat.TOTAL_STRIDE]; - private final ChunkRenderInfo chunkInfo; private final Supplier matrixSupplier; TerrainFallbackConsumer(BlockRenderInfo blockInfo, ChunkRenderInfo chunkInfo, AoCalculator aoCalc, QuadTransform transform, Supplier matrixSupplier) { super(blockInfo, chunkInfo::getInitializedBuffer, aoCalc, transform); - this.chunkInfo = chunkInfo; this.matrixSupplier = matrixSupplier; } @@ -141,7 +139,6 @@ public class TerrainFallbackConsumer extends AbstractQuadRenderer implements Con // needs to happen before offsets are applied editorQuad.invalidateShape(); aoCalc.compute(editorQuad, true); - chunkInfo.applyOffsets(editorQuad); tesselateSmooth(editorQuad, blockInfo.defaultLayer, editorQuad.colorIndex()); } else { // vanilla compatibility hack @@ -154,7 +151,6 @@ public class TerrainFallbackConsumer extends AbstractQuadRenderer implements Con editorQuad.geometryFlags(GeometryHelper.LIGHT_FACE_FLAG); editorQuad.lightFace(cullFace); } - chunkInfo.applyOffsets(editorQuad); tesselateFlat(editorQuad, blockInfo.defaultLayer, editorQuad.colorIndex()); } } diff --git a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/TerrainRenderContext.java b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/TerrainRenderContext.java index 23ad20e55..e65b21130 100644 --- a/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/TerrainRenderContext.java +++ b/fabric-renderer-indigo/src/main/java/net/fabricmc/indigo/renderer/render/TerrainRenderContext.java @@ -44,7 +44,7 @@ import net.minecraft.util.math.BlockPos; public class TerrainRenderContext extends AbstractRenderContext implements RenderContext { public static final ThreadLocal POOL = ThreadLocal.withInitial(TerrainRenderContext::new); private final TerrainBlockRenderInfo blockInfo = new TerrainBlockRenderInfo(); - private final ChunkRenderInfo chunkInfo = new ChunkRenderInfo(blockInfo); + private final ChunkRenderInfo chunkInfo = new ChunkRenderInfo(); private final AoCalculator aoCalc = new AoCalculator(blockInfo, chunkInfo::cachedBrightness, chunkInfo::cachedAoLevel); private final TerrainMeshConsumer meshConsumer = new TerrainMeshConsumer(blockInfo, chunkInfo, aoCalc, this::transform, this::matrix); private final TerrainFallbackConsumer fallbackConsumer = new TerrainFallbackConsumer(blockInfo, chunkInfo, aoCalc, this::transform, this::matrix); @@ -70,7 +70,6 @@ public class TerrainRenderContext extends AbstractRenderContext implements Rende try { aoCalc.clear(); blockInfo.prepareForBlock(blockState, blockPos, model.useAmbientOcclusion()); - chunkInfo.beginBlock(); ((FabricBakedModel) model).emitBlockQuads(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, blockInfo.randomSupplier, this); } catch (Throwable var9) { CrashReport crashReport_1 = CrashReport.create(var9, "Tesselating block in world - Indigo Renderer"); diff --git a/fabric-renderer-registries-v1/build.gradle b/fabric-renderer-registries-v1/build.gradle index 9a7c29664..666d95a52 100644 --- a/fabric-renderer-registries-v1/build.gradle +++ b/fabric-renderer-registries-v1/build.gradle @@ -1,5 +1,5 @@ archivesBaseName = "fabric-renderer-registries-v1" -version = getSubprojectVersion(project, "1.0.1") +version = getSubprojectVersion(project, "1.0.2") dependencies { compile project(path: ':fabric-api-base', configuration: 'dev') diff --git a/fabric-renderer-registries-v1/src/main/java/net/fabricmc/fabric/mixin/client/rendereregistry/MixinEntityRenderDispatcher.java b/fabric-renderer-registries-v1/src/main/java/net/fabricmc/fabric/mixin/client/rendereregistry/MixinEntityRenderDispatcher.java index f1358d92c..170817484 100644 --- a/fabric-renderer-registries-v1/src/main/java/net/fabricmc/fabric/mixin/client/rendereregistry/MixinEntityRenderDispatcher.java +++ b/fabric-renderer-registries-v1/src/main/java/net/fabricmc/fabric/mixin/client/rendereregistry/MixinEntityRenderDispatcher.java @@ -16,30 +16,29 @@ package net.fabricmc.fabric.mixin.client.rendereregistry; -import net.fabricmc.fabric.api.client.rendereregistry.v1.EntityRendererRegistry; -import net.minecraft.client.font.TextRenderer; -import net.minecraft.client.options.GameOptions; -import net.minecraft.client.render.entity.EntityRenderDispatcher; -import net.minecraft.client.render.entity.EntityRenderer; -import net.minecraft.client.render.item.ItemRenderer; -import net.minecraft.client.texture.TextureManager; -import net.minecraft.entity.EntityType; -import net.minecraft.resource.ReloadableResourceManager; +import java.util.Map; + 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 java.util.Map; +import net.fabricmc.fabric.api.client.rendereregistry.v1.EntityRendererRegistry; +import net.minecraft.client.render.entity.EntityRenderDispatcher; +import net.minecraft.client.render.entity.EntityRenderer; +import net.minecraft.client.render.item.ItemRenderer; +import net.minecraft.entity.EntityType; +import net.minecraft.resource.ReloadableResourceManager; @Mixin(EntityRenderDispatcher.class) public class MixinEntityRenderDispatcher { @Shadow Map, EntityRenderer> renderers; - @Inject(method = "(Lnet/minecraft/client/texture/TextureManager;Lnet/minecraft/client/render/item/ItemRenderer;Lnet/minecraft/resource/ReloadableResourceManager;Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/client/options/GameOptions;)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/entity/EntityRenderDispatcher;method_23167(Lnet/minecraft/client/render/item/ItemRenderer;Lnet/minecraft/resource/ReloadableResourceManager;)V", shift = At.Shift.AFTER), require = 0) - public void init(TextureManager textureManager, ItemRenderer itemRenderer, ReloadableResourceManager manager, TextRenderer textRenderer, GameOptions gameOptions, CallbackInfo info) { - EntityRendererRegistry.INSTANCE.initialize((EntityRenderDispatcher) (Object) this, textureManager, manager, itemRenderer, renderers); + @Inject(method = "method_23167", at = @At(value = "RETURN"), require = 1) + public void on_method_23167(ItemRenderer itemRenderer, ReloadableResourceManager manager, CallbackInfo info) { + final EntityRenderDispatcher me = (EntityRenderDispatcher) (Object) this; + EntityRendererRegistry.INSTANCE.initialize(me, me.textureManager, manager, itemRenderer, renderers); } -} +} \ No newline at end of file diff --git a/fabric-rendering-data-attachment-v1/src/main/java/net/fabricmc/fabric/mixin/rendering/data/client/MixinChunkRendererRegion.java b/fabric-rendering-data-attachment-v1/src/main/java/net/fabricmc/fabric/mixin/rendering/data/client/MixinChunkRendererRegion.java index 397fadb61..3d0fda518 100644 --- a/fabric-rendering-data-attachment-v1/src/main/java/net/fabricmc/fabric/mixin/rendering/data/client/MixinChunkRendererRegion.java +++ b/fabric-rendering-data-attachment-v1/src/main/java/net/fabricmc/fabric/mixin/rendering/data/client/MixinChunkRendererRegion.java @@ -16,52 +16,104 @@ package net.fabricmc.fabric.mixin.rendering.data.client; -import java.util.HashMap; +import java.util.ConcurrentModificationException; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; -import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachedBlockView; -import net.minecraft.client.render.chunk.ChunkRendererRegion; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; 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 it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachedBlockView; import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachmentBlockEntity; import net.minecraft.block.entity.BlockEntity; +import net.minecraft.client.render.chunk.ChunkRendererRegion; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraft.world.chunk.WorldChunk; @Mixin(ChunkRendererRegion.class) public abstract class MixinChunkRendererRegion implements RenderAttachedBlockView { - private HashMap fabric_renderDataObjects; + private Int2ObjectOpenHashMap fabric_renderDataObjects; - @Inject(at = @At("RETURN"), method = "") - public void init(World world, int cxOff, int czOff, WorldChunk[][] chunks, BlockPos posFrom, BlockPos posTo, CallbackInfo info) { - HashMap map = new HashMap<>(); + @Shadow + protected abstract int getIndex(BlockPos pos); - for (WorldChunk[] chunkA : chunks) { - for (WorldChunk chunkB : chunkA) { - for (Map.Entry entry: chunkB.getBlockEntities().entrySet()) { - BlockPos entPos = entry.getKey(); - if (entPos.getX() >= posFrom.getX() && entPos.getX() <= posTo.getX() - && entPos.getY() >= posFrom.getY() && entPos.getY() <= posTo.getY() - && entPos.getZ() >= posFrom.getZ() && entPos.getZ() <= posTo.getZ()) { + @Shadow + protected abstract int getIndex(int x, int y, int z); - Object o = ((RenderAttachmentBlockEntity) entry.getValue()).getRenderAttachmentData(); - if (o != null) { - map.put(entPos, o); - } - } - } - } - } + private static final AtomicInteger ERROR_COUNTER = new AtomicInteger(); + private static final Logger LOGGER = LogManager.getLogger(); - this.fabric_renderDataObjects = map; - } + @Inject(at = @At("RETURN"), method = "") + public void init(World world, int cxOff, int czOff, WorldChunk[][] chunks, BlockPos posFrom, BlockPos posTo, CallbackInfo info) { + // instantiated lazily - avoids allocation for chunks without any data objects - which is most of them! + Int2ObjectOpenHashMap map = null; - @Override - public Object getBlockEntityRenderAttachment(BlockPos pos) { - return fabric_renderDataObjects.get(pos); - } + for (WorldChunk[] chunkOuter : chunks) { + for (WorldChunk chunk : chunkOuter) { + // Hash maps in chunks should generally not be modified outside of client thread + // but does happen in practice, due to mods or inconsistent vanilla behaviors, causing + // CMEs when we iterate the map. (Vanilla does not iterate these maps when it builds + // the chunk cache and does not suffer from this problem.) + // + // We handle this simply by retrying until it works. Ugly but effective. + for (;;) { + try { + map = mapChunk(chunk, posFrom, posTo, map); + break; + } catch (ConcurrentModificationException e) { + final int count = ERROR_COUNTER.incrementAndGet(); + + if (count <= 5) { + LOGGER.warn("[Render Data Attachment] Encountered CME during render region build. A mod is accessing or changing chunk data outside the main thread. Retrying.", e); + + if (count == 5) { + LOGGER.info("[Render Data Attachment] Subsequent exceptions will be suppressed."); + } + } + } + } + } + } + + this.fabric_renderDataObjects = map; + } + + private Int2ObjectOpenHashMap mapChunk(WorldChunk chunk, BlockPos posFrom, BlockPos posTo, Int2ObjectOpenHashMap map) { + final int xMin = posFrom.getX(); + final int xMax = posTo.getX(); + final int zMin = posFrom.getZ(); + final int zMax = posTo.getZ(); + final int yMin = posFrom.getY(); + final int yMax = posTo.getY(); + + for (Map.Entry entry : chunk.getBlockEntities().entrySet()) { + final BlockPos entPos = entry.getKey(); + + if (entPos.getX() >= xMin && entPos.getX() <= xMax + && entPos.getY() >= yMin && entPos.getY() <= yMax + && entPos.getZ() >= zMin && entPos.getZ() <= zMax) { + + final Object o = ((RenderAttachmentBlockEntity) entry.getValue()).getRenderAttachmentData(); + if (o != null) { + if (map == null) { + map = new Int2ObjectOpenHashMap<>(); + } + map.put(getIndex(entPos), o); + } + } + } + return map; + } + + @Override + public Object getBlockEntityRenderAttachment(BlockPos pos) { + return fabric_renderDataObjects == null ? null : fabric_renderDataObjects.get(getIndex(pos)); + } } diff --git a/fabric-testmods/java/net/fabricmc/fabric/particle/ParticleModClient.java b/fabric-testmods/java/net/fabricmc/fabric/particle/ParticleModClient.java new file mode 100644 index 000000000..06b0aee86 --- /dev/null +++ b/fabric-testmods/java/net/fabricmc/fabric/particle/ParticleModClient.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2016, 2017, 2018 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.particle; + +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.client.particle.v1.FabricSpriteProvider; +import net.fabricmc.fabric.api.client.particle.v1.ParticleFactoryRegistry; +import net.fabricmc.fabric.api.particle.v1.FabricParticleTypes; +import net.minecraft.block.Blocks; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.particle.AnimatedParticle; +import net.minecraft.client.particle.Particle; +import net.minecraft.client.particle.ParticleFactory; +import net.minecraft.client.particle.ParticleTextureSheet; +import net.minecraft.client.particle.SpriteBillboardParticle; +import net.minecraft.client.particle.SpriteProvider; +import net.minecraft.particle.DefaultParticleType; +import net.minecraft.particle.ParticleEffect; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; +import net.minecraft.world.World; + +public class ParticleModClient implements ClientModInitializer, ModInitializer { + public static final DefaultParticleType SIMPLE_TEST_PARTICLE = FabricParticleTypes.simple(); + public static final DefaultParticleType CUSTOM_TEST_PARTICLE = FabricParticleTypes.simple(); + + @Override + public void onInitialize() { + Registry.register(Registry.PARTICLE_TYPE, new Identifier("testmod", "simple"), SIMPLE_TEST_PARTICLE); + Registry.register(Registry.PARTICLE_TYPE, new Identifier("testmod", "custom"), CUSTOM_TEST_PARTICLE); + } + + @Override + public void onInitializeClient() { + ParticleFactoryRegistry.getInstance().register(SIMPLE_TEST_PARTICLE, SimpleTestParticle::new); + ParticleFactoryRegistry.getInstance().register(CUSTOM_TEST_PARTICLE, CustomTestParticle.Factory::new); + } + + @Environment(EnvType.CLIENT) + static class SimpleTestParticle extends SpriteBillboardParticle { + public SimpleTestParticle(ParticleEffect effect, World world, double x, double y, double z, double velX, double velY, double velZ) { + super(world, x, y, z, velX, velY, velZ); + setSprite(MinecraftClient.getInstance().getItemRenderer().getModels().getSprite(Blocks.BARRIER.asItem())); + } + + @Override + public ParticleTextureSheet getType() { + return ParticleTextureSheet.PARTICLE_SHEET_TRANSLUCENT; + } + } + + @Environment(EnvType.CLIENT) + static class CustomTestParticle extends AnimatedParticle { + protected CustomTestParticle(World world, double x, double y, double z, SpriteProvider sprites) { + super(world, x, y, z, sprites, 1); + setSprite(sprites.getSprite(world.random)); + } + + @Environment(EnvType.CLIENT) + public static class Factory implements ParticleFactory { + private final FabricSpriteProvider sprites; + + public Factory(FabricSpriteProvider sprites) { + this.sprites = sprites; + } + + @Override + public Particle createParticle(DefaultParticleType type, World world, double x, double y, double z, double vX, double vY, double vZ) { + return new CustomTestParticle(world, x, y, z, sprites); + } + } + } +} diff --git a/fabric-testmods/resources/assets/testmod/particles/custom.json b/fabric-testmods/resources/assets/testmod/particles/custom.json new file mode 100644 index 000000000..e7e925b5f --- /dev/null +++ b/fabric-testmods/resources/assets/testmod/particles/custom.json @@ -0,0 +1,12 @@ +{ + "textures": [ + "minecraft:spell_7", + "minecraft:spell_6", + "minecraft:spell_5", + "minecraft:spell_4", + "minecraft:spell_3", + "minecraft:spell_2", + "minecraft:spell_1", + "minecraft:spell_0" + ] +} diff --git a/fabric-testmods/resources/assets/testmod/particles/simple.json b/fabric-testmods/resources/assets/testmod/particles/simple.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/fabric-testmods/resources/assets/testmod/particles/simple.json @@ -0,0 +1 @@ +{} diff --git a/settings.gradle b/settings.gradle index 176f9ba51..1335d3656 100644 --- a/settings.gradle +++ b/settings.gradle @@ -30,6 +30,7 @@ include 'fabric-models-v0' include 'fabric-networking-v0' include 'fabric-networking-blockentity-v0' include 'fabric-object-builders-v0' +include 'fabric-particles-v1' include 'fabric-registry-sync-v0' include 'fabric-renderer-api-v1' include 'fabric-renderer-indigo'