mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-01-27 05:17:56 +01:00
Summer squash git pasta
This commit is contained in:
parent
b2483def1d
commit
f5a6e5af0c
34 changed files with 1592 additions and 47 deletions
|
@ -8,6 +8,7 @@ import dev.engine_room.flywheel.api.event.RenderContext;
|
|||
import dev.engine_room.flywheel.api.visualization.VisualizationManager;
|
||||
import dev.engine_room.flywheel.backend.mixin.LevelRendererAccessor;
|
||||
import dev.engine_room.flywheel.lib.math.MatrixMath;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.BlockPos;
|
||||
|
@ -16,7 +17,7 @@ import net.minecraft.world.level.Level;
|
|||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public final class FrameUniforms extends UniformWriter {
|
||||
private static final int SIZE = 96 + 64 * 9 + 16 * 4 + 8 * 2 + 8 + 4 * 10;
|
||||
private static final int SIZE = 96 + 64 * 9 + 16 * 4 + 8 * 2 + 8 + 4 * 12;
|
||||
static final UniformBuffer BUFFER = new UniformBuffer(Uniforms.FRAME_INDEX, SIZE);
|
||||
|
||||
private static final Matrix4f VIEW = new Matrix4f();
|
||||
|
@ -150,11 +151,15 @@ public final class FrameUniforms extends UniformWriter {
|
|||
float partialTick = context.partialTick();
|
||||
float renderTicks = ticks + partialTick;
|
||||
float renderSeconds = renderTicks / 20f;
|
||||
float systemSeconds = Util.getMillis() / 1000f;
|
||||
int systemMillis = (int) (Util.getMillis() % Integer.MAX_VALUE);
|
||||
|
||||
ptr = writeInt(ptr, ticks);
|
||||
ptr = writeFloat(ptr, partialTick);
|
||||
ptr = writeFloat(ptr, renderTicks);
|
||||
ptr = writeFloat(ptr, renderSeconds);
|
||||
ptr = writeFloat(ptr, systemSeconds);
|
||||
ptr = writeInt(ptr, systemMillis);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,8 @@ layout(std140) uniform _FlwFrameUniforms {
|
|||
float flw_partialTick;
|
||||
float flw_renderTicks;
|
||||
float flw_renderSeconds;
|
||||
float flw_systemSeconds;
|
||||
uint flw_systemMillis;
|
||||
|
||||
/** 0 means no fluid. Use FLW_CAMERA_IN_FLUID_* defines to detect fluid type. */
|
||||
uint flw_cameraInFluid;
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
package dev.engine_room.flywheel.lib.internal;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
|
||||
import dev.engine_room.flywheel.api.internal.DependencyInjection;
|
||||
import dev.engine_room.flywheel.lib.transform.PoseTransformStack;
|
||||
import net.minecraft.client.model.geom.ModelPart;
|
||||
|
||||
public interface FlwLibLink {
|
||||
FlwLibLink INSTANCE = DependencyInjection.load(FlwLibLink.class, "dev.engine_room.flywheel.impl.FlwLibLinkImpl");
|
||||
|
@ -13,4 +17,8 @@ public interface FlwLibLink {
|
|||
Logger getLogger();
|
||||
|
||||
PoseTransformStack getPoseTransformStackOf(PoseStack stack);
|
||||
|
||||
Map<String, ModelPart> getModelPartChildren(ModelPart part);
|
||||
|
||||
void compileModelPart(ModelPart part, PoseStack.Pose pose, VertexConsumer consumer, int light, int overlay, float red, float green, float blue, float alpha);
|
||||
}
|
||||
|
|
|
@ -5,11 +5,13 @@ import org.jetbrains.annotations.Nullable;
|
|||
import dev.engine_room.flywheel.api.internal.DependencyInjection;
|
||||
import dev.engine_room.flywheel.lib.model.baked.BakedModelBuilder;
|
||||
import dev.engine_room.flywheel.lib.model.baked.BlockModelBuilder;
|
||||
import dev.engine_room.flywheel.lib.model.baked.ItemModelBuilder;
|
||||
import dev.engine_room.flywheel.lib.model.baked.MultiBlockModelBuilder;
|
||||
import dev.engine_room.flywheel.lib.util.ShadersModHandler;
|
||||
import net.minecraft.client.renderer.block.BlockRenderDispatcher;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
|
@ -26,4 +28,6 @@ public interface FlwLibXplat {
|
|||
|
||||
@Nullable
|
||||
ShadersModHandler.InternalHandler createIrisHandler();
|
||||
|
||||
ItemModelBuilder createItemModelBuilder(ItemStack stack, BakedModel model);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package dev.engine_room.flywheel.lib.material;
|
||||
|
||||
import dev.engine_room.flywheel.api.material.DepthTest;
|
||||
import dev.engine_room.flywheel.api.material.Material;
|
||||
import dev.engine_room.flywheel.api.material.Transparency;
|
||||
import dev.engine_room.flywheel.api.material.WriteMask;
|
||||
import net.minecraft.client.renderer.Sheets;
|
||||
import net.minecraft.client.renderer.entity.ItemRenderer;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public final class Materials {
|
||||
|
@ -69,6 +72,21 @@ public final class Materials {
|
|||
.mipmap(false)
|
||||
.build();
|
||||
|
||||
public static final Material GLINT = SimpleMaterial.builder()
|
||||
.texture(ItemRenderer.ENCHANTED_GLINT_ITEM)
|
||||
.shaders(StandardMaterialShaders.GLINT)
|
||||
.transparency(Transparency.GLINT)
|
||||
.writeMask(WriteMask.COLOR)
|
||||
.depthTest(DepthTest.EQUAL)
|
||||
.backfaceCulling(false)
|
||||
.blur(true)
|
||||
.mipmap(false)
|
||||
.build();
|
||||
|
||||
public static final Material GLINT_ENTITY = SimpleMaterial.builderOf(GLINT)
|
||||
.texture(ItemRenderer.ENCHANTED_GLINT_ENTITY)
|
||||
.build();
|
||||
|
||||
private Materials() {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -124,6 +124,38 @@ public class SimpleMaterial implements Material {
|
|||
return diffuse;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SimpleMaterial that = (SimpleMaterial) o;
|
||||
return blur == that.blur && mipmap == that.mipmap && backfaceCulling == that.backfaceCulling && polygonOffset == that.polygonOffset && useOverlay == that.useOverlay && useLight == that.useLight && diffuse == that.diffuse && shaders.equals(that.shaders) && fog.equals(that.fog) && cutout.equals(that.cutout) && texture.equals(that.texture) && depthTest == that.depthTest && transparency == that.transparency && writeMask == that.writeMask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = shaders.hashCode();
|
||||
result = 31 * result + fog.hashCode();
|
||||
result = 31 * result + cutout.hashCode();
|
||||
result = 31 * result + texture.hashCode();
|
||||
result = 31 * result + Boolean.hashCode(blur);
|
||||
result = 31 * result + Boolean.hashCode(mipmap);
|
||||
result = 31 * result + Boolean.hashCode(backfaceCulling);
|
||||
result = 31 * result + Boolean.hashCode(polygonOffset);
|
||||
result = 31 * result + depthTest.hashCode();
|
||||
result = 31 * result + transparency.hashCode();
|
||||
result = 31 * result + writeMask.hashCode();
|
||||
result = 31 * result + Boolean.hashCode(useOverlay);
|
||||
result = 31 * result + Boolean.hashCode(useLight);
|
||||
result = 31 * result + Boolean.hashCode(diffuse);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static class Builder implements Material {
|
||||
protected MaterialShaders shaders;
|
||||
protected FogShader fog;
|
||||
|
|
|
@ -14,6 +14,8 @@ public final class StandardMaterialShaders {
|
|||
|
||||
public static final MaterialShaders LINE = MaterialShaders.REGISTRY.registerAndGet(new SimpleMaterialShaders(Flywheel.rl("material/lines.vert"), Flywheel.rl("material/lines.frag")));
|
||||
|
||||
public static final MaterialShaders GLINT = MaterialShaders.REGISTRY.registerAndGet(new SimpleMaterialShaders(Flywheel.rl("material/glint.vert"), Flywheel.rl("material/default.frag")));
|
||||
|
||||
private StandardMaterialShaders() {
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import dev.engine_room.flywheel.lib.material.Materials;
|
|||
import dev.engine_room.flywheel.lib.memory.MemoryBlock;
|
||||
import dev.engine_room.flywheel.lib.vertex.PosVertexView;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.Sheets;
|
||||
import net.minecraft.client.renderer.block.BlockRenderDispatcher;
|
||||
|
||||
public final class ModelUtil {
|
||||
|
@ -48,6 +49,35 @@ public final class ModelUtil {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Material getItemMaterial(RenderType renderType, boolean shaded) {
|
||||
if (renderType == RenderType.solid()) {
|
||||
return shaded ? Materials.CHUNK_SOLID_SHADED : Materials.CHUNK_SOLID_UNSHADED;
|
||||
}
|
||||
if (renderType == RenderType.cutoutMipped()) {
|
||||
return shaded ? Materials.CHUNK_CUTOUT_MIPPED_SHADED : Materials.CHUNK_CUTOUT_MIPPED_UNSHADED;
|
||||
}
|
||||
if (renderType == RenderType.cutout() || renderType == Sheets.cutoutBlockSheet()) {
|
||||
return shaded ? Materials.CHUNK_CUTOUT_SHADED : Materials.CHUNK_CUTOUT_UNSHADED;
|
||||
}
|
||||
if (renderType == RenderType.translucent()) {
|
||||
return shaded ? Materials.CHUNK_TRANSLUCENT_SHADED : Materials.CHUNK_TRANSLUCENT_UNSHADED;
|
||||
}
|
||||
if (renderType == RenderType.tripwire()) {
|
||||
return shaded ? Materials.CHUNK_TRIPWIRE_SHADED : Materials.CHUNK_TRIPWIRE_UNSHADED;
|
||||
}
|
||||
if (renderType == Sheets.translucentCullBlockSheet() || renderType == Sheets.translucentItemSheet()) {
|
||||
return shaded ? Materials.CHUNK_CUTOUT_SHADED : Materials.CHUNK_CUTOUT_UNSHADED;
|
||||
}
|
||||
if (renderType == RenderType.glint() || renderType == RenderType.glintDirect()) {
|
||||
return Materials.GLINT;
|
||||
}
|
||||
if (renderType == RenderType.entityGlint() || renderType == RenderType.entityGlintDirect()) {
|
||||
return Materials.GLINT_ENTITY;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static int computeTotalVertexCount(Iterable<Mesh> meshes) {
|
||||
int vertexCount = 0;
|
||||
for (Mesh mesh : meshes) {
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
package dev.engine_room.flywheel.lib.model;
|
||||
|
||||
import org.joml.Vector4fc;
|
||||
|
||||
import dev.engine_room.flywheel.api.model.IndexSequence;
|
||||
import dev.engine_room.flywheel.api.model.Mesh;
|
||||
import dev.engine_room.flywheel.api.vertex.MutableVertexList;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
|
||||
public record RetexturedMesh(Mesh mesh, TextureAtlasSprite sprite) implements Mesh {
|
||||
@Override
|
||||
public int vertexCount() {
|
||||
return mesh.vertexCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(MutableVertexList vertexList) {
|
||||
mesh.write(new RetexturingVertexList(vertexList, sprite));
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexSequence indexSequence() {
|
||||
return mesh.indexSequence();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int indexCount() {
|
||||
return mesh.indexCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector4fc boundingSphere() {
|
||||
return mesh.boundingSphere();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package dev.engine_room.flywheel.lib.model;
|
||||
|
||||
import dev.engine_room.flywheel.api.vertex.MutableVertexList;
|
||||
import dev.engine_room.flywheel.lib.vertex.WrappedVertexList;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
|
||||
/**
|
||||
* A wrapper so that differently textured models from the same mesh tree can share the same backing memory
|
||||
*/
|
||||
public class RetexturingVertexList extends WrappedVertexList {
|
||||
private final TextureAtlasSprite sprite;
|
||||
|
||||
public RetexturingVertexList(MutableVertexList delegate, TextureAtlasSprite sprite) {
|
||||
super(delegate);
|
||||
|
||||
this.sprite = sprite;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void u(int index, float u) {
|
||||
super.u(index, sprite.getU(u * 16));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void v(int index, float v) {
|
||||
super.v(index, sprite.getV(v * 16));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package dev.engine_room.flywheel.lib.model.baked;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
|
||||
import dev.engine_room.flywheel.api.material.Material;
|
||||
import dev.engine_room.flywheel.lib.internal.FlwLibXplat;
|
||||
import dev.engine_room.flywheel.lib.model.SimpleModel;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.world.item.ItemDisplayContext;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
@ApiStatus.NonExtendable
|
||||
public abstract class ItemModelBuilder {
|
||||
final ItemStack itemStack;
|
||||
final BakedModel model;
|
||||
@Nullable
|
||||
PoseStack poseStack;
|
||||
@Nullable
|
||||
ItemDisplayContext displayContext;
|
||||
boolean leftHand;
|
||||
@Nullable
|
||||
BiFunction<RenderType, Boolean, Material> materialFunc;
|
||||
|
||||
ItemModelBuilder(ItemStack itemStack, BakedModel model) {
|
||||
this.itemStack = itemStack;
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public static ItemModelBuilder create(ItemStack stack, BakedModel model) {
|
||||
return FlwLibXplat.INSTANCE.createItemModelBuilder(stack, model);
|
||||
}
|
||||
|
||||
public ItemModelBuilder poseStack(PoseStack poseStack) {
|
||||
this.poseStack = poseStack;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemModelBuilder displayContext(ItemDisplayContext displayContext) {
|
||||
this.displayContext = displayContext;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemModelBuilder leftHand(boolean leftHand) {
|
||||
this.leftHand = leftHand;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
|
||||
this.materialFunc = materialFunc;
|
||||
return this;
|
||||
}
|
||||
|
||||
public abstract SimpleModel build();
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
package dev.engine_room.flywheel.lib.model.part;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.ObjIntConsumer;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Quaternionf;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
|
||||
import dev.engine_room.flywheel.api.instance.InstancerProvider;
|
||||
import dev.engine_room.flywheel.api.material.Material;
|
||||
import dev.engine_room.flywheel.api.model.Mesh;
|
||||
import dev.engine_room.flywheel.lib.instance.InstanceTypes;
|
||||
import dev.engine_room.flywheel.lib.instance.TransformedInstance;
|
||||
import dev.engine_room.flywheel.lib.model.ModelCache;
|
||||
import dev.engine_room.flywheel.lib.model.SingleMeshModel;
|
||||
import net.minecraft.client.model.geom.ModelPart;
|
||||
import net.minecraft.client.model.geom.PartPose;
|
||||
|
||||
public class InstanceTree {
|
||||
private static final ModelCache<Entry> CACHE = new ModelCache<>(entry -> new SingleMeshModel(entry.mesh(), entry.material()));
|
||||
|
||||
public float x;
|
||||
public float y;
|
||||
public float z;
|
||||
public float xRot;
|
||||
public float yRot;
|
||||
public float zRot;
|
||||
public float xScale = ModelPart.DEFAULT_SCALE;
|
||||
public float yScale = ModelPart.DEFAULT_SCALE;
|
||||
public float zScale = ModelPart.DEFAULT_SCALE;
|
||||
public boolean visible = true;
|
||||
public boolean skipDraw;
|
||||
|
||||
@Nullable
|
||||
public TransformedInstance instance;
|
||||
|
||||
private final Quaternionf rotation = new Quaternionf();
|
||||
private final Entry entry;
|
||||
private final PartPose initialPose;
|
||||
|
||||
private final Map<String, InstanceTree> children;
|
||||
|
||||
private InstanceTree(InstancerProvider provider, Entry entry, Map<String, InstanceTree> children, PartPose initialPose) {
|
||||
this.entry = entry;
|
||||
this.children = children;
|
||||
this.initialPose = initialPose;
|
||||
|
||||
if (entry.mesh() != null) {
|
||||
this.instance = provider.instancer(InstanceTypes.TRANSFORMED, CACHE.get(entry))
|
||||
.createInstance();
|
||||
}
|
||||
|
||||
this.x = initialPose.x;
|
||||
this.y = initialPose.y;
|
||||
this.z = initialPose.z;
|
||||
this.xRot = initialPose.xRot;
|
||||
this.yRot = initialPose.yRot;
|
||||
this.zRot = initialPose.zRot;
|
||||
}
|
||||
|
||||
public static InstanceTree create(InstancerProvider provider, MeshTree meshTree, Material material) {
|
||||
Map<String, InstanceTree> children = new HashMap<>();
|
||||
for (Map.Entry<String, MeshTree> child : meshTree.children()
|
||||
.entrySet()) {
|
||||
var childTree = InstanceTree.create(provider, child.getValue(), material);
|
||||
|
||||
children.put(child.getKey(), childTree);
|
||||
}
|
||||
|
||||
return new InstanceTree(provider, new Entry(meshTree.mesh(), material), children, meshTree.initialPose());
|
||||
}
|
||||
|
||||
public void offsetPos(Vector3f pOffset) {
|
||||
this.x += pOffset.x();
|
||||
this.y += pOffset.y();
|
||||
this.z += pOffset.z();
|
||||
}
|
||||
|
||||
public void offsetRotation(Vector3f pOffset) {
|
||||
this.xRot += pOffset.x();
|
||||
this.yRot += pOffset.y();
|
||||
this.zRot += pOffset.z();
|
||||
}
|
||||
|
||||
public void offsetScale(Vector3f pOffset) {
|
||||
this.xScale += pOffset.x();
|
||||
this.yScale += pOffset.y();
|
||||
this.zScale += pOffset.z();
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
if (instance != null) {
|
||||
instance.delete();
|
||||
}
|
||||
children.values()
|
||||
.forEach(InstanceTree::delete);
|
||||
}
|
||||
|
||||
public InstanceTree child(String name) {
|
||||
return children.get(name);
|
||||
}
|
||||
|
||||
public void walkInstances(Consumer<TransformedInstance> consumer) {
|
||||
if (instance != null) {
|
||||
consumer.accept(instance);
|
||||
}
|
||||
for (InstanceTree child : children.values()) {
|
||||
child.walkInstances(consumer);
|
||||
}
|
||||
}
|
||||
|
||||
public void walkInstances(int i, ObjIntConsumer<TransformedInstance> consumer) {
|
||||
if (instance != null) {
|
||||
consumer.accept(instance, i);
|
||||
}
|
||||
for (InstanceTree child : children.values()) {
|
||||
child.walkInstances(i, consumer);
|
||||
}
|
||||
}
|
||||
|
||||
public void walkInstances(int i, int j, ObjIntIntConsumer<TransformedInstance> consumer) {
|
||||
if (instance != null) {
|
||||
consumer.accept(instance, i, j);
|
||||
}
|
||||
for (InstanceTree child : children.values()) {
|
||||
child.walkInstances(i, j, consumer);
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ObjIntIntConsumer<T> {
|
||||
void accept(T t, int i, int j);
|
||||
}
|
||||
|
||||
public void updateInstances(PoseStack pPoseStack) {
|
||||
if (this.visible) {
|
||||
pPoseStack.pushPose();
|
||||
this.translateAndRotate(pPoseStack);
|
||||
if (!this.skipDraw && instance != null) {
|
||||
instance.setTransform(pPoseStack.last())
|
||||
.setChanged();
|
||||
}
|
||||
|
||||
for (InstanceTree modelpart : this.children.values()) {
|
||||
modelpart.updateInstances(pPoseStack);
|
||||
}
|
||||
|
||||
pPoseStack.popPose();
|
||||
}
|
||||
}
|
||||
|
||||
public void translateAndRotate(PoseStack pPoseStack) {
|
||||
pPoseStack.translate(this.x / 16.0F, this.y / 16.0F, this.z / 16.0F);
|
||||
if (this.xRot != 0.0F || this.yRot != 0.0F || this.zRot != 0.0F) {
|
||||
pPoseStack.mulPose(rotation.rotationZYX(this.zRot, this.yRot, this.xRot));
|
||||
}
|
||||
|
||||
if (this.xScale != 1.0F || this.yScale != 1.0F || this.zScale != 1.0F) {
|
||||
pPoseStack.scale(this.xScale, this.yScale, this.zScale);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private record Entry(@Nullable Mesh mesh, Material material) {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
package dev.engine_room.flywheel.lib.model.part;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
|
||||
import dev.engine_room.flywheel.api.model.Mesh;
|
||||
import dev.engine_room.flywheel.lib.internal.FlwLibLink;
|
||||
import dev.engine_room.flywheel.lib.model.SimpleMesh;
|
||||
import dev.engine_room.flywheel.lib.vertex.PosTexNormalVertexView;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.model.geom.EntityModelSet;
|
||||
import net.minecraft.client.model.geom.ModelLayerLocation;
|
||||
import net.minecraft.client.model.geom.ModelPart;
|
||||
import net.minecraft.client.model.geom.PartPose;
|
||||
|
||||
public class MeshTree {
|
||||
private static final ThreadLocal<ThreadLocalObjects> THREAD_LOCAL_OBJECTS = ThreadLocal.withInitial(ThreadLocalObjects::new);
|
||||
private static final PoseStack.Pose IDENTITY = new PoseStack().last();
|
||||
private static final Map<ModelLayerLocation, MeshTree> MESH_TREES = new ConcurrentHashMap<>();
|
||||
|
||||
private final PartPose initialPose;
|
||||
@Nullable
|
||||
private final Mesh mesh;
|
||||
private final Map<String, MeshTree> children;
|
||||
|
||||
private MeshTree(PartPose initialPose, @Nullable Mesh mesh, Map<String, MeshTree> children) {
|
||||
this.initialPose = initialPose;
|
||||
this.mesh = mesh;
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
public static MeshTree get(ModelLayerLocation key) {
|
||||
return MESH_TREES.computeIfAbsent(key, MeshTree::convert);
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static void onEndClientResourceReload() {
|
||||
MESH_TREES.values()
|
||||
.forEach(MeshTree::delete);
|
||||
MESH_TREES.clear();
|
||||
}
|
||||
|
||||
public PartPose initialPose() {
|
||||
return initialPose;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Mesh mesh() {
|
||||
return mesh;
|
||||
}
|
||||
|
||||
public Map<String, MeshTree> children() {
|
||||
return children;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public MeshTree child(String name) {
|
||||
return children.get(name);
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
if (mesh != null) {
|
||||
mesh.delete();
|
||||
}
|
||||
children.values()
|
||||
.forEach(MeshTree::delete);
|
||||
}
|
||||
|
||||
public static MeshTree convert(ModelLayerLocation location) {
|
||||
EntityModelSet entityModels = Minecraft.getInstance()
|
||||
.getEntityModels();
|
||||
ModelPart modelPart = entityModels.bakeLayer(location);
|
||||
|
||||
return convert(modelPart, THREAD_LOCAL_OBJECTS.get());
|
||||
}
|
||||
|
||||
private static MeshTree convert(ModelPart modelPart, ThreadLocalObjects objects) {
|
||||
var childModelParts = FlwLibLink.INSTANCE.getModelPartChildren(modelPart);
|
||||
|
||||
Map<String, MeshTree> children = new HashMap<>();
|
||||
|
||||
for (Map.Entry<String, ModelPart> entry : childModelParts.entrySet()) {
|
||||
children.put(entry.getKey(), convert(entry.getValue(), objects));
|
||||
}
|
||||
|
||||
return new MeshTree(modelPart.getInitialPose(), compile(modelPart, objects), children);
|
||||
}
|
||||
|
||||
private static Mesh compile(ModelPart modelPart, ThreadLocalObjects objects) {
|
||||
var vertexWriter = objects.vertexWriter;
|
||||
|
||||
FlwLibLink.INSTANCE.compileModelPart(modelPart, IDENTITY, vertexWriter, 0, 0, 1.0F, 1.0F, 1.0F, 1.0F);
|
||||
|
||||
var data = vertexWriter.copyDataAndReset();
|
||||
|
||||
return new SimpleMesh(new PosTexNormalVertexView(), data, "source=ModelPartConverter");
|
||||
}
|
||||
|
||||
private static class ThreadLocalObjects {
|
||||
public final VertexWriter vertexWriter = new VertexWriter();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
package dev.engine_room.flywheel.lib.vertex;
|
||||
|
||||
import dev.engine_room.flywheel.api.vertex.MutableVertexList;
|
||||
|
||||
public class WrappedVertexList implements MutableVertexList {
|
||||
protected final MutableVertexList delegate;
|
||||
|
||||
public WrappedVertexList(MutableVertexList delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void x(int index, float x) {
|
||||
delegate.x(index, x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void y(int index, float y) {
|
||||
delegate.y(index, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void z(int index, float z) {
|
||||
delegate.z(index, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void r(int index, float r) {
|
||||
delegate.r(index, r);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void g(int index, float g) {
|
||||
delegate.g(index, g);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void b(int index, float b) {
|
||||
delegate.b(index, b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void a(int index, float a) {
|
||||
delegate.a(index, a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void u(int index, float u) {
|
||||
delegate.u(index, u);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void v(int index, float v) {
|
||||
delegate.v(index, v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void overlay(int index, int overlay) {
|
||||
delegate.overlay(index, overlay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void light(int index, int light) {
|
||||
delegate.light(index, light);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void normalX(int index, float normalX) {
|
||||
delegate.normalX(index, normalX);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void normalY(int index, float normalY) {
|
||||
delegate.normalY(index, normalY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void normalZ(int index, float normalZ) {
|
||||
delegate.normalZ(index, normalZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float x(int index) {
|
||||
return delegate.x(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float y(int index) {
|
||||
return delegate.y(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float z(int index) {
|
||||
return delegate.z(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float r(int index) {
|
||||
return delegate.r(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float g(int index) {
|
||||
return delegate.g(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float b(int index) {
|
||||
return delegate.b(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float a(int index) {
|
||||
return delegate.a(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float u(int index) {
|
||||
return delegate.u(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float v(int index) {
|
||||
return delegate.v(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int overlay(int index) {
|
||||
return delegate.overlay(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int light(int index) {
|
||||
return delegate.light(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float normalX(int index) {
|
||||
return delegate.normalX(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float normalY(int index) {
|
||||
return delegate.normalY(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float normalZ(int index) {
|
||||
return delegate.normalZ(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int vertexCount() {
|
||||
return delegate.vertexCount();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
void flw_materialVertex() {
|
||||
float p = flw_glintSpeedOption * flw_systemSeconds * 8.;
|
||||
|
||||
flw_vertexTexCoord *= 8.;
|
||||
// Rotate by 0.17453292 radians
|
||||
flw_vertexTexCoord *= mat2(0.98480775, 0.17364817, -0.17364817, 0.98480775);
|
||||
flw_vertexTexCoord += vec2(-p / 110., p / 30.);
|
||||
}
|
|
@ -1,12 +1,17 @@
|
|||
package dev.engine_room.flywheel.impl;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
|
||||
import dev.engine_room.flywheel.impl.extension.PoseStackExtension;
|
||||
import dev.engine_room.flywheel.impl.mixin.ModelPartAccessor;
|
||||
import dev.engine_room.flywheel.lib.internal.FlwLibLink;
|
||||
import dev.engine_room.flywheel.lib.transform.PoseTransformStack;
|
||||
import net.minecraft.client.model.geom.ModelPart;
|
||||
|
||||
public class FlwLibLinkImpl implements FlwLibLink {
|
||||
@Override
|
||||
|
@ -18,4 +23,14 @@ public class FlwLibLinkImpl implements FlwLibLink {
|
|||
public PoseTransformStack getPoseTransformStackOf(PoseStack stack) {
|
||||
return ((PoseStackExtension) stack).flywheel$transformStack();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ModelPart> getModelPartChildren(ModelPart part) {
|
||||
return ((ModelPartAccessor) (Object) part).flywheel$children();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compileModelPart(ModelPart part, PoseStack.Pose pose, VertexConsumer consumer, int light, int overlay, float red, float green, float blue, float alpha) {
|
||||
((ModelPartAccessor) (Object) part).flywheel$compile(pose, consumer, light, overlay, red, green, blue, alpha);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
package dev.engine_room.flywheel.impl.mixin;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import dev.engine_room.flywheel.api.visualization.VisualizationManager;
|
||||
import dev.engine_room.flywheel.lib.visual.VisualizationHelper;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
|
||||
@Mixin(ClientLevel.class)
|
||||
abstract class ClientLevelMixin {
|
||||
@Inject(method = "entitiesForRendering()Ljava/lang/Iterable;", at = @At("RETURN"), cancellable = true)
|
||||
private void flywheel$filterEntities(CallbackInfoReturnable<Iterable<Entity>> cir) {
|
||||
if (!VisualizationManager.supportsVisualization((ClientLevel) (Object) this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Iterable<Entity> entities = cir.getReturnValue();
|
||||
ArrayList<Entity> filtered = Lists.newArrayList(entities);
|
||||
|
||||
filtered.removeIf(VisualizationHelper::shouldSkipRender);
|
||||
|
||||
cir.setReturnValue(filtered);
|
||||
}
|
||||
}
|
|
@ -18,17 +18,21 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
|||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
|
||||
import dev.engine_room.flywheel.api.event.RenderStage;
|
||||
import dev.engine_room.flywheel.api.visualization.VisualizationManager;
|
||||
import dev.engine_room.flywheel.impl.FlwImplXplat;
|
||||
import dev.engine_room.flywheel.impl.event.RenderContextImpl;
|
||||
import dev.engine_room.flywheel.impl.visualization.VisualizationManagerImpl;
|
||||
import dev.engine_room.flywheel.lib.visual.VisualizationHelper;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.renderer.GameRenderer;
|
||||
import net.minecraft.client.renderer.LevelRenderer;
|
||||
import net.minecraft.client.renderer.LightTexture;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.RenderBuffers;
|
||||
import net.minecraft.server.level.BlockDestructionProgress;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
|
||||
@Mixin(value = LevelRenderer.class, priority = 1001) // Higher priority to go after Sodium
|
||||
abstract class LevelRendererMixin {
|
||||
|
@ -78,6 +82,14 @@ abstract class LevelRendererMixin {
|
|||
}
|
||||
}
|
||||
|
||||
// ENTITY CANCELLING
|
||||
@Inject(method = "renderEntity", at = @At("HEAD"), cancellable = true)
|
||||
private void flywheel$decideNotToRenderEntity(Entity pEntity, double pCamX, double pCamY, double pCamZ, float pPartialTick, PoseStack pPoseStack, MultiBufferSource pBufferSource, CallbackInfo ci) {
|
||||
if (VisualizationManager.supportsVisualization(pEntity.level()) && VisualizationHelper.shouldSkipRender(pEntity)) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
// STAGE DISPATCHING
|
||||
|
||||
@Unique
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
package dev.engine_room.flywheel.impl.mixin;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
|
||||
import net.minecraft.client.model.geom.ModelPart;
|
||||
|
||||
@Mixin(ModelPart.class)
|
||||
public interface ModelPartAccessor {
|
||||
@Accessor("children")
|
||||
Map<String, ModelPart> flywheel$children();
|
||||
|
||||
@Invoker("compile")
|
||||
void flywheel$compile(PoseStack.Pose pPose, VertexConsumer pVertexConsumer, int pPackedLight, int pPackedOverlay, float pRed, float pGreen, float pBlue, float pAlpha);
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package dev.engine_room.flywheel.vanilla;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
|
||||
import dev.engine_room.flywheel.lib.model.part.InstanceTree;
|
||||
|
||||
public abstract class AgeableListComponent {
|
||||
public float attackTime;
|
||||
public boolean riding;
|
||||
public boolean young = true;
|
||||
protected final Config config;
|
||||
|
||||
public AgeableListComponent(Config config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public void updateInstances(PoseStack pPoseStack) {
|
||||
if (this.young) {
|
||||
pPoseStack.pushPose();
|
||||
if (this.config.scaleHead) {
|
||||
float f = 1.5F / this.config.babyHeadScale;
|
||||
pPoseStack.scale(f, f, f);
|
||||
}
|
||||
|
||||
pPoseStack.translate(0.0F, this.config.babyYHeadOffset / 16.0F, this.config.babyZHeadOffset / 16.0F);
|
||||
for (InstanceTree headPart : this.headParts()) {
|
||||
headPart.updateInstances(pPoseStack);
|
||||
}
|
||||
pPoseStack.popPose();
|
||||
pPoseStack.pushPose();
|
||||
float f1 = 1.0F / this.config.babyBodyScale;
|
||||
pPoseStack.scale(f1, f1, f1);
|
||||
pPoseStack.translate(0.0F, this.config.bodyYOffset / 16.0F, 0.0F);
|
||||
for (InstanceTree bodyPart : this.bodyParts()) {
|
||||
bodyPart.updateInstances(pPoseStack);
|
||||
}
|
||||
pPoseStack.popPose();
|
||||
} else {
|
||||
for (InstanceTree headPart : this.headParts()) {
|
||||
headPart.updateInstances(pPoseStack);
|
||||
}
|
||||
for (InstanceTree bodyPart : this.bodyParts()) {
|
||||
bodyPart.updateInstances(pPoseStack);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected abstract Iterable<InstanceTree> headParts();
|
||||
|
||||
protected abstract Iterable<InstanceTree> bodyParts();
|
||||
|
||||
public record Config(boolean scaleHead, float babyYHeadOffset, float babyZHeadOffset, float babyHeadScale,
|
||||
float babyBodyScale, float bodyYOffset) {
|
||||
}
|
||||
}
|
|
@ -8,14 +8,16 @@ import java.util.function.Consumer;
|
|||
import org.joml.Quaternionf;
|
||||
|
||||
import dev.engine_room.flywheel.api.instance.Instance;
|
||||
import dev.engine_room.flywheel.api.model.Model;
|
||||
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
|
||||
import dev.engine_room.flywheel.lib.instance.InstanceTypes;
|
||||
import dev.engine_room.flywheel.lib.instance.OrientedInstance;
|
||||
import dev.engine_room.flywheel.lib.instance.TransformedInstance;
|
||||
import dev.engine_room.flywheel.lib.material.Materials;
|
||||
import dev.engine_room.flywheel.lib.model.ModelCache;
|
||||
import dev.engine_room.flywheel.lib.model.RetexturedMesh;
|
||||
import dev.engine_room.flywheel.lib.model.SingleMeshModel;
|
||||
import dev.engine_room.flywheel.lib.model.part.ModelPartConverter;
|
||||
import dev.engine_room.flywheel.lib.model.part.MeshTree;
|
||||
import dev.engine_room.flywheel.lib.util.Pair;
|
||||
import dev.engine_room.flywheel.lib.visual.AbstractBlockEntityVisual;
|
||||
import dev.engine_room.flywheel.lib.visual.SimpleDynamicVisual;
|
||||
|
@ -42,13 +44,13 @@ public class ChestVisual<T extends BlockEntity & LidBlockEntity> extends Abstrac
|
|||
}
|
||||
|
||||
private static final ModelCache<Pair<ChestType, Material>> BOTTOM_MODELS = new ModelCache<>(key -> {
|
||||
return new SingleMeshModel(ModelPartConverter.convert(LAYER_LOCATIONS.get(key.first()), key.second().sprite(), "bottom"), Materials.CHEST);
|
||||
return chestModel(key, "bottom");
|
||||
});
|
||||
private static final ModelCache<Pair<ChestType, Material>> LID_MODELS = new ModelCache<>(key -> {
|
||||
return new SingleMeshModel(ModelPartConverter.convert(LAYER_LOCATIONS.get(key.first()), key.second().sprite(), "lid"), Materials.CHEST);
|
||||
return chestModel(key, "lid");
|
||||
});
|
||||
private static final ModelCache<Pair<ChestType, Material>> LOCK_MODELS = new ModelCache<>(key -> {
|
||||
return new SingleMeshModel(ModelPartConverter.convert(LAYER_LOCATIONS.get(key.first()), key.second().sprite(), "lock"), Materials.CHEST);
|
||||
return chestModel(key, "lock");
|
||||
});
|
||||
|
||||
private final OrientedInstance bottom;
|
||||
|
@ -137,15 +139,13 @@ public class ChestVisual<T extends BlockEntity & LidBlockEntity> extends Abstrac
|
|||
.rotateCentered(baseRotation)
|
||||
.translate(0, 9f / 16f, 1f / 16f)
|
||||
.rotateX(angleX)
|
||||
.translate(0, -9f / 16f, -1f / 16f)
|
||||
.setChanged();
|
||||
|
||||
lock.loadIdentity()
|
||||
.translate(getVisualPosition())
|
||||
.rotateCentered(baseRotation)
|
||||
.translate(0, 8f / 16f, 0)
|
||||
.translate(0, 9f / 16f, 1f / 16f)
|
||||
.rotateX(angleX)
|
||||
.translate(0, -8f / 16f, 0)
|
||||
.setChanged();
|
||||
}
|
||||
|
||||
|
@ -167,4 +167,13 @@ public class ChestVisual<T extends BlockEntity & LidBlockEntity> extends Abstrac
|
|||
lid.delete();
|
||||
lock.delete();
|
||||
}
|
||||
|
||||
public static Model chestModel(Pair<ChestType, Material> key, String child) {
|
||||
var mesh = MeshTree.get(LAYER_LOCATIONS.get(key.first()))
|
||||
.child(child)
|
||||
.mesh();
|
||||
var sprite = key.second()
|
||||
.sprite();
|
||||
return new SingleMeshModel(new RetexturedMesh(mesh, sprite), Materials.CHEST);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,218 @@
|
|||
package dev.engine_room.flywheel.vanilla;
|
||||
|
||||
import static net.minecraft.client.renderer.entity.LivingEntityRenderer.getOverlayCoords;
|
||||
import static net.minecraft.client.renderer.entity.LivingEntityRenderer.isEntityUpsideDown;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.math.Axis;
|
||||
|
||||
import dev.engine_room.flywheel.api.material.Material;
|
||||
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
|
||||
import dev.engine_room.flywheel.lib.material.SimpleMaterial;
|
||||
import dev.engine_room.flywheel.lib.transform.TransformStack;
|
||||
import dev.engine_room.flywheel.lib.visual.SimpleEntityVisual;
|
||||
import dev.engine_room.flywheel.lib.visual.component.FireComponent;
|
||||
import dev.engine_room.flywheel.lib.visual.component.HitboxComponent;
|
||||
import dev.engine_room.flywheel.lib.visual.component.ShadowComponent;
|
||||
import net.minecraft.client.model.geom.ModelLayers;
|
||||
import net.minecraft.client.renderer.LightTexture;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.entity.Pose;
|
||||
import net.minecraft.world.entity.animal.Cow;
|
||||
import net.minecraft.world.level.LightLayer;
|
||||
|
||||
public class CowVisual extends SimpleEntityVisual<Cow> {
|
||||
private static final ResourceLocation COW_LOCATION = new ResourceLocation("textures/entity/cow/cow.png");
|
||||
|
||||
public static final AgeableListComponent.Config COW_CONFIG = new AgeableListComponent.Config(false, 10.0F, 4.0F, 2.0F, 2.0F, 24);
|
||||
|
||||
public static final Material COW_MATERIAL = SimpleMaterial.builder()
|
||||
.texture(COW_LOCATION)
|
||||
.build();
|
||||
|
||||
private final QuadrupedComponent cowQuadrupedComponent;
|
||||
|
||||
private final PoseStack stack = new PoseStack();
|
||||
|
||||
public CowVisual(VisualizationContext ctx, Cow entity, float partialTick) {
|
||||
super(ctx, entity, partialTick);
|
||||
|
||||
cowQuadrupedComponent = new QuadrupedComponent(instancerProvider, ModelLayers.COW, COW_MATERIAL, COW_CONFIG);
|
||||
|
||||
addComponent(new ShadowComponent(visualizationContext, entity).radius(0.7f));
|
||||
addComponent(new HitboxComponent(visualizationContext, entity));
|
||||
addComponent(new FireComponent(visualizationContext, entity));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginFrame(Context ctx) {
|
||||
if (!isVisible(ctx.frustum())) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.beginFrame(ctx);
|
||||
|
||||
int overlay = getOverlayCoords(entity, this.getWhiteOverlayProgress(ctx.partialTick()));
|
||||
int light = LightTexture.pack(level.getBrightness(LightLayer.BLOCK, entity.blockPosition()), level.getBrightness(LightLayer.SKY, entity.blockPosition()));
|
||||
cowQuadrupedComponent.root.walkInstances(overlay, light, (i, o, l) -> {
|
||||
i.overlay(o);
|
||||
i.light(l);
|
||||
// We'll #setChanged later
|
||||
});
|
||||
|
||||
stack.setIdentity();
|
||||
TransformStack.of(stack)
|
||||
.translate(getVisualPosition(ctx.partialTick()));
|
||||
|
||||
boolean shouldSit = entity.isPassenger(); // && (entity.getVehicle() != null && entity.getVehicle().shouldRiderSit());
|
||||
this.cowQuadrupedComponent.riding = shouldSit;
|
||||
this.cowQuadrupedComponent.young = entity.isBaby();
|
||||
|
||||
float yBodyRot = Mth.rotLerp(ctx.partialTick(), entity.yBodyRotO, entity.yBodyRot);
|
||||
float yHeadRot = Mth.rotLerp(ctx.partialTick(), entity.yHeadRotO, entity.yHeadRot);
|
||||
float diffRot = yHeadRot - yBodyRot;
|
||||
if (shouldSit && entity.getVehicle() instanceof LivingEntity livingentity) {
|
||||
yBodyRot = Mth.rotLerp(ctx.partialTick(), livingentity.yBodyRotO, livingentity.yBodyRot);
|
||||
diffRot = yHeadRot - yBodyRot;
|
||||
float f3 = Mth.wrapDegrees(diffRot);
|
||||
if (f3 < -85.0F) {
|
||||
f3 = -85.0F;
|
||||
}
|
||||
|
||||
if (f3 >= 85.0F) {
|
||||
f3 = 85.0F;
|
||||
}
|
||||
|
||||
yBodyRot = yHeadRot - f3;
|
||||
if (f3 * f3 > 2500.0F) {
|
||||
yBodyRot += f3 * 0.2F;
|
||||
}
|
||||
|
||||
diffRot = yHeadRot - yBodyRot;
|
||||
}
|
||||
|
||||
float xRot = Mth.lerp(ctx.partialTick(), entity.xRotO, entity.getXRot());
|
||||
if (isEntityUpsideDown(entity)) {
|
||||
xRot *= -1.0F;
|
||||
diffRot *= -1.0F;
|
||||
}
|
||||
|
||||
if (entity.hasPose(Pose.SLEEPING)) {
|
||||
Direction direction = entity.getBedOrientation();
|
||||
if (direction != null) {
|
||||
float f4 = entity.getEyeHeight(Pose.STANDING) - 0.1F;
|
||||
stack.translate((float) (-direction.getStepX()) * f4, 0.0F, (float) (-direction.getStepZ()) * f4);
|
||||
}
|
||||
}
|
||||
|
||||
float bob = this.getBob(ctx.partialTick());
|
||||
this.setupRotations(stack, bob, yBodyRot, ctx.partialTick());
|
||||
stack.scale(-1.0F, -1.0F, 1.0F);
|
||||
this.scale(stack, ctx.partialTick());
|
||||
stack.translate(0.0F, -1.501F, 0.0F);
|
||||
float walkSpeed = 0.0F;
|
||||
float walkPos = 0.0F;
|
||||
if (!shouldSit && entity.isAlive()) {
|
||||
walkSpeed = entity.walkAnimation.speed(ctx.partialTick());
|
||||
walkPos = entity.walkAnimation.position(ctx.partialTick());
|
||||
if (entity.isBaby()) {
|
||||
walkPos *= 3.0F;
|
||||
}
|
||||
|
||||
if (walkSpeed > 1.0F) {
|
||||
walkSpeed = 1.0F;
|
||||
}
|
||||
}
|
||||
|
||||
cowQuadrupedComponent.setupAnim(walkPos, walkSpeed, entity.tickCount, diffRot, xRot);
|
||||
cowQuadrupedComponent.updateInstances(stack);
|
||||
}
|
||||
|
||||
private static float sleepDirectionToRotation(Direction pFacing) {
|
||||
switch (pFacing) {
|
||||
case SOUTH:
|
||||
return 90.0F;
|
||||
case WEST:
|
||||
return 0.0F;
|
||||
case NORTH:
|
||||
return 270.0F;
|
||||
case EAST:
|
||||
return 180.0F;
|
||||
default:
|
||||
return 0.0F;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns where in the swing animation the living entity is (from 0 to 1). Args : entity, partialTickTime
|
||||
*/
|
||||
protected float getAttackAnim(float pPartialTickTime) {
|
||||
return entity.getAttackAnim(pPartialTickTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines what float the third param in setRotationAngles of ModelBase is
|
||||
*/
|
||||
protected float getBob(float pPartialTick) {
|
||||
return (float) entity.tickCount + pPartialTick;
|
||||
}
|
||||
|
||||
protected float getFlipDegrees() {
|
||||
return 90.0F;
|
||||
}
|
||||
|
||||
protected float getWhiteOverlayProgress(float pPartialTicks) {
|
||||
return 0.0F;
|
||||
}
|
||||
|
||||
protected boolean isShaking() {
|
||||
return entity.isFullyFrozen();
|
||||
}
|
||||
|
||||
protected void setupRotations(PoseStack pPoseStack, float pAgeInTicks, float pRotationYaw, float pPartialTicks) {
|
||||
if (this.isShaking()) {
|
||||
pRotationYaw += (float) (Math.cos((double) entity.tickCount * 3.25D) * Math.PI * (double) 0.4F);
|
||||
}
|
||||
|
||||
if (!entity.hasPose(Pose.SLEEPING)) {
|
||||
pPoseStack.mulPose(Axis.YP.rotationDegrees(180.0F - pRotationYaw));
|
||||
}
|
||||
|
||||
if (entity.deathTime > 0) {
|
||||
float f = ((float) entity.deathTime + pPartialTicks - 1.0F) / 20.0F * 1.6F;
|
||||
f = Mth.sqrt(f);
|
||||
if (f > 1.0F) {
|
||||
f = 1.0F;
|
||||
}
|
||||
|
||||
pPoseStack.mulPose(Axis.ZP.rotationDegrees(f * this.getFlipDegrees()));
|
||||
} else if (entity.isAutoSpinAttack()) {
|
||||
pPoseStack.mulPose(Axis.XP.rotationDegrees(-90.0F - entity.getXRot()));
|
||||
pPoseStack.mulPose(Axis.YP.rotationDegrees(((float) entity.tickCount + pPartialTicks) * -75.0F));
|
||||
} else if (entity.hasPose(Pose.SLEEPING)) {
|
||||
Direction direction = entity.getBedOrientation();
|
||||
float f1 = direction != null ? sleepDirectionToRotation(direction) : pRotationYaw;
|
||||
pPoseStack.mulPose(Axis.YP.rotationDegrees(f1));
|
||||
pPoseStack.mulPose(Axis.ZP.rotationDegrees(this.getFlipDegrees()));
|
||||
pPoseStack.mulPose(Axis.YP.rotationDegrees(270.0F));
|
||||
} else if (isEntityUpsideDown(entity)) {
|
||||
pPoseStack.translate(0.0F, entity.getBbHeight() + 0.1F, 0.0F);
|
||||
pPoseStack.mulPose(Axis.ZP.rotationDegrees(180.0F));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected void scale(PoseStack pPoseStack, float pPartialTickTime) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void _delete() {
|
||||
super._delete();
|
||||
|
||||
cowQuadrupedComponent.delete();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,216 @@
|
|||
package dev.engine_room.flywheel.vanilla;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.math.Axis;
|
||||
|
||||
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
|
||||
import dev.engine_room.flywheel.lib.instance.InstanceTypes;
|
||||
import dev.engine_room.flywheel.lib.instance.TransformedInstance;
|
||||
import dev.engine_room.flywheel.lib.model.ModelCache;
|
||||
import dev.engine_room.flywheel.lib.model.baked.ItemModelBuilder;
|
||||
import dev.engine_room.flywheel.lib.transform.TransformStack;
|
||||
import dev.engine_room.flywheel.lib.visual.InstanceRecycler;
|
||||
import dev.engine_room.flywheel.lib.visual.SimpleEntityVisual;
|
||||
import dev.engine_room.flywheel.lib.visual.component.FireComponent;
|
||||
import dev.engine_room.flywheel.lib.visual.component.HitboxComponent;
|
||||
import dev.engine_room.flywheel.lib.visual.component.ShadowComponent;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.LightTexture;
|
||||
import net.minecraft.client.renderer.block.model.ItemOverrides;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.MultiPartBakedModel;
|
||||
import net.minecraft.client.resources.model.SimpleBakedModel;
|
||||
import net.minecraft.client.resources.model.WeightedBakedModel;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.entity.item.ItemEntity;
|
||||
import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.ItemDisplayContext;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.LightLayer;
|
||||
|
||||
public class ItemVisual extends SimpleEntityVisual<ItemEntity> {
|
||||
|
||||
private static final ThreadLocal<RandomSource> RANDOM = ThreadLocal.withInitial(RandomSource::createNewThreadLocalInstance);
|
||||
|
||||
public static final ModelCache<ItemKey> MODEL_CACHE = new ModelCache<>(stack -> {
|
||||
return ItemModelBuilder.create(stack.stack(), stack.model())
|
||||
.build();
|
||||
});
|
||||
|
||||
private final PoseStack pPoseStack = new PoseStack();
|
||||
private final BakedModel model;
|
||||
private final boolean isSupported;
|
||||
|
||||
private final InstanceRecycler<TransformedInstance> instances;
|
||||
|
||||
public ItemVisual(VisualizationContext ctx, ItemEntity entity, float partialTick) {
|
||||
super(ctx, entity, partialTick);
|
||||
|
||||
addComponent(new ShadowComponent(visualizationContext, entity).radius(0.15f)
|
||||
.strength(0.75f));
|
||||
addComponent(new HitboxComponent(visualizationContext, entity));
|
||||
addComponent(new FireComponent(visualizationContext, entity));
|
||||
|
||||
var item = entity.getItem();
|
||||
model = getModel(item);
|
||||
|
||||
isSupported = isSupported(model);
|
||||
|
||||
var key = new ItemKey(item.copy(), model);
|
||||
|
||||
instances = new InstanceRecycler<>(() -> instancerProvider.instancer(InstanceTypes.TRANSFORMED, MODEL_CACHE.get(key))
|
||||
.createInstance());
|
||||
}
|
||||
|
||||
public static boolean isSupported(ItemEntity entity) {
|
||||
return isSupported(getModel(entity.getItem()));
|
||||
}
|
||||
|
||||
public static BakedModel getModel(ItemStack stack) {
|
||||
return Minecraft.getInstance()
|
||||
.getItemRenderer()
|
||||
.getItemModelShaper()
|
||||
.getItemModel(stack);
|
||||
}
|
||||
|
||||
public static boolean isSupported(BakedModel model) {
|
||||
if (model.isCustomRenderer()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (model.getOverrides() != ItemOverrides.EMPTY) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Class<? extends BakedModel> c = model.getClass();
|
||||
if (!(c == SimpleBakedModel.class || c == MultiPartBakedModel.class || c == WeightedBakedModel.class)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginFrame(Context ctx) {
|
||||
if (!isSupported || !isVisible(ctx.frustum())) {
|
||||
return;
|
||||
}
|
||||
|
||||
super.beginFrame(ctx);
|
||||
|
||||
pPoseStack.setIdentity();
|
||||
TransformStack.of(pPoseStack)
|
||||
.translate(getVisualPosition(ctx.partialTick()));
|
||||
|
||||
instances.resetCount();
|
||||
ItemStack itemstack = entity.getItem();
|
||||
int i = itemstack.isEmpty() ? 187 : Item.getId(itemstack.getItem()) + itemstack.getDamageValue();
|
||||
var random = RANDOM.get();
|
||||
random.setSeed(i);
|
||||
boolean flag = model.isGui3d();
|
||||
int j = this.getRenderAmount(itemstack);
|
||||
float f = 0.25F;
|
||||
float f1 = shouldBob() ? Mth.sin(((float) entity.getAge() + ctx.partialTick()) / 10.0F + entity.bobOffs) * 0.1F + 0.1F : 0;
|
||||
float f2 = model.getTransforms()
|
||||
.getTransform(ItemDisplayContext.GROUND).scale.y();
|
||||
pPoseStack.translate(0.0F, f1 + 0.25F * f2, 0.0F);
|
||||
float f3 = entity.getSpin(ctx.partialTick());
|
||||
pPoseStack.mulPose(Axis.YP.rotation(f3));
|
||||
if (!flag) {
|
||||
float f7 = -0.0F * (float) (j - 1) * 0.5F;
|
||||
float f8 = -0.0F * (float) (j - 1) * 0.5F;
|
||||
float f9 = -0.09375F * (float) (j - 1) * 0.5F;
|
||||
pPoseStack.translate(f7, f8, f9);
|
||||
}
|
||||
|
||||
int light = LightTexture.pack(level.getBrightness(LightLayer.BLOCK, entity.blockPosition()), level.getBrightness(LightLayer.SKY, entity.blockPosition()));
|
||||
|
||||
for (int k = 0; k < j; ++k) {
|
||||
pPoseStack.pushPose();
|
||||
if (k > 0) {
|
||||
if (flag) {
|
||||
float f11 = (random.nextFloat() * 2.0F - 1.0F) * 0.15F;
|
||||
float f13 = (random.nextFloat() * 2.0F - 1.0F) * 0.15F;
|
||||
float f10 = (random.nextFloat() * 2.0F - 1.0F) * 0.15F;
|
||||
pPoseStack.translate(shouldSpreadItems() ? f11 : 0, shouldSpreadItems() ? f13 : 0, shouldSpreadItems() ? f10 : 0);
|
||||
} else {
|
||||
float f12 = (random.nextFloat() * 2.0F - 1.0F) * 0.15F * 0.5F;
|
||||
float f14 = (random.nextFloat() * 2.0F - 1.0F) * 0.15F * 0.5F;
|
||||
pPoseStack.translate(shouldSpreadItems() ? f12 : 0, shouldSpreadItems() ? f14 : 0, 0.0D);
|
||||
}
|
||||
}
|
||||
|
||||
instances.get()
|
||||
.setTransform(pPoseStack.last())
|
||||
.light(light)
|
||||
.setChanged();
|
||||
pPoseStack.popPose();
|
||||
if (!flag) {
|
||||
pPoseStack.translate(0.0, 0.0, 0.09375F);
|
||||
}
|
||||
}
|
||||
|
||||
instances.discardExtra();
|
||||
}
|
||||
|
||||
protected int getRenderAmount(ItemStack pStack) {
|
||||
int i = 1;
|
||||
if (pStack.getCount() > 48) {
|
||||
i = 5;
|
||||
} else if (pStack.getCount() > 32) {
|
||||
i = 4;
|
||||
} else if (pStack.getCount() > 16) {
|
||||
i = 3;
|
||||
} else if (pStack.getCount() > 1) {
|
||||
i = 2;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If items should spread out when rendered in 3D
|
||||
*/
|
||||
public boolean shouldSpreadItems() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If items should have a bob effect
|
||||
*/
|
||||
public boolean shouldBob() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void _delete() {
|
||||
super._delete();
|
||||
|
||||
instances.delete();
|
||||
}
|
||||
|
||||
public record ItemKey(ItemStack stack, BakedModel model) {
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var o1 = (ItemKey) o;
|
||||
return Objects.equals(model, o1.model) && stack.hasFoil() == o1.stack.hasFoil();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int out = model.hashCode();
|
||||
out = 31 * out + Boolean.hashCode(stack.hasFoil());
|
||||
return out;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package dev.engine_room.flywheel.vanilla;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import dev.engine_room.flywheel.api.instance.InstancerProvider;
|
||||
import dev.engine_room.flywheel.api.material.Material;
|
||||
import dev.engine_room.flywheel.lib.model.part.InstanceTree;
|
||||
import dev.engine_room.flywheel.lib.model.part.MeshTree;
|
||||
import net.minecraft.client.model.geom.ModelLayerLocation;
|
||||
import net.minecraft.util.Mth;
|
||||
|
||||
public class QuadrupedComponent extends AgeableListComponent {
|
||||
public final InstanceTree root;
|
||||
|
||||
public final InstanceTree head;
|
||||
public final InstanceTree body;
|
||||
public final InstanceTree rightHindLeg;
|
||||
public final InstanceTree leftHindLeg;
|
||||
public final InstanceTree rightFrontLeg;
|
||||
public final InstanceTree leftFrontLeg;
|
||||
|
||||
public QuadrupedComponent(InstancerProvider instancerProvider, ModelLayerLocation layer, Material material, Config config) {
|
||||
super(config);
|
||||
|
||||
var meshTree = MeshTree.get(layer);
|
||||
|
||||
this.root = InstanceTree.create(instancerProvider, meshTree, material);
|
||||
|
||||
this.head = root.child("head");
|
||||
this.body = root.child("body");
|
||||
this.rightHindLeg = root.child("right_hind_leg");
|
||||
this.leftHindLeg = root.child("left_hind_leg");
|
||||
this.rightFrontLeg = root.child("right_front_leg");
|
||||
this.leftFrontLeg = root.child("left_front_leg");
|
||||
}
|
||||
|
||||
public void setupAnim(float pLimbSwing, float pLimbSwingAmount, float pAgeInTicks, float pNetHeadYaw, float pHeadPitch) {
|
||||
this.head.xRot = pHeadPitch * ((float) Math.PI / 180F);
|
||||
this.head.yRot = pNetHeadYaw * ((float) Math.PI / 180F);
|
||||
this.rightHindLeg.xRot = Mth.cos(pLimbSwing * 0.6662F) * 1.4F * pLimbSwingAmount;
|
||||
this.leftHindLeg.xRot = Mth.cos(pLimbSwing * 0.6662F + (float) Math.PI) * 1.4F * pLimbSwingAmount;
|
||||
this.rightFrontLeg.xRot = Mth.cos(pLimbSwing * 0.6662F + (float) Math.PI) * 1.4F * pLimbSwingAmount;
|
||||
this.leftFrontLeg.xRot = Mth.cos(pLimbSwing * 0.6662F) * 1.4F * pLimbSwingAmount;
|
||||
}
|
||||
|
||||
public void delete() {
|
||||
root.delete();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterable<InstanceTree> headParts() {
|
||||
return List.of(head);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterable<InstanceTree> bodyParts() {
|
||||
return List.of(body, rightHindLeg, leftHindLeg, rightFrontLeg, leftFrontLeg);
|
||||
}
|
||||
}
|
|
@ -8,13 +8,15 @@ import com.mojang.blaze3d.vertex.PoseStack;
|
|||
import com.mojang.math.Axis;
|
||||
|
||||
import dev.engine_room.flywheel.api.instance.Instance;
|
||||
import dev.engine_room.flywheel.api.model.Model;
|
||||
import dev.engine_room.flywheel.api.visualization.VisualizationContext;
|
||||
import dev.engine_room.flywheel.lib.instance.InstanceTypes;
|
||||
import dev.engine_room.flywheel.lib.instance.TransformedInstance;
|
||||
import dev.engine_room.flywheel.lib.material.Materials;
|
||||
import dev.engine_room.flywheel.lib.model.ModelCache;
|
||||
import dev.engine_room.flywheel.lib.model.RetexturedMesh;
|
||||
import dev.engine_room.flywheel.lib.model.SingleMeshModel;
|
||||
import dev.engine_room.flywheel.lib.model.part.ModelPartConverter;
|
||||
import dev.engine_room.flywheel.lib.model.part.MeshTree;
|
||||
import dev.engine_room.flywheel.lib.transform.TransformStack;
|
||||
import dev.engine_room.flywheel.lib.visual.AbstractBlockEntityVisual;
|
||||
import dev.engine_room.flywheel.lib.visual.SimpleDynamicVisual;
|
||||
|
@ -28,10 +30,10 @@ import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
|
|||
|
||||
public class ShulkerBoxVisual extends AbstractBlockEntityVisual<ShulkerBoxBlockEntity> implements SimpleDynamicVisual {
|
||||
private static final ModelCache<Material> BASE_MODELS = new ModelCache<>(texture -> {
|
||||
return new SingleMeshModel(ModelPartConverter.convert(ModelLayers.SHULKER, texture.sprite(), "base"), Materials.SHULKER);
|
||||
return shulkerModel(texture, "base");
|
||||
});
|
||||
private static final ModelCache<Material> LID_MODELS = new ModelCache<>(texture -> {
|
||||
return new SingleMeshModel(ModelPartConverter.convert(ModelLayers.SHULKER, texture.sprite(), "lid"), Materials.SHULKER);
|
||||
return shulkerModel(texture, "lid");
|
||||
});
|
||||
|
||||
private final TransformedInstance base;
|
||||
|
@ -58,10 +60,10 @@ public class ShulkerBoxVisual extends AbstractBlockEntityVisual<ShulkerBoxBlockE
|
|||
TransformStack.of(stack)
|
||||
.translate(getVisualPosition())
|
||||
.translate(0.5)
|
||||
.scale(0.9995f)
|
||||
.rotate(rotation)
|
||||
.scale(1, -1, -1)
|
||||
.translateY(-1);
|
||||
.scale(0.9995f)
|
||||
.translate(0, -0.5, 0)
|
||||
.scale(1, -1, -1);
|
||||
|
||||
base = createBaseInstance(texture).setTransform(stack);
|
||||
base.setChanged();
|
||||
|
@ -130,4 +132,11 @@ public class ShulkerBoxVisual extends AbstractBlockEntityVisual<ShulkerBoxBlockE
|
|||
base.delete();
|
||||
lid.delete();
|
||||
}
|
||||
|
||||
private static Model shulkerModel(Material texture, String child) {
|
||||
var mesh = MeshTree.get(ModelLayers.SHULKER)
|
||||
.child(child)
|
||||
.mesh();
|
||||
return new SingleMeshModel(new RetexturedMesh(mesh, texture.sprite()), Materials.SHULKER);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,5 +74,12 @@ public class VanillaVisuals {
|
|||
.factory(TntMinecartVisual::new)
|
||||
.skipVanillaRender(MinecartVisual::shouldSkipRender)
|
||||
.apply();
|
||||
|
||||
builder(EntityType.COW).factory(CowVisual::new)
|
||||
.apply();
|
||||
|
||||
builder(EntityType.ITEM).factory(ItemVisual::new)
|
||||
.skipVanillaRender(ItemVisual::isSupported)
|
||||
.apply();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,12 +7,14 @@
|
|||
"client": [
|
||||
"BlockEntityTypeMixin",
|
||||
"ClientChunkCacheMixin",
|
||||
"ClientLevelMixin",
|
||||
"EntityTypeMixin",
|
||||
"LevelMixin",
|
||||
"LevelRendererMixin",
|
||||
"MinecraftMixin",
|
||||
"ModelPartAccessor",
|
||||
"PoseStackMixin",
|
||||
"VertexMultiConsumerDoubleMixin",
|
||||
"VertexMultiConsumerMultipleMixin",
|
||||
"fix.FixFabulousDepthMixin",
|
||||
"fix.FixNormalScalingMixin",
|
||||
"visualmanage.BlockEntityMixin",
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
package dev.engine_room.flywheel.lib.model.baked;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
|
||||
import dev.engine_room.flywheel.api.material.Material;
|
||||
import dev.engine_room.flywheel.lib.model.SimpleModel;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.world.item.ItemDisplayContext;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
public class FabricItemModelBuilder extends ItemModelBuilder {
|
||||
public FabricItemModelBuilder(ItemStack itemStack, BakedModel model) {
|
||||
super(itemStack, model);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FabricItemModelBuilder poseStack(PoseStack poseStack) {
|
||||
super.poseStack(poseStack);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FabricItemModelBuilder displayContext(ItemDisplayContext displayContext) {
|
||||
super.displayContext(displayContext);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FabricItemModelBuilder leftHand(boolean leftHand) {
|
||||
super.leftHand(leftHand);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FabricItemModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
|
||||
super.materialFunc(materialFunc);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleModel build() {
|
||||
// TODO
|
||||
return new SimpleModel(List.of());
|
||||
}
|
||||
}
|
|
@ -7,7 +7,9 @@ import dev.engine_room.flywheel.lib.model.baked.BakedModelBuilder;
|
|||
import dev.engine_room.flywheel.lib.model.baked.BlockModelBuilder;
|
||||
import dev.engine_room.flywheel.lib.model.baked.FabricBakedModelBuilder;
|
||||
import dev.engine_room.flywheel.lib.model.baked.FabricBlockModelBuilder;
|
||||
import dev.engine_room.flywheel.lib.model.baked.FabricItemModelBuilder;
|
||||
import dev.engine_room.flywheel.lib.model.baked.FabricMultiBlockModelBuilder;
|
||||
import dev.engine_room.flywheel.lib.model.baked.ItemModelBuilder;
|
||||
import dev.engine_room.flywheel.lib.model.baked.MultiBlockModelBuilder;
|
||||
import dev.engine_room.flywheel.lib.util.ShadersModHandler;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
|
@ -16,6 +18,7 @@ import net.minecraft.client.Minecraft;
|
|||
import net.minecraft.client.renderer.block.BlockRenderDispatcher;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
|
@ -62,4 +65,9 @@ public class FlwLibXplatImpl implements FlwLibXplat {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemModelBuilder createItemModelBuilder(ItemStack stack, BakedModel model) {
|
||||
return new FabricItemModelBuilder(stack, model);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,22 @@
|
|||
package dev.engine_room.flywheel.lib.model.baked;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
|
||||
import com.mojang.blaze3d.vertex.BufferBuilder.RenderedBuffer;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ReferenceOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ReferenceArraySet;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.renderer.ItemBlockRenderTypes;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.block.BlockRenderDispatcher;
|
||||
import net.minecraft.client.renderer.block.ModelBlockRenderer;
|
||||
|
@ -16,6 +24,8 @@ import net.minecraft.client.renderer.texture.OverlayTexture;
|
|||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.item.ItemDisplayContext;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraft.world.level.block.RenderShape;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
@ -129,6 +139,22 @@ final class BakedModelBufferer {
|
|||
}
|
||||
}
|
||||
|
||||
public static void bufferItem(BakedModel model, ItemStack stack, ItemDisplayContext displayContext, boolean leftHand, @Nullable PoseStack poseStack, ResultConsumer consumer) {
|
||||
ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get();
|
||||
if (poseStack == null) {
|
||||
poseStack = objects.identityPoseStack;
|
||||
}
|
||||
|
||||
var emitterSource = objects.emitterSource;
|
||||
emitterSource.resultConsumer(consumer);
|
||||
|
||||
Minecraft.getInstance()
|
||||
.getItemRenderer()
|
||||
.render(stack, displayContext, leftHand, poseStack, emitterSource, 0, OverlayTexture.NO_OVERLAY, model);
|
||||
|
||||
emitterSource.end();
|
||||
}
|
||||
|
||||
public interface ResultConsumer {
|
||||
void accept(RenderType renderType, boolean shaded, RenderedBuffer data);
|
||||
}
|
||||
|
@ -140,6 +166,8 @@ final class BakedModelBufferer {
|
|||
public final MeshEmitter[] emitters = new MeshEmitter[CHUNK_LAYER_AMOUNT];
|
||||
public final TransformingVertexConsumer transformingWrapper = new TransformingVertexConsumer();
|
||||
|
||||
public final MeshEmitterSource emitterSource = new MeshEmitterSource();
|
||||
|
||||
{
|
||||
for (int layerIndex = 0; layerIndex < CHUNK_LAYER_AMOUNT; layerIndex++) {
|
||||
RenderType renderType = CHUNK_LAYERS[layerIndex];
|
||||
|
@ -147,4 +175,37 @@ final class BakedModelBufferer {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class MeshEmitterSource implements MultiBufferSource {
|
||||
private final Map<RenderType, MeshEmitter> emitters = new Reference2ReferenceOpenHashMap<>();
|
||||
private final Set<MeshEmitter> active = new ReferenceArraySet<>();
|
||||
|
||||
@UnknownNullability
|
||||
private ResultConsumer resultConsumer;
|
||||
|
||||
@Override
|
||||
public VertexConsumer getBuffer(RenderType renderType) {
|
||||
var out = emitters.computeIfAbsent(renderType, MeshEmitter::new);
|
||||
|
||||
if (active.add(out)) {
|
||||
out.prepare(resultConsumer);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
public void end() {
|
||||
for (var emitter : active) {
|
||||
emitter.end();
|
||||
}
|
||||
|
||||
active.clear();
|
||||
|
||||
resultConsumer = null;
|
||||
}
|
||||
|
||||
public void resultConsumer(ResultConsumer resultConsumer) {
|
||||
this.resultConsumer = resultConsumer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
package dev.engine_room.flywheel.lib.model.baked;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
|
||||
import dev.engine_room.flywheel.api.material.Material;
|
||||
import dev.engine_room.flywheel.api.material.Transparency;
|
||||
import dev.engine_room.flywheel.api.model.Model;
|
||||
import dev.engine_room.flywheel.lib.model.ModelUtil;
|
||||
import dev.engine_room.flywheel.lib.model.SimpleModel;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.world.item.ItemDisplayContext;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
public class ForgeItemModelBuilder extends ItemModelBuilder {
|
||||
public static final Comparator<Model.ConfiguredMesh> GLINT_LAST = (a, b) -> {
|
||||
if (a.material()
|
||||
.transparency() == b.material()
|
||||
.transparency()) {
|
||||
return 0;
|
||||
}
|
||||
return a.material()
|
||||
.transparency() == Transparency.GLINT ? 1 : -1;
|
||||
};
|
||||
|
||||
public ForgeItemModelBuilder(ItemStack itemStack, BakedModel model) {
|
||||
super(itemStack, model);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeItemModelBuilder poseStack(PoseStack poseStack) {
|
||||
super.poseStack(poseStack);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeItemModelBuilder displayContext(ItemDisplayContext displayContext) {
|
||||
super.displayContext(displayContext);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeItemModelBuilder leftHand(boolean leftHand) {
|
||||
super.leftHand(leftHand);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForgeItemModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
|
||||
super.materialFunc(materialFunc);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SimpleModel build() {
|
||||
if (displayContext == null) {
|
||||
displayContext = ItemDisplayContext.GROUND;
|
||||
}
|
||||
if (materialFunc == null) {
|
||||
materialFunc = ModelUtil::getItemMaterial;
|
||||
}
|
||||
|
||||
ArrayList<Model.ConfiguredMesh> out = new ArrayList<>();
|
||||
|
||||
BakedModelBufferer.ResultConsumer resultConsumer = (renderType, shaded, data) -> {
|
||||
Material material = materialFunc.apply(renderType, shaded);
|
||||
if (material != null) {
|
||||
var mesh = MeshHelper.blockVerticesToMesh(data, "source=ItemModelBuilder," + "itemStack=" + itemStack + ",renderType=" + renderType + ",shaded=" + shaded);
|
||||
if (mesh.vertexCount() == 0) {
|
||||
mesh.delete();
|
||||
} else {
|
||||
out.add(new Model.ConfiguredMesh(material, mesh));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BakedModelBufferer.bufferItem(model, itemStack, displayContext, leftHand, poseStack, resultConsumer);
|
||||
|
||||
out.sort(GLINT_LAST);
|
||||
|
||||
return new SimpleModel(ImmutableList.copyOf(out));
|
||||
}
|
||||
}
|
|
@ -9,7 +9,9 @@ import dev.engine_room.flywheel.lib.model.baked.BakedModelBuilder;
|
|||
import dev.engine_room.flywheel.lib.model.baked.BlockModelBuilder;
|
||||
import dev.engine_room.flywheel.lib.model.baked.ForgeBakedModelBuilder;
|
||||
import dev.engine_room.flywheel.lib.model.baked.ForgeBlockModelBuilder;
|
||||
import dev.engine_room.flywheel.lib.model.baked.ForgeItemModelBuilder;
|
||||
import dev.engine_room.flywheel.lib.model.baked.ForgeMultiBlockModelBuilder;
|
||||
import dev.engine_room.flywheel.lib.model.baked.ItemModelBuilder;
|
||||
import dev.engine_room.flywheel.lib.model.baked.MultiBlockModelBuilder;
|
||||
import dev.engine_room.flywheel.lib.util.ShadersModHandler;
|
||||
import net.irisshaders.iris.api.v0.IrisApi;
|
||||
|
@ -18,6 +20,7 @@ import net.minecraft.client.renderer.block.BlockRenderDispatcher;
|
|||
import net.minecraft.client.renderer.block.ModelBlockRenderer;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraftforge.fml.ModList;
|
||||
|
@ -78,4 +81,9 @@ public class FlwLibXplatImpl implements FlwLibXplat {
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemModelBuilder createItemModelBuilder(ItemStack stack, BakedModel model) {
|
||||
return new ForgeItemModelBuilder(stack, model);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
package dev.engine_room.flywheel.impl.mixin;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
|
||||
/**
|
||||
* blaze3d doesn't forward #putBulkData, but we want that for our MeshEmitter
|
||||
*/
|
||||
@Mixin(targets = "com.mojang.blaze3d.vertex.VertexMultiConsumer$Double")
|
||||
public abstract class VertexMultiConsumerDoubleMixin implements VertexConsumer {
|
||||
@Shadow
|
||||
@Final
|
||||
private VertexConsumer first;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private VertexConsumer second;
|
||||
|
||||
@Override
|
||||
public void putBulkData(PoseStack.Pose pose, BakedQuad bakedQuad, float red, float green, float blue, float alpha, int packedLight, int packedOverlay, boolean readExistingColor) {
|
||||
first.putBulkData(pose, bakedQuad, red, green, blue, alpha, packedLight, packedOverlay, readExistingColor);
|
||||
second.putBulkData(pose, bakedQuad, red, green, blue, alpha, packedLight, packedOverlay, readExistingColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putBulkData(PoseStack.Pose pPoseEntry, BakedQuad pQuad, float[] pColorMuls, float pRed, float pGreen, float pBlue, float alpha, int[] pCombinedLights, int pCombinedOverlay, boolean pMulColor) {
|
||||
first.putBulkData(pPoseEntry, pQuad, pColorMuls, pRed, pGreen, pBlue, alpha, pCombinedLights, pCombinedOverlay, pMulColor);
|
||||
second.putBulkData(pPoseEntry, pQuad, pColorMuls, pRed, pGreen, pBlue, alpha, pCombinedLights, pCombinedOverlay, pMulColor);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package dev.engine_room.flywheel.impl.mixin;
|
||||
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
|
||||
import net.minecraft.client.renderer.block.model.BakedQuad;
|
||||
|
||||
@Mixin(targets = "com.mojang.blaze3d.vertex.VertexMultiConsumer$Multiple")
|
||||
public abstract class VertexMultiConsumerMultipleMixin implements VertexConsumer {
|
||||
@Shadow
|
||||
@Final
|
||||
private VertexConsumer[] delegates;
|
||||
|
||||
@Override
|
||||
public void putBulkData(PoseStack.Pose pose, BakedQuad bakedQuad, float red, float green, float blue, float alpha, int packedLight, int packedOverlay, boolean readExistingColor) {
|
||||
for (VertexConsumer delegate : this.delegates) {
|
||||
delegate.putBulkData(pose, bakedQuad, red, green, blue, alpha, packedLight, packedOverlay, readExistingColor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putBulkData(PoseStack.Pose pPoseEntry, BakedQuad pQuad, float[] pColorMuls, float pRed, float pGreen, float pBlue, float alpha, int[] pCombinedLights, int pCombinedOverlay, boolean pMulColor) {
|
||||
for (VertexConsumer delegate : this.delegates) {
|
||||
delegate.putBulkData(pPoseEntry, pQuad, pColorMuls, pRed, pGreen, pBlue, alpha, pCombinedLights, pCombinedOverlay, pMulColor);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue