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:
grondag 2019-09-29 18:21:38 -07:00 committed by GitHub
parent f8c11fca6f
commit 32ac36d518
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 835 additions and 360 deletions

View file

@ -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"
}

View file

@ -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')

View file

@ -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());
}
}

View 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')
}

View file

@ -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();
}

View file

@ -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);
}
}

View file

@ -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) {};
}
}

View file

@ -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);
}
}
}

View file

@ -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));
}
}

View file

@ -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();
}

View file

@ -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();
}
}
}

View file

@ -0,0 +1,11 @@
{
"required": true,
"package": "net.fabricmc.fabric.mixin.client.particle",
"compatibilityLevel": "JAVA_8",
"client": [
"MixinParticleManager"
],
"injectors": {
"defaultRequire": 1
}
}

View 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"
]
}

View file

@ -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')

View file

@ -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++) {

View file

@ -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();
// }
// }
}

View file

@ -4,7 +4,6 @@
"compatibilityLevel": "JAVA_8",
"client": [
"client.MixinBakedModel",
"client.MixinBlockRenderManager",
"client.MixinSpriteAtlasTexture"
],
"injectors": {

View file

@ -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')

View file

@ -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;

View file

@ -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

View file

@ -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);
}

View file

@ -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();
}
}
}

View file

@ -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);
}

View file

@ -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();
}
}

View file

@ -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);
}

View file

@ -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}.

View file

@ -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();
}
}

View file

@ -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());
}
}

View file

@ -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");

View file

@ -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')

View file

@ -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);
}
}
}

View file

@ -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));
}
}

View file

@ -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);
}
}
}
}

View file

@ -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"
]
}

View file

@ -0,0 +1 @@
{}

View file

@ -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'