Add renderable API to allow easier rendering of OBJ and other custom models, from Entity and BlockEntity renderers. (#8259)

This is a redesign of a discarded section of my initial model system rewrite, back in 1.14.
In order to use it with the OBJ loader, you can use OBJLoader.INSTANCE.loadModel to get the OBJModel, and then call OBJModel#bakeRenderable() to get a SimpleRenderable object to render with.

The SimpleRenderable support animation, by providing different transformation matrices for each part in the MultipartTransforms.

Additionally, a BakedRenderable helper exists to turn an arbitrary BakedModel into a renderable.

After trying to get the B3D loader to work, I decided it wasn't worth the trouble and marked it for removal instead.
This commit is contained in:
gigaherz authored and GitHub committed 2022-01-20 13:30:39 -08:00
1 parent 6c6a4817e7
commit f704d65d5a
14 files changed
+793 -70

No files matched your search

@@ -39,6 +39,7 @@ import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.core.Direction;
import net.minecraft.util.GsonHelper;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.client.ForgeRenderTypes;
import net.minecraftforge.client.MinecraftForgeClient;
import net.minecraftforge.client.model.data.IDynamicBakedModel;
@@ -234,14 +235,13 @@ public final class MultiLayerModel implements IModelGeometry<MultiLayerModel>
return itemLayers;
}
public static BiMap<RenderType, RenderType> ITEM_RENDER_TYPE_MAPPING = HashBiMap.create();
// TODO ForgeRenderTypes
/* static {
public static final BiMap<RenderType, RenderType> ITEM_RENDER_TYPE_MAPPING = HashBiMap.create();
static {
ITEM_RENDER_TYPE_MAPPING.put(RenderType.solid(), ForgeRenderTypes.ITEM_LAYERED_SOLID.get());
ITEM_RENDER_TYPE_MAPPING.put(RenderType.cutout(), ForgeRenderTypes.ITEM_LAYERED_CUTOUT.get());
ITEM_RENDER_TYPE_MAPPING.put(RenderType.cutoutMipped(), ForgeRenderTypes.ITEM_LAYERED_CUTOUT_MIPPED.get());
ITEM_RENDER_TYPE_MAPPING.put(RenderType.translucent(), ForgeRenderTypes.ITEM_LAYERED_TRANSLUCENT.get());
}*/
}
}
public static final class Loader implements IModelLoader<MultiLayerModel>
@@ -0,0 +1,119 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2021.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model;
import net.minecraft.client.renderer.block.model.ItemTransforms;
import net.minecraft.client.resources.model.BlockModelRotation;
import net.minecraft.client.resources.model.Material;
import net.minecraft.client.resources.model.ModelState;
import net.minecraft.client.resources.model.UnbakedModel;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.client.textures.UnitSprite;
import javax.annotation.Nullable;
import java.util.Map;
/**
* Helper to allow baking models outwise the context of block/item model loading.
*/
public class StandaloneModelConfiguration implements IModelConfiguration
{
public static final ResourceLocation LOCATION = new ResourceLocation("forge", "standalone");
public static final StandaloneModelConfiguration INSTANCE = new StandaloneModelConfiguration(LOCATION, Map.of());
public static StandaloneModelConfiguration create(ResourceLocation modelName)
{
return new StandaloneModelConfiguration(modelName, Map.of());
}
public static StandaloneModelConfiguration create(Map<String, ResourceLocation> textures)
{
return new StandaloneModelConfiguration(LOCATION, textures);
}
public static StandaloneModelConfiguration create(ResourceLocation modelName, Map<String, ResourceLocation> textures)
{
return new StandaloneModelConfiguration(modelName, textures);
}
private final Map<String, ResourceLocation> textures;
public StandaloneModelConfiguration(ResourceLocation modelName, Map<String, ResourceLocation> textures)
{
this.textures = textures;
}
@Nullable
@Override
public UnbakedModel getOwnerModel()
{
return null;
}
@Override
public String getModelName()
{
return LOCATION.toString();
}
@Override
public boolean isTexturePresent(String name)
{
return textures.containsKey(name);
}
@Override
public Material resolveTexture(String name)
{
return new Material(UnitSprite.LOCATION, textures.getOrDefault(name, UnitSprite.LOCATION));
}
@Override
public boolean isShadedInGui()
{
return true;
}
@Override
public boolean isSideLit()
{
return true;
}
@Override
public boolean useSmoothLighting()
{
return true;
}
@Override
public ItemTransforms getCameraTransforms()
{
return ItemTransforms.NO_TRANSFORMS;
}
@Override
public ModelState getCombinedTransform()
{
return BlockModelRotation.X0_Y0;
}
}
@@ -72,11 +72,8 @@ import java.util.stream.Collectors;
/*
* Loader for Blitz3D models.
* To enable for your mod call instance.addDomain(modId).
* If you need more control over accepted resources - extend the class, and register a new instance with ModelLoaderRegistry.
*/
// TODO: Implement as a new model loader
@SuppressWarnings("unused")
@Deprecated(forRemoval = true, since = "1.18")
public enum B3DLoader
{
INSTANCE;
@@ -64,6 +64,7 @@ import com.mojang.math.Vector3f;
import com.mojang.math.Vector4f;
import net.minecraft.world.phys.Vec2;
@Deprecated(forRemoval = true, since = "1.18")
public class B3DModel
{
static final Logger logger = LogManager.getLogger(ForgeVersion.MOD_ID + ".B3DModel");
@@ -72,7 +72,7 @@ public class OBJLoader implements IModelLoader<OBJModel>
{
return modelCache.computeIfAbsent(settings, (data) -> {
try(Resource resource = manager.getResource(settings.modelLocation);
try(Resource resource = manager.getResource(settings.modelLocation());
LineReader rdr = new LineReader(resource))
{
return new OBJModel(rdr, settings);
@@ -38,6 +38,8 @@ import net.minecraftforge.client.model.geometry.IModelGeometryPart;
import net.minecraftforge.client.model.geometry.IMultipartModelGeometry;
import net.minecraftforge.client.model.pipeline.BakedQuadBuilder;
import net.minecraftforge.client.model.pipeline.IVertexConsumer;
import net.minecraftforge.client.model.renderable.SimpleRenderable;
import net.minecraftforge.client.textures.UnitSprite;
import org.apache.commons.lang3.tuple.Pair;
import javax.annotation.Nonnull;
@@ -46,6 +48,7 @@ import java.io.IOException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.resources.model.Material;
@@ -510,6 +513,20 @@ public class OBJModel implements IMultipartModelGeometry<OBJModel>
}
}
public SimpleRenderable bakeRenderable(IModelConfiguration configuration)
{
var builder = SimpleRenderable.builder();
for(var entry : parts.entrySet())
{
var name = entry.getKey();
var part = entry.getValue();
part.bake(builder.child(name), configuration);
}
return builder.get();
}
public class ModelObject implements IModelGeometryPart
{
public final String name;
@@ -532,28 +549,26 @@ public class OBJModel implements IMultipartModelGeometry<OBJModel>
{
for(ModelMesh mesh : meshes)
{
MaterialLibrary.Material mat = mesh.mat;
if (mat == null)
continue;
TextureAtlasSprite texture = spriteGetter.apply(ModelLoaderRegistry.resolveTexture(mat.diffuseColorMap, owner));
int tintIndex = mat.diffuseTintIndex;
Vector4f colorTint = mat.diffuseColor;
mesh.addQuads(owner, modelBuilder, spriteGetter, modelTransform);
}
}
for (int[][] face : mesh.faces)
{
Pair<BakedQuad, Direction> quad = makeQuad(face, tintIndex, colorTint, mat.ambientColor, texture, modelTransform.getRotation());
if (quad.getRight() == null)
modelBuilder.addGeneralQuad(quad.getLeft());
else
modelBuilder.addFaceQuad(quad.getRight(), quad.getLeft());
}
public void bake(SimpleRenderable.PartBuilder<?> builder, IModelConfiguration configuration)
{
for (ModelMesh mesh : this.meshes)
{
mesh.bake(builder, configuration);
}
}
@Override
public Collection<Material> getTextures(IModelConfiguration owner, Function<ResourceLocation, UnbakedModel> modelGetter, Set<com.mojang.datafixers.util.Pair<String, String>> missingTextureErrors)
{
return meshes.stream().map(mesh -> ModelLoaderRegistry.resolveTexture(mesh.mat.diffuseColorMap, owner)).collect(Collectors.toSet());
return meshes.stream()
.flatMap(mesh -> mesh.mat != null
? Stream.of(ModelLoaderRegistry.resolveTexture(mesh.mat.diffuseColorMap, owner))
: Stream.of())
.collect(Collectors.toSet());
}
}
@@ -576,10 +591,23 @@ public class OBJModel implements IMultipartModelGeometry<OBJModel>
{
super.addQuads(owner, modelBuilder, bakery, spriteGetter, modelTransform, modelLocation);
getParts().stream().filter(part -> owner.getPartVisibility(part))
getParts().stream().filter(owner::getPartVisibility)
.forEach(part -> part.addQuads(owner, modelBuilder, bakery, spriteGetter, modelTransform, modelLocation));
}
@Override
public void bake(SimpleRenderable.PartBuilder<?> builder, IModelConfiguration configuration)
{
super.bake(builder, configuration);
for(var entry : parts.entrySet())
{
var name = entry.getKey();
var part = entry.getValue();
part.bake(builder.child(name), configuration);
}
}
@Override
public Collection<Material> getTextures(IModelConfiguration owner, Function<ResourceLocation, UnbakedModel> modelGetter, Set<com.mojang.datafixers.util.Pair<String, String>> missingTextureErrors)
{
@@ -604,53 +632,50 @@ public class OBJModel implements IMultipartModelGeometry<OBJModel>
this.mat = currentMat;
this.smoothingGroup = currentSmoothingGroup;
}
}
public static class ModelSettings
{
@Nonnull
public final ResourceLocation modelLocation;
public final boolean detectCullableFaces;
public final boolean diffuseLighting;
public final boolean flipV;
public final boolean ambientToFullbright;
@Nullable
public final String materialLibraryOverrideLocation;
public ModelSettings(@Nonnull ResourceLocation modelLocation, boolean detectCullableFaces, boolean diffuseLighting, boolean flipV, boolean ambientToFullbright,
@Nullable String materialLibraryOverrideLocation)
public void addQuads(IModelConfiguration owner, IModelBuilder<?> modelBuilder, Function<Material, TextureAtlasSprite> spriteGetter, ModelState modelTransform)
{
this.modelLocation = modelLocation;
this.detectCullableFaces = detectCullableFaces;
this.diffuseLighting = diffuseLighting;
this.flipV = flipV;
this.ambientToFullbright = ambientToFullbright;
this.materialLibraryOverrideLocation = materialLibraryOverrideLocation;
if (mat == null)
return;
TextureAtlasSprite texture = spriteGetter.apply(ModelLoaderRegistry.resolveTexture(mat.diffuseColorMap, owner));
int tintIndex = mat.diffuseTintIndex;
Vector4f colorTint = mat.diffuseColor;
for (int[][] face : faces)
{
Pair<BakedQuad, Direction> quad = makeQuad(face, tintIndex, colorTint, mat.ambientColor, texture, modelTransform.getRotation());
if (quad.getRight() == null)
modelBuilder.addGeneralQuad(quad.getLeft());
else
modelBuilder.addFaceQuad(quad.getRight(), quad.getLeft());
}
}
@Override
public boolean equals(Object o)
public void bake(SimpleRenderable.PartBuilder<?> builder, IModelConfiguration configuration)
{
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ModelSettings that = (ModelSettings) o;
return equals(that);
}
MaterialLibrary.Material mat = this.mat;
if (mat == null)
return;
int tintIndex = mat.diffuseTintIndex;
Vector4f colorTint = mat.diffuseColor;
public boolean equals(@Nonnull ModelSettings that)
{
return detectCullableFaces == that.detectCullableFaces &&
diffuseLighting == that.diffuseLighting &&
flipV == that.flipV &&
ambientToFullbright == that.ambientToFullbright &&
modelLocation.equals(that.modelLocation) &&
Objects.equals(materialLibraryOverrideLocation, that.materialLibraryOverrideLocation);
}
final List<BakedQuad> quads = new ArrayList<>();
@Override
public int hashCode()
{
return Objects.hash(modelLocation, detectCullableFaces, diffuseLighting, flipV, ambientToFullbright, materialLibraryOverrideLocation);
for(var face : this.faces)
{
var pair = makeQuad(face, tintIndex, colorTint, mat.ambientColor, UnitSprite.INSTANCE, Transformation.identity());
quads.add(pair.getLeft());
}
ResourceLocation textureLocation = ModelLoaderRegistry.resolveTexture(mat.diffuseColorMap, configuration).texture();
ResourceLocation texturePath = new ResourceLocation(textureLocation.getNamespace(), "textures/" + textureLocation.getPath() + ".png");
builder.addMesh(texturePath, quads);
}
}
public record ModelSettings(@Nonnull ResourceLocation modelLocation,
boolean detectCullableFaces, boolean diffuseLighting, boolean flipV,
boolean ambientToFullbright, @Nullable String materialLibraryOverrideLocation)
{}
}
@@ -0,0 +1,89 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2021.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model.renderable;
import com.google.common.collect.ImmutableList;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.Util;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.inventory.InventoryMenu;
import net.minecraftforge.client.model.data.IModelData;
import java.util.Random;
import java.util.function.Function;
/**
* Wrapper to make it easier to draw baked models in renderers.
*/
public class BakedRenderable implements IRenderable<IModelData>
{
private static final ImmutableList<Direction> MODEL_FACINGS = Util.make(ImmutableList.<Direction>builder(), builder -> {
builder.add(Direction.values());
builder.add((Direction)null);
}).build();
/**
* Constructs a BakedRenderable from the given model location.
* The model is expected to have been baked ahead of time.
* You can ensure the model is baked by calling {@link net.minecraftforge.client.model.ForgeModelBakery#addSpecialModel(ResourceLocation)}
* from within {@link net.minecraftforge.client.event.ModelRegistryEvent}.
*/
public static BakedRenderable of(ResourceLocation model)
{
return of(Minecraft.getInstance().getModelManager().getModel(model));
}
/**
* Constructs a BakedRenderable from the given model.
*/
public static BakedRenderable of(BakedModel model)
{
return new BakedRenderable(model);
}
private final Random rand = new Random();
private final BakedModel model;
private BakedRenderable(BakedModel model)
{
this.model = model;
}
@Override
public void render(PoseStack poseStack, MultiBufferSource bufferSource, Function<ResourceLocation, RenderType> renderTypeFunction, int lightmapCoord, int overlayCoord, float partialTicks, IModelData renderValues)
{
var rt = renderTypeFunction.apply(InventoryMenu.BLOCK_ATLAS);
VertexConsumer bb = bufferSource.getBuffer(rt);
for(Direction direction : MODEL_FACINGS)
{
for (BakedQuad quad : model.getQuads(null, direction, rand, renderValues))
{
bb.putBulkData(poseStack.last(), quad, 1, 1, 1, 1, lightmapCoord, overlayCoord, true);
}
}
}
}
@@ -17,10 +17,21 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
package net.minecraftforge.client.model.animation;
package net.minecraftforge.client.model.renderable;
import net.minecraft.MethodsReturnNonnullByDefault;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
/**
* A standard interface for renderable context values that support providing different values for parts of the model.
* @param <T> the type of value to be used for each part
*/
public interface IMultipartRenderValues<T>
{
/**
* Returns the value for the given part.
* @param part the name of the part
* @return the context value for the part, or {@code null}
*/
@Nullable
T getPartValues(String part);
}
@@ -0,0 +1,60 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2021.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model.renderable;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Unit;
import java.util.function.Function;
/**
* Defines a standard interface for things that can be rendered.
* @param <T> the type of context object used by the rendering logic
*/
@FunctionalInterface
public interface IRenderable<T>
{
/**
* Draws the renderable by adding the geometry to the provided {@link MultiBufferSource}
* @param poseStack the pose stack
* @param bufferSource the target buffer source to dump the data into
* @param renderTypeFunction a function that provides a RenderType for the given texture
* @param lightmapCoord the lightmap coordinates representing the current lighting conditions. See {@link net.minecraft.client.renderer.LightTexture}
* @param overlayCoord the overlay coordinates representing the current overlay status. See {@link net.minecraft.client.renderer.texture.OverlayTexture}
* @param partialTicks the current time expressed in the fraction of a tick elapsed since the last client tick.
* @param renderValues the context data used for rendering
*/
void render(PoseStack poseStack, MultiBufferSource bufferSource, Function<ResourceLocation, RenderType> renderTypeFunction, int lightmapCoord, int overlayCoord, float partialTicks, T renderValues);
/**
* Wraps the current renderable along with its configuration value.
* Useful for keeping a list of various renderables paired with their configuration values.
* @param renderValues the context data to be used for rendering
* @return a renderable that accepts {@link Unit#INSTANCE} as a configuration value, but uses the provided {@code renderValue} instead
*/
default IRenderable<Unit> withValues(T renderValues)
{
return (poseStack, bufferSource, renderTypeFunction, lightmapCoord, overlayCoord, partialTicks, unused) ->
this.render(poseStack, bufferSource, renderTypeFunction, lightmapCoord, overlayCoord, partialTicks, renderValues);
}
}
@@ -0,0 +1,58 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2021.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model.renderable;
import com.google.common.collect.ImmutableMap;
import com.mojang.math.Matrix4f;
import javax.annotation.Nullable;
/**
* A context value that provides {@link Matrix4f} transforms for certain parts of the model.
*/
public class MultipartTransforms implements IMultipartRenderValues<Matrix4f>
{
/**
* A default instance that has no transforms specified.
*/
public static final MultipartTransforms EMPTY = new MultipartTransforms(ImmutableMap.of());
/**
* Builds a MultipartTransforms object with the given mapping.
*/
public static MultipartTransforms of(ImmutableMap<String, Matrix4f> parts)
{
return new MultipartTransforms(parts);
}
private final ImmutableMap<String, Matrix4f> parts;
private MultipartTransforms(ImmutableMap<String, Matrix4f> parts)
{
this.parts = parts;
}
@Nullable
@Override
public Matrix4f getPartValues(String part)
{
return parts.get(part);
}
}
@@ -0,0 +1,168 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2021.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.model.renderable;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix4f;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.resources.ResourceLocation;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
/**
* Implements a simple renderable consisting of a hierarchy of parts, where each part can contain a number of meshes.
* Each mesh pairs a texture, with a set of quads.
*/
public class SimpleRenderable implements IRenderable<MultipartTransforms>
{
private final List<Part> parts = new ArrayList<>();
private SimpleRenderable()
{
}
public static Builder builder()
{
return new Builder();
}
@Override
public void render(PoseStack poseStack, MultiBufferSource bufferSource, Function<ResourceLocation, RenderType> renderTypeFunction, int lightmapCoord, int overlayCoord, float partialTicks, MultipartTransforms renderValues)
{
for(var part : parts)
{
part.render(poseStack, bufferSource, renderTypeFunction, lightmapCoord, overlayCoord, renderValues);
}
}
private static class Part
{
private final String name;
private final List<Part> parts = new ArrayList<>();
private final List<Mesh> meshes = new ArrayList<>();
public Part(String name)
{
this.name = name;
}
public void render(PoseStack poseStack, MultiBufferSource bufferSource, Function<ResourceLocation, RenderType> renderTypeFunction, int lightmapCoord, int overlayCoord, MultipartTransforms renderValues)
{
Matrix4f matrix = renderValues.getPartValues(name);
if (matrix != null)
{
poseStack.pushPose();
poseStack.mulPoseMatrix(matrix);
}
for(var part : parts)
{
part.render(poseStack, bufferSource, renderTypeFunction, lightmapCoord, overlayCoord, renderValues);
}
for(var mesh : meshes)
{
mesh.render(poseStack, bufferSource, renderTypeFunction, lightmapCoord, overlayCoord, renderValues);
}
if (matrix != null)
{
poseStack.popPose();
}
}
}
private static class Mesh
{
private final ResourceLocation texture;
private final List<BakedQuad> quads = new ArrayList<>();
public Mesh(ResourceLocation texture)
{
this.texture = texture;
}
public void render(PoseStack poseStack, MultiBufferSource bufferSource, Function<ResourceLocation, RenderType> renderTypeFunction, int lightmapCoord, int overlayCoord, MultipartTransforms renderValues)
{
var consumer = bufferSource.getBuffer(renderTypeFunction.apply(texture));
for(var quad : quads)
{
consumer.putBulkData(poseStack.last(), quad, 1, 1, 1, 1, lightmapCoord, overlayCoord, true);
}
}
}
public static class Builder
{
private final SimpleRenderable renderable = new SimpleRenderable();
private Builder()
{
}
public PartBuilder<Builder> child(String name)
{
var child = new Part(name);
renderable.parts.add(child);
return new PartBuilder<>(this, child);
}
public SimpleRenderable get()
{
return renderable;
}
}
public static class PartBuilder<T>
{
private final T parent;
private final Part part;
private PartBuilder(T parent, Part part)
{
this.parent = parent;
this.part = part;
}
public PartBuilder<PartBuilder<T>> child(String name)
{
var child = new Part(part.name + "/" + name);
this.part.parts.add(child);
return new PartBuilder<>(this, child);
}
public PartBuilder<T> addMesh(ResourceLocation texture, List<BakedQuad> quads)
{
var mesh = new Mesh(texture);
mesh.quads.addAll(quads);
part.meshes.add(mesh);
return this;
}
public T end()
{
return parent;
}
}
}
@@ -0,0 +1,59 @@
/*
* Minecraft Forge
* Copyright (c) 2016-2021.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.client.textures;
import com.mojang.blaze3d.platform.NativeImage;
import net.minecraft.client.renderer.texture.TextureAtlas;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.metadata.animation.AnimationMetadataSection;
import net.minecraft.client.resources.model.Material;
import net.minecraft.resources.ResourceLocation;
import java.util.function.Function;
/**
* A helper that lets you bake quads that won't be used with an atlas.
*/
public class UnitSprite extends TextureAtlasSprite
{
public static final UnitSprite INSTANCE = new UnitSprite();
public static final ResourceLocation LOCATION = new ResourceLocation("forge", "unit");
public static final Function<Material, TextureAtlasSprite> GETTER = (x) -> INSTANCE;
private UnitSprite()
{
super(new TextureAtlas(LOCATION),
new Info(LOCATION, 1, 1, AnimationMetadataSection.EMPTY),
0, 1, 1,
0, 0, new NativeImage(1, 1, false));
}
@Override
public float getU(double u)
{
return (float) u / 16;
}
@Override
public float getV(double v)
{
return (float) v / 16;
}
}