forked from FabricMC/fabric
1.14 Updates and Render Fixes (#394)
* Fix EntityRenderDispatcher hook (again) * Add fabric-particles-v1 * Add a sync() method to BlockEntityClientSerializable * Prevent CME due to off-thread block entity updates (#367) * Restore rendering features disabled in first 19w39a release * Remove rendering stuffs no longer needed due to snapshots * Bump versions as needed
This commit is contained in:
parent
f8c11fca6f
commit
32ac36d518
36 changed files with 835 additions and 360 deletions
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
* <p>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());
|
||||
}
|
||||
}
|
||||
|
|
7
fabric-particles-v1/build.gradle
Normal file
7
fabric-particles-v1/build.gradle
Normal file
|
@ -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')
|
||||
}
|
|
@ -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<T>
|
||||
*/
|
||||
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<Sprite> getSprites();
|
||||
}
|
|
@ -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 <T extends ParticleEffect> void register(ParticleType<T> type, ParticleFactory<T> 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 <T extends ParticleEffect> void register(ParticleType<T> type, PendingParticleFactory<T> constructor);
|
||||
|
||||
/**
|
||||
* A pending particle factory.
|
||||
*
|
||||
* @param <T> The type of particle effects this factory deals with.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface PendingParticleFactory<T extends ParticleEffect> {
|
||||
/**
|
||||
* 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<T> create(FabricSpriteProvider provider);
|
||||
}
|
||||
}
|
|
@ -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 <T extends ParticleEffect> ParticleType<T> complex(ParticleEffect.Factory<T> 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 <T extends ParticleEffect> ParticleType<T> complex(boolean alwaysSpawn, ParticleEffect.Factory<T> factory) {
|
||||
return new ParticleType<T>(alwaysSpawn, factory) {};
|
||||
}
|
||||
}
|
|
@ -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<FabricSpriteProviderImpl> 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<Identifier> 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<Identifier> spriteIds;
|
||||
|
||||
// @Nullable
|
||||
private List<Sprite> 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<Sprite> getSprites() {
|
||||
if (sprites == null) {
|
||||
sprites = spriteIds.stream().map(getAtlas()::getSprite).collect(Collectors.toList());
|
||||
}
|
||||
return sprites;
|
||||
}
|
||||
|
||||
public void setSprites(List<Identifier> sprites) {
|
||||
this.sprites = null;
|
||||
this.spriteIds = ImmutableList.copyOf(sprites);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<ParticleFactory<?>> factories = new Int2ObjectOpenHashMap<>();
|
||||
final Int2ObjectMap<PendingParticleFactory<?>> constructors = new Int2ObjectOpenHashMap<>();
|
||||
final Map<Identifier, Integer> constructorsIdsMap = new HashMap<>();
|
||||
|
||||
private ParticleFactoryRegistryImpl() { }
|
||||
|
||||
@Override
|
||||
public <T extends ParticleEffect> void register(ParticleType<T> type, ParticleFactory<T> factory) {
|
||||
factories.put(Registry.PARTICLE_TYPE.getRawId(type), factory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends ParticleEffect> void register(ParticleType<T> type, PendingParticleFactory<T> factory) {
|
||||
constructors.put(Registry.PARTICLE_TYPE.getRawId(type), factory);
|
||||
constructorsIdsMap.put(Registry.PARTICLE_TYPE.getId(type), Registry.PARTICLE_TYPE.getRawId(type));
|
||||
}
|
||||
}
|
|
@ -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<ParticleFactory<?>> getFactories();
|
||||
}
|
|
@ -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<ParticleFactory<?>> 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<Identifier, List<Identifier>> output, CallbackInfo info) {
|
||||
if (fabricParticleManager.loadParticle(manager, id)) {
|
||||
info.cancel();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"required": true,
|
||||
"package": "net.fabricmc.fabric.mixin.client.particle",
|
||||
"compatibilityLevel": "JAVA_8",
|
||||
"client": [
|
||||
"MixinParticleManager"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
12
fabric-particles-v1/src/main/resources/fabric.mod.json
Normal file
12
fabric-particles-v1/src/main/resources/fabric.mod.json
Normal file
|
@ -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"
|
||||
]
|
||||
}
|
|
@ -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')
|
||||
|
|
|
@ -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<BakedQuad>[] result = new List[7];
|
||||
for (int i = 0; i < 7; i++) {
|
||||
|
|
|
@ -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<MutablePair<DamageModel, BakedModel>> 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<DamageModel, BakedModel> 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();
|
||||
// }
|
||||
// }
|
||||
}
|
|
@ -4,7 +4,6 @@
|
|||
"compatibilityLevel": "JAVA_8",
|
||||
"client": [
|
||||
"client.MixinBakedModel",
|
||||
"client.MixinBlockRenderManager",
|
||||
"client.MixinSpriteAtlasTexture"
|
||||
],
|
||||
"injectors": {
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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<BakedQuad> quads, int color, ItemStack stack);
|
||||
//
|
||||
// @Shadow
|
||||
// protected ItemColors colorMap;
|
||||
// private final ThreadLocal<ItemRenderContext> 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<ItemRenderContext> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Mesh> {
|
||||
protected AbstractMeshConsumer(BlockRenderInfo blockInfo, Function<BlockRenderLayer, BufferBuilder> bufferFunc, AoCalculator aoCalc, QuadTransform transform) {
|
||||
protected AbstractMeshConsumer(BlockRenderInfo blockInfo, Function<BlockRenderLayer, class_4588> bufferFunc, AoCalculator aoCalc, QuadTransform transform) {
|
||||
super(blockInfo, bufferFunc, aoCalc, transform);
|
||||
}
|
||||
|
||||
|
|
|
@ -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<BlockRenderLayer, BufferBuilder> bufferFunc;
|
||||
protected final Function<BlockRenderLayer, class_4588> bufferFunc;
|
||||
protected final BlockRenderInfo blockInfo;
|
||||
protected final AoCalculator aoCalc;
|
||||
protected final QuadTransform transform;
|
||||
|
||||
protected abstract Matrix4f matrix();
|
||||
|
||||
AbstractQuadRenderer(BlockRenderInfo blockInfo, Function<BlockRenderLayer, BufferBuilder> bufferFunc, AoCalculator aoCalc, QuadTransform transform) {
|
||||
AbstractQuadRenderer(BlockRenderInfo blockInfo, Function<BlockRenderLayer, class_4588> 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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<BlockRenderLayer, BufferBuilder> bufferFunc, AoCalculator aoCalc, QuadTransform transform) {
|
||||
MeshConsumer(BlockRenderInfo blockInfo, Function<BlockRenderLayer, class_4588> bufferFunc, AoCalculator aoCalc, QuadTransform transform) {
|
||||
super(blockInfo, bufferFunc, aoCalc, transform);
|
||||
}
|
||||
|
||||
|
|
|
@ -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<BlockRenderLayer, BufferBuilder> 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}.
|
||||
|
|
|
@ -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<BakedQuad> 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<BakedModel> 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<Random> 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<BakedQuad> quads, int color, ItemStack stack, Direction cullFace) {
|
||||
private void renderFallbackWithTransform(class_4588 bufferBuilder, List<BakedQuad> 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Matrix4f> matrixSupplier;
|
||||
|
||||
TerrainFallbackConsumer(BlockRenderInfo blockInfo, ChunkRenderInfo chunkInfo, AoCalculator aoCalc, QuadTransform transform, Supplier<Matrix4f> 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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ import net.minecraft.util.math.BlockPos;
|
|||
public class TerrainRenderContext extends AbstractRenderContext implements RenderContext {
|
||||
public static final ThreadLocal<TerrainRenderContext> 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");
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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<EntityType<?>, EntityRenderer<?>> renderers;
|
||||
|
||||
@Inject(method = "<init>(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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<BlockPos, Object> fabric_renderDataObjects;
|
||||
private Int2ObjectOpenHashMap<Object> fabric_renderDataObjects;
|
||||
|
||||
@Inject(at = @At("RETURN"), method = "<init>")
|
||||
public void init(World world, int cxOff, int czOff, WorldChunk[][] chunks, BlockPos posFrom, BlockPos posTo, CallbackInfo info) {
|
||||
HashMap<BlockPos, Object> map = new HashMap<>();
|
||||
@Shadow
|
||||
protected abstract int getIndex(BlockPos pos);
|
||||
|
||||
for (WorldChunk[] chunkA : chunks) {
|
||||
for (WorldChunk chunkB : chunkA) {
|
||||
for (Map.Entry<BlockPos, BlockEntity> 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 = "<init>")
|
||||
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<Object> 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<Object> mapChunk(WorldChunk chunk, BlockPos posFrom, BlockPos posTo, Int2ObjectOpenHashMap<Object> 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<BlockPos, BlockEntity> 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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<DefaultParticleType> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
]
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -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'
|
||||
|
|
Loading…
Reference in a new issue