Summer squash git pasta

This commit is contained in:
Jozufozu 2024-07-13 17:22:27 -07:00
parent b2483def1d
commit f5a6e5af0c
34 changed files with 1592 additions and 47 deletions

View file

@ -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;
}

View file

@ -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;

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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() {
}
}

View file

@ -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;

View file

@ -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() {
}

View file

@ -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) {

View file

@ -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() {
}
}

View file

@ -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));
}
}

View file

@ -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();
}

View file

@ -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) {
}
}

View file

@ -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();
}
}

View file

@ -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();
}
}

View file

@ -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.);
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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

View file

@ -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);
}

View file

@ -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) {
}
}

View file

@ -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);
}
}

View file

@ -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();
}
}

View file

@ -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;
}
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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();
}
}

View file

@ -7,12 +7,14 @@
"client": [
"BlockEntityTypeMixin",
"ClientChunkCacheMixin",
"ClientLevelMixin",
"EntityTypeMixin",
"LevelMixin",
"LevelRendererMixin",
"MinecraftMixin",
"ModelPartAccessor",
"PoseStackMixin",
"VertexMultiConsumerDoubleMixin",
"VertexMultiConsumerMultipleMixin",
"fix.FixFabulousDepthMixin",
"fix.FixNormalScalingMixin",
"visualmanage.BlockEntityMixin",

View file

@ -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());
}
}

View file

@ -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);
}
}

View file

@ -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;
}
}
}

View file

@ -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));
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}
}