mirror of
https://github.com/FabricMC/fabric.git
synced 2025-03-13 16:53:35 -04:00
Interface inject FabricBakedModel (#3099)
### API Changes - Interface inject `FabricBakedModel` This change was done to eliminate the constant need to cast `BakedModel` to `FabricBakedModel`, especially when rendering sub-models. The implementations for the methods of `FabricBakedModel` were moved from `BakedModelMixin` to `FabricBakedModel` as default implementations. Some javadoc was updated to reflect this change. - Deprecate the mesh consumer (`Consumer<Mesh>` retrieved from `RenderContext`) This change was done to ensure consistency across all current and future contexts in which a mesh may need to be output to a quad emitter. The preferred direct replacement is the new method `Mesh#outputTo(QuadEmitter)`. Some javadoc was updated to reflect this change. - Deprecate the baked model consumer (`BakedModelConsumer` retrieved from `RenderContext`) This change was done to ensure consistent rendering of sub-models and to eliminate assumptions about what features a model uses. The preferred direct replacement is to use the appropriate `emit` method in `FabricBakedModel`. Some javadoc was updated to reflect this change. Even though the consumer is now deprecated, the default `FabricBakedModel` method implementations still use it and renderers must still implement the getter. This is because Indigo and Indium sometimes apply smooth lighting differently based on if the quad came from a vanilla model or not. The eventual solution to allow all baked model consumer usage to be removed is to allow renderers to override the default `BakedModelMixin` and handle default vanilla models directly. - Fix `QuadView#toVanilla`'s javadoc reporting the wrong minimum array size ### Implementation Changes - Restructure `RenderContext` implementations This change was done to reduce code duplication, improve standardization, and improve readability. Most code of `AbstractQuadRenderer` was moved into a new class called `AbstractBlockRenderContext` (which `BlockRenderContext` and `TerrainRenderContext` now extend), with the main buffering method being moved to `AbstractRenderContext`. - Remove red blue color swap It is unclear why this code was necessary in the first place. Indigo stores vertex color in ARGB format, then converts it to ABGR format only if the native byte order is little endian, and then reads the vertex color in ABGR format when buffering vertex data. This would mean that on big endian systems, all red and blue color components would be swapped. Now, color is read in ARGB format when buffering and no color format conversion or swapping is done. - Encode face normal This change was done to improve the performance of meshes. Meshes already encoded all other geometry data, but the face normal would still have to be recomputed every time a mesh quad was rendered. Now, it is stored in the quad data header, so it is simply decoded, which is significantly faster. - Fix some bugs - Outputting a mesh via the mesh consumer would result in the emitter retaining the data of the last quad in the mesh. - In non-terrain block rendering, the baked model consumer would incorrectly use the random seed provided by the block state instead of the passed random seed argument. - When converting to or from vanilla quad data, the color would not be converted. Indigo uses ARGB format to store color, while vanilla quads use ABGR or RGBA. See the comment near the bottom of `ColorHelper` for more information. - Fix TerrainRenderContext using incorrect overlay value of `0` instead of `OverlayTexture.DEFAULT_UV`. - Improve performance of some code - `QuadViewImpl#computeGeometry()` was being called before transforms and the cull test were applied. These operations can change the geometry and invalidate computed geometry data or cancel the quad outright. Calling the method explicitly in those contexts was also not necessary in the first place. - A single `Vector4f` instance is now reused to transform the vertex position instead of allocating a new `Vector4f` for each vertex. - The random seed lazy computation now uses a separate boolean instead of checking if the seed is `-1`.
This commit is contained in:
parent
88323df583
commit
c6bbc80da8
34 changed files with 875 additions and 1096 deletions
|
@ -33,8 +33,20 @@ import net.fabricmc.fabric.api.renderer.v1.Renderer;
|
|||
public interface Mesh {
|
||||
/**
|
||||
* Use to access all of the quads encoded in this mesh. The quad instances
|
||||
* sent to the consumer will likely be threadlocal/reused and should never
|
||||
* sent to the consumer will likely be thread-local/reused and should never
|
||||
* be retained by the consumer.
|
||||
*/
|
||||
void forEach(Consumer<QuadView> consumer);
|
||||
|
||||
/**
|
||||
* Outputs all quads in this mesh to the given quad emitter.
|
||||
*
|
||||
* @apiNote The default implementation will be removed in the next breaking release.
|
||||
*/
|
||||
default void outputTo(QuadEmitter emitter) {
|
||||
forEach(quad -> {
|
||||
emitter.copyFrom(quad);
|
||||
emitter.emit();
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
|
@ -115,7 +115,7 @@ public interface MutableQuadView extends QuadView {
|
|||
}
|
||||
|
||||
/**
|
||||
* Set vertex color.
|
||||
* Set vertex color in ARGB format (0xAARRGGBB).
|
||||
*
|
||||
* @apiNote The default implementation will be removed in the next breaking release.
|
||||
*/
|
||||
|
|
|
@ -69,7 +69,7 @@ public interface QuadView {
|
|||
Vector3f copyPos(int vertexIndex, @Nullable Vector3f target);
|
||||
|
||||
/**
|
||||
* Retrieve vertex color.
|
||||
* Retrieve vertex color in ARGB format (0xAARRGGBB).
|
||||
*
|
||||
* @apiNote The default implementation will be removed in the next breaking release.
|
||||
*/
|
||||
|
@ -169,7 +169,7 @@ public interface QuadView {
|
|||
/**
|
||||
* Normal of the quad as implied by geometry. Will be invalid
|
||||
* if quad vertices are not co-planar. Typically computed lazily
|
||||
* on demand and not encoded.
|
||||
* on demand.
|
||||
*
|
||||
* <p>Not typically needed by models. Exposed to enable standard lighting
|
||||
* utility functions for use by renderers.
|
||||
|
@ -201,7 +201,7 @@ public interface QuadView {
|
|||
* @param target Target array for the baked quad data.
|
||||
*
|
||||
* @param targetIndex Starting position in target array - array must have
|
||||
* at least 28 elements available at this index.
|
||||
* at least {@link #VANILLA_QUAD_STRIDE} elements available at this index.
|
||||
*/
|
||||
default void toVanilla(int[] target, int targetIndex) {
|
||||
toVanilla(0, target, targetIndex, false);
|
||||
|
|
|
@ -25,10 +25,9 @@ import net.minecraft.client.render.model.BakedModel;
|
|||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
|
||||
import net.fabricmc.fabric.api.renderer.v1.Renderer;
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
||||
|
||||
/**
|
||||
|
@ -38,9 +37,7 @@ import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
|||
*
|
||||
* <p>Implementors should have a look at {@link ModelHelper} as it contains many useful functions.
|
||||
*
|
||||
* <p>Note for {@link Renderer} implementors: Fabric causes BakedModel to extend this
|
||||
* interface with {@link #isVanillaAdapter()} == true and to produce standard vertex data.
|
||||
* This means any BakedModel instance can be safely cast to this interface without an instanceof check.
|
||||
* <p>Note: This interface is automatically implemented on all baked models via Mixin and interface injection.
|
||||
*/
|
||||
public interface FabricBakedModel {
|
||||
/**
|
||||
|
@ -48,11 +45,13 @@ public interface FabricBakedModel {
|
|||
* Also means the model does not rely on any non-vanilla features.
|
||||
* Allows the renderer to optimize or route vanilla models through the unmodified vanilla pipeline if desired.
|
||||
*
|
||||
* <p>Fabric overrides to true for vanilla baked models.
|
||||
* <p>Vanilla baked models will return true.
|
||||
* Enhanced models that use this API should return false,
|
||||
* otherwise the API will not recognize the model.
|
||||
*/
|
||||
boolean isVanillaAdapter();
|
||||
default boolean isVanillaAdapter() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will be called during chunk rebuilds to generate both the static and
|
||||
|
@ -83,9 +82,7 @@ public interface FabricBakedModel {
|
|||
* <p>Note: with {@link BakedModel#getQuads(BlockState, net.minecraft.util.math.Direction, Random)}, the random
|
||||
* parameter is normally initialized with the same seed prior to each face layer.
|
||||
* Model authors should note this method is called only once per block, and call the provided
|
||||
* Random supplier multiple times if re-seeding is necessary. For wrapped vanilla baked models,
|
||||
* it will probably be easier to use {@link RenderContext#bakedModelConsumer()} which handles
|
||||
* re-seeding per face automatically.
|
||||
* Random supplier multiple times if re-seeding is necessary.
|
||||
*
|
||||
* @param blockView Access to world state. Cast to {@code RenderAttachedBlockView} to
|
||||
* retrieve block entity data unless thread safety can be guaranteed.
|
||||
|
@ -95,7 +92,10 @@ public interface FabricBakedModel {
|
|||
* Will not be thread-safe. Do not cache or retain a reference.
|
||||
* @param context Accepts model output.
|
||||
*/
|
||||
void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context);
|
||||
@SuppressWarnings("deprecation")
|
||||
default void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
|
||||
context.bakedModelConsumer().accept((BakedModel) this, state);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will be called during item rendering to generate both the static and
|
||||
|
@ -124,5 +124,9 @@ public interface FabricBakedModel {
|
|||
* logic here, instead of returning every possible shape from {@link BakedModel#getOverrides}
|
||||
* as vanilla baked models.
|
||||
*/
|
||||
void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context);
|
||||
@SuppressWarnings("deprecation")
|
||||
default void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
|
||||
// Pass null state to enforce item quads in block render contexts
|
||||
context.bakedModelConsumer().accept((BakedModel) this, null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,8 +28,8 @@ import net.minecraft.client.texture.Sprite;
|
|||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
||||
|
||||
|
@ -37,23 +37,23 @@ import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
|||
* Base class for specialized model implementations that need to wrap other baked models.
|
||||
* Avoids boilerplate code for pass-through methods.
|
||||
*/
|
||||
public abstract class ForwardingBakedModel implements BakedModel, FabricBakedModel, WrapperBakedModel {
|
||||
public abstract class ForwardingBakedModel implements BakedModel, WrapperBakedModel {
|
||||
/** implementations must set this somehow. */
|
||||
protected BakedModel wrapped;
|
||||
|
||||
@Override
|
||||
public void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
|
||||
((FabricBakedModel) wrapped).emitBlockQuads(blockView, state, pos, randomSupplier, context);
|
||||
public boolean isVanillaAdapter() {
|
||||
return wrapped.isVanillaAdapter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVanillaAdapter() {
|
||||
return ((FabricBakedModel) wrapped).isVanillaAdapter();
|
||||
public void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
|
||||
wrapped.emitBlockQuads(blockView, state, pos, randomSupplier, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
|
||||
((FabricBakedModel) wrapped).emitItemQuads(stack, randomSupplier, context);
|
||||
wrapped.emitItemQuads(stack, randomSupplier, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,14 +17,17 @@
|
|||
package net.fabricmc.fabric.api.renderer.v1.render;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
|
||||
|
@ -36,47 +39,18 @@ import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
|
|||
*/
|
||||
public interface RenderContext {
|
||||
/**
|
||||
* Used by models to send vertex data previously baked via {@link MeshBuilder}.
|
||||
* The fastest option and preferred whenever feasible.
|
||||
*/
|
||||
Consumer<Mesh> meshConsumer();
|
||||
|
||||
/**
|
||||
* Fallback consumer that can process a vanilla {@link BakedModel}.
|
||||
* Fabric causes vanilla baked models to send themselves
|
||||
* via this interface. Can also be used by compound models that contain a mix
|
||||
* of vanilla baked models, packaged quads and/or dynamic elements.
|
||||
* Returns a {@link QuadEmitter} instance that is used to output quads.
|
||||
* It is necessary to call {@link QuadEmitter#emit()} to output a quad.
|
||||
*
|
||||
* @apiNote The default implementation will be removed in the next breaking release.
|
||||
*/
|
||||
default BakedModelConsumer bakedModelConsumer() {
|
||||
// Default implementation is provided for compat with older renderer implementations,
|
||||
// but they should always override this function.
|
||||
Consumer<BakedModel> fallback = fallbackConsumer();
|
||||
return new BakedModelConsumer() {
|
||||
@Override
|
||||
public void accept(BakedModel model) {
|
||||
fallback.accept(model);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(BakedModel model, @Nullable BlockState state) {
|
||||
fallback.accept(model);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link QuadEmitter} instance that emits directly to the render buffer.
|
||||
* It remains necessary to call {@link QuadEmitter#emit()} to output the quad.
|
||||
*
|
||||
* <p>This method will always be less performant than passing pre-baked meshes
|
||||
* via {@link #meshConsumer()}. It should be used sparingly for model components that
|
||||
* demand it - text, icons, dynamic indicators, or other elements that vary too
|
||||
* much for static baking to be feasible.
|
||||
* <p>The renderer may optimize certain operations such as
|
||||
* {@link Mesh#outputTo(QuadEmitter)} when used with this emitter. Thus, using
|
||||
* those operations is preferred to using the emitter directly. It should be
|
||||
* used sparingly for model components that demand it - text, icons, dynamic
|
||||
* indicators, or other elements that vary too much for static baking to be
|
||||
* feasible.
|
||||
*
|
||||
* <p>Calling this method invalidates any {@link QuadEmitter} returned earlier.
|
||||
* Will be threadlocal/re-used - do not retain references.
|
||||
* Will be thread-local/re-used - do not retain references.
|
||||
*/
|
||||
QuadEmitter getEmitter();
|
||||
|
||||
|
@ -102,12 +76,51 @@ public interface RenderContext {
|
|||
*/
|
||||
void popTransform();
|
||||
|
||||
@FunctionalInterface
|
||||
interface QuadTransform {
|
||||
/**
|
||||
* Return false to filter out quads from rendering. When more than one transform
|
||||
* is in effect, returning false means unapplied transforms will not receive the quad.
|
||||
*/
|
||||
boolean transform(MutableQuadView quad);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fabric causes vanilla baked models to send themselves
|
||||
* via this interface. Can also be used by compound models that contain a mix
|
||||
* of vanilla baked models, packaged quads and/or dynamic elements.
|
||||
*
|
||||
* @deprecated Prefer using the more flexible {@link #bakedModelConsumer}.
|
||||
* @deprecated Use {@link Mesh#outputTo(QuadEmitter)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
default Consumer<Mesh> meshConsumer() {
|
||||
QuadEmitter emitter = getEmitter();
|
||||
return mesh -> mesh.outputTo(emitter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link FabricBakedModel#emitBlockQuads(BlockRenderView, BlockState, BlockPos, Supplier, RenderContext) emitBlockQuads}
|
||||
* or {@link FabricBakedModel#emitItemQuads(ItemStack, Supplier, RenderContext) emitItemQuads} on the baked model
|
||||
* that you want to consume instead.
|
||||
*/
|
||||
@Deprecated
|
||||
default BakedModelConsumer bakedModelConsumer() {
|
||||
// Default implementation is provided for compat with older renderer implementations,
|
||||
// but they should always override this function.
|
||||
Consumer<BakedModel> fallback = fallbackConsumer();
|
||||
return new BakedModelConsumer() {
|
||||
@Override
|
||||
public void accept(BakedModel model) {
|
||||
fallback.accept(model);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(BakedModel model, @Nullable BlockState state) {
|
||||
fallback.accept(model);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link FabricBakedModel#emitBlockQuads(BlockRenderView, BlockState, BlockPos, Supplier, RenderContext) emitBlockQuads}
|
||||
* or {@link FabricBakedModel#emitItemQuads(ItemStack, Supplier, RenderContext) emitItemQuads} on the baked model
|
||||
* that you want to consume instead.
|
||||
*/
|
||||
@Deprecated
|
||||
default Consumer<BakedModel> fallbackConsumer() {
|
||||
|
@ -115,6 +128,7 @@ public interface RenderContext {
|
|||
return bakedModelConsumer();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
interface BakedModelConsumer extends Consumer<BakedModel> {
|
||||
/**
|
||||
* Render a baked model by processing its {@linkplain BakedModel#getQuads} using the rendered block state.
|
||||
|
@ -138,13 +152,4 @@ public interface RenderContext {
|
|||
*/
|
||||
void accept(BakedModel model, @Nullable BlockState state);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface QuadTransform {
|
||||
/**
|
||||
* Return false to filter out quads from rendering. When more than one transform
|
||||
* is in effect, returning false means unapplied transforms will not receive the quad.
|
||||
*/
|
||||
boolean transform(MutableQuadView quad);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,38 +16,15 @@
|
|||
|
||||
package net.fabricmc.fabric.mixin.renderer.client;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
||||
|
||||
/**
|
||||
* Avoids instanceof checks and enables consistent code path for all baked models.
|
||||
*/
|
||||
@Mixin(BakedModel.class)
|
||||
public interface BakedModelMixin extends FabricBakedModel {
|
||||
@Override
|
||||
default boolean isVanillaAdapter() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
default void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
|
||||
context.bakedModelConsumer().accept((BakedModel) this, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
default void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
|
||||
// Pass null state to enforce item quads in block render contexts
|
||||
context.bakedModelConsumer().accept((BakedModel) this, null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,8 +36,8 @@ import net.minecraft.client.render.model.BakedModel;
|
|||
import net.minecraft.client.render.model.MultipartBakedModel;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
||||
|
@ -63,7 +63,7 @@ public class MultipartBakedModelMixin implements FabricBakedModel {
|
|||
@Inject(at = @At("RETURN"), method = "<init>")
|
||||
private void onInit(List<Pair<Predicate<BlockState>, BakedModel>> components, CallbackInfo cb) {
|
||||
for (Pair<Predicate<BlockState>, BakedModel> component : components) {
|
||||
if (!((FabricBakedModel) component.getRight()).isVanillaAdapter()) {
|
||||
if (!component.getRight().isVanillaAdapter()) {
|
||||
isVanilla = false;
|
||||
break;
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ public class MultipartBakedModelMixin implements FabricBakedModel {
|
|||
Pair<Predicate<BlockState>, BakedModel> pair = components.get(i);
|
||||
|
||||
if (pair.getLeft().test(state)) {
|
||||
((FabricBakedModel) pair.getRight()).emitBlockQuads(blockView, state, pos, randomSupplier, context);
|
||||
pair.getRight().emitBlockQuads(blockView, state, pos, randomSupplier, context);
|
||||
bitSet.set(i);
|
||||
}
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ public class MultipartBakedModelMixin implements FabricBakedModel {
|
|||
stateCache.put(state, bitSet);
|
||||
} else {
|
||||
for (int i = 0; i < this.components.size(); i++) {
|
||||
if (bitSet.get(i)) ((FabricBakedModel) components.get(i).getRight()).emitBlockQuads(blockView, state, pos, randomSupplier, context);
|
||||
if (bitSet.get(i)) components.get(i).getRight().emitBlockQuads(blockView, state, pos, randomSupplier, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,8 +34,8 @@ import net.minecraft.item.ItemStack;
|
|||
import net.minecraft.util.collection.Weighted;
|
||||
import net.minecraft.util.collection.Weighting;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
||||
|
@ -54,7 +54,7 @@ public class WeightedBakedModelMixin implements FabricBakedModel {
|
|||
@Inject(at = @At("RETURN"), method = "<init>")
|
||||
private void onInit(List<Weighted.Present<BakedModel>> models, CallbackInfo cb) {
|
||||
for (int i = 0; i < models.size(); i++) {
|
||||
if (!((FabricBakedModel) models.get(i).getData()).isVanillaAdapter()) {
|
||||
if (!models.get(i).getData().isVanillaAdapter()) {
|
||||
isVanilla = false;
|
||||
break;
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ public class WeightedBakedModelMixin implements FabricBakedModel {
|
|||
Weighted.Present<BakedModel> selected = Weighting.getAt(this.models, Math.abs((int) randomSupplier.get().nextLong()) % this.totalWeight).orElse(null);
|
||||
|
||||
if (selected != null) {
|
||||
((FabricBakedModel) selected.getData()).emitBlockQuads(blockView, state, pos, randomSupplier, context);
|
||||
selected.getData().emitBlockQuads(blockView, state, pos, randomSupplier, context);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,7 @@ public class WeightedBakedModelMixin implements FabricBakedModel {
|
|||
Weighted.Present<BakedModel> selected = Weighting.getAt(this.models, Math.abs((int) randomSupplier.get().nextLong()) % this.totalWeight).orElse(null);
|
||||
|
||||
if (selected != null) {
|
||||
((FabricBakedModel) selected.getData()).emitItemQuads(stack, randomSupplier, context);
|
||||
selected.getData().emitItemQuads(stack, randomSupplier, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,9 @@
|
|||
"fabric-renderer-api-v1.debughud.mixins.json"
|
||||
],
|
||||
"custom": {
|
||||
"fabric-api:module-lifecycle": "stable"
|
||||
"fabric-api:module-lifecycle": "stable",
|
||||
"loom:injected_interfaces": {
|
||||
"net/minecraft/class_1087": ["net/fabricmc/fabric/api/renderer/v1/model/FabricBakedModel"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ import org.jetbrains.annotations.Nullable;
|
|||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.BakedQuad;
|
||||
|
@ -32,6 +31,7 @@ import net.minecraft.client.render.model.json.ModelOverrideList;
|
|||
import net.minecraft.client.render.model.json.ModelTransformation;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
|
@ -42,12 +42,11 @@ import net.fabricmc.fabric.api.renderer.v1.material.BlendMode;
|
|||
import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder;
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
||||
import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachedBlockView;
|
||||
|
||||
final class FrameBakedModel implements BakedModel, FabricBakedModel {
|
||||
final class FrameBakedModel implements BakedModel {
|
||||
private final Mesh frameMesh;
|
||||
private final Sprite frameSprite;
|
||||
private final RenderMaterial translucentMaterial;
|
||||
|
@ -111,7 +110,7 @@ final class FrameBakedModel implements BakedModel, FabricBakedModel {
|
|||
@Override
|
||||
public void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
|
||||
// Emit our frame mesh
|
||||
context.meshConsumer().accept(this.frameMesh);
|
||||
this.frameMesh.outputTo(context.getEmitter());
|
||||
|
||||
RenderAttachedBlockView renderAttachedBlockView = (RenderAttachedBlockView) blockView;
|
||||
|
||||
|
@ -131,25 +130,23 @@ final class FrameBakedModel implements BakedModel, FabricBakedModel {
|
|||
|
||||
emitInnerQuads(context, material, () -> {
|
||||
// Use emitBlockQuads to allow for Renderer API features
|
||||
((FabricBakedModel) MinecraftClient.getInstance().getBlockRenderManager().getModel(innerState)).emitBlockQuads(blockView, innerState, pos, randomSupplier, context);
|
||||
MinecraftClient.getInstance().getBlockRenderManager().getModel(innerState).emitBlockQuads(blockView, innerState, pos, randomSupplier, context);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
|
||||
// Emit our frame mesh
|
||||
context.meshConsumer().accept(this.frameMesh);
|
||||
this.frameMesh.outputTo(context.getEmitter());
|
||||
|
||||
// Emit a scaled-down fence for testing, trying both materials again.
|
||||
RenderMaterial material = stack.hasCustomName() ? translucentEmissiveMaterial : translucentMaterial;
|
||||
|
||||
BlockState innerState = Blocks.OAK_FENCE.getDefaultState();
|
||||
ItemStack innerItem = Items.OAK_FENCE.getDefaultStack();
|
||||
BakedModel innerModel = MinecraftClient.getInstance().getItemRenderer().getModel(innerItem, null, null, 0);
|
||||
|
||||
emitInnerQuads(context, material, () -> {
|
||||
// Need to use the fallback consumer directly:
|
||||
// - we can't use emitBlockQuads because we don't have a blockView
|
||||
// - we can't use emitItemQuads because multipart models don't have item quads
|
||||
context.bakedModelConsumer().accept(MinecraftClient.getInstance().getBlockRenderManager().getModel(innerState), innerState);
|
||||
innerModel.emitItemQuads(innerItem, randomSupplier, context);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,6 @@ import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder;
|
|||
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
||||
import net.fabricmc.fabric.api.util.TriState;
|
||||
|
@ -48,7 +47,7 @@ import net.fabricmc.fabric.test.renderer.simple.RendererTest;
|
|||
/**
|
||||
* Very crude implementation of a pillar block model that connects with pillars above and below.
|
||||
*/
|
||||
public class PillarBakedModel implements BakedModel, FabricBakedModel {
|
||||
public class PillarBakedModel implements BakedModel {
|
||||
private enum ConnectedTexture {
|
||||
ALONE, BOTTOM, MIDDLE, TOP
|
||||
}
|
||||
|
|
|
@ -1,19 +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.impl.client.indigo;
|
||||
|
||||
public class IndigoConfig { }
|
|
@ -18,8 +18,6 @@ package net.fabricmc.fabric.impl.client.indigo.renderer.helper;
|
|||
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntFunction;
|
||||
|
||||
/**
|
||||
* Static routines of general utility for renderer implementations.
|
||||
* Renderers are not required to use these helpers, but they were
|
||||
|
@ -28,14 +26,7 @@ import it.unimi.dsi.fastutil.ints.Int2IntFunction;
|
|||
public abstract class ColorHelper {
|
||||
private ColorHelper() { }
|
||||
|
||||
private static final Int2IntFunction colorSwapper = ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN ? color -> ((color & 0xFF00FF00) | ((color & 0x00FF0000) >> 16) | ((color & 0xFF) << 16)) : color -> color;
|
||||
|
||||
/**
|
||||
* Swaps red blue order if needed to match GPU expectations for color component order.
|
||||
*/
|
||||
public static int swapRedBlueIfNeeded(int color) {
|
||||
return colorSwapper.applyAsInt(color);
|
||||
}
|
||||
private static final boolean BIG_ENDIAN = ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN;
|
||||
|
||||
/** Component-wise multiply. Components need to be in same order in both inputs! */
|
||||
public static int multiplyColor(int color1, int color2) {
|
||||
|
@ -45,9 +36,9 @@ public abstract class ColorHelper {
|
|||
return color1;
|
||||
}
|
||||
|
||||
final int alpha = ((color1 >> 24) & 0xFF) * ((color2 >> 24) & 0xFF) / 0xFF;
|
||||
final int red = ((color1 >> 16) & 0xFF) * ((color2 >> 16) & 0xFF) / 0xFF;
|
||||
final int green = ((color1 >> 8) & 0xFF) * ((color2 >> 8) & 0xFF) / 0xFF;
|
||||
final int alpha = ((color1 >>> 24) & 0xFF) * ((color2 >>> 24) & 0xFF) / 0xFF;
|
||||
final int red = ((color1 >>> 16) & 0xFF) * ((color2 >>> 16) & 0xFF) / 0xFF;
|
||||
final int green = ((color1 >>> 8) & 0xFF) * ((color2 >>> 8) & 0xFF) / 0xFF;
|
||||
final int blue = (color1 & 0xFF) * (color2 & 0xFF) / 0xFF;
|
||||
|
||||
return (alpha << 24) | (red << 16) | (green << 8) | blue;
|
||||
|
@ -55,9 +46,9 @@ public abstract class ColorHelper {
|
|||
|
||||
/** Multiplies three lowest components by shade. High byte (usually alpha) unchanged. */
|
||||
public static int multiplyRGB(int color, float shade) {
|
||||
final int alpha = ((color >> 24) & 0xFF);
|
||||
final int red = (int) (((color >> 16) & 0xFF) * shade);
|
||||
final int green = (int) (((color >> 8) & 0xFF) * shade);
|
||||
final int alpha = ((color >>> 24) & 0xFF);
|
||||
final int red = (int) (((color >>> 16) & 0xFF) * shade);
|
||||
final int green = (int) (((color >>> 8) & 0xFF) * shade);
|
||||
final int blue = (int) ((color & 0xFF) * shade);
|
||||
|
||||
return (alpha << 24) | (red << 16) | (green << 8) | blue;
|
||||
|
@ -72,4 +63,54 @@ public abstract class ColorHelper {
|
|||
|
||||
return Math.max(b0 & 0xFFFF, b1 & 0xFFFF) | Math.max(b0 & 0xFFFF0000, b1 & 0xFFFF0000);
|
||||
}
|
||||
|
||||
/*
|
||||
Renderer color format: ARGB (0xAARRGGBB)
|
||||
Vanilla color format (little endian): ABGR (0xAABBGGRR)
|
||||
Vanilla color format (big endian): RGBA (0xRRGGBBAA)
|
||||
|
||||
Why does the vanilla color format change based on endianness?
|
||||
See VertexConsumer#quad. Quad data is loaded as integers into
|
||||
a native byte order buffer. Color is read directly from bytes
|
||||
12, 13, 14 of each vertex. A different byte order will yield
|
||||
different results.
|
||||
|
||||
The renderer always uses ARGB because the API color methods
|
||||
always consume and return ARGB. Vanilla block and item colors
|
||||
also use ARGB.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Converts from ARGB color to ABGR color if little endian or RGBA color if big endian.
|
||||
*/
|
||||
public static int toVanillaColor(int color) {
|
||||
if (color == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (BIG_ENDIAN) {
|
||||
// ARGB to RGBA
|
||||
return ((color & 0x00FFFFFF) << 8) | ((color & 0xFF000000) >>> 24);
|
||||
} else {
|
||||
// ARGB to ABGR
|
||||
return (color & 0xFF00FF00) | ((color & 0x00FF0000) >>> 16) | ((color & 0x000000FF) << 16);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts to ARGB color from ABGR color if little endian or RGBA color if big endian.
|
||||
*/
|
||||
public static int fromVanillaColor(int color) {
|
||||
if (color == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (BIG_ENDIAN) {
|
||||
// RGBA to ARGB
|
||||
return ((color & 0xFFFFFF00) >>> 8) | ((color & 0x000000FF) << 24);
|
||||
} else {
|
||||
// ABGR to ARGB
|
||||
return (color & 0xFF00FF00) | ((color & 0x00FF0000) >>> 16) | ((color & 0x000000FF) << 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,8 +18,6 @@ package net.fabricmc.fabric.impl.client.indigo.renderer.helper;
|
|||
|
||||
import static net.minecraft.util.math.MathHelper.approximatelyEquals;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import net.minecraft.client.render.model.BakedQuad;
|
||||
|
@ -82,11 +80,7 @@ public abstract class GeometryHelper {
|
|||
* Does not validate quad winding order.
|
||||
* Expects convex quads with all points co-planar.
|
||||
*/
|
||||
public static boolean isQuadParallelToFace(@Nullable Direction face, QuadView quad) {
|
||||
if (face == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isQuadParallelToFace(Direction face, QuadView quad) {
|
||||
int i = face.getAxis().ordinal();
|
||||
final float val = quad.posByIndex(0, i);
|
||||
return approximatelyEquals(val, quad.posByIndex(1, i)) && approximatelyEquals(val, quad.posByIndex(2, i)) && approximatelyEquals(val, quad.posByIndex(3, i));
|
||||
|
@ -100,8 +94,6 @@ public abstract class GeometryHelper {
|
|||
* for that purpose. Expects convex quads with all points co-planar.
|
||||
*/
|
||||
public static boolean isParallelQuadOnFace(Direction lightFace, QuadView quad) {
|
||||
if (lightFace == null) return false;
|
||||
|
||||
final float x = quad.posByIndex(0, lightFace.getAxis().ordinal());
|
||||
return lightFace.getDirection() == AxisDirection.POSITIVE ? x >= EPS_MAX : x <= EPS_MIN;
|
||||
}
|
||||
|
@ -114,14 +106,8 @@ public abstract class GeometryHelper {
|
|||
* quad vertices are coplanar with each other.
|
||||
*
|
||||
* <p>Expects convex quads with all points co-planar.
|
||||
*
|
||||
* @param lightFace MUST be non-null.
|
||||
*/
|
||||
public static boolean isQuadCubic(@NotNull Direction lightFace, QuadView quad) {
|
||||
if (lightFace == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isQuadCubic(Direction lightFace, QuadView quad) {
|
||||
int a, b;
|
||||
|
||||
switch (lightFace) {
|
||||
|
|
|
@ -33,9 +33,12 @@ import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView;
|
|||
public abstract class NormalHelper {
|
||||
private NormalHelper() { }
|
||||
|
||||
private static final float PACK = 127.0f;
|
||||
private static final float UNPACK = 1.0f / PACK;
|
||||
|
||||
/**
|
||||
* Stores a normal plus an extra value as a quartet of signed bytes.
|
||||
* This is the same normal format that vanilla item rendering expects.
|
||||
* This is the same normal format that vanilla rendering expects.
|
||||
* The extra value is for use by shaders.
|
||||
*/
|
||||
public static int packNormal(float x, float y, float z, float w) {
|
||||
|
@ -44,7 +47,7 @@ public abstract class NormalHelper {
|
|||
z = MathHelper.clamp(z, -1, 1);
|
||||
w = MathHelper.clamp(w, -1, 1);
|
||||
|
||||
return ((int) (x * 127) & 255) | (((int) (y * 127) & 255) << 8) | (((int) (z * 127) & 255) << 16) | (((int) (w * 127) & 255) << 24);
|
||||
return ((int) (x * PACK) & 0xFF) | (((int) (y * PACK) & 0xFF) << 8) | (((int) (z * PACK) & 0xFF) << 16) | (((int) (w * PACK) & 0xFF) << 24);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -55,12 +58,41 @@ public abstract class NormalHelper {
|
|||
}
|
||||
|
||||
/**
|
||||
* Retrieves values packed by {@link #packNormal(float, float, float, float)}.
|
||||
*
|
||||
* <p>Components are x, y, z, w - zero based.
|
||||
* Like {@link #packNormal(float, float, float, float)}, but without a {@code w} value.
|
||||
*/
|
||||
public static float getPackedNormalComponent(int packedNormal, int component) {
|
||||
return ((byte) (packedNormal >> (8 * component))) / 127f;
|
||||
public static int packNormal(float x, float y, float z) {
|
||||
x = MathHelper.clamp(x, -1, 1);
|
||||
y = MathHelper.clamp(y, -1, 1);
|
||||
z = MathHelper.clamp(z, -1, 1);
|
||||
|
||||
return ((int) (x * PACK) & 0xFF) | (((int) (y * PACK) & 0xFF) << 8) | (((int) (z * PACK) & 0xFF) << 16);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #packNormal(Vector3f, float)}, but without a {@code w} value.
|
||||
*/
|
||||
public static int packNormal(Vector3f normal) {
|
||||
return packNormal(normal.x(), normal.y(), normal.z());
|
||||
}
|
||||
|
||||
public static float unpackNormalX(int packedNormal) {
|
||||
return ((byte) (packedNormal & 0xFF)) * UNPACK;
|
||||
}
|
||||
|
||||
public static float unpackNormalY(int packedNormal) {
|
||||
return ((byte) ((packedNormal >>> 8) & 0xFF)) * UNPACK;
|
||||
}
|
||||
|
||||
public static float unpackNormalZ(int packedNormal) {
|
||||
return ((byte) ((packedNormal >>> 16) & 0xFF)) * UNPACK;
|
||||
}
|
||||
|
||||
public static float unpackNormalW(int packedNormal) {
|
||||
return ((byte) ((packedNormal >>> 24) & 0xFF)) * UNPACK;
|
||||
}
|
||||
|
||||
public static void unpackNormal(int packedNormal, Vector3f target) {
|
||||
target.set(unpackNormalX(packedNormal), unpackNormalY(packedNormal), unpackNormalZ(packedNormal));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -74,7 +106,7 @@ public abstract class NormalHelper {
|
|||
public static void computeFaceNormal(@NotNull Vector3f saveTo, QuadView q) {
|
||||
final Direction nominalFace = q.nominalFace();
|
||||
|
||||
if (GeometryHelper.isQuadParallelToFace(nominalFace, q)) {
|
||||
if (nominalFace != null && GeometryHelper.isQuadParallelToFace(nominalFace, q)) {
|
||||
Vec3i vec = nominalFace.getVector();
|
||||
saveTo.set(vec.getX(), vec.getY(), vec.getZ());
|
||||
return;
|
||||
|
|
|
@ -61,9 +61,9 @@ public class MaterialViewImpl implements MaterialView {
|
|||
}
|
||||
|
||||
protected static boolean areBitsValid(int bits) {
|
||||
int blendMode = (bits & BLEND_MODE_MASK) >> BLEND_MODE_BIT_OFFSET;
|
||||
int ao = (bits & AO_MASK) >> AO_BIT_OFFSET;
|
||||
int glint = (bits & GLINT_MASK) >> GLINT_BIT_OFFSET;
|
||||
int blendMode = (bits & BLEND_MODE_MASK) >>> BLEND_MODE_BIT_OFFSET;
|
||||
int ao = (bits & AO_MASK) >>> AO_BIT_OFFSET;
|
||||
int glint = (bits & GLINT_MASK) >>> GLINT_BIT_OFFSET;
|
||||
|
||||
return blendMode < BLEND_MODE_COUNT
|
||||
&& ao < TRI_STATE_COUNT
|
||||
|
@ -78,7 +78,7 @@ public class MaterialViewImpl implements MaterialView {
|
|||
|
||||
@Override
|
||||
public BlendMode blendMode() {
|
||||
return BLEND_MODES[(bits & BLEND_MODE_MASK) >> BLEND_MODE_BIT_OFFSET];
|
||||
return BLEND_MODES[(bits & BLEND_MODE_MASK) >>> BLEND_MODE_BIT_OFFSET];
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -98,11 +98,11 @@ public class MaterialViewImpl implements MaterialView {
|
|||
|
||||
@Override
|
||||
public TriState ambientOcclusion() {
|
||||
return TRI_STATES[(bits & AO_MASK) >> AO_BIT_OFFSET];
|
||||
return TRI_STATES[(bits & AO_MASK) >>> AO_BIT_OFFSET];
|
||||
}
|
||||
|
||||
@Override
|
||||
public TriState glint() {
|
||||
return TRI_STATES[(bits & GLINT_MASK) >> GLINT_BIT_OFFSET];
|
||||
return TRI_STATES[(bits & GLINT_MASK) >>> GLINT_BIT_OFFSET];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,9 +37,10 @@ public abstract class EncodingFormat {
|
|||
private EncodingFormat() { }
|
||||
|
||||
static final int HEADER_BITS = 0;
|
||||
static final int HEADER_COLOR_INDEX = 1;
|
||||
static final int HEADER_TAG = 2;
|
||||
public static final int HEADER_STRIDE = 3;
|
||||
static final int HEADER_FACE_NORMAL = 1;
|
||||
static final int HEADER_COLOR_INDEX = 2;
|
||||
static final int HEADER_TAG = 3;
|
||||
public static final int HEADER_STRIDE = 4;
|
||||
|
||||
static final int VERTEX_X;
|
||||
static final int VERTEX_Y;
|
||||
|
@ -100,7 +101,7 @@ public abstract class EncodingFormat {
|
|||
}
|
||||
|
||||
static Direction cullFace(int bits) {
|
||||
return ModelHelper.faceFromIndex((bits >> CULL_SHIFT) & DIRECTION_MASK);
|
||||
return ModelHelper.faceFromIndex((bits >>> CULL_SHIFT) & DIRECTION_MASK);
|
||||
}
|
||||
|
||||
static int cullFace(int bits, Direction face) {
|
||||
|
@ -108,7 +109,7 @@ public abstract class EncodingFormat {
|
|||
}
|
||||
|
||||
static Direction lightFace(int bits) {
|
||||
return ModelHelper.faceFromIndex((bits >> LIGHT_SHIFT) & DIRECTION_MASK);
|
||||
return ModelHelper.faceFromIndex((bits >>> LIGHT_SHIFT) & DIRECTION_MASK);
|
||||
}
|
||||
|
||||
static int lightFace(int bits, Direction face) {
|
||||
|
@ -117,7 +118,7 @@ public abstract class EncodingFormat {
|
|||
|
||||
/** indicate if vertex normal has been set - bits correspond to vertex ordinals. */
|
||||
static int normalFlags(int bits) {
|
||||
return (bits >> NORMALS_SHIFT) & NORMALS_MASK;
|
||||
return (bits >>> NORMALS_SHIFT) & NORMALS_MASK;
|
||||
}
|
||||
|
||||
static int normalFlags(int bits, int normalFlags) {
|
||||
|
@ -125,7 +126,7 @@ public abstract class EncodingFormat {
|
|||
}
|
||||
|
||||
static int geometryFlags(int bits) {
|
||||
return (bits >> GEOMETRY_SHIFT) & GEOMETRY_MASK;
|
||||
return (bits >>> GEOMETRY_SHIFT) & GEOMETRY_MASK;
|
||||
}
|
||||
|
||||
static int geometryFlags(int bits, int geometryFlags) {
|
||||
|
@ -133,7 +134,7 @@ public abstract class EncodingFormat {
|
|||
}
|
||||
|
||||
static RenderMaterialImpl material(int bits) {
|
||||
return RenderMaterialImpl.byIndex((bits >> MATERIAL_SHIFT) & MATERIAL_MASK);
|
||||
return RenderMaterialImpl.byIndex((bits >>> MATERIAL_SHIFT) & MATERIAL_MASK);
|
||||
}
|
||||
|
||||
static int material(int bits, RenderMaterialImpl material) {
|
||||
|
|
|
@ -25,13 +25,20 @@ import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
|||
* Not much to it - mainly it just needs to grow the int[] array as quads are appended
|
||||
* and maintain/provide a properly-configured {@link net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView} instance.
|
||||
* All the encoding and other work is handled in the quad base classes.
|
||||
* The one interesting bit is in {@link Maker#emit()}.
|
||||
* The one interesting bit is in {@link Maker#emitDirectly()}.
|
||||
*/
|
||||
public class MeshBuilderImpl implements MeshBuilder {
|
||||
int[] data = new int[256];
|
||||
private int[] data = new int[256];
|
||||
private int index = 0;
|
||||
private int limit = data.length;
|
||||
private final Maker maker = new Maker();
|
||||
int index = 0;
|
||||
int limit = data.length;
|
||||
|
||||
public MeshBuilderImpl() {
|
||||
ensureCapacity(EncodingFormat.TOTAL_STRIDE);
|
||||
maker.data = data;
|
||||
maker.baseIndex = index;
|
||||
maker.clear();
|
||||
}
|
||||
|
||||
protected void ensureCapacity(int stride) {
|
||||
if (stride > limit - index) {
|
||||
|
@ -39,41 +46,39 @@ public class MeshBuilderImpl implements MeshBuilder {
|
|||
final int[] bigger = new int[limit];
|
||||
System.arraycopy(data, 0, bigger, 0, index);
|
||||
data = bigger;
|
||||
maker.data = bigger;
|
||||
maker.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public QuadEmitter getEmitter() {
|
||||
maker.clear();
|
||||
return maker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mesh build() {
|
||||
final int[] packed = new int[index];
|
||||
System.arraycopy(data, 0, packed, 0, index);
|
||||
index = 0;
|
||||
maker.begin(data, index);
|
||||
maker.baseIndex = index;
|
||||
maker.clear();
|
||||
return new MeshImpl(packed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QuadEmitter getEmitter() {
|
||||
ensureCapacity(EncodingFormat.TOTAL_STRIDE);
|
||||
maker.begin(data, index);
|
||||
return maker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Our base classes are used differently so we define final
|
||||
* encoding steps in subtypes. This will be a static mesh used
|
||||
* at render time so we want to capture all geometry now and
|
||||
* apply non-location-dependent lighting.
|
||||
*/
|
||||
private class Maker extends MutableQuadViewImpl implements QuadEmitter {
|
||||
private class Maker extends MutableQuadViewImpl {
|
||||
@Override
|
||||
public Maker emit() {
|
||||
public void emitDirectly() {
|
||||
computeGeometry();
|
||||
index += EncodingFormat.TOTAL_STRIDE;
|
||||
ensureCapacity(EncodingFormat.TOTAL_STRIDE);
|
||||
baseIndex = index;
|
||||
clear();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ package net.fabricmc.fabric.impl.client.indigo.renderer.mesh;
|
|||
import java.util.function.Consumer;
|
||||
|
||||
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.mesh.QuadView;
|
||||
|
||||
/**
|
||||
|
@ -27,7 +28,7 @@ import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView;
|
|||
*/
|
||||
public class MeshImpl implements Mesh {
|
||||
/** Used to satisfy external calls to {@link #forEach(Consumer)}. */
|
||||
ThreadLocal<QuadViewImpl> POOL = ThreadLocal.withInitial(QuadViewImpl::new);
|
||||
private final ThreadLocal<QuadViewImpl> cursorPool = ThreadLocal.withInitial(QuadViewImpl::new);
|
||||
|
||||
final int[] data;
|
||||
|
||||
|
@ -35,28 +36,43 @@ public class MeshImpl implements Mesh {
|
|||
this.data = data;
|
||||
}
|
||||
|
||||
public int[] data() {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEach(Consumer<QuadView> consumer) {
|
||||
forEach(consumer, POOL.get());
|
||||
forEach(consumer, cursorPool.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* The renderer will call this with it's own cursor
|
||||
* The renderer can call this with its own cursor
|
||||
* to avoid the performance hit of a thread-local lookup.
|
||||
* Also means renderer can hold final references to quad buffers.
|
||||
*/
|
||||
void forEach(Consumer<QuadView> consumer, QuadViewImpl cursor) {
|
||||
final int limit = data.length;
|
||||
int index = 0;
|
||||
cursor.data = this.data;
|
||||
|
||||
while (index < limit) {
|
||||
cursor.load(data, index);
|
||||
cursor.baseIndex = index;
|
||||
cursor.load();
|
||||
consumer.accept(cursor);
|
||||
index += EncodingFormat.TOTAL_STRIDE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void outputTo(QuadEmitter emitter) {
|
||||
MutableQuadViewImpl e = (MutableQuadViewImpl) emitter;
|
||||
final int[] data = this.data;
|
||||
final int limit = data.length;
|
||||
int index = 0;
|
||||
|
||||
while (index < limit) {
|
||||
System.arraycopy(data, index, e.data, e.baseIndex, EncodingFormat.TOTAL_STRIDE);
|
||||
e.load();
|
||||
e.emitDirectly();
|
||||
index += EncodingFormat.TOTAL_STRIDE;
|
||||
}
|
||||
|
||||
e.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingForma
|
|||
import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat.HEADER_COLOR_INDEX;
|
||||
import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat.HEADER_STRIDE;
|
||||
import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat.HEADER_TAG;
|
||||
import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat.QUAD_STRIDE;
|
||||
import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat.VERTEX_COLOR;
|
||||
import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat.VERTEX_LIGHTMAP;
|
||||
import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat.VERTEX_NORMAL;
|
||||
|
@ -39,21 +38,22 @@ import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
|||
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.IndigoRenderer;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.ColorHelper;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.NormalHelper;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.TextureHelper;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.material.RenderMaterialImpl;
|
||||
|
||||
/**
|
||||
* Almost-concrete implementation of a mutable quad. The only missing part is {@link #emit()},
|
||||
* Almost-concrete implementation of a mutable quad. The only missing part is {@link #emitDirectly()},
|
||||
* because that depends on where/how it is used. (Mesh encoding vs. render-time transformation).
|
||||
*
|
||||
* <p>In many cases an instance of this class is used as an "editor quad". The editor quad's
|
||||
* {@link #emitDirectly()} method calls some other internal method that transforms the quad
|
||||
* data and then buffers it. Transformations should be the same as they would be in a vanilla
|
||||
* render - the editor is serving mainly as a way to access vertex data without magical
|
||||
* numbers. It also allows for a consistent interface for those transformations.
|
||||
*/
|
||||
public abstract class MutableQuadViewImpl extends QuadViewImpl implements QuadEmitter {
|
||||
public final void begin(int[] data, int baseIndex) {
|
||||
this.data = data;
|
||||
this.baseIndex = baseIndex;
|
||||
clear();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
System.arraycopy(EMPTY, 0, data, baseIndex, EncodingFormat.TOTAL_STRIDE);
|
||||
isGeometryInvalid = true;
|
||||
|
@ -108,7 +108,7 @@ public abstract class MutableQuadViewImpl extends QuadViewImpl implements QuadEm
|
|||
@Override
|
||||
public MutableQuadViewImpl normal(int vertexIndex, float x, float y, float z) {
|
||||
normalFlags(normalFlags() | (1 << vertexIndex));
|
||||
data[baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_NORMAL] = NormalHelper.packNormal(x, y, z, 0);
|
||||
data[baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_NORMAL] = NormalHelper.packNormal(x, y, z);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -120,7 +120,7 @@ public abstract class MutableQuadViewImpl extends QuadViewImpl implements QuadEm
|
|||
|
||||
if (normalFlags == 0b1111) return;
|
||||
|
||||
final int packedFaceNormal = NormalHelper.packNormal(faceNormal(), 0);
|
||||
final int packedFaceNormal = packedFaceNormal();
|
||||
|
||||
for (int v = 0; v < 4; v++) {
|
||||
if ((normalFlags & (1 << v)) == 0) {
|
||||
|
@ -180,8 +180,16 @@ public abstract class MutableQuadViewImpl extends QuadViewImpl implements QuadEm
|
|||
|
||||
@Override
|
||||
public final MutableQuadViewImpl fromVanilla(int[] quadData, int startIndex) {
|
||||
System.arraycopy(quadData, startIndex, data, baseIndex + HEADER_STRIDE, QUAD_STRIDE);
|
||||
System.arraycopy(quadData, startIndex, data, baseIndex + HEADER_STRIDE, VANILLA_QUAD_STRIDE);
|
||||
isGeometryInvalid = true;
|
||||
|
||||
int colorIndex = baseIndex + VERTEX_COLOR;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
data[colorIndex] = ColorHelper.fromVanillaColor(data[colorIndex]);
|
||||
colorIndex += VERTEX_STRIDE;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -200,4 +208,17 @@ public abstract class MutableQuadViewImpl extends QuadViewImpl implements QuadEm
|
|||
tag(0);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emit the quad without clearing the underlying data.
|
||||
* Geometry is not guaranteed to be valid when called, but can be computed by calling {@link #computeGeometry()}.
|
||||
*/
|
||||
public abstract void emitDirectly();
|
||||
|
||||
@Override
|
||||
public final MutableQuadViewImpl emit() {
|
||||
emitDirectly();
|
||||
clear();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ package net.fabricmc.fabric.impl.client.indigo.renderer.mesh;
|
|||
|
||||
import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat.HEADER_BITS;
|
||||
import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat.HEADER_COLOR_INDEX;
|
||||
import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat.HEADER_FACE_NORMAL;
|
||||
import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat.HEADER_STRIDE;
|
||||
import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat.HEADER_TAG;
|
||||
import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat.QUAD_STRIDE;
|
||||
import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat.VERTEX_COLOR;
|
||||
|
@ -38,6 +40,7 @@ import org.joml.Vector3f;
|
|||
import net.minecraft.util.math.Direction;
|
||||
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.ColorHelper;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.NormalHelper;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.material.RenderMaterialImpl;
|
||||
|
@ -49,7 +52,7 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.material.RenderMaterialIm
|
|||
public class QuadViewImpl implements QuadView {
|
||||
@Nullable
|
||||
protected Direction nominalFace;
|
||||
/** True when geometry flags or light face may not match geometry. */
|
||||
/** True when face normal, light face, or geometry flags may not match geometry. */
|
||||
protected boolean isGeometryInvalid = true;
|
||||
protected final Vector3f faceNormal = new Vector3f();
|
||||
|
||||
|
@ -60,45 +63,13 @@ public class QuadViewImpl implements QuadView {
|
|||
protected int baseIndex = 0;
|
||||
|
||||
/**
|
||||
* Use when subtype is "attached" to a pre-existing array.
|
||||
* Sets data reference and index and decodes state from array.
|
||||
* Decodes necessary state from the backing data array.
|
||||
* The encoded data must contain valid computed geometry.
|
||||
*/
|
||||
final void load(int[] data, int baseIndex) {
|
||||
this.data = data;
|
||||
this.baseIndex = baseIndex;
|
||||
load();
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #load(int[], int)} but assumes array and index already set.
|
||||
* Only does the decoding part.
|
||||
*/
|
||||
public final void load() {
|
||||
public void load() {
|
||||
isGeometryInvalid = false;
|
||||
nominalFace = lightFace();
|
||||
|
||||
// face normal isn't encoded
|
||||
NormalHelper.computeFaceNormal(faceNormal, this);
|
||||
}
|
||||
|
||||
/** Reference to underlying array. Use with caution. Meant for fast renderer access */
|
||||
public int[] data() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public int normalFlags() {
|
||||
return EncodingFormat.normalFlags(data[baseIndex + HEADER_BITS]);
|
||||
}
|
||||
|
||||
/** True if any vertex normal has been set. */
|
||||
public boolean hasVertexNormals() {
|
||||
return normalFlags() != 0;
|
||||
}
|
||||
|
||||
/** gets flags used for lighting - lazily computed via {@link GeometryHelper#computeShapeFlags(QuadView)}. */
|
||||
public int geometryFlags() {
|
||||
computeGeometry();
|
||||
return EncodingFormat.geometryFlags(data[baseIndex + HEADER_BITS]);
|
||||
NormalHelper.unpackNormal(packedFaceNormal(), faceNormal);
|
||||
}
|
||||
|
||||
protected void computeGeometry() {
|
||||
|
@ -106,6 +77,7 @@ public class QuadViewImpl implements QuadView {
|
|||
isGeometryInvalid = false;
|
||||
|
||||
NormalHelper.computeFaceNormal(faceNormal, this);
|
||||
data[baseIndex + HEADER_FACE_NORMAL] = NormalHelper.packNormal(faceNormal);
|
||||
|
||||
// depends on face normal
|
||||
data[baseIndex + HEADER_BITS] = EncodingFormat.lightFace(data[baseIndex + HEADER_BITS], GeometryHelper.lightFace(this));
|
||||
|
@ -115,6 +87,12 @@ public class QuadViewImpl implements QuadView {
|
|||
}
|
||||
}
|
||||
|
||||
/** gets flags used for lighting - lazily computed via {@link GeometryHelper#computeShapeFlags(QuadView)}. */
|
||||
public int geometryFlags() {
|
||||
computeGeometry();
|
||||
return EncodingFormat.geometryFlags(data[baseIndex + HEADER_BITS]);
|
||||
}
|
||||
|
||||
public boolean hasShade() {
|
||||
return !material().disableDiffuse();
|
||||
}
|
||||
|
@ -181,28 +159,37 @@ public class QuadViewImpl implements QuadView {
|
|||
return data[baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_LIGHTMAP];
|
||||
}
|
||||
|
||||
public int normalFlags() {
|
||||
return EncodingFormat.normalFlags(data[baseIndex + HEADER_BITS]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNormal(int vertexIndex) {
|
||||
return (normalFlags() & (1 << vertexIndex)) != 0;
|
||||
}
|
||||
|
||||
/** True if any vertex normal has been set. */
|
||||
public boolean hasVertexNormals() {
|
||||
return normalFlags() != 0;
|
||||
}
|
||||
|
||||
protected final int normalIndex(int vertexIndex) {
|
||||
return baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_NORMAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float normalX(int vertexIndex) {
|
||||
return hasNormal(vertexIndex) ? NormalHelper.getPackedNormalComponent(data[normalIndex(vertexIndex)], 0) : Float.NaN;
|
||||
return hasNormal(vertexIndex) ? NormalHelper.unpackNormalX(data[normalIndex(vertexIndex)]) : Float.NaN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float normalY(int vertexIndex) {
|
||||
return hasNormal(vertexIndex) ? NormalHelper.getPackedNormalComponent(data[normalIndex(vertexIndex)], 1) : Float.NaN;
|
||||
return hasNormal(vertexIndex) ? NormalHelper.unpackNormalY(data[normalIndex(vertexIndex)]) : Float.NaN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float normalZ(int vertexIndex) {
|
||||
return hasNormal(vertexIndex) ? NormalHelper.getPackedNormalComponent(data[normalIndex(vertexIndex)], 2) : Float.NaN;
|
||||
return hasNormal(vertexIndex) ? NormalHelper.unpackNormalZ(data[normalIndex(vertexIndex)]) : Float.NaN;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -214,7 +201,7 @@ public class QuadViewImpl implements QuadView {
|
|||
}
|
||||
|
||||
final int normal = data[normalIndex(vertexIndex)];
|
||||
target.set(NormalHelper.getPackedNormalComponent(normal, 0), NormalHelper.getPackedNormalComponent(normal, 1), NormalHelper.getPackedNormalComponent(normal, 2));
|
||||
NormalHelper.unpackNormal(normal, target);
|
||||
return target;
|
||||
} else {
|
||||
return null;
|
||||
|
@ -240,6 +227,11 @@ public class QuadViewImpl implements QuadView {
|
|||
return nominalFace;
|
||||
}
|
||||
|
||||
public final int packedFaceNormal() {
|
||||
computeGeometry();
|
||||
return data[baseIndex + HEADER_FACE_NORMAL];
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Vector3f faceNormal() {
|
||||
computeGeometry();
|
||||
|
@ -263,6 +255,16 @@ public class QuadViewImpl implements QuadView {
|
|||
|
||||
@Override
|
||||
public final void toVanilla(int[] target, int targetIndex) {
|
||||
System.arraycopy(data, baseIndex + VERTEX_X, target, targetIndex, QUAD_STRIDE);
|
||||
System.arraycopy(data, baseIndex + HEADER_STRIDE, target, targetIndex, QUAD_STRIDE);
|
||||
|
||||
// The color is the fourth integer in each vertex.
|
||||
// EncodingFormat.VERTEX_COLOR is not used because it also
|
||||
// contains the header size; vanilla quads do not have a header.
|
||||
int colorIndex = targetIndex + 3;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
target[colorIndex] = ColorHelper.toVanillaColor(target[colorIndex]);
|
||||
colorIndex += VANILLA_VERTEX_STRIDE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
* 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.indigo.renderer.render;
|
||||
|
||||
import static net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper.AXIS_ALIGNED_FLAG;
|
||||
import static net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper.LIGHT_FACE_FLAG;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.LightmapTextureManager;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
import net.minecraft.client.render.WorldRenderer;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.BakedQuad;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
|
||||
import net.fabricmc.fabric.api.util.TriState;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.IndigoRenderer;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.ColorHelper;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl;
|
||||
|
||||
public abstract class AbstractBlockRenderContext extends AbstractRenderContext {
|
||||
protected final BlockRenderInfo blockInfo = new BlockRenderInfo();
|
||||
protected final AoCalculator aoCalc;
|
||||
|
||||
private final MutableQuadViewImpl editorQuad = new MutableQuadViewImpl() {
|
||||
{
|
||||
data = new int[EncodingFormat.TOTAL_STRIDE];
|
||||
clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emitDirectly() {
|
||||
renderQuad(this, false);
|
||||
}
|
||||
};
|
||||
|
||||
private final BakedModelConsumerImpl vanillaModelConsumer = new BakedModelConsumerImpl();
|
||||
|
||||
private final BlockPos.Mutable lightPos = new BlockPos.Mutable();
|
||||
|
||||
protected AbstractBlockRenderContext() {
|
||||
aoCalc = createAoCalc(blockInfo);
|
||||
}
|
||||
|
||||
protected abstract AoCalculator createAoCalc(BlockRenderInfo blockInfo);
|
||||
|
||||
protected abstract VertexConsumer getVertexConsumer(RenderLayer layer);
|
||||
|
||||
@Override
|
||||
public QuadEmitter getEmitter() {
|
||||
editorQuad.clear();
|
||||
return editorQuad;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BakedModelConsumer bakedModelConsumer() {
|
||||
return vanillaModelConsumer;
|
||||
}
|
||||
|
||||
private void renderQuad(MutableQuadViewImpl quad, boolean isVanilla) {
|
||||
if (!transform(quad)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!blockInfo.shouldDrawFace(quad.cullFace())) {
|
||||
return;
|
||||
}
|
||||
|
||||
final RenderMaterial mat = quad.material();
|
||||
final int colorIndex = mat.disableColorIndex() ? -1 : quad.colorIndex();
|
||||
final TriState aoMode = mat.ambientOcclusion();
|
||||
final boolean ao = blockInfo.useAo && (aoMode == TriState.TRUE || (aoMode == TriState.DEFAULT && blockInfo.defaultAo));
|
||||
final boolean emissive = mat.emissive();
|
||||
final VertexConsumer vertexConsumer = getVertexConsumer(blockInfo.effectiveRenderLayer(mat.blendMode()));
|
||||
|
||||
colorizeQuad(quad, colorIndex);
|
||||
shadeQuad(quad, isVanilla, ao, emissive);
|
||||
bufferQuad(quad, vertexConsumer);
|
||||
}
|
||||
|
||||
/** handles block color, common to all renders. */
|
||||
private void colorizeQuad(MutableQuadViewImpl quad, int colorIndex) {
|
||||
if (colorIndex != -1) {
|
||||
final int blockColor = blockInfo.blockColor(colorIndex);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
quad.color(i, ColorHelper.multiplyColor(blockColor, quad.color(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void shadeQuad(MutableQuadViewImpl quad, boolean isVanilla, boolean ao, boolean emissive) {
|
||||
// routines below have a bit of copy-paste code reuse to avoid conditional execution inside a hot loop
|
||||
if (ao) {
|
||||
aoCalc.compute(quad, isVanilla);
|
||||
|
||||
if (emissive) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
quad.color(i, ColorHelper.multiplyRGB(quad.color(i), aoCalc.ao[i]));
|
||||
quad.lightmap(i, LightmapTextureManager.MAX_LIGHT_COORDINATE);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
quad.color(i, ColorHelper.multiplyRGB(quad.color(i), aoCalc.ao[i]));
|
||||
quad.lightmap(i, ColorHelper.maxBrightness(quad.lightmap(i), aoCalc.light[i]));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
shadeFlatQuad(quad);
|
||||
|
||||
if (emissive) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
quad.lightmap(i, LightmapTextureManager.MAX_LIGHT_COORDINATE);
|
||||
}
|
||||
} else {
|
||||
final int brightness = flatBrightness(quad, blockInfo.blockState, blockInfo.blockPos);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
quad.lightmap(i, ColorHelper.maxBrightness(quad.lightmap(i), brightness));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starting in 1.16 flat shading uses dimension-specific diffuse factors that can be < 1.0
|
||||
* even for un-shaded quads. These are also applied with AO shading but that is done in AO calculator.
|
||||
*/
|
||||
private void shadeFlatQuad(MutableQuadViewImpl quad) {
|
||||
if (quad.hasVertexNormals()) {
|
||||
// Quads that have vertex normals need to be shaded using interpolation - vanilla can't
|
||||
// handle them. Generally only applies to modded models.
|
||||
final float faceShade = blockInfo.blockView.getBrightness(quad.lightFace(), quad.hasShade());
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
quad.color(i, ColorHelper.multiplyRGB(quad.color(i), vertexShade(quad, i, faceShade)));
|
||||
}
|
||||
} else {
|
||||
final float diffuseShade = blockInfo.blockView.getBrightness(quad.lightFace(), quad.hasShade());
|
||||
|
||||
if (diffuseShade != 1.0f) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
quad.color(i, ColorHelper.multiplyRGB(quad.color(i), diffuseShade));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private float vertexShade(MutableQuadViewImpl quad, int vertexIndex, float faceShade) {
|
||||
return quad.hasNormal(vertexIndex) ? normalShade(quad.normalX(vertexIndex), quad.normalY(vertexIndex), quad.normalZ(vertexIndex), quad.hasShade()) : faceShade;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds mean of per-face shading factors weighted by normal components.
|
||||
* Not how light actually works but the vanilla diffuse shading model is a hack to start with
|
||||
* and this gives reasonable results for non-cubic surfaces in a vanilla-style renderer.
|
||||
*/
|
||||
private float normalShade(float normalX, float normalY, float normalZ, boolean hasShade) {
|
||||
float sum = 0;
|
||||
float div = 0;
|
||||
|
||||
if (normalX > 0) {
|
||||
sum += normalX * blockInfo.blockView.getBrightness(Direction.EAST, hasShade);
|
||||
div += normalX;
|
||||
} else if (normalX < 0) {
|
||||
sum += -normalX * blockInfo.blockView.getBrightness(Direction.WEST, hasShade);
|
||||
div -= normalX;
|
||||
}
|
||||
|
||||
if (normalY > 0) {
|
||||
sum += normalY * blockInfo.blockView.getBrightness(Direction.UP, hasShade);
|
||||
div += normalY;
|
||||
} else if (normalY < 0) {
|
||||
sum += -normalY * blockInfo.blockView.getBrightness(Direction.DOWN, hasShade);
|
||||
div -= normalY;
|
||||
}
|
||||
|
||||
if (normalZ > 0) {
|
||||
sum += normalZ * blockInfo.blockView.getBrightness(Direction.SOUTH, hasShade);
|
||||
div += normalZ;
|
||||
} else if (normalZ < 0) {
|
||||
sum += -normalZ * blockInfo.blockView.getBrightness(Direction.NORTH, hasShade);
|
||||
div -= normalZ;
|
||||
}
|
||||
|
||||
return sum / div;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles geometry-based check for using self brightness or neighbor brightness.
|
||||
* That logic only applies in flat lighting.
|
||||
*/
|
||||
private int flatBrightness(MutableQuadViewImpl quad, BlockState blockState, BlockPos pos) {
|
||||
lightPos.set(pos);
|
||||
|
||||
// To mirror Vanilla's behavior, if the face has a cull-face, always sample the light value
|
||||
// offset in that direction. See net.minecraft.client.render.block.BlockModelRenderer.renderQuadsFlat
|
||||
// for reference.
|
||||
if (quad.cullFace() != null) {
|
||||
lightPos.move(quad.cullFace());
|
||||
} else {
|
||||
final int flags = quad.geometryFlags();
|
||||
|
||||
if ((flags & LIGHT_FACE_FLAG) != 0 || ((flags & AXIS_ALIGNED_FLAG) != 0 && blockState.isFullCube(blockInfo.blockView, pos))) {
|
||||
lightPos.move(quad.lightFace());
|
||||
}
|
||||
}
|
||||
|
||||
// Unfortunately cannot use brightness cache here unless we implement one specifically for flat lighting. See #329
|
||||
return WorldRenderer.getLightmapCoordinates(blockInfo.blockView, blockState, lightPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumer for vanilla baked models. Generally intended to give visual results matching a vanilla render,
|
||||
* however there could be subtle (and desirable) lighting variations so is good to be able to render
|
||||
* everything consistently.
|
||||
*
|
||||
* <p>Also, the API allows multi-part models that hold multiple vanilla models to render them without
|
||||
* combining quad lists, but the vanilla logic only handles one model per block. To route all of
|
||||
* them through vanilla logic would require additional hooks.
|
||||
*/
|
||||
private class BakedModelConsumerImpl implements BakedModelConsumer {
|
||||
private static final RenderMaterial MATERIAL_SHADED = IndigoRenderer.INSTANCE.materialFinder().find();
|
||||
private static final RenderMaterial MATERIAL_FLAT = IndigoRenderer.INSTANCE.materialFinder().ambientOcclusion(TriState.FALSE).find();
|
||||
|
||||
private final MutableQuadViewImpl editorQuad = new MutableQuadViewImpl() {
|
||||
{
|
||||
data = new int[EncodingFormat.TOTAL_STRIDE];
|
||||
clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emitDirectly() {
|
||||
renderQuad(this, true);
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void accept(BakedModel model) {
|
||||
accept(model, blockInfo.blockState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(BakedModel model, @Nullable BlockState state) {
|
||||
MutableQuadViewImpl editorQuad = this.editorQuad;
|
||||
final RenderMaterial defaultMaterial = model.useAmbientOcclusion() ? MATERIAL_SHADED : MATERIAL_FLAT;
|
||||
|
||||
for (int i = 0; i <= ModelHelper.NULL_FACE_ID; i++) {
|
||||
final Direction cullFace = ModelHelper.faceFromIndex(i);
|
||||
final List<BakedQuad> quads = model.getQuads(state, cullFace, blockInfo.randomSupplier.get());
|
||||
final int count = quads.size();
|
||||
|
||||
for (int j = 0; j < count; j++) {
|
||||
final BakedQuad q = quads.get(j);
|
||||
editorQuad.fromVanilla(q, defaultMaterial, cullFace);
|
||||
// Call renderQuad directly instead of emit for efficiency
|
||||
renderQuad(editorQuad, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Do not clear the editorQuad since it is not accessible to API users.
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,84 +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.impl.client.indigo.renderer.render;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
|
||||
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.render.RenderContext.QuadTransform;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.IndigoRenderer;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MeshImpl;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl;
|
||||
|
||||
/**
|
||||
* Consumer for pre-baked meshes. Works by copying the mesh data to an
|
||||
* "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<RenderLayer, VertexConsumer> bufferFunc, AoCalculator aoCalc, QuadTransform transform) {
|
||||
super(blockInfo, bufferFunc, aoCalc, transform);
|
||||
}
|
||||
|
||||
/**
|
||||
* Where we handle all pre-buffer coloring, lighting, transformation, etc.
|
||||
* Reused for all mesh quads. Fixed baking array sized to hold largest possible mesh quad.
|
||||
*/
|
||||
private class Maker extends MutableQuadViewImpl {
|
||||
{
|
||||
data = new int[EncodingFormat.TOTAL_STRIDE];
|
||||
material(IndigoRenderer.MATERIAL_STANDARD);
|
||||
}
|
||||
|
||||
// only used via RenderContext.getEmitter()
|
||||
@Override
|
||||
public Maker emit() {
|
||||
computeGeometry();
|
||||
renderQuad(this, false);
|
||||
clear();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
private final Maker editorQuad = new Maker();
|
||||
|
||||
@Override
|
||||
public void accept(Mesh mesh) {
|
||||
final MeshImpl m = (MeshImpl) mesh;
|
||||
final int[] data = m.data();
|
||||
final int limit = data.length;
|
||||
int index = 0;
|
||||
|
||||
while (index < limit) {
|
||||
System.arraycopy(data, index, editorQuad.data(), 0, EncodingFormat.TOTAL_STRIDE);
|
||||
editorQuad.load();
|
||||
index += EncodingFormat.TOTAL_STRIDE;
|
||||
renderQuad(editorQuad, false);
|
||||
}
|
||||
}
|
||||
|
||||
public QuadEmitter getEmitter() {
|
||||
editorQuad.clear();
|
||||
return editorQuad;
|
||||
}
|
||||
}
|
|
@ -1,296 +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.impl.client.indigo.renderer.render;
|
||||
|
||||
import static net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper.AXIS_ALIGNED_FLAG;
|
||||
import static net.fabricmc.fabric.impl.client.indigo.renderer.helper.GeometryHelper.LIGHT_FACE_FLAG;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.LightmapTextureManager;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
import net.minecraft.client.render.WorldRenderer;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext.QuadTransform;
|
||||
import net.fabricmc.fabric.api.util.TriState;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.ColorHelper;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl;
|
||||
|
||||
/**
|
||||
* Base quad-rendering class for fallback and mesh consumers.
|
||||
* Has most of the actual buffer-time lighting and coloring logic.
|
||||
*/
|
||||
public abstract class AbstractQuadRenderer {
|
||||
protected final Function<RenderLayer, VertexConsumer> bufferFunc;
|
||||
protected final BlockRenderInfo blockInfo;
|
||||
protected final AoCalculator aoCalc;
|
||||
protected final QuadTransform transform;
|
||||
protected final Vector3f normalVec = new Vector3f();
|
||||
|
||||
protected abstract Matrix4f matrix();
|
||||
|
||||
protected abstract Matrix3f normalMatrix();
|
||||
|
||||
protected abstract int overlay();
|
||||
|
||||
AbstractQuadRenderer(BlockRenderInfo blockInfo, Function<RenderLayer, VertexConsumer> bufferFunc, AoCalculator aoCalc, QuadTransform transform) {
|
||||
this.blockInfo = blockInfo;
|
||||
this.bufferFunc = bufferFunc;
|
||||
this.aoCalc = aoCalc;
|
||||
this.transform = transform;
|
||||
}
|
||||
|
||||
protected void renderQuad(MutableQuadViewImpl quad, boolean isVanilla) {
|
||||
if (!transform.transform(quad)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!blockInfo.shouldDrawFace(quad.cullFace())) {
|
||||
return;
|
||||
}
|
||||
|
||||
tessellateQuad(quad, isVanilla);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines color index and render layer, then routes to appropriate
|
||||
* tessellate routine based on material properties.
|
||||
*/
|
||||
private void tessellateQuad(MutableQuadViewImpl quad, boolean isVanilla) {
|
||||
final RenderMaterial mat = quad.material();
|
||||
final int colorIndex = mat.disableColorIndex() ? -1 : quad.colorIndex();
|
||||
final RenderLayer renderLayer = blockInfo.effectiveRenderLayer(mat.blendMode());
|
||||
final TriState ao = mat.ambientOcclusion();
|
||||
|
||||
if (blockInfo.useAo && (ao == TriState.TRUE || (ao == TriState.DEFAULT && blockInfo.defaultAo))) {
|
||||
// needs to happen before offsets are applied
|
||||
aoCalc.compute(quad, isVanilla);
|
||||
|
||||
if (mat.emissive()) {
|
||||
tessellateSmoothEmissive(quad, renderLayer, colorIndex);
|
||||
} else {
|
||||
tessellateSmooth(quad, renderLayer, colorIndex);
|
||||
}
|
||||
} else {
|
||||
if (mat.emissive()) {
|
||||
tessellateFlatEmissive(quad, renderLayer, colorIndex);
|
||||
} else {
|
||||
tessellateFlat(quad, renderLayer, colorIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** handles block color and red-blue swizzle, common to all renders. */
|
||||
private void colorizeQuad(MutableQuadViewImpl q, int blockColorIndex) {
|
||||
if (blockColorIndex == -1) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
q.color(i, ColorHelper.swapRedBlueIfNeeded(q.color(i)));
|
||||
}
|
||||
} else {
|
||||
final int blockColor = blockInfo.blockColor(blockColorIndex);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
q.color(i, ColorHelper.swapRedBlueIfNeeded(ColorHelper.multiplyColor(blockColor, q.color(i))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** final output step, common to all renders. */
|
||||
private void bufferQuad(MutableQuadViewImpl quad, RenderLayer renderLayer) {
|
||||
bufferQuad(bufferFunc.apply(renderLayer), quad, matrix(), overlay(), normalMatrix(), normalVec);
|
||||
}
|
||||
|
||||
public static void bufferQuad(VertexConsumer buff, MutableQuadViewImpl quad, Matrix4f matrix, int overlay, Matrix3f normalMatrix, Vector3f normalVec) {
|
||||
final boolean useNormals = quad.hasVertexNormals();
|
||||
|
||||
if (useNormals) {
|
||||
quad.populateMissingNormals();
|
||||
} else {
|
||||
normalVec.set(quad.faceNormal());
|
||||
normalVec.mul(normalMatrix);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
buff.vertex(matrix, quad.x(i), quad.y(i), quad.z(i));
|
||||
final int color = quad.color(i);
|
||||
buff.color(color & 0xFF, (color >> 8) & 0xFF, (color >> 16) & 0xFF, (color >> 24) & 0xFF);
|
||||
buff.texture(quad.u(i), quad.v(i));
|
||||
buff.overlay(overlay);
|
||||
buff.light(quad.lightmap(i));
|
||||
|
||||
if (useNormals) {
|
||||
quad.copyNormal(i, normalVec);
|
||||
normalVec.mul(normalMatrix);
|
||||
}
|
||||
|
||||
buff.normal(normalVec.x(), normalVec.y(), normalVec.z());
|
||||
buff.next();
|
||||
}
|
||||
}
|
||||
|
||||
// routines below have a bit of copy-paste code reuse to avoid conditional execution inside a hot loop
|
||||
|
||||
/** for non-emissive mesh quads and all fallback quads with smooth lighting. */
|
||||
protected void tessellateSmooth(MutableQuadViewImpl q, RenderLayer renderLayer, int blockColorIndex) {
|
||||
colorizeQuad(q, blockColorIndex);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
q.color(i, ColorHelper.multiplyRGB(q.color(i), aoCalc.ao[i]));
|
||||
q.lightmap(i, ColorHelper.maxBrightness(q.lightmap(i), aoCalc.light[i]));
|
||||
}
|
||||
|
||||
bufferQuad(q, renderLayer);
|
||||
}
|
||||
|
||||
/** for emissive mesh quads with smooth lighting. */
|
||||
protected void tessellateSmoothEmissive(MutableQuadViewImpl q, RenderLayer renderLayer, int blockColorIndex) {
|
||||
colorizeQuad(q, blockColorIndex);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
q.color(i, ColorHelper.multiplyRGB(q.color(i), aoCalc.ao[i]));
|
||||
q.lightmap(i, LightmapTextureManager.MAX_LIGHT_COORDINATE);
|
||||
}
|
||||
|
||||
bufferQuad(q, renderLayer);
|
||||
}
|
||||
|
||||
/** for non-emissive mesh quads and all fallback quads with flat lighting. */
|
||||
protected void tessellateFlat(MutableQuadViewImpl quad, RenderLayer renderLayer, int blockColorIndex) {
|
||||
colorizeQuad(quad, blockColorIndex);
|
||||
shadeFlatQuad(quad);
|
||||
|
||||
final int brightness = flatBrightness(quad, blockInfo.blockState, blockInfo.blockPos);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
quad.lightmap(i, ColorHelper.maxBrightness(quad.lightmap(i), brightness));
|
||||
}
|
||||
|
||||
bufferQuad(quad, renderLayer);
|
||||
}
|
||||
|
||||
/** for emissive mesh quads with flat lighting. */
|
||||
protected void tessellateFlatEmissive(MutableQuadViewImpl quad, RenderLayer renderLayer, int blockColorIndex) {
|
||||
colorizeQuad(quad, blockColorIndex);
|
||||
shadeFlatQuad(quad);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
quad.lightmap(i, LightmapTextureManager.MAX_LIGHT_COORDINATE);
|
||||
}
|
||||
|
||||
bufferQuad(quad, renderLayer);
|
||||
}
|
||||
|
||||
private final BlockPos.Mutable mpos = new BlockPos.Mutable();
|
||||
|
||||
/**
|
||||
* Handles geometry-based check for using self brightness or neighbor brightness.
|
||||
* That logic only applies in flat lighting.
|
||||
*/
|
||||
int flatBrightness(MutableQuadViewImpl quad, BlockState blockState, BlockPos pos) {
|
||||
mpos.set(pos);
|
||||
|
||||
// To mirror Vanilla's behavior, if the face has a cull-face, always sample the light value
|
||||
// offset in that direction. See net.minecraft.client.render.block.BlockModelRenderer.renderQuadsFlat
|
||||
// for reference.
|
||||
if (quad.cullFace() != null) {
|
||||
mpos.move(quad.cullFace());
|
||||
} else {
|
||||
final int flags = quad.geometryFlags();
|
||||
|
||||
if ((flags & LIGHT_FACE_FLAG) != 0 || ((flags & AXIS_ALIGNED_FLAG) != 0 && blockState.isFullCube(blockInfo.blockView, pos))) {
|
||||
mpos.move(quad.lightFace());
|
||||
}
|
||||
}
|
||||
|
||||
// Unfortunately cannot use brightness cache here unless we implement one specifically for flat lighting. See #329
|
||||
return WorldRenderer.getLightmapCoordinates(blockInfo.blockView, blockState, mpos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starting in 1.16 flat shading uses dimension-specific diffuse factors that can be < 1.0
|
||||
* even for un-shaded quads. These are also applied with AO shading but that is done in AO calculator.
|
||||
*/
|
||||
private void shadeFlatQuad(MutableQuadViewImpl quad) {
|
||||
if (quad.hasVertexNormals()) {
|
||||
// Quads that have vertex normals need to be shaded using interpolation - vanilla can't
|
||||
// handle them. Generally only applies to modded models.
|
||||
final float faceShade = blockInfo.blockView.getBrightness(quad.lightFace(), quad.hasShade());
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
quad.color(i, ColorHelper.multiplyRGB(quad.color(i), vertexShade(quad, i, faceShade)));
|
||||
}
|
||||
} else {
|
||||
final float diffuseShade = blockInfo.blockView.getBrightness(quad.lightFace(), quad.hasShade());
|
||||
|
||||
if (diffuseShade != 1.0f) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
quad.color(i, ColorHelper.multiplyRGB(quad.color(i), diffuseShade));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private float vertexShade(MutableQuadViewImpl quad, int vertexIndex, float faceShade) {
|
||||
return quad.hasNormal(vertexIndex) ? normalShade(quad.normalX(vertexIndex), quad.normalY(vertexIndex), quad.normalZ(vertexIndex), quad.hasShade()) : faceShade;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds mean of per-face shading factors weighted by normal components.
|
||||
* Not how light actually works but the vanilla diffuse shading model is a hack to start with
|
||||
* and this gives reasonable results for non-cubic surfaces in a vanilla-style renderer.
|
||||
*/
|
||||
private float normalShade(float normalX, float normalY, float normalZ, boolean hasShade) {
|
||||
float sum = 0;
|
||||
float div = 0;
|
||||
|
||||
if (normalX > 0) {
|
||||
sum += normalX * blockInfo.blockView.getBrightness(Direction.EAST, hasShade);
|
||||
div += normalX;
|
||||
} else if (normalX < 0) {
|
||||
sum += -normalX * blockInfo.blockView.getBrightness(Direction.WEST, hasShade);
|
||||
div -= normalX;
|
||||
}
|
||||
|
||||
if (normalY > 0) {
|
||||
sum += normalY * blockInfo.blockView.getBrightness(Direction.UP, hasShade);
|
||||
div += normalY;
|
||||
} else if (normalY < 0) {
|
||||
sum += -normalY * blockInfo.blockView.getBrightness(Direction.DOWN, hasShade);
|
||||
div -= normalY;
|
||||
}
|
||||
|
||||
if (normalZ > 0) {
|
||||
sum += normalZ * blockInfo.blockView.getBrightness(Direction.SOUTH, hasShade);
|
||||
div += normalZ;
|
||||
} else if (normalZ < 0) {
|
||||
sum += -normalZ * blockInfo.blockView.getBrightness(Direction.NORTH, hasShade);
|
||||
div -= normalZ;
|
||||
}
|
||||
|
||||
return sum / div;
|
||||
}
|
||||
}
|
|
@ -16,19 +16,27 @@
|
|||
|
||||
package net.fabricmc.fabric.impl.client.indigo.renderer.render;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector4f;
|
||||
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl;
|
||||
|
||||
abstract class AbstractRenderContext implements RenderContext {
|
||||
private static final QuadTransform NO_TRANSFORM = (q) -> true;
|
||||
private static final QuadTransform NO_TRANSFORM = q -> true;
|
||||
|
||||
private QuadTransform activeTransform = NO_TRANSFORM;
|
||||
private final ObjectArrayList<QuadTransform> transformStack = new ObjectArrayList<>();
|
||||
private final QuadTransform stackTransform = (q) -> {
|
||||
private final QuadTransform stackTransform = q -> {
|
||||
int i = transformStack.size() - 1;
|
||||
|
||||
while (i >= 0) {
|
||||
|
@ -40,9 +48,14 @@ abstract class AbstractRenderContext implements RenderContext {
|
|||
return true;
|
||||
};
|
||||
|
||||
@Deprecated
|
||||
private final Consumer<Mesh> meshConsumer = mesh -> mesh.outputTo(getEmitter());
|
||||
|
||||
protected Matrix4f matrix;
|
||||
protected Matrix3f normalMatrix;
|
||||
protected int overlay;
|
||||
private final Vector4f posVec = new Vector4f();
|
||||
private final Vector3f normalVec = new Vector3f();
|
||||
|
||||
protected final boolean transform(MutableQuadView q) {
|
||||
return activeTransform.transform(q);
|
||||
|
@ -77,4 +90,45 @@ abstract class AbstractRenderContext implements RenderContext {
|
|||
activeTransform = transformStack.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Overridden to prevent allocating a lambda every time this method is called.
|
||||
@Deprecated
|
||||
@Override
|
||||
public Consumer<Mesh> meshConsumer() {
|
||||
return meshConsumer;
|
||||
}
|
||||
|
||||
/** final output step, common to all renders. */
|
||||
protected void bufferQuad(MutableQuadViewImpl quad, VertexConsumer vertexConsumer) {
|
||||
final Vector4f posVec = this.posVec;
|
||||
final Vector3f normalVec = this.normalVec;
|
||||
final boolean useNormals = quad.hasVertexNormals();
|
||||
|
||||
if (useNormals) {
|
||||
quad.populateMissingNormals();
|
||||
} else {
|
||||
normalVec.set(quad.faceNormal());
|
||||
normalVec.mul(normalMatrix);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
posVec.set(quad.x(i), quad.y(i), quad.z(i), 1.0f);
|
||||
posVec.mul(matrix);
|
||||
vertexConsumer.vertex(posVec.x(), posVec.y(), posVec.z());
|
||||
|
||||
final int color = quad.color(i);
|
||||
vertexConsumer.color((color >>> 16) & 0xFF, (color >>> 8) & 0xFF, color & 0xFF, (color >>> 24) & 0xFF);
|
||||
vertexConsumer.texture(quad.u(i), quad.v(i));
|
||||
vertexConsumer.overlay(overlay);
|
||||
vertexConsumer.light(quad.lightmap(i));
|
||||
|
||||
if (useNormals) {
|
||||
quad.copyNormal(i, normalVec);
|
||||
normalVec.mul(normalMatrix);
|
||||
}
|
||||
|
||||
vertexConsumer.normal(normalVec.x(), normalVec.y(), normalVec.z());
|
||||
vertexConsumer.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,12 +16,6 @@
|
|||
|
||||
package net.fabricmc.fabric.impl.client.indigo.renderer.render;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
|
@ -32,114 +26,53 @@ import net.minecraft.util.math.BlockPos;
|
|||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
|
||||
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;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoLuminanceFix;
|
||||
|
||||
/**
|
||||
* Context for non-terrain block rendering.
|
||||
*/
|
||||
public class BlockRenderContext extends AbstractRenderContext {
|
||||
private final BlockRenderInfo blockInfo = new BlockRenderInfo();
|
||||
public class BlockRenderContext extends AbstractBlockRenderContext {
|
||||
private VertexConsumer vertexConsumer;
|
||||
|
||||
private final AoCalculator aoCalc = new AoCalculator(blockInfo) {
|
||||
@Override
|
||||
public int light(BlockPos pos, BlockState state) {
|
||||
return WorldRenderer.getLightmapCoordinates(blockInfo.blockView, state, pos);
|
||||
}
|
||||
@Override
|
||||
protected AoCalculator createAoCalc(BlockRenderInfo blockInfo) {
|
||||
return new AoCalculator(blockInfo) {
|
||||
@Override
|
||||
public int light(BlockPos pos, BlockState state) {
|
||||
return WorldRenderer.getLightmapCoordinates(blockInfo.blockView, state, pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float ao(BlockPos pos, BlockState state) {
|
||||
return AoLuminanceFix.INSTANCE.apply(blockInfo.blockView, pos, state);
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public float ao(BlockPos pos, BlockState state) {
|
||||
return AoLuminanceFix.INSTANCE.apply(blockInfo.blockView, pos, state);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private VertexConsumer bufferBuilder;
|
||||
// These are kept as fields to avoid the heap allocation for a supplier.
|
||||
// BlockModelRenderer allows the caller to supply both the random object and seed.
|
||||
private Random random;
|
||||
private long seed;
|
||||
private final Supplier<Random> randomSupplier = () -> {
|
||||
random.setSeed(seed);
|
||||
return random;
|
||||
};
|
||||
|
||||
private final AbstractMeshConsumer meshConsumer = new AbstractMeshConsumer(blockInfo, this::outputBuffer, aoCalc, this::transform) {
|
||||
@Override
|
||||
protected Matrix4f matrix() {
|
||||
return matrix;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Matrix3f normalMatrix() {
|
||||
return normalMatrix;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int overlay() {
|
||||
return overlay;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Reuse the fallback consumer from the render context used during chunk rebuild to make it properly
|
||||
* apply the current transforms to vanilla models.
|
||||
*/
|
||||
private final TerrainFallbackConsumer fallbackConsumer = new TerrainFallbackConsumer(blockInfo, this::outputBuffer, aoCalc, this::transform) {
|
||||
@Override
|
||||
protected Matrix4f matrix() {
|
||||
return matrix;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Matrix3f normalMatrix() {
|
||||
return normalMatrix;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int overlay() {
|
||||
return overlay;
|
||||
}
|
||||
};
|
||||
|
||||
private VertexConsumer outputBuffer(RenderLayer renderLayer) {
|
||||
return bufferBuilder;
|
||||
@Override
|
||||
protected VertexConsumer getVertexConsumer(RenderLayer layer) {
|
||||
return vertexConsumer;
|
||||
}
|
||||
|
||||
public void render(BlockRenderView blockView, BakedModel model, BlockState state, BlockPos pos, MatrixStack matrixStack, VertexConsumer buffer, boolean cull, Random random, long seed, int overlay) {
|
||||
this.bufferBuilder = buffer;
|
||||
this.vertexConsumer = buffer;
|
||||
this.matrix = matrixStack.peek().getPositionMatrix();
|
||||
this.normalMatrix = matrixStack.peek().getNormalMatrix();
|
||||
this.random = random;
|
||||
this.seed = seed;
|
||||
|
||||
this.overlay = overlay;
|
||||
|
||||
blockInfo.random = random;
|
||||
blockInfo.seed = seed;
|
||||
blockInfo.recomputeSeed = false;
|
||||
|
||||
aoCalc.clear();
|
||||
blockInfo.prepareForWorld(blockView, cull);
|
||||
blockInfo.prepareForBlock(state, pos, model.useAmbientOcclusion());
|
||||
|
||||
((FabricBakedModel) model).emitBlockQuads(blockView, state, pos, randomSupplier, this);
|
||||
model.emitBlockQuads(blockView, state, pos, blockInfo.randomSupplier, this);
|
||||
|
||||
blockInfo.release();
|
||||
this.bufferBuilder = null;
|
||||
this.random = null;
|
||||
this.seed = seed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Consumer<Mesh> meshConsumer() {
|
||||
return meshConsumer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BakedModelConsumer bakedModelConsumer() {
|
||||
return fallbackConsumer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QuadEmitter getEmitter() {
|
||||
return meshConsumer.getEmitter();
|
||||
blockInfo.random = null;
|
||||
this.vertexConsumer = null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,8 +34,8 @@ import net.minecraft.world.BlockRenderView;
|
|||
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode;
|
||||
|
||||
/**
|
||||
* Holds, manages and provides access to the block/world related state
|
||||
* needed by fallback and mesh consumers.
|
||||
* Holds, manages, and provides access to the block/world related state
|
||||
* needed to render quads.
|
||||
*
|
||||
* <p>Exception: per-block position offsets are tracked in {@link ChunkRenderInfo}
|
||||
* so they can be applied together with chunk offsets.
|
||||
|
@ -43,44 +43,47 @@ import net.fabricmc.fabric.api.renderer.v1.material.BlendMode;
|
|||
public class BlockRenderInfo {
|
||||
private final BlockColors blockColorMap = MinecraftClient.getInstance().getBlockColors();
|
||||
private final BlockPos.Mutable searchPos = new BlockPos.Mutable();
|
||||
private final Random random = Random.create();
|
||||
|
||||
public BlockRenderView blockView;
|
||||
public BlockPos blockPos;
|
||||
public BlockState blockState;
|
||||
public long seed;
|
||||
|
||||
boolean useAo;
|
||||
boolean defaultAo;
|
||||
RenderLayer defaultLayer;
|
||||
|
||||
Random random;
|
||||
long seed;
|
||||
boolean recomputeSeed;
|
||||
public final Supplier<Random> randomSupplier = () -> {
|
||||
long seed = this.seed;
|
||||
|
||||
if (recomputeSeed) {
|
||||
seed = blockState.getRenderingSeed(blockPos);
|
||||
this.seed = seed;
|
||||
recomputeSeed = false;
|
||||
}
|
||||
|
||||
final Random random = this.random;
|
||||
random.setSeed(seed);
|
||||
return random;
|
||||
};
|
||||
|
||||
private boolean enableCulling;
|
||||
private int cullCompletionFlags;
|
||||
private int cullResultFlags;
|
||||
|
||||
public final Supplier<Random> randomSupplier = () -> {
|
||||
final Random result = random;
|
||||
long seed = this.seed;
|
||||
|
||||
if (seed == -1L) {
|
||||
seed = blockState.getRenderingSeed(blockPos);
|
||||
this.seed = seed;
|
||||
}
|
||||
|
||||
result.setSeed(seed);
|
||||
return result;
|
||||
};
|
||||
|
||||
public void prepareForWorld(BlockRenderView blockView, boolean enableCulling) {
|
||||
this.blockView = blockView;
|
||||
this.enableCulling = enableCulling;
|
||||
}
|
||||
|
||||
public void prepareForBlock(BlockState blockState, BlockPos blockPos, boolean modelAO) {
|
||||
public void prepareForBlock(BlockState blockState, BlockPos blockPos, boolean modelAo) {
|
||||
this.blockPos = blockPos;
|
||||
this.blockState = blockState;
|
||||
// in the unlikely case seed actually matches this, we'll simply retrieve it more than once
|
||||
seed = -1L;
|
||||
|
||||
useAo = MinecraftClient.isAmbientOcclusionEnabled();
|
||||
defaultAo = useAo && modelAO && blockState.getLuminance() == 0;
|
||||
defaultAo = useAo && modelAo && blockState.getLuminance() == 0;
|
||||
|
||||
defaultLayer = RenderLayers.getBlockLayer(blockState);
|
||||
|
||||
|
@ -89,6 +92,7 @@ public class BlockRenderInfo {
|
|||
}
|
||||
|
||||
public void release() {
|
||||
blockView = null;
|
||||
blockPos = null;
|
||||
blockState = null;
|
||||
}
|
||||
|
|
|
@ -17,11 +17,9 @@
|
|||
package net.fabricmc.fabric.impl.client.indigo.renderer.render;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
|
@ -45,15 +43,12 @@ import net.minecraft.util.math.random.Random;
|
|||
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode;
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
||||
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;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
|
||||
import net.fabricmc.fabric.api.util.TriState;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.IndigoRenderer;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.ColorHelper;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MeshImpl;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl;
|
||||
|
||||
/**
|
||||
|
@ -63,24 +58,26 @@ public class ItemRenderContext extends AbstractRenderContext {
|
|||
/** Value vanilla uses for item rendering. The only sensible choice, of course. */
|
||||
private static final long ITEM_RANDOM_SEED = 42L;
|
||||
|
||||
/** used to accept a method reference from the ItemRenderer. */
|
||||
@FunctionalInterface
|
||||
public interface VanillaQuadHandler {
|
||||
void accept(BakedModel model, ItemStack stack, int color, int overlay, MatrixStack matrixStack, VertexConsumer buffer);
|
||||
}
|
||||
|
||||
private final ItemColors colorMap;
|
||||
private final Random random = Random.create();
|
||||
private final Vector3f normalVec = new Vector3f();
|
||||
|
||||
private final Supplier<Random> randomSupplier = () -> {
|
||||
random.setSeed(ITEM_RANDOM_SEED);
|
||||
return random;
|
||||
};
|
||||
|
||||
private final Maker editorQuad = new Maker();
|
||||
private final MeshConsumer meshConsumer = new MeshConsumer();
|
||||
private final FallbackConsumer fallbackConsumer = new FallbackConsumer();
|
||||
private final MutableQuadViewImpl editorQuad = new MutableQuadViewImpl() {
|
||||
{
|
||||
data = new int[EncodingFormat.TOTAL_STRIDE];
|
||||
clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emitDirectly() {
|
||||
renderQuad(this);
|
||||
}
|
||||
};
|
||||
|
||||
private final BakedModelConsumerImpl vanillaModelConsumer = new BakedModelConsumerImpl();
|
||||
|
||||
private ItemStack itemStack;
|
||||
private ModelTransformationMode transformMode;
|
||||
|
@ -103,6 +100,17 @@ public class ItemRenderContext extends AbstractRenderContext {
|
|||
this.colorMap = colorMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QuadEmitter getEmitter() {
|
||||
editorQuad.clear();
|
||||
return editorQuad;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BakedModelConsumer bakedModelConsumer() {
|
||||
return vanillaModelConsumer;
|
||||
}
|
||||
|
||||
public void renderModel(ItemStack itemStack, ModelTransformationMode transformMode, boolean invert, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int lightmap, int overlay, BakedModel model, VanillaQuadHandler vanillaHandler) {
|
||||
this.itemStack = itemStack;
|
||||
this.transformMode = transformMode;
|
||||
|
@ -116,10 +124,11 @@ public class ItemRenderContext extends AbstractRenderContext {
|
|||
matrix = matrixStack.peek().getPositionMatrix();
|
||||
normalMatrix = matrixStack.peek().getNormalMatrix();
|
||||
|
||||
((FabricBakedModel) model).emitItemQuads(itemStack, randomSupplier, this);
|
||||
model.emitItemQuads(itemStack, randomSupplier, this);
|
||||
|
||||
this.itemStack = null;
|
||||
this.matrixStack = null;
|
||||
this.vertexConsumerProvider = null;
|
||||
this.vanillaHandler = null;
|
||||
|
||||
translucentVertexConsumer = null;
|
||||
|
@ -150,21 +159,46 @@ public class ItemRenderContext extends AbstractRenderContext {
|
|||
|
||||
isDefaultGlint = itemStack.hasGlint();
|
||||
|
||||
defaultVertexConsumer = quadVertexConsumer(BlendMode.DEFAULT, TriState.DEFAULT);
|
||||
defaultVertexConsumer = getVertexConsumer(BlendMode.DEFAULT, TriState.DEFAULT);
|
||||
}
|
||||
|
||||
private VertexConsumer createTranslucentVertexConsumer(boolean glint) {
|
||||
if (isTranslucentDirect) {
|
||||
return ItemRenderer.getDirectItemGlintConsumer(vertexConsumerProvider, TexturedRenderLayers.getEntityTranslucentCull(), true, glint);
|
||||
} else if (MinecraftClient.isFabulousGraphicsOrBetter()) {
|
||||
return ItemRenderer.getItemGlintConsumer(vertexConsumerProvider, TexturedRenderLayers.getItemEntityTranslucentCull(), true, glint);
|
||||
} else {
|
||||
return ItemRenderer.getItemGlintConsumer(vertexConsumerProvider, TexturedRenderLayers.getEntityTranslucentCull(), true, glint);
|
||||
private void renderQuad(MutableQuadViewImpl quad) {
|
||||
if (!transform(quad)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final RenderMaterial mat = quad.material();
|
||||
final int colorIndex = mat.disableColorIndex() ? -1 : quad.colorIndex();
|
||||
final boolean emissive = mat.emissive();
|
||||
final VertexConsumer vertexConsumer = getVertexConsumer(mat.blendMode(), mat.glint());
|
||||
|
||||
colorizeQuad(quad, colorIndex);
|
||||
shadeQuad(quad, emissive);
|
||||
bufferQuad(quad, vertexConsumer);
|
||||
}
|
||||
|
||||
private void colorizeQuad(MutableQuadViewImpl quad, int colorIndex) {
|
||||
if (colorIndex != -1) {
|
||||
final int itemColor = 0xFF000000 | colorMap.getColor(itemStack, colorIndex);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
quad.color(i, ColorHelper.multiplyColor(itemColor, quad.color(i)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private VertexConsumer createCutoutVertexConsumer(boolean glint) {
|
||||
return ItemRenderer.getDirectItemGlintConsumer(vertexConsumerProvider, TexturedRenderLayers.getEntityCutout(), true, glint);
|
||||
private void shadeQuad(MutableQuadViewImpl quad, boolean emissive) {
|
||||
if (emissive) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
quad.lightmap(i, LightmapTextureManager.MAX_LIGHT_COORDINATE);
|
||||
}
|
||||
} else {
|
||||
final int lightmap = this.lightmap;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
quad.lightmap(i, ColorHelper.maxBrightness(quad.lightmap(i), lightmap));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -172,7 +206,7 @@ public class ItemRenderContext extends AbstractRenderContext {
|
|||
* in {@code RenderLayers.getEntityBlockLayer}. Layers other than
|
||||
* translucent are mapped to cutout.
|
||||
*/
|
||||
private VertexConsumer quadVertexConsumer(BlendMode blendMode, TriState glintMode) {
|
||||
private VertexConsumer getVertexConsumer(BlendMode blendMode, TriState glintMode) {
|
||||
boolean translucent;
|
||||
boolean glint;
|
||||
|
||||
|
@ -219,97 +253,21 @@ public class ItemRenderContext extends AbstractRenderContext {
|
|||
}
|
||||
}
|
||||
|
||||
private void bufferQuad(MutableQuadViewImpl quad, BlendMode blendMode, TriState glint) {
|
||||
AbstractQuadRenderer.bufferQuad(quadVertexConsumer(blendMode, glint), quad, matrix, overlay, normalMatrix, normalVec);
|
||||
}
|
||||
|
||||
private void colorizeQuad(MutableQuadViewImpl q, int colorIndex) {
|
||||
if (colorIndex == -1) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
q.color(i, ColorHelper.swapRedBlueIfNeeded(q.color(i)));
|
||||
}
|
||||
private VertexConsumer createTranslucentVertexConsumer(boolean glint) {
|
||||
if (isTranslucentDirect) {
|
||||
return ItemRenderer.getDirectItemGlintConsumer(vertexConsumerProvider, TexturedRenderLayers.getEntityTranslucentCull(), true, glint);
|
||||
} else if (MinecraftClient.isFabulousGraphicsOrBetter()) {
|
||||
return ItemRenderer.getItemGlintConsumer(vertexConsumerProvider, TexturedRenderLayers.getItemEntityTranslucentCull(), true, glint);
|
||||
} else {
|
||||
final int itemColor = 0xFF000000 | colorMap.getColor(itemStack, colorIndex);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
q.color(i, ColorHelper.swapRedBlueIfNeeded(ColorHelper.multiplyColor(itemColor, q.color(i))));
|
||||
}
|
||||
return ItemRenderer.getItemGlintConsumer(vertexConsumerProvider, TexturedRenderLayers.getEntityTranslucentCull(), true, glint);
|
||||
}
|
||||
}
|
||||
|
||||
private void renderQuad(MutableQuadViewImpl quad, BlendMode blendMode, TriState glint, int colorIndex) {
|
||||
colorizeQuad(quad, colorIndex);
|
||||
|
||||
final int lightmap = this.lightmap;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
quad.lightmap(i, ColorHelper.maxBrightness(quad.lightmap(i), lightmap));
|
||||
}
|
||||
|
||||
bufferQuad(quad, blendMode, glint);
|
||||
private VertexConsumer createCutoutVertexConsumer(boolean glint) {
|
||||
return ItemRenderer.getDirectItemGlintConsumer(vertexConsumerProvider, TexturedRenderLayers.getEntityCutout(), true, glint);
|
||||
}
|
||||
|
||||
private void renderQuadEmissive(MutableQuadViewImpl quad, BlendMode blendMode, TriState glint, int colorIndex) {
|
||||
colorizeQuad(quad, colorIndex);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
quad.lightmap(i, LightmapTextureManager.MAX_LIGHT_COORDINATE);
|
||||
}
|
||||
|
||||
bufferQuad(quad, blendMode, glint);
|
||||
}
|
||||
|
||||
private void renderMeshQuad(MutableQuadViewImpl quad) {
|
||||
if (!transform(quad)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final RenderMaterial mat = quad.material();
|
||||
|
||||
final int colorIndex = mat.disableColorIndex() ? -1 : quad.colorIndex();
|
||||
final BlendMode blendMode = mat.blendMode();
|
||||
final TriState glint = mat.glint();
|
||||
|
||||
if (mat.emissive()) {
|
||||
renderQuadEmissive(quad, blendMode, glint, colorIndex);
|
||||
} else {
|
||||
renderQuad(quad, blendMode, glint, colorIndex);
|
||||
}
|
||||
}
|
||||
|
||||
private class Maker extends MutableQuadViewImpl implements QuadEmitter {
|
||||
{
|
||||
data = new int[EncodingFormat.TOTAL_STRIDE];
|
||||
clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Maker emit() {
|
||||
computeGeometry();
|
||||
renderMeshQuad(this);
|
||||
clear();
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
private class MeshConsumer implements Consumer<Mesh> {
|
||||
@Override
|
||||
public void accept(Mesh mesh) {
|
||||
final MeshImpl m = (MeshImpl) mesh;
|
||||
final int[] data = m.data();
|
||||
final int limit = data.length;
|
||||
int index = 0;
|
||||
|
||||
while (index < limit) {
|
||||
System.arraycopy(data, index, editorQuad.data(), 0, EncodingFormat.TOTAL_STRIDE);
|
||||
editorQuad.load();
|
||||
index += EncodingFormat.TOTAL_STRIDE;
|
||||
renderMeshQuad(editorQuad);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class FallbackConsumer implements BakedModelConsumer {
|
||||
private class BakedModelConsumerImpl implements BakedModelConsumer {
|
||||
@Override
|
||||
public void accept(BakedModel model) {
|
||||
accept(model, null);
|
||||
|
@ -318,6 +276,8 @@ public class ItemRenderContext extends AbstractRenderContext {
|
|||
@Override
|
||||
public void accept(BakedModel model, @Nullable BlockState state) {
|
||||
if (hasTransform()) {
|
||||
MutableQuadViewImpl editorQuad = ItemRenderContext.this.editorQuad;
|
||||
|
||||
// if there's a transform in effect, convert to mesh-based quads so that we can apply it
|
||||
for (int i = 0; i <= ModelHelper.NULL_FACE_ID; i++) {
|
||||
final Direction cullFace = ModelHelper.faceFromIndex(i);
|
||||
|
@ -325,33 +285,24 @@ public class ItemRenderContext extends AbstractRenderContext {
|
|||
final List<BakedQuad> quads = model.getQuads(state, cullFace, random);
|
||||
final int count = quads.size();
|
||||
|
||||
if (count != 0) {
|
||||
for (int j = 0; j < count; j++) {
|
||||
final BakedQuad q = quads.get(j);
|
||||
editorQuad.fromVanilla(q, IndigoRenderer.MATERIAL_STANDARD, cullFace);
|
||||
renderMeshQuad(editorQuad);
|
||||
}
|
||||
for (int j = 0; j < count; j++) {
|
||||
final BakedQuad q = quads.get(j);
|
||||
editorQuad.fromVanilla(q, IndigoRenderer.MATERIAL_STANDARD, cullFace);
|
||||
// Call renderQuad directly instead of emit for efficiency
|
||||
renderQuad(editorQuad);
|
||||
}
|
||||
}
|
||||
|
||||
editorQuad.clear();
|
||||
} else {
|
||||
vanillaHandler.accept(model, itemStack, lightmap, overlay, matrixStack, defaultVertexConsumer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Consumer<Mesh> meshConsumer() {
|
||||
return meshConsumer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BakedModelConsumer bakedModelConsumer() {
|
||||
return fallbackConsumer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QuadEmitter getEmitter() {
|
||||
editorQuad.clear();
|
||||
return editorQuad;
|
||||
/** used to accept a method reference from the ItemRenderer. */
|
||||
@FunctionalInterface
|
||||
public interface VanillaQuadHandler {
|
||||
void accept(BakedModel model, ItemStack stack, int color, int overlay, MatrixStack matrixStack, VertexConsumer buffer);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,114 +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.impl.client.indigo.renderer.render;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.BakedQuad;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext.QuadTransform;
|
||||
import net.fabricmc.fabric.api.util.TriState;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.IndigoRenderer;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl;
|
||||
|
||||
/**
|
||||
* Consumer for vanilla baked models. Generally intended to give visual results matching a vanilla render,
|
||||
* however there could be subtle (and desirable) lighting variations so is good to be able to render
|
||||
* everything consistently.
|
||||
*
|
||||
* <p>Also, the API allows multi-part models that hold multiple vanilla models to render them without
|
||||
* combining quad lists, but the vanilla logic only handles one model per block. To route all of
|
||||
* them through vanilla logic would require additional hooks.
|
||||
*
|
||||
* <p>Works by copying the quad data to an "editor" quad held in the instance,
|
||||
* where all transformations are applied before buffering. Transformations should be
|
||||
* the same as they would be in a vanilla render - the editor is serving mainly
|
||||
* as a way to access vertex data without magical numbers. It also allows a consistent interface
|
||||
* for downstream tesselation routines.
|
||||
*
|
||||
* <p>Another difference from vanilla render is that all transformation happens before the
|
||||
* vertex data is sent to the byte buffer. Generally POJO array access will be faster than
|
||||
* manipulating the data via NIO.
|
||||
*/
|
||||
public abstract class TerrainFallbackConsumer extends AbstractQuadRenderer implements RenderContext.BakedModelConsumer {
|
||||
private static final RenderMaterial MATERIAL_FLAT = IndigoRenderer.INSTANCE.materialFinder().ambientOcclusion(TriState.FALSE).find();
|
||||
private static final RenderMaterial MATERIAL_SHADED = IndigoRenderer.INSTANCE.materialFinder().find();
|
||||
|
||||
TerrainFallbackConsumer(BlockRenderInfo blockInfo, Function<RenderLayer, VertexConsumer> bufferFunc, AoCalculator aoCalc, QuadTransform transform) {
|
||||
super(blockInfo, bufferFunc, aoCalc, transform);
|
||||
}
|
||||
|
||||
private final MutableQuadViewImpl editorQuad = new MutableQuadViewImpl() {
|
||||
{
|
||||
data = new int[EncodingFormat.TOTAL_STRIDE];
|
||||
material(MATERIAL_SHADED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QuadEmitter emit() {
|
||||
// should not be called
|
||||
throw new UnsupportedOperationException("Fallback consumer does not support .emit()");
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void accept(BakedModel bakedModel) {
|
||||
accept(bakedModel, blockInfo.blockState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(BakedModel model, @Nullable BlockState blockState) {
|
||||
final Supplier<Random> random = blockInfo.randomSupplier;
|
||||
final RenderMaterial defaultMaterial = model.useAmbientOcclusion() ? MATERIAL_SHADED : MATERIAL_FLAT;
|
||||
|
||||
for (int i = 0; i <= ModelHelper.NULL_FACE_ID; i++) {
|
||||
final Direction cullFace = ModelHelper.faceFromIndex(i);
|
||||
final List<BakedQuad> quads = model.getQuads(blockState, cullFace, random.get());
|
||||
final int count = quads.size();
|
||||
|
||||
if (count != 0) {
|
||||
for (int j = 0; j < count; j++) {
|
||||
final BakedQuad q = quads.get(j);
|
||||
renderQuad(q, cullFace, defaultMaterial);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void renderQuad(BakedQuad quad, Direction cullFace, RenderMaterial defaultMaterial) {
|
||||
final MutableQuadViewImpl editorQuad = this.editorQuad;
|
||||
editorQuad.fromVanilla(quad, defaultMaterial, cullFace);
|
||||
|
||||
renderQuad(editorQuad, true);
|
||||
}
|
||||
}
|
|
@ -17,13 +17,11 @@
|
|||
package net.fabricmc.fabric.impl.client.indigo.renderer.render;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.joml.Matrix3f;
|
||||
import org.joml.Matrix4f;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.OverlayTexture;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
import net.minecraft.client.render.chunk.BlockBufferBuilderStorage;
|
||||
import net.minecraft.client.render.chunk.ChunkBuilder.BuiltChunk;
|
||||
import net.minecraft.client.render.chunk.ChunkRendererRegion;
|
||||
|
@ -33,10 +31,9 @@ import net.minecraft.util.crash.CrashException;
|
|||
import net.minecraft.util.crash.CrashReport;
|
||||
import net.minecraft.util.crash.CrashReportSection;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
|
||||
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;
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator;
|
||||
|
||||
|
@ -45,56 +42,35 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator;
|
|||
* Dispatches calls from models during chunk rebuild to the appropriate consumer,
|
||||
* and holds/manages all of the state needed by them.
|
||||
*/
|
||||
public class TerrainRenderContext extends AbstractRenderContext {
|
||||
public class TerrainRenderContext extends AbstractBlockRenderContext {
|
||||
public static final ThreadLocal<TerrainRenderContext> POOL = ThreadLocal.withInitial(TerrainRenderContext::new);
|
||||
|
||||
private final BlockRenderInfo blockInfo = new BlockRenderInfo();
|
||||
private final ChunkRenderInfo chunkInfo = new ChunkRenderInfo();
|
||||
private final AoCalculator aoCalc = new AoCalculator(blockInfo) {
|
||||
@Override
|
||||
public int light(BlockPos pos, BlockState state) {
|
||||
return chunkInfo.cachedBrightness(pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float ao(BlockPos pos, BlockState state) {
|
||||
return chunkInfo.cachedAoLevel(pos, state);
|
||||
}
|
||||
};
|
||||
public TerrainRenderContext() {
|
||||
overlay = OverlayTexture.DEFAULT_UV;
|
||||
blockInfo.random = Random.create();
|
||||
}
|
||||
|
||||
private final AbstractMeshConsumer meshConsumer = new AbstractMeshConsumer(blockInfo, chunkInfo::getInitializedBuffer, aoCalc, this::transform) {
|
||||
@Override
|
||||
protected int overlay() {
|
||||
return overlay;
|
||||
}
|
||||
@Override
|
||||
protected AoCalculator createAoCalc(BlockRenderInfo blockInfo) {
|
||||
return new AoCalculator(blockInfo) {
|
||||
@Override
|
||||
public int light(BlockPos pos, BlockState state) {
|
||||
return chunkInfo.cachedBrightness(pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Matrix4f matrix() {
|
||||
return matrix;
|
||||
}
|
||||
@Override
|
||||
public float ao(BlockPos pos, BlockState state) {
|
||||
return chunkInfo.cachedAoLevel(pos, state);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Matrix3f normalMatrix() {
|
||||
return normalMatrix;
|
||||
}
|
||||
};
|
||||
|
||||
private final TerrainFallbackConsumer fallbackConsumer = new TerrainFallbackConsumer(blockInfo, chunkInfo::getInitializedBuffer, aoCalc, this::transform) {
|
||||
@Override
|
||||
protected int overlay() {
|
||||
return overlay;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Matrix4f matrix() {
|
||||
return matrix;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Matrix3f normalMatrix() {
|
||||
return normalMatrix;
|
||||
}
|
||||
};
|
||||
@Override
|
||||
protected VertexConsumer getVertexConsumer(RenderLayer layer) {
|
||||
return chunkInfo.getInitializedBuffer(layer);
|
||||
}
|
||||
|
||||
public void prepare(ChunkRendererRegion blockView, BuiltChunk chunkRenderer, BuiltChunk.RebuildTask.RenderData renderData, BlockBufferBuilderStorage builders, Set<RenderLayer> initializedLayers) {
|
||||
blockInfo.prepareForWorld(blockView, true);
|
||||
|
@ -108,13 +84,18 @@ public class TerrainRenderContext extends AbstractRenderContext {
|
|||
|
||||
/** Called from chunk renderer hook. */
|
||||
public void tessellateBlock(BlockState blockState, BlockPos blockPos, final BakedModel model, MatrixStack matrixStack) {
|
||||
this.matrix = matrixStack.peek().getPositionMatrix();
|
||||
this.normalMatrix = matrixStack.peek().getNormalMatrix();
|
||||
|
||||
try {
|
||||
Vec3d vec3d = blockState.getModelOffset(chunkInfo.blockView, blockPos);
|
||||
matrixStack.translate(vec3d.x, vec3d.y, vec3d.z);
|
||||
|
||||
this.matrix = matrixStack.peek().getPositionMatrix();
|
||||
this.normalMatrix = matrixStack.peek().getNormalMatrix();
|
||||
|
||||
blockInfo.recomputeSeed = true;
|
||||
|
||||
aoCalc.clear();
|
||||
blockInfo.prepareForBlock(blockState, blockPos, model.useAmbientOcclusion());
|
||||
((FabricBakedModel) model).emitBlockQuads(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, blockInfo.randomSupplier, this);
|
||||
model.emitBlockQuads(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, blockInfo.randomSupplier, this);
|
||||
} catch (Throwable throwable) {
|
||||
CrashReport crashReport = CrashReport.create(throwable, "Tessellating block in world - Indigo Renderer");
|
||||
CrashReportSection crashReportSection = crashReport.addElement("Block being tessellated");
|
||||
|
@ -122,19 +103,4 @@ public class TerrainRenderContext extends AbstractRenderContext {
|
|||
throw new CrashException(crashReport);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Consumer<Mesh> meshConsumer() {
|
||||
return meshConsumer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BakedModelConsumer bakedModelConsumer() {
|
||||
return fallbackConsumer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QuadEmitter getEmitter() {
|
||||
return meshConsumer.getEmitter();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,6 @@ import net.minecraft.util.math.BlockPos;
|
|||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.VanillaAoHelper;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderContext;
|
||||
|
||||
|
@ -42,7 +41,7 @@ public abstract class BlockModelRendererMixin {
|
|||
|
||||
@Inject(at = @At("HEAD"), method = "render(Lnet/minecraft/world/BlockRenderView;Lnet/minecraft/client/render/model/BakedModel;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumer;ZLnet/minecraft/util/math/random/Random;JI)V", cancellable = true)
|
||||
private void hookRender(BlockRenderView blockView, BakedModel model, BlockState state, BlockPos pos, MatrixStack matrix, VertexConsumer buffer, boolean cull, Random rand, long seed, int overlay, CallbackInfo ci) {
|
||||
if (!((FabricBakedModel) model).isVanillaAdapter()) {
|
||||
if (!model.isVanillaAdapter()) {
|
||||
BlockRenderContext context = fabric_contexts.get();
|
||||
context.render(blockView, model, state, pos, matrix, buffer, cull, rand, seed, overlay);
|
||||
ci.cancel();
|
||||
|
|
|
@ -40,11 +40,9 @@ import net.minecraft.client.render.chunk.ChunkRendererRegion;
|
|||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
|
||||
import net.fabricmc.fabric.impl.client.indigo.Indigo;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.accessor.AccessChunkRendererRegion;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.render.TerrainRenderContext;
|
||||
|
@ -104,13 +102,11 @@ public abstract class ChunkBuilderBuiltChunkRebuildTaskMixin {
|
|||
*/
|
||||
@Redirect(method = "render", require = 1, at = @At(value = "INVOKE",
|
||||
target = "Lnet/minecraft/client/render/block/BlockRenderManager;renderBlock(Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/world/BlockRenderView;Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumer;ZLnet/minecraft/util/math/random/Random;)V"))
|
||||
private void hookChunkBuildTesselate(BlockRenderManager renderManager, BlockState blockState, BlockPos blockPos, BlockRenderView blockView, MatrixStack matrix, VertexConsumer bufferBuilder, boolean checkSides, Random random) {
|
||||
private void hookChunkBuildTessellate(BlockRenderManager renderManager, BlockState blockState, BlockPos blockPos, BlockRenderView blockView, MatrixStack matrix, VertexConsumer bufferBuilder, boolean checkSides, Random random) {
|
||||
if (blockState.getRenderType() == BlockRenderType.MODEL) {
|
||||
final BakedModel model = renderManager.getModel(blockState);
|
||||
|
||||
if (Indigo.ALWAYS_TESSELATE_INDIGO || !((FabricBakedModel) model).isVanillaAdapter()) {
|
||||
Vec3d vec3d = blockState.getModelOffset(blockView, blockPos);
|
||||
matrix.translate(vec3d.x, vec3d.y, vec3d.z);
|
||||
if (Indigo.ALWAYS_TESSELATE_INDIGO || !model.isVanillaAdapter()) {
|
||||
((AccessChunkRendererRegion) blockView).fabric_getRenderer().tessellateBlock(blockState, blockPos, model, matrix);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -32,7 +32,6 @@ import net.minecraft.client.render.model.json.ModelTransformationMode;
|
|||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.render.IndigoQuadHandler;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.render.ItemRenderContext;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.render.ItemRenderContext.VanillaQuadHandler;
|
||||
|
@ -51,7 +50,7 @@ public abstract class ItemRendererMixin {
|
|||
|
||||
@Inject(at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/model/BakedModel;isBuiltin()Z"), method = "renderItem(Lnet/minecraft/item/ItemStack;Lnet/minecraft/client/render/model/json/ModelTransformationMode;ZLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider;IILnet/minecraft/client/render/model/BakedModel;)V", cancellable = true)
|
||||
public void hook_renderItem(ItemStack stack, ModelTransformationMode transformMode, boolean invert, MatrixStack matrixStack, VertexConsumerProvider vertexConsumerProvider, int light, int overlay, BakedModel model, CallbackInfo ci) {
|
||||
if (!((FabricBakedModel) model).isVanillaAdapter()) {
|
||||
if (!model.isVanillaAdapter()) {
|
||||
fabric_contexts.get().renderModel(stack, transformMode, invert, matrixStack, vertexConsumerProvider, light, overlay, model, fabric_vanillaHandler);
|
||||
matrixStack.pop();
|
||||
ci.cancel();
|
||||
|
|
Loading…
Reference in a new issue