diff --git a/src/main/java/net/minecraftforge/client/model/MultiLayerModel.java b/src/main/java/net/minecraftforge/client/model/MultiLayerModel.java index df611684ee..8cac26e347 100644 --- a/src/main/java/net/minecraftforge/client/model/MultiLayerModel.java +++ b/src/main/java/net/minecraftforge/client/model/MultiLayerModel.java @@ -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 return itemLayers; } - public static BiMap ITEM_RENDER_TYPE_MAPPING = HashBiMap.create(); - // TODO ForgeRenderTypes -/* static { + public static final BiMap 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 diff --git a/src/main/java/net/minecraftforge/client/model/StandaloneModelConfiguration.java b/src/main/java/net/minecraftforge/client/model/StandaloneModelConfiguration.java new file mode 100644 index 0000000000..b92bbcb94f --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/StandaloneModelConfiguration.java @@ -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 textures) + { + return new StandaloneModelConfiguration(LOCATION, textures); + } + + public static StandaloneModelConfiguration create(ResourceLocation modelName, Map textures) + { + return new StandaloneModelConfiguration(modelName, textures); + } + + private final Map textures; + + public StandaloneModelConfiguration(ResourceLocation modelName, Map 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; + } +} diff --git a/src/main/java/net/minecraftforge/client/model/b3d/B3DLoader.java b/src/main/java/net/minecraftforge/client/model/b3d/B3DLoader.java index f4f804af43..f82bf7fe1d 100644 --- a/src/main/java/net/minecraftforge/client/model/b3d/B3DLoader.java +++ b/src/main/java/net/minecraftforge/client/model/b3d/B3DLoader.java @@ -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; diff --git a/src/main/java/net/minecraftforge/client/model/b3d/B3DModel.java b/src/main/java/net/minecraftforge/client/model/b3d/B3DModel.java index 3d8076254b..b5eee40311 100644 --- a/src/main/java/net/minecraftforge/client/model/b3d/B3DModel.java +++ b/src/main/java/net/minecraftforge/client/model/b3d/B3DModel.java @@ -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"); diff --git a/src/main/java/net/minecraftforge/client/model/obj/OBJLoader.java b/src/main/java/net/minecraftforge/client/model/obj/OBJLoader.java index f5e7be9616..582a3b9253 100644 --- a/src/main/java/net/minecraftforge/client/model/obj/OBJLoader.java +++ b/src/main/java/net/minecraftforge/client/model/obj/OBJLoader.java @@ -72,7 +72,7 @@ public class OBJLoader implements IModelLoader { 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); diff --git a/src/main/java/net/minecraftforge/client/model/obj/OBJModel.java b/src/main/java/net/minecraftforge/client/model/obj/OBJModel.java index 77b6e802d3..6383c1982a 100644 --- a/src/main/java/net/minecraftforge/client/model/obj/OBJModel.java +++ b/src/main/java/net/minecraftforge/client/model/obj/OBJModel.java @@ -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 } } + 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 { 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 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 getTextures(IModelConfiguration owner, Function modelGetter, Set> 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 { 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 getTextures(IModelConfiguration owner, Function modelGetter, Set> missingTextureErrors) { @@ -604,53 +632,50 @@ public class OBJModel implements IMultipartModelGeometry 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 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 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 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) + {} } diff --git a/src/main/java/net/minecraftforge/client/model/renderable/BakedRenderable.java b/src/main/java/net/minecraftforge/client/model/renderable/BakedRenderable.java new file mode 100644 index 0000000000..46b3651eab --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/renderable/BakedRenderable.java @@ -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 +{ + private static final ImmutableList MODEL_FACINGS = Util.make(ImmutableList.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 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); + } + } + } +} diff --git a/src/main/java/net/minecraftforge/client/model/animation/package-info.java b/src/main/java/net/minecraftforge/client/model/renderable/IMultipartRenderValues.java similarity index 58% rename from src/main/java/net/minecraftforge/client/model/animation/package-info.java rename to src/main/java/net/minecraftforge/client/model/renderable/IMultipartRenderValues.java index b6b6656129..a5179f76c4 100644 --- a/src/main/java/net/minecraftforge/client/model/animation/package-info.java +++ b/src/main/java/net/minecraftforge/client/model/renderable/IMultipartRenderValues.java @@ -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 the type of value to be used for each part + */ +public interface IMultipartRenderValues +{ + /** + * 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); +} diff --git a/src/main/java/net/minecraftforge/client/model/renderable/IRenderable.java b/src/main/java/net/minecraftforge/client/model/renderable/IRenderable.java new file mode 100644 index 0000000000..a4b97d2b25 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/renderable/IRenderable.java @@ -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 the type of context object used by the rendering logic + */ +@FunctionalInterface +public interface IRenderable +{ + /** + * 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 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 withValues(T renderValues) + { + return (poseStack, bufferSource, renderTypeFunction, lightmapCoord, overlayCoord, partialTicks, unused) -> + this.render(poseStack, bufferSource, renderTypeFunction, lightmapCoord, overlayCoord, partialTicks, renderValues); + } +} diff --git a/src/main/java/net/minecraftforge/client/model/renderable/MultipartTransforms.java b/src/main/java/net/minecraftforge/client/model/renderable/MultipartTransforms.java new file mode 100644 index 0000000000..a9f3f62b04 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/renderable/MultipartTransforms.java @@ -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 +{ + /** + * 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 parts) + { + return new MultipartTransforms(parts); + } + + private final ImmutableMap parts; + + private MultipartTransforms(ImmutableMap parts) + { + this.parts = parts; + } + + @Nullable + @Override + public Matrix4f getPartValues(String part) + { + return parts.get(part); + } +} diff --git a/src/main/java/net/minecraftforge/client/model/renderable/SimpleRenderable.java b/src/main/java/net/minecraftforge/client/model/renderable/SimpleRenderable.java new file mode 100644 index 0000000000..c0440564a8 --- /dev/null +++ b/src/main/java/net/minecraftforge/client/model/renderable/SimpleRenderable.java @@ -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 +{ + private final List parts = new ArrayList<>(); + + private SimpleRenderable() + { + } + + public static Builder builder() + { + return new Builder(); + } + + @Override + public void render(PoseStack poseStack, MultiBufferSource bufferSource, Function 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 parts = new ArrayList<>(); + private final List meshes = new ArrayList<>(); + + public Part(String name) + { + this.name = name; + } + + public void render(PoseStack poseStack, MultiBufferSource bufferSource, Function 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 quads = new ArrayList<>(); + + public Mesh(ResourceLocation texture) + { + this.texture = texture; + } + + public void render(PoseStack poseStack, MultiBufferSource bufferSource, Function 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 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 + { + private final T parent; + private final Part part; + + private PartBuilder(T parent, Part part) + { + this.parent = parent; + this.part = part; + } + + public PartBuilder> child(String name) + { + var child = new Part(part.name + "/" + name); + this.part.parts.add(child); + return new PartBuilder<>(this, child); + } + + public PartBuilder addMesh(ResourceLocation texture, List quads) + { + var mesh = new Mesh(texture); + mesh.quads.addAll(quads); + part.meshes.add(mesh); + return this; + } + + public T end() + { + return parent; + } + } +} diff --git a/src/main/java/net/minecraftforge/client/textures/UnitSprite.java b/src/main/java/net/minecraftforge/client/textures/UnitSprite.java new file mode 100644 index 0000000000..32999a9a7b --- /dev/null +++ b/src/main/java/net/minecraftforge/client/textures/UnitSprite.java @@ -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 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; + } +} diff --git a/src/test/java/net/minecraftforge/debug/client/rendering/RenderableTest.java b/src/test/java/net/minecraftforge/debug/client/rendering/RenderableTest.java new file mode 100644 index 0000000000..1d7861f695 --- /dev/null +++ b/src/test/java/net/minecraftforge/debug/client/rendering/RenderableTest.java @@ -0,0 +1,134 @@ +/* + * 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.debug.client.rendering; + +import com.google.common.collect.ImmutableMap; +import com.mojang.math.Matrix4f; +import com.mojang.math.Quaternion; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.LightTexture; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.packs.resources.ResourceManager; +import net.minecraft.server.packs.resources.SimplePreparableReloadListener; +import net.minecraft.util.profiling.ProfilerFiller; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.event.RegisterClientReloadListenersEvent; +import net.minecraftforge.client.event.RenderLevelLastEvent; +import net.minecraftforge.client.model.StandaloneModelConfiguration; +import net.minecraftforge.client.model.obj.OBJLoader; +import net.minecraftforge.client.model.obj.OBJModel; +import net.minecraftforge.client.model.renderable.IRenderable; +import net.minecraftforge.client.model.renderable.MultipartTransforms; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; +import net.minecraftforge.fml.loading.FMLEnvironment; + +import java.util.Map; + +@Mod(RenderableTest.MODID) +public class RenderableTest +{ + public static final String MODID = "renderable_test"; + + public static final boolean ENABLED = false; + + public RenderableTest() + { + if (ENABLED) + { + if (FMLEnvironment.dist == Dist.CLIENT) + { + Client.init(); + } + } + } + + private static class Client + { + private static IRenderable renderable; + + public static void init() + { + var bus = FMLJavaModLoadingContext.get().getModEventBus(); + bus.addListener(Client::registerReloadListeners); + MinecraftForge.EVENT_BUS.addListener(Client::renderLast); + } + + public static void registerReloadListeners(RegisterClientReloadListenersEvent event) + { + event.registerReloadListener(new SimplePreparableReloadListener() + { + @Override + protected OBJModel prepare(ResourceManager resourceManager, ProfilerFiller profilerFiller) + { + var settings = new OBJModel.ModelSettings( + new ResourceLocation("new_model_loader_test:models/item/sugar_glider.obj"), + false, + true, + true, + false, + null + ); + return OBJLoader.INSTANCE.loadModel(settings); + } + + @Override + protected void apply(OBJModel model, ResourceManager resourceManager, ProfilerFiller profilerFiller) + { + var config = StandaloneModelConfiguration.create(Map.of( + "#qr", new ResourceLocation("minecraft:block/quartz_block_top") + )); + renderable = model.bakeRenderable(config); + } + }); + } + + private static double time; + + public static void renderLast(RenderLevelLastEvent event) + { + var poseStack = event.getPoseStack(); + var bufferSource = Minecraft.getInstance().renderBuffers().bufferSource(); + + time += Minecraft.getInstance().getDeltaFrameTime(); + + var map = ImmutableMap.builder(); + + var left = new Matrix4f(); + left.setIdentity(); + left.multiply(Quaternion.fromYXZ(0, 0, (float)Math.sin(time * 0.05) * 0.1f)); + map.put("object_1", left); + + var right = new Matrix4f(); + right.setIdentity(); + right.multiply(Quaternion.fromYXZ(0, 0, -(float)Math.sin(time * 0.05) * 0.1f)); + map.put("object_9", right); + + var transforms = MultipartTransforms.of(map.build()); + + renderable.render(poseStack, bufferSource, RenderType::entitySolid, LightTexture.FULL_BRIGHT, OverlayTexture.NO_OVERLAY, event.getPartialTick(), transforms); + + bufferSource.endBatch(); + } + } +} diff --git a/src/test/resources/META-INF/mods.toml b/src/test/resources/META-INF/mods.toml index 46ab655ce8..d305624ab0 100644 --- a/src/test/resources/META-INF/mods.toml +++ b/src/test/resources/META-INF/mods.toml @@ -178,6 +178,8 @@ license="LGPL v2.1" modId="duplicate_optional_tag_test" [[mods]] modId="custom_particle_type_test" +[[mods]] + modId="renderable_test" [[mods]] modId="ingredient_invalidation" [[mods]]