Remove texture indices and make material AO a TriState (#3044)

* Reorganize method order

* Remove texture indices

- Add allow passing shade boolean to QuadView#toBakedQuad
- Add MutableQuadView#fromVanilla(int[], int)
- Add NonExtendable annotation to SpriteFinder and RendererAccess
- Add note to RenderContext#bakedModelConsumer

* Make Material AO a TriState

Co-authored-by: PepperCode1 <44146161+PepperCode1@users.noreply.github.com>

* Rename spriteUnitSquare -> uvUnitSquare

* Remove toBakedQuad with shade boolean parameter

* Add custom apiNote, implSpec, and implNote javadoc tags

* Reject null BlendMode and AO; clarify fromVanilla(int[], int) javadoc

* cullFace nullability, fromBakedQuad shade clarification, toBakedQuad color index

* Add QuadView#copyUv and minor improvements

* Nullability tweaks

---------

Co-authored-by: PepperCode1 <44146161+PepperCode1@users.noreply.github.com>
This commit is contained in:
Technici4n 2023-05-14 15:57:28 +02:00 committed by modmuss50
parent f7923f6d3d
commit 7bf08b2e0c
23 changed files with 1017 additions and 668 deletions

View file

@ -331,6 +331,12 @@ javadoc {
)
// Disable the crazy super-strict doclint tool in Java 8
addStringOption("Xdoclint:none", "-quiet")
tags(
'apiNote:a:API Note:',
'implSpec:a:Implementation Requirements:',
'implNote:a:Implementation Note:'
)
}
allprojects.each {

View file

@ -16,6 +16,7 @@
package net.fabricmc.fabric.api.renderer.v1;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import net.fabricmc.fabric.impl.renderer.RendererAccessImpl;
@ -23,6 +24,7 @@ import net.fabricmc.fabric.impl.renderer.RendererAccessImpl;
/**
* Registration and access for rendering extensions.
*/
@ApiStatus.NonExtendable
public interface RendererAccess {
RendererAccess INSTANCE = RendererAccessImpl.INSTANCE;

View file

@ -16,11 +16,14 @@
package net.fabricmc.fabric.api.renderer.v1.material;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.model.BakedModel;
import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.fabricmc.fabric.api.util.TriState;
/**
* Finds standard {@link RenderMaterial} instances used to communicate
@ -29,6 +32,72 @@ import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
* <p>Must be obtained via {@link Renderer#materialFinder()}.
*/
public interface MaterialFinder {
/**
* Defines how sprite pixels will be blended with the scene.
*
* <p>See {@link BlendMode} for more information.
*
* @apiNote The default implementation will be removed in the next breaking release.
*/
default MaterialFinder blendMode(BlendMode blendMode) {
return blendMode(0, blendMode);
}
/**
* Vertex color(s) will be modified for quad color index unless disabled.
*
* @apiNote The default implementation will be removed in the next breaking release.
*/
default MaterialFinder disableColorIndex(boolean disable) {
return disableColorIndex(0, disable);
}
/**
* When true, sprite texture and color will be rendered at full brightness.
* Lightmap values provided via {@link QuadEmitter#lightmap(int)} will be ignored.
* False by default
*
* <p>This is the preferred method for emissive lighting effects. Some renderers
* with advanced lighting models may not use block lightmaps and this method will
* allow per-sprite emissive lighting in future extensions that support overlay sprites.
*
* <p>Note that color will still be modified by diffuse shading and ambient occlusion,
* unless disabled via {@link #disableDiffuse(boolean)} and {@link #ambientOcclusion(TriState)}.
*
* @apiNote The default implementation will be removed in the next breaking release.
*/
default MaterialFinder emissive(boolean isEmissive) {
return emissive(0, isEmissive);
}
/**
* Vertex color(s) will be modified for diffuse shading unless disabled.
*
* @apiNote The default implementation will be removed in the next breaking release.
*/
default MaterialFinder disableDiffuse(boolean disable) {
return disableDiffuse(0, disable);
}
/**
* Controls whether vertex color(s) will be modified for ambient occlusion.
*
* <p>By default, ambient occlusion will be used if {@link BakedModel#useAmbientOcclusion() the model uses ambient occlusion}
* and the block state has {@link BlockState#getLuminance() a luminance} of 0.
* Set to {@link TriState#TRUE} or {@link TriState#FALSE} to override this behavior.
*
* @apiNote The default implementation will be removed in the next breaking release.
*/
default MaterialFinder ambientOcclusion(TriState mode) {
return disableAo(0, mode == TriState.FALSE);
}
/**
* Resets this instance to default values. Values will match those
* in effect when an instance is newly obtained via {@link Renderer#materialFinder()}.
*/
MaterialFinder clear();
/**
* Returns the standard material encoding all
* of the current settings in this finder. The settings in
@ -41,67 +110,63 @@ public interface MaterialFinder {
RenderMaterial find();
/**
* Resets this instance to default values. Values will match those
* in effect when an instance is newly obtained via {@link Renderer#materialFinder()}.
*/
MaterialFinder clear();
/**
* Reserved for future use. Behavior for values &gt; 1 is currently undefined.
*/
MaterialFinder spriteDepth(int depth);
/**
* Defines how sprite pixels will be blended with the scene.
* Accepts {link @BlockRenderLayer} values and blending behavior
* will emulate the way that Minecraft renders those instances. This does
* NOT mean the sprite will be rendered in a specific render pass - some
* implementations may not use the standard vanilla render passes.
*
* <p>CAN be null and is null by default. A null value means the renderer
* will use the value normally associated with the block being rendered, or
* {@code TRANSLUCENT} for item renders. (Normal Minecraft rendering)
*
* @deprecated Use {@code BlendMode} version instead.
* @deprecated Use {@link #blendMode(BlendMode)} instead.
*/
@Deprecated
default MaterialFinder blendMode(int spriteIndex, RenderLayer renderLayer) {
return blendMode(spriteIndex, BlendMode.fromRenderLayer(renderLayer));
return blendMode(BlendMode.fromRenderLayer(renderLayer));
}
/**
* Defines how sprite pixels will be blended with the scene.
*
* <p>See {@link BlendMode} for more information.
* @deprecated Use {@link #blendMode(BlendMode)} instead.
*/
MaterialFinder blendMode(int spriteIndex, BlendMode blendMode);
@Deprecated
default MaterialFinder blendMode(int spriteIndex, BlendMode blendMode) {
// Null check is kept for legacy reasons, but the new blendMode method will NPE if passed null!
if (blendMode == null) {
blendMode = BlendMode.DEFAULT;
}
return blendMode(blendMode);
}
/**
* Vertex color(s) will be modified for quad color index unless disabled.
* @deprecated Use {@link #disableColorIndex(boolean)} instead.
*/
MaterialFinder disableColorIndex(int spriteIndex, boolean disable);
@Deprecated
default MaterialFinder disableColorIndex(int spriteIndex, boolean disable) {
return disableColorIndex(disable);
}
/**
* Vertex color(s) will be modified for diffuse shading unless disabled.
* @deprecated Use {@link #emissive(boolean)} instead.
*/
MaterialFinder disableDiffuse(int spriteIndex, boolean disable);
@Deprecated
default MaterialFinder emissive(int spriteIndex, boolean isEmissive) {
return emissive(isEmissive);
}
/**
* Vertex color(s) will be modified for ambient occlusion unless disabled.
* @deprecated Use {@link #disableDiffuse(boolean)} instead.
*/
MaterialFinder disableAo(int spriteIndex, boolean disable);
@Deprecated
default MaterialFinder disableDiffuse(int spriteIndex, boolean disable) {
return disableDiffuse(disable);
}
/**
* When true, sprite texture and color will be rendered at full brightness.
* Lightmap values provided via {@link QuadEmitter#lightmap(int)} will be ignored.
* False by default
*
* <p>This is the preferred method for emissive lighting effects. Some renderers
* with advanced lighting models may not use block lightmaps and this method will
* allow per-sprite emissive lighting in future extensions that support overlay sprites.
*
* <p>Note that color will still be modified by diffuse shading and ambient occlusion,
* unless disabled via {@link #disableAo(int, boolean)} and {@link #disableDiffuse(int, boolean)}.
* @deprecated Use {@link #ambientOcclusion(TriState)} instead.
*/
MaterialFinder emissive(int spriteIndex, boolean isEmissive);
@Deprecated
default MaterialFinder disableAo(int spriteIndex, boolean disable) {
return ambientOcclusion(disable ? TriState.FALSE : TriState.DEFAULT);
}
/**
* Do not use. Does nothing.
*/
@Deprecated
default MaterialFinder spriteDepth(int depth) {
return this;
}
}

View file

@ -76,9 +76,10 @@ public interface RenderMaterial {
Identifier MATERIAL_STANDARD = new Identifier("fabric", "standard");
/**
* How many sprite color/uv coordinates are in the material.
* Behavior for values &gt; 1 is currently undefined.
* See {@link MaterialFinder#spriteDepth(int)}
* Do not use. Always returns 1.
*/
int spriteDepth();
@Deprecated
default int spriteDepth() {
return 1;
}
}

View file

@ -17,6 +17,7 @@
package net.fabricmc.fabric.api.renderer.v1.mesh;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector2f;
import org.joml.Vector3f;
import net.minecraft.client.render.model.BakedQuad;
@ -33,39 +34,39 @@ import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
* {@link QuadEmitter} and for dynamic renders/mesh transforms.
*
* <p>Instances of {@link MutableQuadView} will practically always be
* threadlocal and/or reused - do not retain references.
* thread local and/or reused - do not retain references.
*
* <p>Only the renderer should implement or extend this interface.
*/
public interface MutableQuadView extends QuadView {
/**
* Causes texture to appear with no rotation.
* Pass in bakeFlags parameter to {@link #spriteBake(int, Sprite, int)}.
* Pass in bakeFlags parameter to {@link #spriteBake(Sprite, int)}.
*/
int BAKE_ROTATE_NONE = 0;
/**
* Causes texture to appear rotated 90 deg. clockwise relative to nominal face.
* Pass in bakeFlags parameter to {@link #spriteBake(int, Sprite, int)}.
* Pass in bakeFlags parameter to {@link #spriteBake(Sprite, int)}.
*/
int BAKE_ROTATE_90 = 1;
/**
* Causes texture to appear rotated 180 deg. relative to nominal face.
* Pass in bakeFlags parameter to {@link #spriteBake(int, Sprite, int)}.
* Pass in bakeFlags parameter to {@link #spriteBake(Sprite, int)}.
*/
int BAKE_ROTATE_180 = 2;
/**
* Causes texture to appear rotated 270 deg. clockwise relative to nominal face.
* Pass in bakeFlags parameter to {@link #spriteBake(int, Sprite, int)}.
* Pass in bakeFlags parameter to {@link #spriteBake(Sprite, int)}.
*/
int BAKE_ROTATE_270 = 3;
/**
* When enabled, texture coordinate are assigned based on vertex position.
* Any existing uv coordinates will be replaced.
* Pass in bakeFlags parameter to {@link #spriteBake(int, Sprite, int)}.
* Any existing UV coordinates will be replaced.
* Pass in bakeFlags parameter to {@link #spriteBake(Sprite, int)}.
*
* <p>UV lock always derives texture coordinates based on nominal face, even
* when the quad is not co-planar with that face, and the result is
@ -79,12 +80,12 @@ public interface MutableQuadView extends QuadView {
* flipped as part of baking. Can be useful for some randomization
* and texture mapping scenarios. Results are different from what
* can be obtained via rotation and both can be applied.
* Pass in bakeFlags parameter to {@link #spriteBake(int, Sprite, int)}.
* Pass in bakeFlags parameter to {@link #spriteBake(Sprite, int)}.
*/
int BAKE_FLIP_U = 8;
/**
* Same as {@link MutableQuadView#BAKE_FLIP_U} but for V coordinate.
* Same as {@link #BAKE_FLIP_U} but for V coordinate.
*/
int BAKE_FLIP_V = 16;
@ -93,86 +94,13 @@ public interface MutableQuadView extends QuadView {
* with conventional Minecraft model format. This is scaled to 0-1 during
* baking before interpolation. Model loaders that already have 0-1 coordinates
* can avoid wasteful multiplication/division by passing 0-1 coordinates directly.
* Pass in bakeFlags parameter to {@link #spriteBake(int, Sprite, int)}.
* Pass in bakeFlags parameter to {@link #spriteBake(Sprite, int)}.
*/
int BAKE_NORMALIZED = 32;
/**
* Assigns a different material to this quad. Useful for transformation of
* existing meshes because lighting and texture blending are controlled by material.
*/
MutableQuadView material(RenderMaterial material);
/**
* If non-null, quad is coplanar with a block face which, if known, simplifies
* or shortcuts geometric analysis that might otherwise be needed.
* Set to null if quad is not coplanar or if this is not known.
* Also controls face culling during block rendering.
*
* <p>Null by default.
*
* <p>When called with a non-null value, also sets {@link #nominalFace(Direction)}
* to the same value.
*
* <p>This is different from the value reported by {@link BakedQuad#getFace()}. That value
* is computed based on face geometry and must be non-null in vanilla quads.
* That computed value is returned by {@link #lightFace()}.
*/
@Nullable
MutableQuadView cullFace(@Nullable Direction face);
/**
* Provides a hint to renderer about the facing of this quad. Not required,
* but if provided can shortcut some geometric analysis if the quad is parallel to a block face.
* Should be the expected value of {@link #lightFace()}. Value will be confirmed
* and if invalid the correct light face will be calculated.
*
* <p>Null by default, and set automatically by {@link #cullFace()}.
*
* <p>Models may also find this useful as the face for texture UV locking and rotation semantics.
*
* <p>Note: This value is not persisted independently when the quad is encoded.
* When reading encoded quads, this value will always be the same as {@link #lightFace()}.
*/
@Nullable
MutableQuadView nominalFace(Direction face);
/**
* Value functions identically to {@link BakedQuad#getColorIndex()} and is
* used by renderer / model builder in same way. Default value is -1.
*/
MutableQuadView colorIndex(int colorIndex);
/**
* Enables bulk vertex data transfer using the standard Minecraft vertex formats.
* This method should be performant whenever caller's vertex representation makes it feasible.
*
* <p>Calling this method does not emit the quad.
*
* @deprecated Use {@link #fromVanilla(BakedQuad, RenderMaterial, Direction)}
* which has better encapsulation and removed outdated item flag
*/
@Deprecated
MutableQuadView fromVanilla(int[] quadData, int startIndex, boolean isItem);
/**
* Enables bulk vertex data transfer using the standard Minecraft vertex formats.
* This method should be performant whenever caller's vertex representation makes it feasible.
*
* <p>Calling this method does not emit the quad.
*/
MutableQuadView fromVanilla(BakedQuad quad, RenderMaterial material, Direction cullFace);
/**
* Encodes an integer tag with this quad that can later be retrieved via
* {@link QuadView#tag()}. Useful for models that want to perform conditional
* transformation or filtering on static meshes.
*/
MutableQuadView tag(int tag);
/**
* Sets the geometric vertex position for the given vertex,
* relative to block origin. (0,0,0). Minecraft rendering is designed
* relative to block origin, (0,0,0). Minecraft rendering is designed
* for models that fit within a single block space and is recommended
* that coordinates remain in the 0-1 range, with multi-block meshes
* split into multiple per-block models.
@ -182,8 +110,81 @@ public interface MutableQuadView extends QuadView {
/**
* Same as {@link #pos(int, float, float, float)} but accepts vector type.
*/
default MutableQuadView pos(int vertexIndex, Vector3f vec) {
return pos(vertexIndex, vec.x(), vec.y(), vec.z());
default MutableQuadView pos(int vertexIndex, Vector3f pos) {
return pos(vertexIndex, pos.x(), pos.y(), pos.z());
}
/**
* Set vertex color.
*
* @apiNote The default implementation will be removed in the next breaking release.
*/
default MutableQuadView color(int vertexIndex, int color) {
return spriteColor(vertexIndex, 0, color);
}
/**
* Convenience: set vertex color for all vertices at once.
*/
default MutableQuadView color(int c0, int c1, int c2, int c3) {
color(0, c0);
color(1, c1);
color(2, c2);
color(3, c3);
return this;
}
/**
* Set texture coordinates.
*
* @apiNote The default implementation will be removed in the next breaking release.
*/
default MutableQuadView uv(int vertexIndex, float u, float v) {
return sprite(vertexIndex, 0, u, v);
}
/**
* Set texture coordinates.
*
* <p>Only use this function if you already have a {@link Vector2f}.
* Otherwise, see {@link MutableQuadView#uv(int, float, float)}.
*/
default MutableQuadView uv(int vertexIndex, Vector2f uv) {
return uv(vertexIndex, uv.x, uv.y);
}
/**
* Assigns sprite atlas u,v coordinates to this quad for the given sprite.
* Can handle UV locking, rotation, interpolation, etc. Control this behavior
* by passing additive combinations of the BAKE_ flags defined in this interface.
*
* @apiNote The default implementation will be removed in the next breaking release.
*/
default MutableQuadView spriteBake(Sprite sprite, int bakeFlags) {
return spriteBake(0, sprite, bakeFlags);
}
/**
* Accept vanilla lightmap values. Input values will override lightmap values
* computed from world state if input values are higher. Exposed for completeness
* but some rendering implementations with non-standard lighting model may not honor it.
*
* <p>For emissive rendering, it is better to use {@link MaterialFinder#emissive(boolean)}.
*/
MutableQuadView lightmap(int vertexIndex, int lightmap);
/**
* Convenience: set lightmap for all vertices at once.
*
* <p>For emissive rendering, it is better to use {@link MaterialFinder#emissive(boolean)}.
* See {@link #lightmap(int, int)}.
*/
default MutableQuadView lightmap(int b0, int b1, int b2, int b3) {
lightmap(0, b0);
lightmap(1, b1);
lightmap(2, b2);
lightmap(3, b3);
return this;
}
/**
@ -200,69 +201,135 @@ public interface MutableQuadView extends QuadView {
/**
* Same as {@link #normal(int, float, float, float)} but accepts vector type.
*/
default MutableQuadView normal(int vertexIndex, Vector3f vec) {
return normal(vertexIndex, vec.x(), vec.y(), vec.z());
default MutableQuadView normal(int vertexIndex, Vector3f normal) {
return normal(vertexIndex, normal.x(), normal.y(), normal.z());
}
/**
* Accept vanilla lightmap values. Input values will override lightmap values
* computed from world state if input values are higher. Exposed for completeness
* but some rendering implementations with non-standard lighting model may not honor it.
* If non-null, quad is coplanar with a block face which, if known, simplifies
* or shortcuts geometric analysis that might otherwise be needed.
* Set to null if quad is not coplanar or if this is not known.
* Also controls face culling during block rendering.
*
* <p>For emissive rendering, it is better to use {@link MaterialFinder#emissive(int, boolean)}.
* <p>Null by default.
*
* <p>When called with a non-null value, also sets {@link #nominalFace(Direction)}
* to the same value.
*
* <p>This is different from the value reported by {@link BakedQuad#getFace()}. That value
* is computed based on face geometry and must be non-null in vanilla quads.
* That computed value is returned by {@link #lightFace()}.
*/
MutableQuadView lightmap(int vertexIndex, int lightmap);
MutableQuadView cullFace(@Nullable Direction face);
/**
* Convenience: set lightmap for all vertices at once.
* Provides a hint to renderer about the facing of this quad. Not required,
* but if provided can shortcut some geometric analysis if the quad is parallel to a block face.
* Should be the expected value of {@link #lightFace()}. Value will be confirmed
* and if invalid the correct light face will be calculated.
*
* <p>For emissive rendering, it is better to use {@link MaterialFinder#emissive(int, boolean)}.
* See {@link #lightmap(int, int)}.
* <p>Null by default, and set automatically by {@link #cullFace()}.
*
* <p>Models may also find this useful as the face for texture UV locking and rotation semantics.
*
* <p>Note: This value is not persisted independently when the quad is encoded.
* When reading encoded quads, this value will always be the same as {@link #lightFace()}.
*/
default MutableQuadView lightmap(int b0, int b1, int b2, int b3) {
lightmap(0, b0);
lightmap(1, b1);
lightmap(2, b2);
lightmap(3, b3);
return this;
MutableQuadView nominalFace(@Nullable Direction face);
/**
* Assigns a different material to this quad. Useful for transformation of
* existing meshes because lighting and texture blending are controlled by material.
*/
MutableQuadView material(RenderMaterial material);
/**
* Value functions identically to {@link BakedQuad#getColorIndex()} and is
* used by renderer / model builder in same way. Default value is -1.
*/
MutableQuadView colorIndex(int colorIndex);
/**
* Encodes an integer tag with this quad that can later be retrieved via
* {@link QuadView#tag()}. Useful for models that want to perform conditional
* transformation or filtering on static meshes.
*/
MutableQuadView tag(int tag);
/**
* Enables bulk vertex data transfer using the standard Minecraft vertex formats.
* Only the {@link BakedQuad#getVertexData() quad vertex data} is copied.
* This method should be performant whenever caller's vertex representation makes it feasible.
*
* <p>Use {@link #fromVanilla(BakedQuad, RenderMaterial, Direction) the other overload} which has better encapsulation
* unless you have a specific reason to use this one.
*
* <p>Calling this method does not emit the quad.
*
* @apiNote The default implementation will be removed in the next breaking release.
*/
default MutableQuadView fromVanilla(int[] quadData, int startIndex) {
return fromVanilla(quadData, startIndex, false);
}
/**
* Set sprite color. Behavior for {@code spriteIndex > 0} is currently undefined.
* Enables bulk vertex data transfer using the standard Minecraft quad format.
*
* <p>Calling this method does not emit the quad.
*
* <p>The material applied to this quad view might be slightly different from the {@code material} parameter regarding diffuse shading.
* If either the baked quad {@link BakedQuad#hasShade() does not have shade} or the material {@link MaterialFinder#disableDiffuse(boolean) does not have shade},
* diffuse shading will be disabled for this quad view.
* This is reflected in the quad view's {@link #material()}, but the {@code material} parameter is unchanged (it is immutable anyway).
*/
MutableQuadView spriteColor(int vertexIndex, int spriteIndex, int color);
MutableQuadView fromVanilla(BakedQuad quad, RenderMaterial material, @Nullable Direction cullFace);
/**
* Convenience: set sprite color for all vertices at once. Behavior for {@code spriteIndex > 0} is currently undefined.
* @deprecated Use {@link #color(int, int)} instead.
*/
@Deprecated
default MutableQuadView spriteColor(int vertexIndex, int spriteIndex, int color) {
return color(vertexIndex, color);
}
/**
* @deprecated Use {@link #color(int, int, int, int)} instead.
*/
@Deprecated
default MutableQuadView spriteColor(int spriteIndex, int c0, int c1, int c2, int c3) {
spriteColor(0, spriteIndex, c0);
spriteColor(1, spriteIndex, c1);
spriteColor(2, spriteIndex, c2);
spriteColor(3, spriteIndex, c3);
color(c0, c1, c2, c3);
return this;
}
/**
* Set sprite atlas coordinates. Behavior for {@code spriteIndex > 0} is currently undefined.
* @deprecated Use {@link #uv(int, float, float)} instead.
*/
MutableQuadView sprite(int vertexIndex, int spriteIndex, float u, float v);
/**
* Set sprite atlas coordinates. Behavior for {@code spriteIndex > 0} is currently undefined.
*
* <p>Only use this function if you already have a {@link Vec2f}.
* Otherwise, see {@link MutableQuadView#sprite(int, int, float, float)}.
*/
default MutableQuadView sprite(int vertexIndex, int spriteIndex, Vec2f uv) {
return sprite(vertexIndex, spriteIndex, uv.x, uv.y);
@Deprecated
default MutableQuadView sprite(int vertexIndex, int spriteIndex, float u, float v) {
return uv(vertexIndex, u, v);
}
/**
* Assigns sprite atlas u,v coordinates to this quad for the given sprite.
* Can handle UV locking, rotation, interpolation, etc. Control this behavior
* by passing additive combinations of the BAKE_ flags defined in this interface.
* Behavior for {@code spriteIndex > 0} is currently undefined.
* @deprecated Use {@link #uv(int, Vector2f)} instead.
*/
MutableQuadView spriteBake(int spriteIndex, Sprite sprite, int bakeFlags);
@Deprecated
default MutableQuadView sprite(int vertexIndex, int spriteIndex, Vec2f uv) {
return uv(vertexIndex, uv.x, uv.y);
}
/**
* @deprecated Use {@link #spriteBake(Sprite, int)} instead.
*/
@Deprecated
default MutableQuadView spriteBake(int spriteIndex, Sprite sprite, int bakeFlags) {
return spriteBake(sprite, bakeFlags);
}
/**
* @deprecated Use {@link #fromVanilla(int[], int)} instead.
*/
@Deprecated
default MutableQuadView fromVanilla(int[] quadData, int startIndex, boolean isItem) {
return fromVanilla(quadData, startIndex);
}
}

View file

@ -16,6 +16,7 @@
package net.fabricmc.fabric.api.renderer.v1.mesh;
import org.joml.Vector2f;
import org.joml.Vector3f;
import net.minecraft.client.texture.Sprite;
@ -38,36 +39,59 @@ import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
* <p>Only the renderer should implement or extend this interface.
*/
public interface QuadEmitter extends MutableQuadView {
@Override
QuadEmitter material(RenderMaterial material);
@Override
QuadEmitter cullFace(Direction face);
@Override
QuadEmitter nominalFace(Direction face);
@Override
QuadEmitter colorIndex(int colorIndex);
@Override
QuadEmitter fromVanilla(int[] quadData, int startIndex, boolean isItem);
@Override
QuadEmitter tag(int tag);
@Override
QuadEmitter pos(int vertexIndex, float x, float y, float z);
@Override
default QuadEmitter pos(int vertexIndex, Vector3f vec) {
MutableQuadView.super.pos(vertexIndex, vec);
default QuadEmitter pos(int vertexIndex, Vector3f pos) {
MutableQuadView.super.pos(vertexIndex, pos);
return this;
}
/**
* @apiNote The default implementation will be removed in the next breaking release.
*/
@Override
default QuadEmitter color(int vertexIndex, int color) {
MutableQuadView.super.color(vertexIndex, color);
return this;
}
@Override
default QuadEmitter normal(int vertexIndex, Vector3f vec) {
MutableQuadView.super.normal(vertexIndex, vec);
default QuadEmitter color(int c0, int c1, int c2, int c3) {
MutableQuadView.super.color(c0, c1, c2, c3);
return this;
}
/**
* @apiNote The default implementation will be removed in the next breaking release.
*/
@Override
default QuadEmitter uv(int vertexIndex, float u, float v) {
MutableQuadView.super.uv(vertexIndex, u, v);
return this;
}
@Override
default QuadEmitter uv(int vertexIndex, Vector2f uv) {
MutableQuadView.super.uv(vertexIndex, uv);
return this;
}
/**
* @apiNote The default implementation will be removed in the next breaking release.
*/
@Override
default QuadEmitter spriteBake(Sprite sprite, int bakeFlags) {
MutableQuadView.super.spriteBake(sprite, bakeFlags);
return this;
}
default QuadEmitter uvUnitSquare() {
uv(0, 0, 0);
uv(1, 0, 1);
uv(2, 1, 1);
uv(3, 1, 0);
return this;
}
@ -80,38 +104,43 @@ public interface QuadEmitter extends MutableQuadView {
return this;
}
@Override
QuadEmitter spriteColor(int vertexIndex, int spriteIndex, int color);
// TODO: uncomment right before next breaking release
// @Override
// QuadEmitter normal(int vertexIndex, float x, float y, float z);
@Override
default QuadEmitter spriteColor(int spriteIndex, int c0, int c1, int c2, int c3) {
MutableQuadView.super.spriteColor(spriteIndex, c0, c1, c2, c3);
default QuadEmitter normal(int vertexIndex, Vector3f normal) {
MutableQuadView.super.normal(vertexIndex, normal);
return this;
}
@Override
QuadEmitter sprite(int vertexIndex, int spriteIndex, float u, float v);
QuadEmitter cullFace(Direction face);
@Override
QuadEmitter nominalFace(Direction face);
@Override
QuadEmitter material(RenderMaterial material);
@Override
QuadEmitter colorIndex(int colorIndex);
@Override
QuadEmitter tag(int tag);
/**
* Set sprite atlas coordinates. Behavior for {@code spriteIndex > 0} is currently undefined.
*
* <p>Only use this function if you already have a {@link Vec2f}.
* Otherwise, see {@link QuadEmitter#sprite(int, int, float, float)}.
* @apiNote The default implementation will be removed in the next breaking release.
*/
default QuadEmitter sprite(int vertexIndex, int spriteIndex, Vec2f uv) {
return sprite(vertexIndex, spriteIndex, uv.x, uv.y);
}
default QuadEmitter spriteUnitSquare(int spriteIndex) {
sprite(0, spriteIndex, 0, 0);
sprite(1, spriteIndex, 0, 1);
sprite(2, spriteIndex, 1, 1);
sprite(3, spriteIndex, 1, 0);
@Override
default QuadEmitter fromVanilla(int[] quadData, int startIndex) {
MutableQuadView.super.fromVanilla(quadData, startIndex);
return this;
}
@Override
QuadEmitter spriteBake(int spriteIndex, Sprite sprite, int bakeFlags);
// TODO: uncomment right before next breaking release
// @Override
// QuadEmitter fromVanilla(BakedQuad quad, RenderMaterial material, @Nullable Direction cullFace);
/**
* Tolerance for determining if the depth parameter to {@link #square(Direction, float, float, float, float, float)}
@ -185,4 +214,55 @@ public interface QuadEmitter extends MutableQuadView {
* In both cases, current instance is reset to default values.
*/
QuadEmitter emit();
@Override
@Deprecated
default QuadEmitter spriteColor(int vertexIndex, int spriteIndex, int color) {
MutableQuadView.super.spriteColor(vertexIndex, spriteIndex, color);
return this;
}
@Override
@Deprecated
default QuadEmitter spriteColor(int spriteIndex, int c0, int c1, int c2, int c3) {
MutableQuadView.super.spriteColor(spriteIndex, c0, c1, c2, c3);
return this;
}
@Override
@Deprecated
default QuadEmitter sprite(int vertexIndex, int spriteIndex, float u, float v) {
MutableQuadView.super.sprite(vertexIndex, spriteIndex, u, v);
return this;
}
@Override
@Deprecated
default QuadEmitter sprite(int vertexIndex, int spriteIndex, Vec2f uv) {
MutableQuadView.super.sprite(vertexIndex, spriteIndex, uv);
return this;
}
@Override
@Deprecated
default QuadEmitter spriteBake(int spriteIndex, Sprite sprite, int bakeFlags) {
MutableQuadView.super.spriteBake(spriteIndex, sprite, bakeFlags);
return this;
}
/**
* @deprecated Use {@link #uvUnitSquare()} instead.
*/
@Deprecated
default QuadEmitter spriteUnitSquare(int spriteIndex) {
uvUnitSquare();
return this;
}
@Override
@Deprecated
default QuadEmitter fromVanilla(int[] quadData, int startIndex, boolean isItem) {
MutableQuadView.super.fromVanilla(quadData, startIndex, isItem);
return this;
}
}

View file

@ -18,6 +18,7 @@ package net.fabricmc.fabric.api.renderer.v1.mesh;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector2f;
import org.joml.Vector3f;
import net.minecraft.client.render.VertexFormats;
@ -42,98 +43,24 @@ public interface QuadView {
int VANILLA_QUAD_STRIDE = VANILLA_VERTEX_STRIDE * 4;
/**
* Reads baked vertex data and outputs standard baked quad
* vertex data in the given array and location.
*
* @param spriteIndex The sprite to be used for the quad.
* Behavior for values &gt; 0 is currently undefined.
*
* @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.
*
* @param isItem If true, will output vertex normals. Otherwise will output
* lightmaps, per Minecraft vertex formats for baked models.
* Retrieve geometric position, x coordinate.
*/
void toVanilla(int spriteIndex, int[] target, int targetIndex, boolean isItem);
float x(int vertexIndex);
/**
* Extracts all quad properties except material to the given {@link MutableQuadView} instance.
* Must be used before calling {link QuadEmitter#emit()} on the target instance.
* Meant for re-texturing, analysis and static transformation use cases.
* Retrieve geometric position, y coordinate.
*/
void copyTo(MutableQuadView target);
float y(int vertexIndex);
/**
* Retrieves the material serialized with the quad.
* Retrieve geometric position, z coordinate.
*/
RenderMaterial material();
float z(int vertexIndex);
/**
* Retrieves the quad color index serialized with the quad.
* Convenience: access x, y, z by index 0-2.
*/
int colorIndex();
/**
* Equivalent to {@link BakedQuad#getFace()}. This is the face used for vanilla lighting
* calculations and will be the block face to which the quad is most closely aligned. Always
* the same as cull face for quads that are on a block face, but never null.
*/
@NotNull
Direction lightFace();
/**
* If non-null, quad should not be rendered in-world if the
* opposite face of a neighbor block occludes it.
*
* @see MutableQuadView#cullFace(Direction)
*/
@Nullable Direction cullFace();
/**
* See {@link MutableQuadView#nominalFace(Direction)}.
*/
Direction nominalFace();
/**
* 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.
*
* <p>Not typically needed by models. Exposed to enable standard lighting
* utility functions for use by renderers.
*/
Vector3f faceNormal();
/**
* Generates a new BakedQuad instance with texture
* coordinates and colors from the given sprite.
*
* @param spriteIndex The sprite to be used for the quad.
* Behavior for {@code spriteIndex > 0} is currently undefined.
*
* @param sprite {@link MutableQuadView} does not serialize sprites
* so the sprite must be provided by the caller.
*
* @param isItem If true, will output vertex normals. Otherwise will output
* lightmaps, per Minecraft vertex formats for baked models.
*
* @return A new baked quad instance with the closest-available appearance
* supported by vanilla features. Will retain emissive light maps, for example,
* but the standard Minecraft renderer will not use them.
*/
default BakedQuad toBakedQuad(int spriteIndex, Sprite sprite, boolean isItem) {
int[] vertexData = new int[VANILLA_QUAD_STRIDE];
toVanilla(spriteIndex, vertexData, 0, isItem);
return new BakedQuad(vertexData, colorIndex(), lightFace(), sprite, true /* TODO:20w09a check me */);
}
/**
* Retrieves the integer tag encoded with this quad via {@link MutableQuadView#tag(int)}.
* Will return zero if no tag was set. For use by models.
*/
int tag();
float posByIndex(int vertexIndex, int coordinateIndex);
/**
* Pass a non-null target to avoid allocation - will be returned with values.
@ -142,24 +69,51 @@ public interface QuadView {
Vector3f copyPos(int vertexIndex, @Nullable Vector3f target);
/**
* Convenience: access x, y, z by index 0-2.
* Retrieve vertex color.
*
* @apiNote The default implementation will be removed in the next breaking release.
*/
float posByIndex(int vertexIndex, int coordinateIndex);
default int color(int vertexIndex) {
return spriteColor(vertexIndex, 0);
}
/**
* Geometric position, x coordinate.
* Retrieve horizontal texture coordinates.
*
* @apiNote The default implementation will be removed in the next breaking release.
*/
float x(int vertexIndex);
default float u(int vertexIndex) {
return spriteU(vertexIndex, 0);
}
/**
* Geometric position, y coordinate.
* Retrieve vertical texture coordinates.
*
* @apiNote The default implementation will be removed in the next breaking release.
*/
float y(int vertexIndex);
default float v(int vertexIndex) {
return spriteV(vertexIndex, 0);
}
/**
* Geometric position, z coordinate.
* Pass a non-null target to avoid allocation - will be returned with values.
* Otherwise returns a new instance.
*
* @apiNote The default implementation will be removed in the next breaking release.
*/
float z(int vertexIndex);
default Vector2f copyUv(int vertexIndex, @Nullable Vector2f target) {
if (target == null) {
target = new Vector2f();
}
target.set(u(vertexIndex), v(vertexIndex));
return target;
}
/**
* Minimum block brightness. Zero if not set.
*/
int lightmap(int vertexIndex);
/**
* If false, no vertex normal was provided.
@ -167,13 +121,6 @@ public interface QuadView {
*/
boolean hasNormal(int vertexIndex);
/**
* Pass a non-null target to avoid allocation - will be returned with values.
* Otherwise returns a new instance. Returns null if normal not present.
*/
@Nullable
Vector3f copyNormal(int vertexIndex, @Nullable Vector3f target);
/**
* Will return {@link Float#NaN} if normal not present.
*/
@ -190,22 +137,138 @@ public interface QuadView {
float normalZ(int vertexIndex);
/**
* Minimum block brightness. Zero if not set.
* Pass a non-null target to avoid allocation - will be returned with values.
* Otherwise returns a new instance. Returns null if normal not present.
*/
int lightmap(int vertexIndex);
@Nullable
Vector3f copyNormal(int vertexIndex, @Nullable Vector3f target);
/**
* Retrieve vertex color.
* If non-null, quad should not be rendered in-world if the
* opposite face of a neighbor block occludes it.
*
* @see MutableQuadView#cullFace(Direction)
*/
int spriteColor(int vertexIndex, int spriteIndex);
@Nullable
Direction cullFace();
/**
* Retrieve horizontal sprite atlas coordinates.
* Equivalent to {@link BakedQuad#getFace()}. This is the face used for vanilla lighting
* calculations and will be the block face to which the quad is most closely aligned. Always
* the same as cull face for quads that are on a block face, but never null.
*/
float spriteU(int vertexIndex, int spriteIndex);
@NotNull
Direction lightFace();
/**
* Retrieve vertical sprite atlas coordinates.
* See {@link MutableQuadView#nominalFace(Direction)}.
*/
float spriteV(int vertexIndex, int spriteIndex);
Direction nominalFace();
/**
* 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.
*
* <p>Not typically needed by models. Exposed to enable standard lighting
* utility functions for use by renderers.
*/
Vector3f faceNormal();
/**
* Retrieves the material serialized with the quad.
*/
RenderMaterial material();
/**
* Retrieves the quad color index serialized with the quad.
*/
int colorIndex();
/**
* Retrieves the integer tag encoded with this quad via {@link MutableQuadView#tag(int)}.
* Will return zero if no tag was set. For use by models.
*/
int tag();
/**
* Extracts all quad properties except material to the given {@link MutableQuadView} instance.
* Must be used before calling {link QuadEmitter#emit()} on the target instance.
* Meant for re-texturing, analysis and static transformation use cases.
*/
void copyTo(MutableQuadView target);
/**
* Reads baked vertex data and outputs standard {@link BakedQuad#getVertexData() baked quad vertex data}
* in the given array and location.
*
* @apiNote The default implementation will be removed in the next breaking release.
*
* @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.
*/
default void toVanilla(int[] target, int targetIndex) {
toVanilla(0, target, targetIndex, false);
}
/**
* Generates a new BakedQuad instance with texture
* coordinates and colors from the given sprite.
*
* @param sprite {@link MutableQuadView} does not serialize sprites
* so the sprite must be provided by the caller.
*
* @return A new baked quad instance with the closest-available appearance
* supported by vanilla features. Will retain emissive light maps, for example,
* but the standard Minecraft renderer will not use them.
*/
default BakedQuad toBakedQuad(Sprite sprite) {
int[] vertexData = new int[VANILLA_QUAD_STRIDE];
toVanilla(vertexData, 0);
// TODO material inspection: set shade as !disableDiffuse
// TODO material inspection: set color index to -1 if the material disables it
return new BakedQuad(vertexData, colorIndex(), lightFace(), sprite, true);
}
/**
* @deprecated Use {@link #color(int)} instead.
*/
@Deprecated
default int spriteColor(int vertexIndex, int spriteIndex) {
return color(vertexIndex);
}
/**
* @deprecated Use {@link #u(int)} instead.
*/
@Deprecated
default float spriteU(int vertexIndex, int spriteIndex) {
return u(vertexIndex);
}
/**
* @deprecated Use {@link #v(int)} instead.
*/
@Deprecated
default float spriteV(int vertexIndex, int spriteIndex) {
return v(vertexIndex);
}
/**
* @deprecated Use {@link #toVanilla(int[], int)} instead.
*/
@Deprecated
default void toVanilla(int spriteIndex, int[] target, int targetIndex, boolean isItem) {
toVanilla(target, targetIndex);
}
/**
* @deprecated Use {@link #toBakedQuad(Sprite)} instead.
*/
@Deprecated
default BakedQuad toBakedQuad(int spriteIndex, Sprite sprite, boolean isItem) {
return toBakedQuad(sprite);
}
}

View file

@ -84,12 +84,8 @@ public final class ModelHelper {
if (mesh != null) {
mesh.forEach(q -> {
final int limit = q.material().spriteDepth();
for (int l = 0; l < limit; l++) {
Direction face = q.cullFace();
builders[face == null ? 6 : face.getId()].add(q.toBakedQuad(l, finder.find(q, l), false));
}
Direction cullFace = q.cullFace();
builders[cullFace == null ? NULL_FACE_ID : cullFace.getId()].add(q.toBakedQuad(finder.find(q)));
});
}

View file

@ -16,6 +16,8 @@
package net.fabricmc.fabric.api.renderer.v1.model;
import org.jetbrains.annotations.ApiStatus;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.texture.SpriteAtlasTexture;
@ -29,8 +31,9 @@ import net.fabricmc.fabric.impl.renderer.SpriteFinderImpl;
* baked vertex coordinates. Main use is for {@link Mesh}-based models
* to generate vanilla quads on demand without tracking and retaining
* the sprites that were baked into the mesh. In other words, this class
* supplies the sprite parameter for {@link QuadView#toBakedQuad(int, Sprite, boolean)}.
* supplies the sprite parameter for {@link QuadView#toBakedQuad(Sprite)}.
*/
@ApiStatus.NonExtendable
public interface SpriteFinder {
/**
* Retrieves or creates the finder for the given atlas.
@ -51,13 +54,13 @@ public interface SpriteFinder {
* Note that all the above refers to u,v coordinates. Geometric vertex does not matter,
* except to the extent it was used to determine u,v.
*/
Sprite find(QuadView quad, int textureIndex);
Sprite find(QuadView quad);
/**
* Alternative to {@link #find(QuadView, int)} when vertex centroid is already
* known or unsuitable. Expects normalized (0-1) coordinates on the atlas texture,
* which should already be the case for u,v values in vanilla baked quads and in
* {@link QuadView} after calling {@link MutableQuadView#spriteBake(int, Sprite, int)}.
* {@link QuadView} after calling {@link MutableQuadView#spriteBake(Sprite, int)}.
*
* <p>Coordinates must be in the sprite interior for reliable results. Generally will
* be easier to use {@link #find(QuadView, int)} unless you know the vertex
@ -65,4 +68,12 @@ public interface SpriteFinder {
* faster if you already have the centroid or another appropriate value.
*/
Sprite find(float u, float v);
/**
* @deprecated Use {@link #find(QuadView)} instead.
*/
@Deprecated
default Sprite find(QuadView quad, int textureIndex) {
return find(quad);
}
}

View file

@ -46,6 +46,8 @@ public interface RenderContext {
* 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.
*
* @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,
@ -64,43 +66,6 @@ public interface RenderContext {
};
}
interface BakedModelConsumer extends Consumer<BakedModel> {
/**
* Render a baked model by processing its {@linkplain BakedModel#getQuads} using the rendered block state.
*
* <p>For block contexts, this will pass the block state being rendered to {@link BakedModel#getQuads}.
* For item contexts, this will pass a {@code null} block state to {@link BakedModel#getQuads}.
* {@link #accept(BakedModel, BlockState)} can be used instead to pass the block state explicitly.
*/
@Override
void accept(BakedModel model);
/**
* Render a baked model by processing its {@linkplain BakedModel#getQuads} with an explicit block state.
*
* <p>This overload allows passing the block state (or {@code null} to query the item quads).
* This is useful when a model is being wrapped, and expects a different
* block state than the one of the block being rendered.
*
* <p>For item render contexts, you can use this function if you want to render the model with a specific block state.
* Otherwise, use {@linkplain #accept(BakedModel)} the other overload} to render the usual item quads.
*/
void accept(BakedModel model, @Nullable BlockState state);
}
/**
* 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
default Consumer<BakedModel> fallbackConsumer() {
// This default implementation relies on implementors overriding bakedModelConsumer().
return bakedModelConsumer();
}
/**
* Returns a {@link QuadEmitter} instance that emits directly to the render buffer.
* It remains necessary to call {@link QuadEmitter#emit()} to output the quad.
@ -137,6 +102,43 @@ public interface RenderContext {
*/
void popTransform();
/**
* 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
default Consumer<BakedModel> fallbackConsumer() {
// This default implementation relies on implementors overriding bakedModelConsumer().
return bakedModelConsumer();
}
interface BakedModelConsumer extends Consumer<BakedModel> {
/**
* Render a baked model by processing its {@linkplain BakedModel#getQuads} using the rendered block state.
*
* <p>For block contexts, this will pass the block state being rendered to {@link BakedModel#getQuads}.
* For item contexts, this will pass a {@code null} block state to {@link BakedModel#getQuads}.
* {@link #accept(BakedModel, BlockState)} can be used instead to pass the block state explicitly.
*/
@Override
void accept(BakedModel model);
/**
* Render a baked model by processing its {@linkplain BakedModel#getQuads} with an explicit block state.
*
* <p>This overload allows passing the block state (or {@code null} to query the item quads).
* This is useful when a model is being wrapped, and expects a different
* block state than the one of the block being rendered.
*
* <p>For item render contexts, you can use this function if you want to render the model with a specific block state.
* Otherwise, use {@linkplain #accept(BakedModel)} the other overload} to render the usual item quads.
*/
void accept(BakedModel model, @Nullable BlockState state);
}
@FunctionalInterface
interface QuadTransform {
/**

View file

@ -53,13 +53,13 @@ public class SpriteFinderImpl implements SpriteFinder {
}
@Override
public Sprite find(QuadView quad, int textureIndex) {
public Sprite find(QuadView quad) {
float u = 0;
float v = 0;
for (int i = 0; i < 4; i++) {
u += quad.spriteU(i, textureIndex);
v += quad.spriteV(i, textureIndex);
u += quad.u(i);
v += quad.v(i);
}
return find(u * 0.25f, v * 0.25f);

View file

@ -58,9 +58,9 @@ final class FrameBakedModel implements BakedModel, FabricBakedModel {
this.frameSprite = frameSprite;
MaterialFinder finder = RendererAccess.INSTANCE.getRenderer().materialFinder();
this.translucentMaterial = finder.blendMode(0, BlendMode.TRANSLUCENT).find();
this.translucentMaterial = finder.blendMode(BlendMode.TRANSLUCENT).find();
finder.clear();
this.translucentEmissiveMaterial = finder.blendMode(0, BlendMode.TRANSLUCENT).emissive(0, true).find();
this.translucentEmissiveMaterial = finder.blendMode(BlendMode.TRANSLUCENT).emissive(true).find();
}
@Override
@ -173,11 +173,11 @@ final class FrameBakedModel implements BakedModel, FabricBakedModel {
// Change vertex colors to be partially transparent
for (int vertex = 0; vertex < 4; ++vertex) {
int color = quad.spriteColor(vertex, 0);
int color = quad.color(vertex);
int alpha = (color >> 24) & 0xFF;
alpha = alpha * 3 / 4;
color = (color & 0xFFFFFF) | (alpha << 24);
quad.spriteColor(vertex, 0, color);
quad.color(vertex, color);
}
// Return true because we want the quad to be rendered

View file

@ -70,44 +70,44 @@ final class FrameUnbakedModel implements UnbakedModel {
for (Direction direction : Direction.values()) {
// Draw outer frame
emitter.square(direction, 0.0F, 0.9F, 0.9F, 1.0F, 0.0F)
.spriteBake(0, frameSprite, MutableQuadView.BAKE_LOCK_UV)
.spriteColor(0, -1, -1, -1, -1)
.spriteBake(frameSprite, MutableQuadView.BAKE_LOCK_UV)
.color(-1, -1, -1, -1)
.emit();
emitter.square(direction, 0.0F, 0.0F, 0.1F, 0.9F, 0.0F)
.spriteBake(0, frameSprite, MutableQuadView.BAKE_LOCK_UV)
.spriteColor(0, -1, -1, -1, -1)
.spriteBake(frameSprite, MutableQuadView.BAKE_LOCK_UV)
.color(-1, -1, -1, -1)
.emit();
emitter.square(direction, 0.9F, 0.1F, 1.0F, 1.0F, 0.0F)
.spriteBake(0, frameSprite, MutableQuadView.BAKE_LOCK_UV)
.spriteColor(0, -1, -1, -1, -1)
.spriteBake(frameSprite, MutableQuadView.BAKE_LOCK_UV)
.color(-1, -1, -1, -1)
.emit();
emitter.square(direction, 0.1F, 0.0F, 1.0F, 0.1F, 0.0F)
.spriteBake(0, frameSprite, MutableQuadView.BAKE_LOCK_UV)
.spriteColor(0, -1, -1, -1, -1)
.spriteBake(frameSprite, MutableQuadView.BAKE_LOCK_UV)
.color(-1, -1, -1, -1)
.emit();
// Draw inner frame - inset by 0.9 so the frame looks like an actual mesh
emitter.square(direction, 0.0F, 0.9F, 0.9F, 1.0F, 0.9F)
.spriteBake(0, frameSprite, MutableQuadView.BAKE_LOCK_UV)
.spriteColor(0, -1, -1, -1, -1)
.spriteBake(frameSprite, MutableQuadView.BAKE_LOCK_UV)
.color(-1, -1, -1, -1)
.emit();
emitter.square(direction, 0.0F, 0.0F, 0.1F, 0.9F, 0.9F)
.spriteBake(0, frameSprite, MutableQuadView.BAKE_LOCK_UV)
.spriteColor(0, -1, -1, -1, -1)
.spriteBake(frameSprite, MutableQuadView.BAKE_LOCK_UV)
.color(-1, -1, -1, -1)
.emit();
emitter.square(direction, 0.9F, 0.1F, 1.0F, 1.0F, 0.9F)
.spriteBake(0, frameSprite, MutableQuadView.BAKE_LOCK_UV)
.spriteColor(0, -1, -1, -1, -1)
.spriteBake(frameSprite, MutableQuadView.BAKE_LOCK_UV)
.color(-1, -1, -1, -1)
.emit();
emitter.square(direction, 0.1F, 0.0F, 1.0F, 0.1F, 0.9F)
.spriteBake(0, frameSprite, MutableQuadView.BAKE_LOCK_UV)
.spriteColor(0, -1, -1, -1, -1)
.spriteBake(frameSprite, MutableQuadView.BAKE_LOCK_UV)
.color(-1, -1, -1, -1)
.emit();
}

View file

@ -89,8 +89,8 @@ public class PillarBakedModel implements BakedModel, FabricBakedModel {
}
emitter.square(side, 0, 0, 1, 1, 0);
emitter.spriteBake(0, sprites[texture.ordinal()], MutableQuadView.BAKE_LOCK_UV);
emitter.spriteColor(0, -1, -1, -1, -1);
emitter.spriteBake(sprites[texture.ordinal()], MutableQuadView.BAKE_LOCK_UV);
emitter.color(-1, -1, -1, -1);
emitter.emit();
}
}

View file

@ -16,13 +16,14 @@
package net.fabricmc.fabric.impl.client.indigo.renderer;
import com.google.common.base.Preconditions;
import java.util.Objects;
import net.minecraft.util.math.MathHelper;
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.util.TriState;
/**
* Default implementation of the standard render materials.
@ -32,13 +33,34 @@ import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
*/
public abstract class RenderMaterialImpl {
private static final BlendMode[] BLEND_MODES = BlendMode.values();
private static final int BLEND_MODE_COUNT = BLEND_MODES.length;
private static final TriState[] TRI_STATES = TriState.values();
private static final int TRI_STATE_COUNT = TRI_STATES.length;
private static final int BLEND_MODE_MASK = MathHelper.smallestEncompassingPowerOfTwo(BlendMode.values().length) - 1;
private static final int COLOR_DISABLE_FLAG = BLEND_MODE_MASK + 1;
private static final int EMISSIVE_FLAG = COLOR_DISABLE_FLAG << 1;
private static final int DIFFUSE_FLAG = EMISSIVE_FLAG << 1;
private static final int AO_FLAG = DIFFUSE_FLAG << 1;
public static final int VALUE_COUNT = (AO_FLAG << 1);
protected static final int BLEND_MODE_BIT_LENGTH = MathHelper.ceilLog2(BLEND_MODE_COUNT);
protected static final int COLOR_DISABLE_BIT_LENGTH = 1;
protected static final int EMISSIVE_BIT_LENGTH = 1;
protected static final int DIFFUSE_BIT_LENGTH = 1;
protected static final int AO_BIT_LENGTH = MathHelper.ceilLog2(TRI_STATE_COUNT);
protected static final int BLEND_MODE_BIT_OFFSET = 0;
protected static final int COLOR_DISABLE_BIT_OFFSET = BLEND_MODE_BIT_OFFSET + BLEND_MODE_BIT_LENGTH;
protected static final int EMISSIVE_BIT_OFFSET = COLOR_DISABLE_BIT_OFFSET + COLOR_DISABLE_BIT_LENGTH;
protected static final int DIFFUSE_BIT_OFFSET = EMISSIVE_BIT_OFFSET + EMISSIVE_BIT_LENGTH;
protected static final int AO_BIT_OFFSET = DIFFUSE_BIT_OFFSET + DIFFUSE_BIT_LENGTH;
protected static final int TOTAL_BIT_LENGTH = AO_BIT_OFFSET + AO_BIT_LENGTH;
protected static final int BLEND_MODE_MASK = bitMask(BLEND_MODE_BIT_LENGTH, BLEND_MODE_BIT_OFFSET);
protected static final int COLOR_DISABLE_FLAG = bitMask(COLOR_DISABLE_BIT_LENGTH, COLOR_DISABLE_BIT_OFFSET);
protected static final int EMISSIVE_FLAG = bitMask(EMISSIVE_BIT_LENGTH, EMISSIVE_BIT_OFFSET);
protected static final int DIFFUSE_FLAG = bitMask(DIFFUSE_BIT_LENGTH, DIFFUSE_BIT_OFFSET);
protected static final int AO_MASK = bitMask(AO_BIT_LENGTH, AO_BIT_OFFSET);
public static final int VALUE_COUNT = 1 << TOTAL_BIT_LENGTH;
protected static int bitMask(int bitLength, int bitOffset) {
return ((1 << bitLength) - 1) << bitOffset;
}
private static final Value[] VALUES = new Value[VALUE_COUNT];
@ -52,8 +74,8 @@ public abstract class RenderMaterialImpl {
return VALUES[index];
}
public static Value setDisableDiffuse(Value material, int textureIndex, boolean disable) {
if (material.disableDiffuse(textureIndex) != disable) {
public static Value setDisableDiffuse(Value material, boolean disable) {
if (material.disableDiffuse() != disable) {
return byIndex(disable ? (material.bits | DIFFUSE_FLAG) : (material.bits & ~DIFFUSE_FLAG));
}
@ -62,33 +84,45 @@ public abstract class RenderMaterialImpl {
protected int bits;
public BlendMode blendMode(int textureIndex) {
return BLEND_MODES[bits & BLEND_MODE_MASK];
protected RenderMaterialImpl(int bits) {
this.bits = bits;
}
public boolean disableColorIndex(int textureIndex) {
public BlendMode blendMode() {
int ordinal = (bits & BLEND_MODE_MASK) >> BLEND_MODE_BIT_OFFSET;
if (ordinal >= BLEND_MODE_COUNT) {
return BlendMode.DEFAULT;
}
return BLEND_MODES[ordinal];
}
public boolean disableColorIndex() {
return (bits & COLOR_DISABLE_FLAG) != 0;
}
public int spriteDepth() {
return 1;
}
public boolean emissive(int textureIndex) {
public boolean emissive() {
return (bits & EMISSIVE_FLAG) != 0;
}
public boolean disableDiffuse(int textureIndex) {
public boolean disableDiffuse() {
return (bits & DIFFUSE_FLAG) != 0;
}
public boolean disableAo(int textureIndex) {
return (bits & AO_FLAG) != 0;
public TriState ambientOcclusion() {
int ordinal = (bits & AO_MASK) >> AO_BIT_OFFSET;
if (ordinal >= TRI_STATE_COUNT) {
return TriState.DEFAULT;
}
return TRI_STATES[ordinal];
}
public static class Value extends RenderMaterialImpl implements RenderMaterial {
private Value(int bits) {
this.bits = bits;
super(bits);
}
public int index() {
@ -97,56 +131,61 @@ public abstract class RenderMaterialImpl {
}
public static class Finder extends RenderMaterialImpl implements MaterialFinder {
@Override
public RenderMaterial find() {
return VALUES[bits];
private static int defaultBits = 0;
static {
Finder finder = new Finder();
finder.ambientOcclusion(TriState.DEFAULT);
defaultBits = finder.bits;
}
public Finder() {
super(defaultBits);
}
@Override
public MaterialFinder clear() {
bits = 0;
public MaterialFinder blendMode(BlendMode blendMode) {
Objects.requireNonNull(blendMode, "BlendMode may not be null");
bits = (bits & ~BLEND_MODE_MASK) | (blendMode.ordinal() << BLEND_MODE_BIT_OFFSET);
return this;
}
@Override
public MaterialFinder blendMode(int textureIndex, BlendMode blendMode) {
if (blendMode == null) {
blendMode = BlendMode.DEFAULT;
}
bits = (bits & ~BLEND_MODE_MASK) | blendMode.ordinal();
return this;
}
@Override
public MaterialFinder disableColorIndex(int textureIndex, boolean disable) {
public MaterialFinder disableColorIndex(boolean disable) {
bits = disable ? (bits | COLOR_DISABLE_FLAG) : (bits & ~COLOR_DISABLE_FLAG);
return this;
}
@Override
public MaterialFinder spriteDepth(int depth) {
Preconditions.checkArgument(depth == 1, "Unsupported sprite depth: %s", depth);
return this;
}
@Override
public MaterialFinder emissive(int textureIndex, boolean isEmissive) {
public MaterialFinder emissive(boolean isEmissive) {
bits = isEmissive ? (bits | EMISSIVE_FLAG) : (bits & ~EMISSIVE_FLAG);
return this;
}
@Override
public MaterialFinder disableDiffuse(int textureIndex, boolean disable) {
public MaterialFinder disableDiffuse(boolean disable) {
bits = disable ? (bits | DIFFUSE_FLAG) : (bits & ~DIFFUSE_FLAG);
return this;
}
@Override
public MaterialFinder disableAo(int textureIndex, boolean disable) {
bits = disable ? (bits | AO_FLAG) : (bits & ~AO_FLAG);
public MaterialFinder ambientOcclusion(TriState mode) {
Objects.requireNonNull(mode, "ambient occlusion TriState may not be null");
bits = (bits & ~AO_MASK) | (mode.ordinal() << AO_BIT_OFFSET);
return this;
}
@Override
public MaterialFinder clear() {
bits = defaultBits;
return this;
}
@Override
public RenderMaterial find() {
return VALUES[bits];
}
}
}

View file

@ -170,7 +170,7 @@ public abstract class AoCalculator {
private void calcVanilla(MutableQuadViewImpl quad, float[] aoDest, int[] lightDest) {
vanillaAoControlBits.clear();
final Direction lightFace = quad.lightFace();
quad.toVanilla(0, vertexData, 0, false);
quad.toVanilla(vertexData, 0);
VanillaAoHelper.updateShape(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, vertexData, lightFace, vanillaAoData, vanillaAoControlBits);
vanillaCalc.apply(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, lightFace, vanillaAoData, vanillaAoControlBits, quad.hasShade());

View file

@ -23,7 +23,7 @@ import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
/**
* Handles most texture-baking use cases for model loaders and model libraries
* via {@link #bakeSprite(MutableQuadView, int, Sprite, int)}. Also used by the API
* via {@link #bakeSprite(MutableQuadView, Sprite, int)}. Also used by the API
* itself to implement automatic block-breaking models for enhanced models.
*/
public class TextureHelper {
@ -35,13 +35,13 @@ public class TextureHelper {
* Bakes textures in the provided vertex data, handling UV locking,
* rotation, interpolation, etc. Textures must not be already baked.
*/
public static void bakeSprite(MutableQuadView quad, int spriteIndex, Sprite sprite, int bakeFlags) {
public static void bakeSprite(MutableQuadView quad, Sprite sprite, int bakeFlags) {
if (quad.nominalFace() != null && (MutableQuadView.BAKE_LOCK_UV & bakeFlags) != 0) {
// Assigns normalized UV coordinates based on vertex positions
applyModifier(quad, spriteIndex, UVLOCKERS[quad.nominalFace().getId()]);
applyModifier(quad, UVLOCKERS[quad.nominalFace().getId()]);
} else if ((MutableQuadView.BAKE_NORMALIZED & bakeFlags) == 0) { // flag is NOT set, UVs are assumed to not be normalized yet as is the default, normalize through dividing by 16
// Scales from 0-16 to 0-1
applyModifier(quad, spriteIndex, (q, i, t) -> q.sprite(i, t, q.spriteU(i, t) * NORMALIZER, q.spriteV(i, t) * NORMALIZER));
applyModifier(quad, (q, i) -> q.uv(i, q.u(i) * NORMALIZER, q.v(i) * NORMALIZER));
}
final int rotation = bakeFlags & 3;
@ -49,63 +49,63 @@ public class TextureHelper {
if (rotation != 0) {
// Rotates texture around the center of sprite.
// Assumes normalized coordinates.
applyModifier(quad, spriteIndex, ROTATIONS[rotation]);
applyModifier(quad, ROTATIONS[rotation]);
}
if ((MutableQuadView.BAKE_FLIP_U & bakeFlags) != 0) {
// Inverts U coordinates. Assumes normalized (0-1) values.
applyModifier(quad, spriteIndex, (q, i, t) -> q.sprite(i, t, 1 - q.spriteU(i, t), q.spriteV(i, t)));
applyModifier(quad, (q, i) -> q.uv(i, 1 - q.u(i), q.v(i)));
}
if ((MutableQuadView.BAKE_FLIP_V & bakeFlags) != 0) {
// Inverts V coordinates. Assumes normalized (0-1) values.
applyModifier(quad, spriteIndex, (q, i, t) -> q.sprite(i, t, q.spriteU(i, t), 1 - q.spriteV(i, t)));
applyModifier(quad, (q, i) -> q.uv(i, q.u(i), 1 - q.v(i)));
}
interpolate(quad, spriteIndex, sprite);
interpolate(quad, sprite);
}
/**
* Faster than sprite method. Sprite computes span and normalizes inputs each call,
* so we'd have to denormalize before we called, only to have the sprite renormalize immediately.
*/
private static void interpolate(MutableQuadView q, int spriteIndex, Sprite sprite) {
private static void interpolate(MutableQuadView q, Sprite sprite) {
final float uMin = sprite.getMinU();
final float uSpan = sprite.getMaxU() - uMin;
final float vMin = sprite.getMinV();
final float vSpan = sprite.getMaxV() - vMin;
for (int i = 0; i < 4; i++) {
q.sprite(i, spriteIndex, uMin + q.spriteU(i, spriteIndex) * uSpan, vMin + q.spriteV(i, spriteIndex) * vSpan);
q.uv(i, uMin + q.u(i) * uSpan, vMin + q.v(i) * vSpan);
}
}
@FunctionalInterface
private interface VertexModifier {
void apply(MutableQuadView quad, int vertexIndex, int spriteIndex);
void apply(MutableQuadView quad, int vertexIndex);
}
private static void applyModifier(MutableQuadView quad, int spriteIndex, VertexModifier modifier) {
private static void applyModifier(MutableQuadView quad, VertexModifier modifier) {
for (int i = 0; i < 4; i++) {
modifier.apply(quad, i, spriteIndex);
modifier.apply(quad, i);
}
}
private static final VertexModifier[] ROTATIONS = new VertexModifier[] {
null,
(q, i, t) -> q.sprite(i, t, q.spriteV(i, t), 1 - q.spriteU(i, t)), //90
(q, i, t) -> q.sprite(i, t, 1 - q.spriteU(i, t), 1 - q.spriteV(i, t)), //180
(q, i, t) -> q.sprite(i, t, 1 - q.spriteV(i, t), q.spriteU(i, t)) // 270
(q, i) -> q.uv(i, q.v(i), 1 - q.u(i)), //90
(q, i) -> q.uv(i, 1 - q.u(i), 1 - q.v(i)), //180
(q, i) -> q.uv(i, 1 - q.v(i), q.u(i)) // 270
};
private static final VertexModifier[] UVLOCKERS = new VertexModifier[6];
static {
UVLOCKERS[Direction.EAST.getId()] = (q, i, t) -> q.sprite(i, t, 1 - q.z(i), 1 - q.y(i));
UVLOCKERS[Direction.WEST.getId()] = (q, i, t) -> q.sprite(i, t, q.z(i), 1 - q.y(i));
UVLOCKERS[Direction.NORTH.getId()] = (q, i, t) -> q.sprite(i, t, 1 - q.x(i), 1 - q.y(i));
UVLOCKERS[Direction.SOUTH.getId()] = (q, i, t) -> q.sprite(i, t, q.x(i), 1 - q.y(i));
UVLOCKERS[Direction.DOWN.getId()] = (q, i, t) -> q.sprite(i, t, q.x(i), 1 - q.z(i));
UVLOCKERS[Direction.UP.getId()] = (q, i, t) -> q.sprite(i, t, q.x(i), q.z(i));
UVLOCKERS[Direction.EAST.getId()] = (q, i) -> q.uv(i, 1 - q.z(i), 1 - q.y(i));
UVLOCKERS[Direction.WEST.getId()] = (q, i) -> q.uv(i, q.z(i), 1 - q.y(i));
UVLOCKERS[Direction.NORTH.getId()] = (q, i) -> q.uv(i, 1 - q.x(i), 1 - q.y(i));
UVLOCKERS[Direction.SOUTH.getId()] = (q, i) -> q.uv(i, q.x(i), 1 - q.y(i));
UVLOCKERS[Direction.DOWN.getId()] = (q, i) -> q.uv(i, q.x(i), 1 - q.z(i));
UVLOCKERS[Direction.UP.getId()] = (q, i) -> q.uv(i, q.x(i), q.z(i));
}
}

View file

@ -29,7 +29,7 @@ import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingForma
import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat.VERTEX_U;
import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat.VERTEX_X;
import com.google.common.base.Preconditions;
import org.jetbrains.annotations.Nullable;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.texture.Sprite;
@ -65,69 +65,6 @@ public abstract class MutableQuadViewImpl extends QuadViewImpl implements QuadEm
material(IndigoRenderer.MATERIAL_STANDARD);
}
@Override
public final MutableQuadViewImpl material(RenderMaterial material) {
if (material == null) {
material = IndigoRenderer.MATERIAL_STANDARD;
}
data[baseIndex + HEADER_BITS] = EncodingFormat.material(data[baseIndex + HEADER_BITS], (Value) material);
return this;
}
@Override
public final MutableQuadViewImpl cullFace(Direction face) {
data[baseIndex + HEADER_BITS] = EncodingFormat.cullFace(data[baseIndex + HEADER_BITS], face);
nominalFace(face);
return this;
}
@Override
public final MutableQuadViewImpl nominalFace(Direction face) {
nominalFace = face;
return this;
}
@Override
public final MutableQuadViewImpl colorIndex(int colorIndex) {
data[baseIndex + HEADER_COLOR_INDEX] = colorIndex;
return this;
}
@Override
public final MutableQuadViewImpl tag(int tag) {
data[baseIndex + HEADER_TAG] = tag;
return this;
}
/**
* @deprecated will be removed in 1.17 cycle - see docs in interface
*/
@Deprecated
@Override
public final MutableQuadViewImpl fromVanilla(int[] quadData, int startIndex, boolean isItem) {
System.arraycopy(quadData, startIndex, data, baseIndex + HEADER_STRIDE, QUAD_STRIDE);
isGeometryInvalid = true;
return this;
}
@Override
public final MutableQuadViewImpl fromVanilla(BakedQuad quad, RenderMaterial material, Direction cullFace) {
System.arraycopy(quad.getVertexData(), 0, data, baseIndex + HEADER_STRIDE, QUAD_STRIDE);
data[baseIndex + HEADER_BITS] = EncodingFormat.cullFace(0, cullFace);
nominalFace(quad.getFace());
colorIndex(quad.getColorIndex());
if (!quad.hasShade()) {
material = RenderMaterialImpl.setDisableDiffuse((Value) material, 0, true);
}
material(material);
tag(0);
isGeometryInvalid = true;
return this;
}
@Override
public MutableQuadViewImpl pos(int vertexIndex, float x, float y, float z) {
final int index = baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_X;
@ -138,6 +75,32 @@ public abstract class MutableQuadViewImpl extends QuadViewImpl implements QuadEm
return this;
}
@Override
public MutableQuadViewImpl color(int vertexIndex, int color) {
data[baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_COLOR] = color;
return this;
}
@Override
public MutableQuadViewImpl uv(int vertexIndex, float u, float v) {
final int i = baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_U;
data[i] = Float.floatToRawIntBits(u);
data[i + 1] = Float.floatToRawIntBits(v);
return this;
}
@Override
public MutableQuadViewImpl spriteBake(Sprite sprite, int bakeFlags) {
TextureHelper.bakeSprite(this, sprite, bakeFlags);
return this;
}
@Override
public MutableQuadViewImpl lightmap(int vertexIndex, int lightmap) {
data[baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_LIGHTMAP] = lightmap;
return this;
}
protected void normalFlags(int flags) {
data[baseIndex + HEADER_BITS] = EncodingFormat.normalFlags(data[baseIndex + HEADER_BITS], flags);
}
@ -169,34 +132,60 @@ public abstract class MutableQuadViewImpl extends QuadViewImpl implements QuadEm
}
@Override
public MutableQuadViewImpl lightmap(int vertexIndex, int lightmap) {
data[baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_LIGHTMAP] = lightmap;
public final MutableQuadViewImpl cullFace(@Nullable Direction face) {
data[baseIndex + HEADER_BITS] = EncodingFormat.cullFace(data[baseIndex + HEADER_BITS], face);
nominalFace(face);
return this;
}
@Override
public MutableQuadViewImpl spriteColor(int vertexIndex, int spriteIndex, int color) {
Preconditions.checkArgument(spriteIndex == 0, "Unsupported sprite index: %s", spriteIndex);
data[baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_COLOR] = color;
public final MutableQuadViewImpl nominalFace(@Nullable Direction face) {
nominalFace = face;
return this;
}
@Override
public MutableQuadViewImpl sprite(int vertexIndex, int spriteIndex, float u, float v) {
Preconditions.checkArgument(spriteIndex == 0, "Unsupported sprite index: %s", spriteIndex);
public final MutableQuadViewImpl material(RenderMaterial material) {
if (material == null) {
material = IndigoRenderer.MATERIAL_STANDARD;
}
final int i = baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_U;
data[i] = Float.floatToRawIntBits(u);
data[i + 1] = Float.floatToRawIntBits(v);
data[baseIndex + HEADER_BITS] = EncodingFormat.material(data[baseIndex + HEADER_BITS], (Value) material);
return this;
}
@Override
public MutableQuadViewImpl spriteBake(int spriteIndex, Sprite sprite, int bakeFlags) {
Preconditions.checkArgument(spriteIndex == 0, "Unsupported sprite index: %s", spriteIndex);
public final MutableQuadViewImpl colorIndex(int colorIndex) {
data[baseIndex + HEADER_COLOR_INDEX] = colorIndex;
return this;
}
TextureHelper.bakeSprite(this, spriteIndex, sprite, bakeFlags);
@Override
public final MutableQuadViewImpl tag(int tag) {
data[baseIndex + HEADER_TAG] = tag;
return this;
}
@Override
public final MutableQuadViewImpl fromVanilla(int[] quadData, int startIndex) {
System.arraycopy(quadData, startIndex, data, baseIndex + HEADER_STRIDE, QUAD_STRIDE);
isGeometryInvalid = true;
return this;
}
@Override
public final MutableQuadViewImpl fromVanilla(BakedQuad quad, RenderMaterial material, @Nullable Direction cullFace) {
fromVanilla(quad.getVertexData(), 0);
data[baseIndex + HEADER_BITS] = EncodingFormat.cullFace(0, cullFace);
nominalFace(quad.getFace());
colorIndex(quad.getColorIndex());
if (!quad.hasShade()) {
material = RenderMaterialImpl.setDisableDiffuse((Value) material, true);
}
material(material);
tag(0);
return this;
}
}

View file

@ -30,9 +30,13 @@ import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingForma
import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat.VERTEX_Y;
import static net.fabricmc.fabric.impl.client.indigo.renderer.mesh.EncodingFormat.VERTEX_Z;
import com.google.common.base.Preconditions;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector2f;
import org.joml.Vector3f;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.texture.Sprite;
import net.minecraft.util.math.Direction;
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
@ -114,76 +118,8 @@ public class QuadViewImpl implements QuadView {
}
}
@Override
public final void toVanilla(int textureIndex, int[] target, int targetIndex, boolean isItem) {
System.arraycopy(data, baseIndex + VERTEX_X, target, targetIndex, QUAD_STRIDE);
}
@Override
public final RenderMaterialImpl.Value material() {
return EncodingFormat.material(data[baseIndex + HEADER_BITS]);
}
@Override
public final int colorIndex() {
return data[baseIndex + HEADER_COLOR_INDEX];
}
@Override
public final int tag() {
return data[baseIndex + HEADER_TAG];
}
@Override
public final Direction lightFace() {
computeGeometry();
return EncodingFormat.lightFace(data[baseIndex + HEADER_BITS]);
}
@Override
public final Direction cullFace() {
return EncodingFormat.cullFace(data[baseIndex + HEADER_BITS]);
}
@Override
public final Direction nominalFace() {
return nominalFace;
}
@Override
public final Vector3f faceNormal() {
computeGeometry();
return faceNormal;
}
@Override
public void copyTo(MutableQuadView target) {
computeGeometry();
final MutableQuadViewImpl quad = (MutableQuadViewImpl) target;
// copy everything except the material
RenderMaterial material = quad.material();
System.arraycopy(data, baseIndex, quad.data, quad.baseIndex, EncodingFormat.TOTAL_STRIDE);
quad.material(material);
quad.faceNormal.set(faceNormal.x(), faceNormal.y(), faceNormal.z());
quad.nominalFace = this.nominalFace;
quad.isGeometryInvalid = false;
}
@Override
public Vector3f copyPos(int vertexIndex, Vector3f target) {
if (target == null) {
target = new Vector3f();
}
final int index = baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_X;
target.set(Float.intBitsToFloat(data[index]), Float.intBitsToFloat(data[index + 1]), Float.intBitsToFloat(data[index + 2]));
return target;
}
@Override
public float posByIndex(int vertexIndex, int coordinateIndex) {
return Float.intBitsToFloat(data[baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_X + coordinateIndex]);
public boolean hasShade() {
return !material().disableDiffuse();
}
@Override
@ -201,6 +137,53 @@ public class QuadViewImpl implements QuadView {
return Float.intBitsToFloat(data[baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_Z]);
}
@Override
public float posByIndex(int vertexIndex, int coordinateIndex) {
return Float.intBitsToFloat(data[baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_X + coordinateIndex]);
}
@Override
public Vector3f copyPos(int vertexIndex, @Nullable Vector3f target) {
if (target == null) {
target = new Vector3f();
}
final int index = baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_X;
target.set(Float.intBitsToFloat(data[index]), Float.intBitsToFloat(data[index + 1]), Float.intBitsToFloat(data[index + 2]));
return target;
}
@Override
public int color(int vertexIndex) {
return data[baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_COLOR];
}
@Override
public float u(int vertexIndex) {
return Float.intBitsToFloat(data[baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_U]);
}
@Override
public float v(int vertexIndex) {
return Float.intBitsToFloat(data[baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_V]);
}
@Override
public Vector2f copyUv(int vertexIndex, @Nullable Vector2f target) {
if (target == null) {
target = new Vector2f();
}
final int index = baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_U;
target.set(Float.intBitsToFloat(data[index]), Float.intBitsToFloat(data[index + 1]));
return target;
}
@Override
public int lightmap(int vertexIndex) {
return data[baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_LIGHTMAP];
}
@Override
public boolean hasNormal(int vertexIndex) {
return (normalFlags() & (1 << vertexIndex)) != 0;
@ -210,21 +193,6 @@ public class QuadViewImpl implements QuadView {
return baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_NORMAL;
}
@Override
public Vector3f copyNormal(int vertexIndex, Vector3f target) {
if (hasNormal(vertexIndex)) {
if (target == null) {
target = new Vector3f();
}
final int normal = data[normalIndex(vertexIndex)];
target.set(NormalHelper.getPackedNormalComponent(normal, 0), NormalHelper.getPackedNormalComponent(normal, 1), NormalHelper.getPackedNormalComponent(normal, 2));
return target;
} else {
return null;
}
}
@Override
public float normalX(int vertexIndex) {
return hasNormal(vertexIndex) ? NormalHelper.getPackedNormalComponent(data[normalIndex(vertexIndex)], 0) : Float.NaN;
@ -241,32 +209,88 @@ public class QuadViewImpl implements QuadView {
}
@Override
public int lightmap(int vertexIndex) {
return data[baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_LIGHTMAP];
@Nullable
public Vector3f copyNormal(int vertexIndex, @Nullable Vector3f target) {
if (hasNormal(vertexIndex)) {
if (target == null) {
target = new Vector3f();
}
final int normal = data[normalIndex(vertexIndex)];
target.set(NormalHelper.getPackedNormalComponent(normal, 0), NormalHelper.getPackedNormalComponent(normal, 1), NormalHelper.getPackedNormalComponent(normal, 2));
return target;
} else {
return null;
}
}
@Override
public int spriteColor(int vertexIndex, int spriteIndex) {
Preconditions.checkArgument(spriteIndex == 0, "Unsupported sprite index: %s", spriteIndex);
return data[baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_COLOR];
@Nullable
public final Direction cullFace() {
return EncodingFormat.cullFace(data[baseIndex + HEADER_BITS]);
}
@Override
public float spriteU(int vertexIndex, int spriteIndex) {
Preconditions.checkArgument(spriteIndex == 0, "Unsupported sprite index: %s", spriteIndex);
return Float.intBitsToFloat(data[baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_U]);
@NotNull
public final Direction lightFace() {
computeGeometry();
return EncodingFormat.lightFace(data[baseIndex + HEADER_BITS]);
}
@Override
public float spriteV(int vertexIndex, int spriteIndex) {
Preconditions.checkArgument(spriteIndex == 0, "Unsupported sprite index: %s", spriteIndex);
return Float.intBitsToFloat(data[baseIndex + vertexIndex * VERTEX_STRIDE + VERTEX_V]);
public final Direction nominalFace() {
return nominalFace;
}
public boolean hasShade() {
return !material().disableDiffuse(0);
@Override
public final Vector3f faceNormal() {
computeGeometry();
return faceNormal;
}
@Override
public final RenderMaterialImpl.Value material() {
return EncodingFormat.material(data[baseIndex + HEADER_BITS]);
}
@Override
public final int colorIndex() {
return data[baseIndex + HEADER_COLOR_INDEX];
}
@Override
public final int tag() {
return data[baseIndex + HEADER_TAG];
}
@Override
public void copyTo(MutableQuadView target) {
computeGeometry();
final MutableQuadViewImpl quad = (MutableQuadViewImpl) target;
// copy everything except the material
RenderMaterial material = quad.material();
System.arraycopy(data, baseIndex, quad.data, quad.baseIndex, EncodingFormat.TOTAL_STRIDE);
quad.material(material);
quad.faceNormal.set(faceNormal);
quad.nominalFace = this.nominalFace;
quad.isGeometryInvalid = false;
}
@Override
public final void toVanilla(int[] target, int targetIndex) {
System.arraycopy(data, baseIndex + VERTEX_X, target, targetIndex, QUAD_STRIDE);
}
// TODO material inspection: remove
@Override
public final BakedQuad toBakedQuad(Sprite sprite) {
int[] vertexData = new int[VANILLA_QUAD_STRIDE];
toVanilla(vertexData, 0);
// Mimic material properties to the largest possible extent
int outputColorIndex = material().disableColorIndex() ? -1 : colorIndex();
boolean outputShade = !material().disableDiffuse();
return new BakedQuad(vertexData, outputColorIndex, lightFace(), sprite, outputShade);
}
}

View file

@ -34,6 +34,7 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
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.RenderMaterialImpl;
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator;
import net.fabricmc.fabric.impl.client.indigo.renderer.helper.ColorHelper;
@ -72,29 +73,30 @@ public abstract class AbstractQuadRenderer {
return;
}
tessellateQuad(quad, 0, isVanilla);
tessellateQuad(quad, isVanilla);
}
/**
* Determines color index and render layer, then routes to appropriate
* tessellate routine based on material properties.
*/
private void tessellateQuad(MutableQuadViewImpl quad, int textureIndex, boolean isVanilla) {
private void tessellateQuad(MutableQuadViewImpl quad, boolean isVanilla) {
final RenderMaterialImpl.Value mat = quad.material();
final int colorIndex = mat.disableColorIndex(textureIndex) ? -1 : quad.colorIndex();
final RenderLayer renderLayer = blockInfo.effectiveRenderLayer(mat.blendMode(textureIndex));
final int colorIndex = mat.disableColorIndex() ? -1 : quad.colorIndex();
final RenderLayer renderLayer = blockInfo.effectiveRenderLayer(mat.blendMode());
final TriState ao = mat.ambientOcclusion();
if (blockInfo.defaultAo && !mat.disableAo(textureIndex)) {
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(textureIndex)) {
if (mat.emissive()) {
tessellateSmoothEmissive(quad, renderLayer, colorIndex);
} else {
tessellateSmooth(quad, renderLayer, colorIndex);
}
} else {
if (mat.emissive(textureIndex)) {
if (mat.emissive()) {
tessellateFlatEmissive(quad, renderLayer, colorIndex);
} else {
tessellateFlat(quad, renderLayer, colorIndex);
@ -106,13 +108,13 @@ public abstract class AbstractQuadRenderer {
private void colorizeQuad(MutableQuadViewImpl q, int blockColorIndex) {
if (blockColorIndex == -1) {
for (int i = 0; i < 4; i++) {
q.spriteColor(i, 0, ColorHelper.swapRedBlueIfNeeded(q.spriteColor(i, 0)));
q.color(i, ColorHelper.swapRedBlueIfNeeded(q.color(i)));
}
} else {
final int blockColor = blockInfo.blockColor(blockColorIndex);
for (int i = 0; i < 4; i++) {
q.spriteColor(i, 0, ColorHelper.swapRedBlueIfNeeded(ColorHelper.multiplyColor(blockColor, q.spriteColor(i, 0))));
q.color(i, ColorHelper.swapRedBlueIfNeeded(ColorHelper.multiplyColor(blockColor, q.color(i))));
}
}
}
@ -128,21 +130,20 @@ public abstract class AbstractQuadRenderer {
if (useNormals) {
quad.populateMissingNormals();
} else {
final Vector3f faceNormal = quad.faceNormal();
normalVec.set(faceNormal.x(), faceNormal.y(), faceNormal.z());
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.spriteColor(i, 0);
final int color = quad.color(i);
buff.color(color & 0xFF, (color >> 8) & 0xFF, (color >> 16) & 0xFF, (color >> 24) & 0xFF);
buff.texture(quad.spriteU(i, 0), quad.spriteV(i, 0));
buff.texture(quad.u(i), quad.v(i));
buff.overlay(overlay);
buff.light(quad.lightmap(i));
if (useNormals) {
normalVec.set(quad.normalX(i), quad.normalY(i), quad.normalZ(i));
quad.copyNormal(i, normalVec);
normalVec.mul(normalMatrix);
}
@ -158,7 +159,7 @@ public abstract class AbstractQuadRenderer {
colorizeQuad(q, blockColorIndex);
for (int i = 0; i < 4; i++) {
q.spriteColor(i, 0, ColorHelper.multiplyRGB(q.spriteColor(i, 0), aoCalc.ao[i]));
q.color(i, ColorHelper.multiplyRGB(q.color(i), aoCalc.ao[i]));
q.lightmap(i, ColorHelper.maxBrightness(q.lightmap(i), aoCalc.light[i]));
}
@ -170,7 +171,7 @@ public abstract class AbstractQuadRenderer {
colorizeQuad(q, blockColorIndex);
for (int i = 0; i < 4; i++) {
q.spriteColor(i, 0, ColorHelper.multiplyRGB(q.spriteColor(i, 0), aoCalc.ao[i]));
q.color(i, ColorHelper.multiplyRGB(q.color(i), aoCalc.ao[i]));
q.lightmap(i, LightmapTextureManager.MAX_LIGHT_COORDINATE);
}
@ -240,14 +241,14 @@ public abstract class AbstractQuadRenderer {
final float faceShade = blockInfo.blockView.getBrightness(quad.lightFace(), quad.hasShade());
for (int i = 0; i < 4; i++) {
quad.spriteColor(i, 0, ColorHelper.multiplyRGB(quad.spriteColor(i, 0), vertexShade(quad, i, faceShade)));
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.spriteColor(i, 0, ColorHelper.multiplyRGB(quad.spriteColor(i, 0), diffuseShade));
quad.color(i, ColorHelper.multiplyRGB(quad.color(i), diffuseShade));
}
}
}

View file

@ -48,6 +48,7 @@ public class BlockRenderInfo {
public BlockPos blockPos;
public BlockState blockState;
public long seed;
boolean useAo;
boolean defaultAo;
RenderLayer defaultLayer;
@ -78,7 +79,8 @@ public class BlockRenderInfo {
this.blockState = blockState;
// in the unlikely case seed actually matches this, we'll simply retrieve it more than once
seed = -1L;
defaultAo = modelAO && MinecraftClient.isAmbientOcclusionEnabled() && blockState.getLuminance() == 0;
useAo = MinecraftClient.isAmbientOcclusionEnabled();
defaultAo = useAo && modelAO && blockState.getLuminance() == 0;
defaultLayer = RenderLayers.getBlockLayer(blockState);

View file

@ -185,13 +185,13 @@ public class ItemRenderContext extends AbstractRenderContext {
private void colorizeQuad(MutableQuadViewImpl q, int colorIndex) {
if (colorIndex == -1) {
for (int i = 0; i < 4; i++) {
q.spriteColor(i, 0, ColorHelper.swapRedBlueIfNeeded(q.spriteColor(i, 0)));
q.color(i, ColorHelper.swapRedBlueIfNeeded(q.color(i)));
}
} else {
final int itemColor = 0xFF000000 | colorMap.getColor(itemStack, colorIndex);
for (int i = 0; i < 4; i++) {
q.spriteColor(i, 0, ColorHelper.swapRedBlueIfNeeded(ColorHelper.multiplyColor(itemColor, q.spriteColor(i, 0))));
q.color(i, ColorHelper.swapRedBlueIfNeeded(ColorHelper.multiplyColor(itemColor, q.color(i))));
}
}
}
@ -225,10 +225,10 @@ public class ItemRenderContext extends AbstractRenderContext {
final RenderMaterialImpl.Value mat = quad.material();
final int colorIndex = mat.disableColorIndex(0) ? -1 : quad.colorIndex();
final BlendMode blendMode = mat.blendMode(0);
final int colorIndex = mat.disableColorIndex() ? -1 : quad.colorIndex();
final BlendMode blendMode = mat.blendMode();
if (mat.emissive(0)) {
if (mat.emissive()) {
renderQuadEmissive(quad, blendMode, colorIndex);
} else {
renderQuad(quad, blendMode, colorIndex);

View file

@ -34,6 +34,7 @@ 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.RenderMaterialImpl.Value;
import net.fabricmc.fabric.impl.client.indigo.renderer.aocalc.AoCalculator;
@ -60,7 +61,7 @@ import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl;
* manipulating the data via NIO.
*/
public abstract class TerrainFallbackConsumer extends AbstractQuadRenderer implements RenderContext.BakedModelConsumer {
private static final Value MATERIAL_FLAT = (Value) IndigoRenderer.INSTANCE.materialFinder().disableAo(0, true).find();
private static final Value MATERIAL_FLAT = (Value) IndigoRenderer.INSTANCE.materialFinder().ambientOcclusion(TriState.FALSE).find();
private static final Value MATERIAL_SHADED = (Value) IndigoRenderer.INSTANCE.materialFinder().find();
TerrainFallbackConsumer(BlockRenderInfo blockInfo, Function<RenderLayer, VertexConsumer> bufferFunc, AoCalculator aoCalc, QuadTransform transform) {
@ -88,7 +89,7 @@ public abstract class TerrainFallbackConsumer extends AbstractQuadRenderer imple
@Override
public void accept(BakedModel model, @Nullable BlockState blockState) {
final Supplier<Random> random = blockInfo.randomSupplier;
final Value defaultMaterial = blockInfo.defaultAo && model.useAmbientOcclusion() ? MATERIAL_SHADED : MATERIAL_FLAT;
final Value defaultMaterial = model.useAmbientOcclusion() ? MATERIAL_SHADED : MATERIAL_FLAT;
for (int i = 0; i <= ModelHelper.NULL_FACE_ID; i++) {
final Direction cullFace = ModelHelper.faceFromIndex(i);