Skip to content

Commit

Permalink
Convert MeshBuilder into MutableMesh
Browse files Browse the repository at this point in the history
- Effectively split MeshBuilder#build into MutableMesh#immutableCopy and #clear
- Effectively add MutableMesh#forEachMutable
- Add Mesh#size
- Remove QuadEmitter#hasTransform
- Optimize MutableQuadViewImpl#copyFrom by not precomputing geometry
- Give UnwrappableBakedModel#unwrap a default implementation to prevent issues with interface injection
- Finish porting FRAPI and Model Loading API testmods
  • Loading branch information
PepperCode1 committed Dec 2, 2024
1 parent 386c4a9 commit 4b58d9c
Show file tree
Hide file tree
Showing 28 changed files with 375 additions and 296 deletions.
4 changes: 2 additions & 2 deletions fabric-model-loading-api-v1/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ version = getSubprojectVersion(project)
moduleDependencies(project, ['fabric-api-base'])

testDependencies(project, [
// ':fabric-renderer-api-v1',
// ':fabric-renderer-indigo',
':fabric-renderer-api-v1',
':fabric-renderer-indigo',
':fabric-rendering-v1',
':fabric-resource-loader-v0'
])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
* and you would normally access the model by its identifier and then cast it:
* call {@link #unwrap(BakedModel, Predicate)} on the model first, in case another
* mod is wrapping your model to alter its rendering.
*
* <p>Note: This interface is automatically implemented on {@link WrapperBakedModel} and subclasses via Mixin and
* interface injection.
*/
public interface UnwrappableBakedModel {
/**
Expand All @@ -39,7 +42,9 @@ public interface UnwrappableBakedModel {
* <p>If there are multiple layers of wrapping, this method does not necessarily return the innermost model.
*/
@Nullable
BakedModel getWrappedModel();
default BakedModel getWrappedModel() {
return null;
}

/**
* Iteratively unwrap the given model until the given condition returns true or all models in the hierarchy have
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
package net.fabricmc.fabric.test.model.loading;

import java.util.List;
import java.util.function.Predicate;
import java.util.function.Supplier;

import org.jetbrains.annotations.Nullable;

import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
Expand All @@ -29,18 +33,25 @@
import net.minecraft.client.render.model.MissingModel;
import net.minecraft.client.render.model.ModelBakeSettings;
import net.minecraft.client.render.model.ModelTextures;
import net.minecraft.client.render.model.WrapperBakedModel;
import net.minecraft.client.render.model.json.ModelTransformation;
import net.minecraft.client.render.model.json.ModelVariant;
import net.minecraft.client.render.model.json.WeightedUnbakedModel;
import net.minecraft.resource.ResourceType;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.AffineTransformation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.BlockRenderView;

import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.model.loading.v1.ModelLoadingPlugin;
import net.fabricmc.fabric.api.client.model.loading.v1.ModelModifier;
import net.fabricmc.fabric.api.client.model.loading.v1.WrapperUnbakedModel;
import net.fabricmc.fabric.api.client.rendering.v1.LivingEntityFeatureRendererRegistrationCallback;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
import net.fabricmc.fabric.api.resource.ResourceManagerHelper;

public class ModelTestModClient implements ClientModInitializer {
Expand All @@ -50,19 +61,6 @@ public class ModelTestModClient implements ClientModInitializer {
public static final Identifier GOLD_BLOCK_MODEL_ID = Identifier.ofVanilla("block/gold_block");
public static final Identifier BROWN_GLAZED_TERRACOTTA_MODEL_ID = Identifier.ofVanilla("block/brown_glazed_terracotta");

//static class DownQuadRemovingModel extends ForwardingBakedModel {
// DownQuadRemovingModel(BakedModel model) {
// wrapped = model;
// }
//
// @Override
// public void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
// context.pushTransform(q -> q.cullFace() != Direction.DOWN);
// super.emitBlockQuads(blockView, state, pos, randomSupplier, context);
// context.popTransform();
// }
//}

@Override
public void onInitializeClient() {
ModelLoadingPlugin.register(pluginContext -> {
Expand Down Expand Up @@ -115,20 +113,19 @@ public BakedModel bake(ModelTextures textures, Baker baker, ModelBakeSettings se
return model;
});

// TODO 1.21.4: reintroduce test once FRAPI+Indigo are ported
// remove bottom face of gold blocks
//pluginContext.modifyModelOnLoad().register(ModelModifier.WRAP_PHASE, (model, context) -> {
// if (context.id().equals(GOLD_BLOCK_MODEL_ID)) {
// return new WrapperUnbakedModel(model) {
// @Override
// public BakedModel bake(ModelTextures textures, Baker baker, ModelBakeSettings settings, boolean ambientOcclusion, boolean isSideLit, ModelTransformation transformation) {
// return new DownQuadRemovingModel(super.bake(textures, baker, settings, ambientOcclusion, isSideLit, transformation));
// }
// };
// }
//
// return model;
//});
// Remove bottom face of gold blocks
pluginContext.modifyModelOnLoad().register(ModelModifier.WRAP_PHASE, (model, context) -> {
if (context.id().equals(GOLD_BLOCK_MODEL_ID)) {
return new WrapperUnbakedModel(model) {
@Override
public BakedModel bake(ModelTextures textures, Baker baker, ModelBakeSettings settings, boolean ambientOcclusion, boolean isSideLit, ModelTransformation transformation) {
return new DownQuadRemovingModel(super.bake(textures, baker, settings, ambientOcclusion, isSideLit, transformation));
}
};
}

return model;
});
});

ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(SpecificModelReloadListener.INSTANCE);
Expand All @@ -147,4 +144,17 @@ public static Identifier id(String path) {
private static GroupableModel simpleGroupableModel(Identifier model) {
return new WeightedUnbakedModel(List.of(new ModelVariant(model, AffineTransformation.identity(), false, 1)));
}

private static class DownQuadRemovingModel extends WrapperBakedModel implements FabricBakedModel {
DownQuadRemovingModel(BakedModel model) {
super(model);
}

@Override
public void emitBlockQuads(QuadEmitter emitter, BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, Predicate<@Nullable Direction> cullTest) {
emitter.pushTransform(q -> q.cullFace() != Direction.DOWN);
((FabricBakedModel) wrapped).emitBlockQuads(emitter, blockView, state, pos, randomSupplier, cullTest);
emitter.popTransform();
}
}
}
2 changes: 1 addition & 1 deletion fabric-renderer-api-v1/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ testDependencies(project, [
':fabric-block-api-v1',
':fabric-block-view-api-v2',
':fabric-blockrenderlayer-v1',
// ':fabric-model-loading-api-v1',
':fabric-model-loading-api-v1',
':fabric-object-builder-api-v1',
':fabric-renderer-indigo',
':fabric-resource-loader-v0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

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.MeshBuilder;
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableMesh;
import net.fabricmc.fabric.impl.renderer.RendererManager;

/**
Expand Down Expand Up @@ -51,19 +51,19 @@ static void register(Renderer renderer) {
}

/**
* Obtain a new {@link MeshBuilder} instance used to create
* baked models with enhanced features.
* Obtain a new {@link MutableMesh} instance to build optimized meshes and create baked models
* with enhanced features.
*
* <p>Renderer does not retain a reference to returned instances and they should be re-used for
* multiple models when possible to avoid memory allocation overhead.
* <p>Renderer does not retain a reference to returned instances, so they should be re-used
* when possible to avoid memory allocation overhead.
*/
MeshBuilder meshBuilder();
MutableMesh mutableMesh();

/**
* Obtain a new {@link MaterialFinder} instance used to retrieve
* standard {@link RenderMaterial} instances.
* Obtain a new {@link MaterialFinder} instance to retrieve standard {@link RenderMaterial}
* instances.
*
* <p>Renderer does not retain a reference to returned instances and they should be re-used for
* <p>Renderer does not retain a reference to returned instances, so they should be re-used for
* multiple materials when possible to avoid memory allocation overhead.
*/
MaterialFinder materialFinder();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import net.minecraft.util.Identifier;

import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder;
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableMesh;
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;

/**
Expand Down Expand Up @@ -56,7 +56,7 @@
*
* <p>Special materials are implemented directly by the Renderer implementation, typically
* with the aim of providing advanced/extended features. Such materials may offer additional
* vertex attributes via extensions to {@link MeshBuilder} and {@link MutableQuadView}.
* vertex attributes via extensions to {@link MutableMesh} and {@link MutableQuadView}.
*
* <p>Special materials can be obtained using {@link Renderer#materialById(Identifier)}
* with a known identifier. Renderers may provide other means of access. Popular
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,30 +18,39 @@

import java.util.function.Consumer;

import org.jetbrains.annotations.Range;

import net.minecraft.block.BlockState;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.random.Random;

import net.fabricmc.fabric.api.renderer.v1.Renderer;

/**
* A bundle of one or more {@link QuadView} instances encoded by the renderer,
* typically via {@link Renderer#meshBuilder()}.
* A bundle of {@link QuadView} instances encoded by the renderer, typically
* via {@link MutableMesh#immutableCopy()}.
*
* <p>Similar in purpose to the {@code List<BakedQuad>} instances returned by
* {@link BakedModel#getQuads(BlockState, Direction, Random)}, but allows the
* renderer to optimize the format for performance and memory allocation.
*
* <p>All declared methods in this interface are thread-safe and can be used
* concurrently.
*
* <p>Only the renderer should implement or extend this interface.
*/
public interface Mesh {
/**
* Use to access all the quads encoded in this mesh. The quad instances
* sent to the consumer may be reused and should never be retained outside
* the current call to the consumer.
* Returns the number of quads encoded in this mesh.
*/
@Range(from = 0, to = Integer.MAX_VALUE)
int size();

/**
* Use to access all the quads encoded in this mesh. The quad instance sent
* to the consumer should never be retained outside the current call to the
* consumer.
*/
void forEach(Consumer<QuadView> consumer);
void forEach(Consumer<? super QuadView> action);

/**
* Outputs all quads in this mesh to the given quad emitter.
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright (c) 2016, 2017, 2018, 2019 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.fabricmc.fabric.api.renderer.v1.mesh;

import java.util.function.Consumer;

/**
* A bundle of {@link MutableQuadView} instances encoded by the renderer that
* can have more quads added to it. Typically used to build optimized,
* immutable {@link Mesh}es via {@link #emitter()}, {@link #immutableCopy()},
* and {@link #clear()}. Encoded quads can also be inspected, modified, and
* outputted directly to allow for advanced use cases where creating an
* immutable {@link Mesh} is not desirable.
*
* <p>All declared methods in this interface are <b>not</b> thread-safe and
* should not be used concurrently. Inherited methods from {@link Mesh} are
* still thread-safe when used in isolation.
*
* <p>Only the renderer should implement or extend this interface.
*/
public interface MutableMesh extends Mesh {
/**
* Returns the {@link QuadEmitter} used to append quads to this mesh.
* Calling this method a second time invalidates any prior result.
* Do not retain references outside the context of this mesh.
*/
QuadEmitter emitter();

/**
* Use to access all the quads encoded in this mesh and modify them as
* necessary. The quad instance sent to the consumer should never be
* retained outside the current call to the consumer.
*/
void forEachMutable(Consumer<? super MutableQuadView> action);

/**
* Returns a new, optimized, immutable {@link Mesh} instance containing all
* quads currently encoded in {@code this} mesh. This operation does not
* change the state of {@code this} mesh; if you need to build another
* immutable mesh, call {@link #clear()} first.
*
* <p>If quad data has been added to the {@link #emitter()} but has
* not yet been emitted, calling this method will not affect it.
*/
Mesh immutableCopy();

/**
* Resets this mesh to an empty state with zero quads, effectively clearing
* all existing quads.
*/
void clear();
}
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ default MutableQuadView normal(int vertexIndex, Vector3fc normal) {
MutableQuadView material(RenderMaterial material);

/**
* Value functions identically to {@link BakedQuad#getColorIndex()} and is
* Value functions identically to {@link BakedQuad#getTintIndex()} and is
* used by renderer in same way. Default value is -1.
*/
MutableQuadView tintIndex(int tintIndex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,13 +201,6 @@ default QuadEmitter square(Direction nominalFace, float left, float bottom, floa
return this;
}

/**
* Returns whether this emitter currently has at least one transform.
*
* @see #pushTransform(QuadTransform)
*/
boolean hasTransform();

/**
* Pushed transforms will be applied immediately after every call to {@link #emit()} and before the quad data is
* delivered to its destination. If any transform returns {@code false}, the emitted quad will be discarded and will
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;

/**
* Interface for reading quad data encoded by {@link MeshBuilder}.
* Interface for reading quad data encoded in {@link Mesh}es.
* Enables models to do analysis, re-texturing or translation without knowing the
* renderer's vertex formats and without retaining redundant information.
*
Expand Down
Loading

0 comments on commit 4b58d9c

Please sign in to comment.