mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2024-12-29 08:26:37 +01:00
Cramming
- Implement cow and item visuals (very buggy!) - Mostly copy vanilla's LivingEntityRenderer and ItemRenderer to implement the #beginFrame for CowVisual and ItemVisual - Cache translations of vanilla LayerDefinition into flywheel MeshTrees - Create models for individual parts in a second layer of cache - Add so many accessors to get into LayerDefinition and friends - Add glint material and material shader - Add flw_systemSeconds and flw_systemMillis uniforms - Create models from items via ItemModelBuilder - Shove MeshEmitter into vanilla's item renderer via a hacky implementation of MultiBufferSource - Fix options uniforms getting zeroed on a renderer reload
This commit is contained in:
parent
09cb15e2ed
commit
163649e792
31 changed files with 1698 additions and 1 deletions
|
@ -98,6 +98,7 @@ mixin {
|
||||||
config 'flywheel.backend.mixins.json'
|
config 'flywheel.backend.mixins.json'
|
||||||
config 'flywheel.impl.mixins.json'
|
config 'flywheel.impl.mixins.json'
|
||||||
config 'flywheel.impl.sodium.mixins.json'
|
config 'flywheel.impl.sodium.mixins.json'
|
||||||
|
config 'flywheel.vanilla.mixins.json'
|
||||||
|
|
||||||
debug.verbose = true
|
debug.verbose = true
|
||||||
debug.export = true
|
debug.export = true
|
||||||
|
|
|
@ -31,6 +31,7 @@ import com.jozufozu.flywheel.lib.util.LevelAttached;
|
||||||
import com.jozufozu.flywheel.lib.util.ShadersModHandler;
|
import com.jozufozu.flywheel.lib.util.ShadersModHandler;
|
||||||
import com.jozufozu.flywheel.lib.util.StringUtil;
|
import com.jozufozu.flywheel.lib.util.StringUtil;
|
||||||
import com.jozufozu.flywheel.vanilla.VanillaVisuals;
|
import com.jozufozu.flywheel.vanilla.VanillaVisuals;
|
||||||
|
import com.jozufozu.flywheel.vanilla.model.MeshTreeCache;
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.commands.synchronization.ArgumentTypeInfos;
|
import net.minecraft.commands.synchronization.ArgumentTypeInfos;
|
||||||
|
@ -106,6 +107,7 @@ public class Flywheel {
|
||||||
modEventBus.addListener(BackendManagerImpl::onEndClientResourceReload);
|
modEventBus.addListener(BackendManagerImpl::onEndClientResourceReload);
|
||||||
|
|
||||||
modEventBus.addListener((EndClientResourceReloadEvent e) -> ModelCache.onEndClientResourceReload(e));
|
modEventBus.addListener((EndClientResourceReloadEvent e) -> ModelCache.onEndClientResourceReload(e));
|
||||||
|
modEventBus.addListener(MeshTreeCache::onEndClientResourceReload);
|
||||||
modEventBus.addListener(ModelHolder::onEndClientResourceReload);
|
modEventBus.addListener(ModelHolder::onEndClientResourceReload);
|
||||||
|
|
||||||
modEventBus.addListener(PartialModel::onModelRegistry);
|
modEventBus.addListener(PartialModel::onModelRegistry);
|
||||||
|
|
|
@ -10,6 +10,7 @@ import com.jozufozu.flywheel.api.event.RenderContext;
|
||||||
import com.jozufozu.flywheel.api.visualization.VisualizationManager;
|
import com.jozufozu.flywheel.api.visualization.VisualizationManager;
|
||||||
import com.jozufozu.flywheel.lib.math.MatrixMath;
|
import com.jozufozu.flywheel.lib.math.MatrixMath;
|
||||||
|
|
||||||
|
import net.minecraft.Util;
|
||||||
import net.minecraft.client.Camera;
|
import net.minecraft.client.Camera;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
|
@ -155,12 +156,15 @@ public class FrameUniforms implements UniformProvider {
|
||||||
float partialTick = context.partialTick();
|
float partialTick = context.partialTick();
|
||||||
float renderTicks = ticks + partialTick;
|
float renderTicks = ticks + partialTick;
|
||||||
float renderSeconds = renderTicks / 20f;
|
float renderSeconds = renderTicks / 20f;
|
||||||
|
float systemSeconds = Util.getMillis() / 1000f;
|
||||||
|
|
||||||
MemoryUtil.memPutInt(ptr, ticks);
|
MemoryUtil.memPutInt(ptr, ticks);
|
||||||
MemoryUtil.memPutFloat(ptr + 4, partialTick);
|
MemoryUtil.memPutFloat(ptr + 4, partialTick);
|
||||||
MemoryUtil.memPutFloat(ptr + 8, renderTicks);
|
MemoryUtil.memPutFloat(ptr + 8, renderTicks);
|
||||||
MemoryUtil.memPutFloat(ptr + 12, renderSeconds);
|
MemoryUtil.memPutFloat(ptr + 12, renderSeconds);
|
||||||
return ptr + 16;
|
MemoryUtil.memPutFloat(ptr + 16, systemSeconds);
|
||||||
|
MemoryUtil.memPutInt(ptr + 20, (int) (Util.getMillis() % Integer.MAX_VALUE));
|
||||||
|
return ptr + 24;
|
||||||
}
|
}
|
||||||
|
|
||||||
private long writeCameraIn(long ptr) {
|
private long writeCameraIn(long ptr) {
|
||||||
|
|
|
@ -163,6 +163,8 @@ public class Uniforms {
|
||||||
level.delete();
|
level.delete();
|
||||||
level = null;
|
level = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
optionsRequiresUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static long writeVec4(long ptr, float x, float y, float z, float w) {
|
static long writeVec4(long ptr, float x, float y, float z, float w) {
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
package com.jozufozu.flywheel.lib.material;
|
package com.jozufozu.flywheel.lib.material;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.material.DepthTest;
|
||||||
import com.jozufozu.flywheel.api.material.Material;
|
import com.jozufozu.flywheel.api.material.Material;
|
||||||
import com.jozufozu.flywheel.api.material.Transparency;
|
import com.jozufozu.flywheel.api.material.Transparency;
|
||||||
|
import com.jozufozu.flywheel.api.material.WriteMask;
|
||||||
|
|
||||||
import net.minecraft.client.renderer.Sheets;
|
import net.minecraft.client.renderer.Sheets;
|
||||||
|
import net.minecraft.client.renderer.entity.ItemRenderer;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
|
||||||
public final class Materials {
|
public final class Materials {
|
||||||
|
@ -70,6 +73,21 @@ public final class Materials {
|
||||||
.mipmap(false)
|
.mipmap(false)
|
||||||
.build();
|
.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() {
|
private Materials() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,6 +125,38 @@ public class SimpleMaterial implements Material {
|
||||||
return diffuse;
|
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 {
|
public static class Builder implements Material {
|
||||||
protected MaterialShaders shaders;
|
protected MaterialShaders shaders;
|
||||||
protected FogShader fog;
|
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 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() {
|
private StandardMaterialShaders() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import com.mojang.logging.LogUtils;
|
||||||
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.renderer.RenderType;
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
import net.minecraft.client.renderer.Sheets;
|
||||||
import net.minecraft.client.renderer.block.BlockRenderDispatcher;
|
import net.minecraft.client.renderer.block.BlockRenderDispatcher;
|
||||||
import net.minecraft.client.renderer.block.ModelBlockRenderer;
|
import net.minecraft.client.renderer.block.ModelBlockRenderer;
|
||||||
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
|
import net.minecraftforge.fml.util.ObfuscationReflectionHelper;
|
||||||
|
@ -100,6 +101,35 @@ public final class ModelUtil {
|
||||||
return null;
|
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) {
|
public static int computeTotalVertexCount(Iterable<Mesh> meshes) {
|
||||||
int vertexCount = 0;
|
int vertexCount = 0;
|
||||||
for (Mesh mesh : meshes) {
|
for (Mesh mesh : meshes) {
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
package com.jozufozu.flywheel.lib.model.baked;
|
package com.jozufozu.flywheel.lib.model.baked;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
@ -8,8 +12,11 @@ import org.jetbrains.annotations.Nullable;
|
||||||
import com.mojang.blaze3d.vertex.BufferBuilder;
|
import com.mojang.blaze3d.vertex.BufferBuilder;
|
||||||
import com.mojang.blaze3d.vertex.BufferBuilder.RenderedBuffer;
|
import com.mojang.blaze3d.vertex.BufferBuilder.RenderedBuffer;
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.renderer.ItemBlockRenderTypes;
|
import net.minecraft.client.renderer.ItemBlockRenderTypes;
|
||||||
|
import net.minecraft.client.renderer.MultiBufferSource;
|
||||||
import net.minecraft.client.renderer.RenderType;
|
import net.minecraft.client.renderer.RenderType;
|
||||||
import net.minecraft.client.renderer.block.BlockRenderDispatcher;
|
import net.minecraft.client.renderer.block.BlockRenderDispatcher;
|
||||||
import net.minecraft.client.renderer.block.ModelBlockRenderer;
|
import net.minecraft.client.renderer.block.ModelBlockRenderer;
|
||||||
|
@ -17,6 +24,8 @@ import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||||
import net.minecraft.client.resources.model.BakedModel;
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.util.RandomSource;
|
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.BlockAndTintGetter;
|
||||||
import net.minecraft.world.level.block.RenderShape;
|
import net.minecraft.world.level.block.RenderShape;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
@ -132,6 +141,23 @@ final class BakedModelBufferer {
|
||||||
transformingWrapper.clear();
|
transformingWrapper.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
var itemRenderer = Minecraft.getInstance()
|
||||||
|
.getItemRenderer();
|
||||||
|
|
||||||
|
itemRenderer.render(stack, displayContext, leftHand, poseStack, emitterSource, 0, OverlayTexture.NO_OVERLAY, model);
|
||||||
|
|
||||||
|
emitterSource.end();
|
||||||
|
}
|
||||||
|
|
||||||
public interface ResultConsumer {
|
public interface ResultConsumer {
|
||||||
void accept(RenderType renderType, boolean shaded, RenderedBuffer data);
|
void accept(RenderType renderType, boolean shaded, RenderedBuffer data);
|
||||||
}
|
}
|
||||||
|
@ -142,6 +168,8 @@ final class BakedModelBufferer {
|
||||||
|
|
||||||
public final TransformingVertexConsumer transformingWrapper = new TransformingVertexConsumer();
|
public final TransformingVertexConsumer transformingWrapper = new TransformingVertexConsumer();
|
||||||
|
|
||||||
|
public final MeshEmitterSource emitterSource = new MeshEmitterSource();
|
||||||
|
|
||||||
public final MeshEmitter[] emitters = new MeshEmitter[CHUNK_LAYER_AMOUNT];
|
public final MeshEmitter[] emitters = new MeshEmitter[CHUNK_LAYER_AMOUNT];
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -152,4 +180,52 @@ final class BakedModelBufferer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class MeshEmitterSource implements MultiBufferSource {
|
||||||
|
private final Map<RenderType, MeshEmitter> emitters = new HashMap<>();
|
||||||
|
|
||||||
|
private final Set<MeshEmitter> active = new LinkedHashSet<>();
|
||||||
|
// Hack: we want glint to render after everything else so track it separately here
|
||||||
|
private final Set<MeshEmitter> activeGlint = new LinkedHashSet<>();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private ResultConsumer resultConsumer;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VertexConsumer getBuffer(RenderType renderType) {
|
||||||
|
var out = emitters.computeIfAbsent(renderType, type -> new MeshEmitter(new BufferBuilder(type.bufferSize()), type));
|
||||||
|
|
||||||
|
Set<MeshEmitter> active;
|
||||||
|
if (renderType == RenderType.glint() || renderType == RenderType.glintDirect() || renderType == RenderType.entityGlint() || renderType == RenderType.entityGlintDirect()) {
|
||||||
|
active = this.activeGlint;
|
||||||
|
} else {
|
||||||
|
active = this.active;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (active.add(out)) {
|
||||||
|
out.begin(resultConsumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void end() {
|
||||||
|
for (var emitter : active) {
|
||||||
|
emitter.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var emitter : activeGlint) {
|
||||||
|
emitter.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
active.clear();
|
||||||
|
activeGlint.clear();
|
||||||
|
|
||||||
|
resultConsumer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resultConsumer(ResultConsumer resultConsumer) {
|
||||||
|
this.resultConsumer = resultConsumer;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
package com.jozufozu.flywheel.lib.model.baked;
|
||||||
|
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.jozufozu.flywheel.api.material.Material;
|
||||||
|
import com.jozufozu.flywheel.api.model.Model;
|
||||||
|
import com.jozufozu.flywheel.api.vertex.VertexView;
|
||||||
|
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
||||||
|
import com.jozufozu.flywheel.lib.model.ModelUtil;
|
||||||
|
import com.jozufozu.flywheel.lib.model.SimpleMesh;
|
||||||
|
import com.jozufozu.flywheel.lib.model.SimpleModel;
|
||||||
|
import com.jozufozu.flywheel.lib.model.baked.BakedModelBufferer.ResultConsumer;
|
||||||
|
import com.jozufozu.flywheel.lib.vertex.NoOverlayVertexView;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.renderer.RenderType;
|
||||||
|
import net.minecraft.world.item.ItemDisplayContext;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
|
||||||
|
public class ItemModelBuilder {
|
||||||
|
private final ItemStack itemStack;
|
||||||
|
@Nullable
|
||||||
|
private PoseStack poseStack;
|
||||||
|
@Nullable
|
||||||
|
private ItemDisplayContext displayContext;
|
||||||
|
private boolean leftHand;
|
||||||
|
private int seed = 42;
|
||||||
|
@Nullable
|
||||||
|
private BiFunction<RenderType, Boolean, Material> materialFunc;
|
||||||
|
|
||||||
|
public ItemModelBuilder(ItemStack itemStack) {
|
||||||
|
this.itemStack = itemStack;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 seed(int seed) {
|
||||||
|
this.seed = seed;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemModelBuilder materialFunc(BiFunction<RenderType, Boolean, Material> materialFunc) {
|
||||||
|
this.materialFunc = materialFunc;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleModel build() {
|
||||||
|
if (displayContext == null) {
|
||||||
|
displayContext = ItemDisplayContext.GROUND;
|
||||||
|
}
|
||||||
|
if (materialFunc == null) {
|
||||||
|
materialFunc = ModelUtil::getItemMaterial;
|
||||||
|
}
|
||||||
|
|
||||||
|
var out = ImmutableList.<Model.ConfiguredMesh>builder();
|
||||||
|
|
||||||
|
ResultConsumer resultConsumer = (renderType, shaded, data) -> {
|
||||||
|
Material material = materialFunc.apply(renderType, shaded);
|
||||||
|
if (material != null) {
|
||||||
|
VertexView vertexView = new NoOverlayVertexView();
|
||||||
|
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, vertexView);
|
||||||
|
var mesh = new SimpleMesh(vertexView, meshData, "source=ItemModelBuilder," + "itemStack=" + itemStack + ",renderType=" + renderType + ",shaded=" + shaded);
|
||||||
|
out.add(new Model.ConfiguredMesh(material, mesh));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var model = Minecraft.getInstance()
|
||||||
|
.getItemRenderer()
|
||||||
|
.getModel(itemStack, null, null, seed);
|
||||||
|
|
||||||
|
BakedModelBufferer.bufferItem(model, itemStack, displayContext, leftHand, poseStack, resultConsumer);
|
||||||
|
|
||||||
|
return new SimpleModel(out.build());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,156 @@
|
||||||
|
package com.jozufozu.flywheel.lib.vertex;
|
||||||
|
|
||||||
|
import com.jozufozu.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,55 @@
|
||||||
|
package com.jozufozu.flywheel.vanilla;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.vanilla.model.InstanceTree;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
|
||||||
|
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 p_102081_ : this.headParts()) {
|
||||||
|
p_102081_.render(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 p_102071_ : this.bodyParts()) {
|
||||||
|
p_102071_.render(pPoseStack);
|
||||||
|
}
|
||||||
|
pPoseStack.popPose();
|
||||||
|
} else {
|
||||||
|
for (InstanceTree p_102061_ : this.headParts()) {
|
||||||
|
p_102061_.render(pPoseStack);
|
||||||
|
}
|
||||||
|
for (InstanceTree p_102051_ : this.bodyParts()) {
|
||||||
|
p_102051_.render(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) {
|
||||||
|
}
|
||||||
|
}
|
224
src/main/java/com/jozufozu/flywheel/vanilla/CowVisual.java
Normal file
224
src/main/java/com/jozufozu/flywheel/vanilla/CowVisual.java
Normal file
|
@ -0,0 +1,224 @@
|
||||||
|
package com.jozufozu.flywheel.vanilla;
|
||||||
|
|
||||||
|
import static net.minecraft.client.renderer.entity.LivingEntityRenderer.getOverlayCoords;
|
||||||
|
import static net.minecraft.client.renderer.entity.LivingEntityRenderer.isEntityUpsideDown;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.material.Material;
|
||||||
|
import com.jozufozu.flywheel.api.visualization.VisualizationContext;
|
||||||
|
import com.jozufozu.flywheel.lib.material.SimpleMaterial;
|
||||||
|
import com.jozufozu.flywheel.lib.transform.TransformStack;
|
||||||
|
import com.jozufozu.flywheel.lib.visual.SimpleEntityVisual;
|
||||||
|
import com.jozufozu.flywheel.lib.visual.components.FireComponent;
|
||||||
|
import com.jozufozu.flywheel.lib.visual.components.HitboxComponent;
|
||||||
|
import com.jozufozu.flywheel.lib.visual.components.ShadowComponent;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import com.mojang.math.Axis;
|
||||||
|
|
||||||
|
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 QuadrupedComponent cowQuadrupedComponent;
|
||||||
|
|
||||||
|
private final PoseStack stack = new PoseStack();
|
||||||
|
|
||||||
|
public CowVisual(VisualizationContext ctx, Cow entity) {
|
||||||
|
super(ctx, entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(float 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));
|
||||||
|
|
||||||
|
super.init(partialTick);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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.setOverlay(o);
|
||||||
|
i.light(l);
|
||||||
|
i.setChanged();
|
||||||
|
});
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
197
src/main/java/com/jozufozu/flywheel/vanilla/ItemVisual.java
Normal file
197
src/main/java/com/jozufozu/flywheel/vanilla/ItemVisual.java
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
package com.jozufozu.flywheel.vanilla;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.visualization.VisualizationContext;
|
||||||
|
import com.jozufozu.flywheel.lib.instance.InstanceTypes;
|
||||||
|
import com.jozufozu.flywheel.lib.instance.TransformedInstance;
|
||||||
|
import com.jozufozu.flywheel.lib.model.ModelCache;
|
||||||
|
import com.jozufozu.flywheel.lib.model.baked.ItemModelBuilder;
|
||||||
|
import com.jozufozu.flywheel.lib.transform.TransformStack;
|
||||||
|
import com.jozufozu.flywheel.lib.visual.InstanceRecycler;
|
||||||
|
import com.jozufozu.flywheel.lib.visual.SimpleEntityVisual;
|
||||||
|
import com.jozufozu.flywheel.lib.visual.components.FireComponent;
|
||||||
|
import com.jozufozu.flywheel.lib.visual.components.HitboxComponent;
|
||||||
|
import com.jozufozu.flywheel.lib.visual.components.ShadowComponent;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import com.mojang.math.Axis;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.renderer.LightTexture;
|
||||||
|
import net.minecraft.client.resources.model.BakedModel;
|
||||||
|
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 new ItemModelBuilder(stack.stack()).build();
|
||||||
|
});
|
||||||
|
|
||||||
|
private final PoseStack pPoseStack = new PoseStack();
|
||||||
|
private final BakedModel model;
|
||||||
|
|
||||||
|
private final InstanceRecycler<TransformedInstance> instances;
|
||||||
|
|
||||||
|
public ItemVisual(VisualizationContext ctx, ItemEntity entity) {
|
||||||
|
super(ctx, entity);
|
||||||
|
|
||||||
|
model = Minecraft.getInstance()
|
||||||
|
.getItemRenderer()
|
||||||
|
.getModel(entity.getItem(), entity.level(), null, entity.getId());
|
||||||
|
|
||||||
|
instances = new InstanceRecycler<>(() -> instancerProvider.instancer(InstanceTypes.TRANSFORMED, MODEL_CACHE.get(new ItemKey(entity.getItem())))
|
||||||
|
.createInstance());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(float partialTick) {
|
||||||
|
super.init(partialTick);
|
||||||
|
|
||||||
|
addComponent(new ShadowComponent(visualizationContext, entity).radius(0.15f)
|
||||||
|
.strength(0.75f));
|
||||||
|
addComponent(new HitboxComponent(visualizationContext, entity));
|
||||||
|
addComponent(new FireComponent(visualizationContext, entity));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beginFrame(Context ctx) {
|
||||||
|
if (!isVisible(ctx.frustum())) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.beginFrame(ctx);
|
||||||
|
|
||||||
|
pPoseStack.setIdentity();
|
||||||
|
TransformStack.of(pPoseStack)
|
||||||
|
.translate(getVisualPosition(ctx.partialTick()));
|
||||||
|
|
||||||
|
instances.resetCount();
|
||||||
|
pPoseStack.pushPose();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pPoseStack.popPose();
|
||||||
|
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) {
|
||||||
|
public ItemKey(ItemStack stack) {
|
||||||
|
this.stack = stack.copy();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemKey itemKey = (ItemKey) o;
|
||||||
|
if (stack.isEmpty()) {
|
||||||
|
return itemKey.stack.isEmpty();
|
||||||
|
} else {
|
||||||
|
return !itemKey.stack.isEmpty() && stack.getItem() == itemKey.stack.getItem() && Objects.equals(stack.getTag(), itemKey.stack.getTag());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int out = stack.getItem()
|
||||||
|
.hashCode();
|
||||||
|
out = 31 * out + (stack.getTag() != null ? stack.getTag()
|
||||||
|
.hashCode() : 0);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package com.jozufozu.flywheel.vanilla;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.instance.InstancerProvider;
|
||||||
|
import com.jozufozu.flywheel.api.material.Material;
|
||||||
|
import com.jozufozu.flywheel.vanilla.model.InstanceTree;
|
||||||
|
import com.jozufozu.flywheel.vanilla.model.MeshTreeCache;
|
||||||
|
|
||||||
|
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 = MeshTreeCache.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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -74,5 +74,11 @@ public class VanillaVisuals {
|
||||||
.factory(TntMinecartVisual::new)
|
.factory(TntMinecartVisual::new)
|
||||||
.skipVanillaRender(MinecartVisual::shouldSkipRender)
|
.skipVanillaRender(MinecartVisual::shouldSkipRender)
|
||||||
.apply();
|
.apply();
|
||||||
|
|
||||||
|
builder(EntityType.COW).factory(CowVisual::new)
|
||||||
|
.apply();
|
||||||
|
|
||||||
|
builder(EntityType.ITEM).factory(ItemVisual::new)
|
||||||
|
.apply();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.jozufozu.flywheel.vanilla.mixin;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.joml.Vector3f;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
|
import net.minecraft.client.model.geom.builders.CubeDefinition;
|
||||||
|
import net.minecraft.client.model.geom.builders.CubeDeformation;
|
||||||
|
import net.minecraft.client.model.geom.builders.UVPair;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
|
||||||
|
@Mixin(CubeDefinition.class)
|
||||||
|
public interface CubeDefinitionAccessor {
|
||||||
|
@Accessor("origin")
|
||||||
|
Vector3f vanillin$origin();
|
||||||
|
|
||||||
|
@Accessor("dimensions")
|
||||||
|
Vector3f vanillin$dimensions();
|
||||||
|
|
||||||
|
@Accessor("grow")
|
||||||
|
CubeDeformation vanillin$grow();
|
||||||
|
|
||||||
|
@Accessor("mirror")
|
||||||
|
boolean vanillin$mirror();
|
||||||
|
|
||||||
|
@Accessor("texCoord")
|
||||||
|
UVPair vanillin$texCoord();
|
||||||
|
|
||||||
|
@Accessor("texScale")
|
||||||
|
UVPair vanillin$texScale();
|
||||||
|
|
||||||
|
@Accessor("visibleFaces")
|
||||||
|
Set<Direction> vanillin$visibleFaces();
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.jozufozu.flywheel.vanilla.mixin;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
|
import net.minecraft.client.model.geom.builders.CubeDeformation;
|
||||||
|
|
||||||
|
@Mixin(CubeDeformation.class)
|
||||||
|
public interface CubeDeformationAccessor {
|
||||||
|
@Accessor("growX")
|
||||||
|
float vanillin$growX();
|
||||||
|
|
||||||
|
@Accessor("growY")
|
||||||
|
float vanillin$growY();
|
||||||
|
|
||||||
|
@Accessor("growZ")
|
||||||
|
float vanillin$growZ();
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.jozufozu.flywheel.vanilla.mixin;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
|
import net.minecraft.client.model.geom.EntityModelSet;
|
||||||
|
import net.minecraft.client.model.geom.ModelLayerLocation;
|
||||||
|
import net.minecraft.client.model.geom.builders.LayerDefinition;
|
||||||
|
|
||||||
|
@Mixin(EntityModelSet.class)
|
||||||
|
public interface EntityModelSetAccessor {
|
||||||
|
@Accessor("roots")
|
||||||
|
Map<ModelLayerLocation, LayerDefinition> vanillin$roots();
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.jozufozu.flywheel.vanilla.mixin;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
|
import net.minecraft.client.model.geom.builders.LayerDefinition;
|
||||||
|
import net.minecraft.client.model.geom.builders.MaterialDefinition;
|
||||||
|
import net.minecraft.client.model.geom.builders.MeshDefinition;
|
||||||
|
|
||||||
|
@Mixin(LayerDefinition.class)
|
||||||
|
public interface LayerDefinitionAccessor {
|
||||||
|
@Accessor("mesh")
|
||||||
|
MeshDefinition vanillin$mesh();
|
||||||
|
|
||||||
|
@Accessor("material")
|
||||||
|
MaterialDefinition vanillin$material();
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package com.jozufozu.flywheel.vanilla.mixin;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
|
import net.minecraft.client.model.geom.builders.MaterialDefinition;
|
||||||
|
|
||||||
|
@Mixin(MaterialDefinition.class)
|
||||||
|
public interface MaterialDefinitionAccessor {
|
||||||
|
@Accessor("xTexSize")
|
||||||
|
int vanillin$xTexSize();
|
||||||
|
|
||||||
|
@Accessor("yTexSize")
|
||||||
|
int vanillin$yTexSize();
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.jozufozu.flywheel.vanilla.mixin;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
|
import net.minecraft.client.model.geom.PartPose;
|
||||||
|
import net.minecraft.client.model.geom.builders.CubeDefinition;
|
||||||
|
import net.minecraft.client.model.geom.builders.PartDefinition;
|
||||||
|
|
||||||
|
@Mixin(PartDefinition.class)
|
||||||
|
public interface PartDefinitionAccessor {
|
||||||
|
@Accessor("cubes")
|
||||||
|
List<CubeDefinition> vanillin$cubes();
|
||||||
|
|
||||||
|
@Accessor("partPose")
|
||||||
|
PartPose vanillin$partPose();
|
||||||
|
|
||||||
|
@Accessor("children")
|
||||||
|
Map<String, PartDefinition> vanillin$children();
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.jozufozu.flywheel.vanilla.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,170 @@
|
||||||
|
package com.jozufozu.flywheel.vanilla.model;
|
||||||
|
|
||||||
|
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.jozufozu.flywheel.api.instance.InstancerProvider;
|
||||||
|
import com.jozufozu.flywheel.api.material.Material;
|
||||||
|
import com.jozufozu.flywheel.api.model.Mesh;
|
||||||
|
import com.jozufozu.flywheel.lib.instance.InstanceTypes;
|
||||||
|
import com.jozufozu.flywheel.lib.instance.TransformedInstance;
|
||||||
|
import com.jozufozu.flywheel.lib.model.ModelCache;
|
||||||
|
import com.jozufozu.flywheel.lib.model.SingleMeshModel;
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
|
||||||
|
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 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 render(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.render(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((new Quaternionf()).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) {
|
||||||
|
}
|
||||||
|
}
|
281
src/main/java/com/jozufozu/flywheel/vanilla/model/MeshTree.java
Normal file
281
src/main/java/com/jozufozu/flywheel/vanilla/model/MeshTree.java
Normal file
|
@ -0,0 +1,281 @@
|
||||||
|
package com.jozufozu.flywheel.vanilla.model;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.model.Mesh;
|
||||||
|
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
||||||
|
import com.jozufozu.flywheel.lib.model.SimpleMesh;
|
||||||
|
import com.jozufozu.flywheel.lib.vertex.PosTexNormalVertexView;
|
||||||
|
import com.jozufozu.flywheel.vanilla.mixin.CubeDefinitionAccessor;
|
||||||
|
import com.jozufozu.flywheel.vanilla.mixin.CubeDeformationAccessor;
|
||||||
|
import com.jozufozu.flywheel.vanilla.mixin.EntityModelSetAccessor;
|
||||||
|
import com.jozufozu.flywheel.vanilla.mixin.LayerDefinitionAccessor;
|
||||||
|
import com.jozufozu.flywheel.vanilla.mixin.MaterialDefinitionAccessor;
|
||||||
|
import com.jozufozu.flywheel.vanilla.mixin.PartDefinitionAccessor;
|
||||||
|
|
||||||
|
import net.minecraft.client.Minecraft;
|
||||||
|
import net.minecraft.client.model.geom.ModelLayerLocation;
|
||||||
|
import net.minecraft.client.model.geom.PartPose;
|
||||||
|
import net.minecraft.client.model.geom.builders.CubeDefinition;
|
||||||
|
import net.minecraft.client.model.geom.builders.LayerDefinition;
|
||||||
|
import net.minecraft.client.model.geom.builders.PartDefinition;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
|
||||||
|
public class MeshTree {
|
||||||
|
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 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) {
|
||||||
|
var entityModels = (EntityModelSetAccessor) Minecraft.getInstance()
|
||||||
|
.getEntityModels();
|
||||||
|
|
||||||
|
return convert(entityModels.vanillin$roots()
|
||||||
|
.get(location));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MeshTree convert(LayerDefinition layerDefinition) {
|
||||||
|
var accessor = (LayerDefinitionAccessor) layerDefinition;
|
||||||
|
var root = accessor.vanillin$mesh()
|
||||||
|
.getRoot();
|
||||||
|
|
||||||
|
var material = (MaterialDefinitionAccessor) accessor.vanillin$material();
|
||||||
|
|
||||||
|
int xTexSize = material.vanillin$xTexSize();
|
||||||
|
int yTexSize = material.vanillin$yTexSize();
|
||||||
|
|
||||||
|
return convert(root, xTexSize, yTexSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MeshTree convert(PartDefinition part, int xTexSize, int yTexSize) {
|
||||||
|
var accessor = (PartDefinitionAccessor) part;
|
||||||
|
|
||||||
|
var cubes = accessor.vanillin$cubes();
|
||||||
|
var initialPose = accessor.vanillin$partPose();
|
||||||
|
var childDefinitions = accessor.vanillin$children();
|
||||||
|
|
||||||
|
Map<String, MeshTree> children = new HashMap<>();
|
||||||
|
|
||||||
|
for (Map.Entry<String, PartDefinition> entry : childDefinitions.entrySet()) {
|
||||||
|
children.put(entry.getKey(), convert(entry.getValue(), xTexSize, yTexSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MeshTree(initialPose, convertCubes(cubes, xTexSize, yTexSize), children);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Mesh convertCubes(List<CubeDefinition> cubes, int xTexSize, int yTexSize) {
|
||||||
|
if (cubes.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var totalVisibleFaces = countVisibleFaces(cubes);
|
||||||
|
|
||||||
|
if (totalVisibleFaces == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalVertices = totalVisibleFaces * 4;
|
||||||
|
|
||||||
|
var block = MemoryBlock.malloc(totalVertices * PosTexNormalVertexView.STRIDE);
|
||||||
|
var view = new PosTexNormalVertexView();
|
||||||
|
|
||||||
|
view.ptr(block.ptr());
|
||||||
|
view.vertexCount(totalVertices);
|
||||||
|
|
||||||
|
int vertexIndex = 0;
|
||||||
|
for (CubeDefinition cube : cubes) {
|
||||||
|
CubeDefinitionAccessor accessor = cast(cube);
|
||||||
|
|
||||||
|
var origin = accessor.vanillin$origin();
|
||||||
|
var dimensions = accessor.vanillin$dimensions();
|
||||||
|
var grow = (CubeDeformationAccessor) accessor.vanillin$grow();
|
||||||
|
var pMirror = accessor.vanillin$mirror();
|
||||||
|
var texCoord = accessor.vanillin$texCoord();
|
||||||
|
var texScale = accessor.vanillin$texScale();
|
||||||
|
|
||||||
|
var visibleFaces = accessor.vanillin$visibleFaces();
|
||||||
|
|
||||||
|
float pOriginX = origin.x();
|
||||||
|
float pOriginY = origin.y();
|
||||||
|
float pOriginZ = origin.z();
|
||||||
|
|
||||||
|
float pDimensionX = dimensions.x();
|
||||||
|
float pDimensionY = dimensions.y();
|
||||||
|
float pDimensionZ = dimensions.z();
|
||||||
|
|
||||||
|
float pGrowX = grow.vanillin$growX();
|
||||||
|
float pGrowY = grow.vanillin$growY();
|
||||||
|
float pGrowZ = grow.vanillin$growZ();
|
||||||
|
|
||||||
|
float pTexCoordU = texCoord.u();
|
||||||
|
float pTexCoordV = texCoord.v();
|
||||||
|
|
||||||
|
float pTexScaleU = xTexSize * texScale.u();
|
||||||
|
float pTexScaleV = yTexSize * texScale.v();
|
||||||
|
|
||||||
|
float f = pOriginX + pDimensionX;
|
||||||
|
float f1 = pOriginY + pDimensionY;
|
||||||
|
float f2 = pOriginZ + pDimensionZ;
|
||||||
|
pOriginX -= pGrowX;
|
||||||
|
pOriginY -= pGrowY;
|
||||||
|
pOriginZ -= pGrowZ;
|
||||||
|
f += pGrowX;
|
||||||
|
f1 += pGrowY;
|
||||||
|
f2 += pGrowZ;
|
||||||
|
if (pMirror) {
|
||||||
|
float f3 = f;
|
||||||
|
f = pOriginX;
|
||||||
|
pOriginX = f3;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vertex modelpart$vertex7 = new Vertex(pOriginX, pOriginY, pOriginZ, 0.0F, 0.0F);
|
||||||
|
Vertex modelpart$vertex = new Vertex(f, pOriginY, pOriginZ, 0.0F, 8.0F);
|
||||||
|
Vertex modelpart$vertex1 = new Vertex(f, f1, pOriginZ, 8.0F, 8.0F);
|
||||||
|
Vertex modelpart$vertex2 = new Vertex(pOriginX, f1, pOriginZ, 8.0F, 0.0F);
|
||||||
|
Vertex modelpart$vertex3 = new Vertex(pOriginX, pOriginY, f2, 0.0F, 0.0F);
|
||||||
|
Vertex modelpart$vertex4 = new Vertex(f, pOriginY, f2, 0.0F, 8.0F);
|
||||||
|
Vertex modelpart$vertex5 = new Vertex(f, f1, f2, 8.0F, 8.0F);
|
||||||
|
Vertex modelpart$vertex6 = new Vertex(pOriginX, f1, f2, 8.0F, 0.0F);
|
||||||
|
float f4 = pTexCoordU;
|
||||||
|
float f5 = pTexCoordU + pDimensionZ;
|
||||||
|
float f6 = pTexCoordU + pDimensionZ + pDimensionX;
|
||||||
|
float f7 = pTexCoordU + pDimensionZ + pDimensionX + pDimensionX;
|
||||||
|
float f8 = pTexCoordU + pDimensionZ + pDimensionX + pDimensionZ;
|
||||||
|
float f9 = pTexCoordU + pDimensionZ + pDimensionX + pDimensionZ + pDimensionX;
|
||||||
|
float f10 = pTexCoordV;
|
||||||
|
float f11 = pTexCoordV + pDimensionZ;
|
||||||
|
float f12 = pTexCoordV + pDimensionZ + pDimensionY;
|
||||||
|
if (visibleFaces.contains(Direction.DOWN)) {
|
||||||
|
addFace(view, vertexIndex, new Vertex[]{modelpart$vertex4, modelpart$vertex3, modelpart$vertex7, modelpart$vertex}, f5, f10, f6, f11, pTexScaleU, pTexScaleV, pMirror, Direction.DOWN);
|
||||||
|
vertexIndex += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visibleFaces.contains(Direction.UP)) {
|
||||||
|
addFace(view, vertexIndex, new Vertex[]{modelpart$vertex1, modelpart$vertex2, modelpart$vertex6, modelpart$vertex5}, f6, f11, f7, f10, pTexScaleU, pTexScaleV, pMirror, Direction.UP);
|
||||||
|
vertexIndex += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visibleFaces.contains(Direction.WEST)) {
|
||||||
|
addFace(view, vertexIndex, new Vertex[]{modelpart$vertex7, modelpart$vertex3, modelpart$vertex6, modelpart$vertex2}, f4, f11, f5, f12, pTexScaleU, pTexScaleV, pMirror, Direction.WEST);
|
||||||
|
vertexIndex += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visibleFaces.contains(Direction.NORTH)) {
|
||||||
|
addFace(view, vertexIndex, new Vertex[]{modelpart$vertex, modelpart$vertex7, modelpart$vertex2, modelpart$vertex1}, f5, f11, f6, f12, pTexScaleU, pTexScaleV, pMirror, Direction.NORTH);
|
||||||
|
vertexIndex += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visibleFaces.contains(Direction.EAST)) {
|
||||||
|
addFace(view, vertexIndex, new Vertex[]{modelpart$vertex4, modelpart$vertex, modelpart$vertex1, modelpart$vertex5}, f6, f11, f8, f12, pTexScaleU, pTexScaleV, pMirror, Direction.EAST);
|
||||||
|
vertexIndex += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visibleFaces.contains(Direction.SOUTH)) {
|
||||||
|
addFace(view, vertexIndex, new Vertex[]{modelpart$vertex3, modelpart$vertex4, modelpart$vertex5, modelpart$vertex6}, f8, f11, f9, f12, pTexScaleU, pTexScaleV, pMirror, Direction.SOUTH);
|
||||||
|
vertexIndex += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SimpleMesh(view, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addFace(PosTexNormalVertexView view, int index, Vertex[] pVertices, float pU1, float pV1, float pU2, float pV2, float pTextureWidth, float pTextureHeight, boolean pMirror, Direction pDirection) {
|
||||||
|
float f = 0.0F / pTextureWidth;
|
||||||
|
float f1 = 0.0F / pTextureHeight;
|
||||||
|
pVertices[0] = pVertices[0].remap(pU2 / pTextureWidth - f, pV1 / pTextureHeight + f1);
|
||||||
|
pVertices[1] = pVertices[1].remap(pU1 / pTextureWidth + f, pV1 / pTextureHeight + f1);
|
||||||
|
pVertices[2] = pVertices[2].remap(pU1 / pTextureWidth + f, pV2 / pTextureHeight - f1);
|
||||||
|
pVertices[3] = pVertices[3].remap(pU2 / pTextureWidth - f, pV2 / pTextureHeight - f1);
|
||||||
|
if (pMirror) {
|
||||||
|
int i = pVertices.length;
|
||||||
|
|
||||||
|
for (int j = 0; j < i / 2; ++j) {
|
||||||
|
Vertex modelpart$vertex = pVertices[j];
|
||||||
|
pVertices[j] = pVertices[i - 1 - j];
|
||||||
|
pVertices[i - 1 - j] = modelpart$vertex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var normal = pDirection.step();
|
||||||
|
if (pMirror) {
|
||||||
|
normal.mul(-1.0F, 1.0F, 1.0F);
|
||||||
|
}
|
||||||
|
|
||||||
|
int i = index;
|
||||||
|
for (Vertex modelpart$vertex : pVertices) {
|
||||||
|
float f3 = modelpart$vertex.x() / 16.0F;
|
||||||
|
float f4 = modelpart$vertex.y() / 16.0F;
|
||||||
|
float f5 = modelpart$vertex.z() / 16.0F;
|
||||||
|
view.x(i, f3);
|
||||||
|
view.y(i, f4);
|
||||||
|
view.z(i, f5);
|
||||||
|
view.u(i, modelpart$vertex.u());
|
||||||
|
view.v(i, modelpart$vertex.v());
|
||||||
|
view.normalX(i, normal.x());
|
||||||
|
view.normalY(i, normal.y());
|
||||||
|
view.normalZ(i, normal.z());
|
||||||
|
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int countVisibleFaces(List<CubeDefinition> cubes) {
|
||||||
|
int totalVisibleFaces = 0;
|
||||||
|
for (CubeDefinition cube : cubes) {
|
||||||
|
totalVisibleFaces += cast(cube).vanillin$visibleFaces()
|
||||||
|
.size();
|
||||||
|
}
|
||||||
|
return totalVisibleFaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static CubeDefinitionAccessor cast(CubeDefinition cube) {
|
||||||
|
return (CubeDefinitionAccessor) (Object) cube;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
|
record Vertex(float x, float y, float z, float u, float v) {
|
||||||
|
public Vertex remap(float pU, float pV) {
|
||||||
|
return new Vertex(x, y, z, pU, pV);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package com.jozufozu.flywheel.vanilla.model;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.ApiStatus;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.event.EndClientResourceReloadEvent;
|
||||||
|
|
||||||
|
import net.minecraft.client.model.geom.ModelLayerLocation;
|
||||||
|
|
||||||
|
public class MeshTreeCache {
|
||||||
|
private static final Map<ModelLayerLocation, MeshTree> MESH_TREES = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public static MeshTree get(ModelLayerLocation key) {
|
||||||
|
return MESH_TREES.computeIfAbsent(key, MeshTree::convert);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiStatus.Internal
|
||||||
|
public static void onEndClientResourceReload(EndClientResourceReloadEvent event) {
|
||||||
|
MESH_TREES.values()
|
||||||
|
.forEach(MeshTree::delete);
|
||||||
|
MESH_TREES.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private MeshTreeCache() {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package com.jozufozu.flywheel.vanilla.model;
|
||||||
|
|
||||||
|
import org.joml.Vector4fc;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.model.IndexSequence;
|
||||||
|
import com.jozufozu.flywheel.api.model.Mesh;
|
||||||
|
import com.jozufozu.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,29 @@
|
||||||
|
package com.jozufozu.flywheel.vanilla.model;
|
||||||
|
|
||||||
|
import com.jozufozu.flywheel.api.vertex.MutableVertexList;
|
||||||
|
import com.jozufozu.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));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void v(int index, float v) {
|
||||||
|
super.v(index, sprite.getV(v));
|
||||||
|
}
|
||||||
|
}
|
|
@ -39,6 +39,8 @@ layout(std140) uniform _FlwFrameUniforms {
|
||||||
|
|
||||||
float flw_renderTicks;
|
float flw_renderTicks;
|
||||||
float flw_renderSeconds;
|
float flw_renderSeconds;
|
||||||
|
float flw_systemSeconds;
|
||||||
|
uint flw_systemMillis;
|
||||||
|
|
||||||
/** 0 means no fluid. Use FLW_CAMERA_IN_FLUID_* defines to detect fluid type. */
|
/** 0 means no fluid. Use FLW_CAMERA_IN_FLUID_* defines to detect fluid type. */
|
||||||
uint flw_cameraInFluid;
|
uint flw_cameraInFluid;
|
||||||
|
|
|
@ -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.);
|
||||||
|
}
|
19
src/main/resources/flywheel.vanilla.mixins.json
Normal file
19
src/main/resources/flywheel.vanilla.mixins.json
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"required" : true,
|
||||||
|
"minVersion" : "0.8",
|
||||||
|
"package" : "com.jozufozu.flywheel.vanilla.mixin",
|
||||||
|
"compatibilityLevel" : "JAVA_17",
|
||||||
|
"refmap" : "flywheel.refmap.json",
|
||||||
|
"client" : [
|
||||||
|
"CubeDefinitionAccessor",
|
||||||
|
"CubeDeformationAccessor",
|
||||||
|
"EntityModelSetAccessor",
|
||||||
|
"LayerDefinitionAccessor",
|
||||||
|
"MaterialDefinitionAccessor",
|
||||||
|
"PartDefinitionAccessor",
|
||||||
|
"VertexMultiConsumerDoubleMixin"
|
||||||
|
],
|
||||||
|
"injectors" : {
|
||||||
|
"defaultRequire" : 1
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue