mirror of
https://github.com/Jozufozu/Flywheel.git
synced 2025-02-05 01:34:58 +01:00
Less building, more converting
- Add ModelPartConverter and remove ModelPartBuilder - ModelPartConverter can convert ModelParts into Flywheel Meshes, optionally using a transformation and TextureMapper. A ModelLayerLocation and TextureAtlasSprite can be used instead to quickly replicate entity models. - Add ModelHolder and ModelCache to lazily initialize models and automatically delete them on renderer reload - Add SimpleModel and remove SimpleLazyModel - Add flw.useSerialExecutor system property - Rename ModelBufferingUtil to BakedModelBufferer - Remove Instance.copy - Remove Mesh.name - Make FlwMemoryTracker thread-safe - Fix potential thread-safety issues, texture issues, and memory leaks related to previously memoized models - Fix MinecartVisual only updating when not visible - Fix ChestVisual locks not moving when opening the chest
This commit is contained in:
parent
8126a63216
commit
78e959d1a9
30 changed files with 675 additions and 830 deletions
|
@ -5,6 +5,7 @@ import java.util.ArrayList;
|
|||
import org.apache.maven.artifact.versioning.ArtifactVersion;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
|
||||
import com.jozufozu.flywheel.api.visualization.VisualizationManager;
|
||||
import com.jozufozu.flywheel.backend.Backends;
|
||||
import com.jozufozu.flywheel.backend.Loader;
|
||||
|
@ -24,7 +25,8 @@ import com.jozufozu.flywheel.lib.light.LightUpdater;
|
|||
import com.jozufozu.flywheel.lib.material.MaterialIndices;
|
||||
import com.jozufozu.flywheel.lib.material.Materials;
|
||||
import com.jozufozu.flywheel.lib.memory.FlwMemoryTracker;
|
||||
import com.jozufozu.flywheel.lib.model.Models;
|
||||
import com.jozufozu.flywheel.lib.model.ModelCache;
|
||||
import com.jozufozu.flywheel.lib.model.ModelHolder;
|
||||
import com.jozufozu.flywheel.lib.model.baked.PartialModel;
|
||||
import com.jozufozu.flywheel.lib.util.LevelAttached;
|
||||
import com.jozufozu.flywheel.lib.util.ShadersModHandler;
|
||||
|
@ -97,7 +99,8 @@ public class Flywheel {
|
|||
forgeEventBus.addListener(UniformBuffer::onReloadRenderers);
|
||||
|
||||
forgeEventBus.addListener(LightUpdater::onClientTick);
|
||||
forgeEventBus.addListener(Models::onReloadRenderers);
|
||||
forgeEventBus.addListener((ReloadRenderersEvent e) -> ModelCache.onReloadRenderers(e));
|
||||
forgeEventBus.addListener(ModelHolder::onReloadRenderers);
|
||||
forgeEventBus.addListener((WorldEvent.Unload e) -> LevelAttached.onUnloadLevel(e));
|
||||
|
||||
modEventBus.addListener(PartialModel::onModelRegistry);
|
||||
|
|
|
@ -2,7 +2,4 @@ package com.jozufozu.flywheel.api.instance;
|
|||
|
||||
public interface Instance {
|
||||
InstanceType<?> type();
|
||||
|
||||
@Deprecated
|
||||
Instance copy(InstanceHandle handle);
|
||||
}
|
||||
|
|
|
@ -63,9 +63,4 @@ public interface Mesh {
|
|||
* Free this mesh's resources, memory, etc.
|
||||
*/
|
||||
void delete();
|
||||
|
||||
/**
|
||||
* A name uniquely identifying this mesh.
|
||||
*/
|
||||
String name();
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ public class BatchedMeshPool {
|
|||
private final List<BufferedMesh> allBuffered = new ArrayList<>();
|
||||
private final List<BufferedMesh> pendingBuffer = new ArrayList<>();
|
||||
|
||||
private MemoryBlock memory;
|
||||
private MemoryBlock data;
|
||||
private long byteSize;
|
||||
|
||||
private boolean dirty;
|
||||
|
@ -37,7 +37,7 @@ public class BatchedMeshPool {
|
|||
public BatchedMeshPool(VertexFormat vertexFormat) {
|
||||
this.vertexFormat = vertexFormat;
|
||||
vertexList = VertexListProviderRegistry.getProvider(vertexFormat).createVertexList();
|
||||
growthMargin = vertexFormat.getVertexSize() * 32;
|
||||
growthMargin = vertexFormat.getVertexSize() * 128;
|
||||
}
|
||||
|
||||
public VertexFormat getVertexFormat() {
|
||||
|
@ -116,10 +116,10 @@ public class BatchedMeshPool {
|
|||
return;
|
||||
}
|
||||
|
||||
if (memory == null) {
|
||||
memory = MemoryBlock.malloc(byteSize);
|
||||
} else if (byteSize > memory.size()) {
|
||||
memory = memory.realloc(byteSize + growthMargin);
|
||||
if (data == null) {
|
||||
data = MemoryBlock.malloc(byteSize);
|
||||
} else if (byteSize > data.size()) {
|
||||
data = data.realloc(byteSize + growthMargin);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,8 +136,8 @@ public class BatchedMeshPool {
|
|||
}
|
||||
|
||||
public void delete() {
|
||||
if (memory != null) {
|
||||
memory.free();
|
||||
if (data != null) {
|
||||
data.free();
|
||||
}
|
||||
meshes.clear();
|
||||
allBuffered.clear();
|
||||
|
@ -191,7 +191,7 @@ public class BatchedMeshPool {
|
|||
}
|
||||
|
||||
private long ptr() {
|
||||
return BatchedMeshPool.this.memory.ptr() + byteIndex;
|
||||
return BatchedMeshPool.this.data.ptr() + byteIndex;
|
||||
}
|
||||
|
||||
private void buffer(ReusableVertexList vertexList) {
|
||||
|
|
|
@ -29,7 +29,7 @@ public class DrawBuffer {
|
|||
private final int stride;
|
||||
private final VertexListProvider provider;
|
||||
|
||||
private MemoryBlock memory;
|
||||
private MemoryBlock data;
|
||||
private ByteBuffer buffer;
|
||||
|
||||
private boolean prepared;
|
||||
|
@ -64,12 +64,12 @@ public class DrawBuffer {
|
|||
// is called and reallocates the buffer if there is not space for one more vertex.
|
||||
int byteSize = stride * (vertexCount + 1);
|
||||
|
||||
if (memory == null) {
|
||||
memory = MemoryBlock.malloc(byteSize);
|
||||
buffer = memory.asBuffer();
|
||||
} else if (byteSize > memory.size()) {
|
||||
memory = memory.realloc(byteSize);
|
||||
buffer = memory.asBuffer();
|
||||
if (data == null) {
|
||||
data = MemoryBlock.malloc(byteSize);
|
||||
buffer = data.asBuffer();
|
||||
} else if (byteSize > data.size()) {
|
||||
data = data.realloc(byteSize);
|
||||
buffer = data.asBuffer();
|
||||
}
|
||||
|
||||
prepared = true;
|
||||
|
@ -91,7 +91,7 @@ public class DrawBuffer {
|
|||
}
|
||||
|
||||
public long ptrForVertex(long startVertex) {
|
||||
return memory.ptr() + startVertex * stride;
|
||||
return data.ptr() + startVertex * stride;
|
||||
}
|
||||
|
||||
public void verticesToDraw(int verticesToDraw) {
|
||||
|
@ -157,12 +157,12 @@ public class DrawBuffer {
|
|||
public void free() {
|
||||
reset();
|
||||
|
||||
if (memory == null) {
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
memory.free();
|
||||
memory = null;
|
||||
data.free();
|
||||
data = null;
|
||||
buffer = null;
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ public class InstancedMeshPool {
|
|||
this.vertexType = vertexType;
|
||||
int stride = vertexType.getLayout().getStride();
|
||||
vbo = new GlBuffer();
|
||||
vbo.growthFunction(l -> Math.max(l + stride * 32L, (long) (l * 1.6)));
|
||||
vbo.growthFunction(l -> Math.max(l + stride * 128L, (long) (l * 1.6)));
|
||||
}
|
||||
|
||||
public VertexType getVertexType() {
|
||||
|
|
|
@ -3,8 +3,11 @@ package com.jozufozu.flywheel.impl.task;
|
|||
import org.apache.commons.lang3.concurrent.AtomicSafeInitializer;
|
||||
import org.apache.commons.lang3.concurrent.ConcurrentUtils;
|
||||
|
||||
import com.jozufozu.flywheel.api.task.TaskExecutor;
|
||||
|
||||
public final class FlwTaskExecutor {
|
||||
// TODO: system property to use SerialTaskExecutor
|
||||
public static final boolean USE_SERIAL_EXECUTOR = System.getProperty("flw.useSerialExecutor") != null;
|
||||
|
||||
private static final Initializer INITIALIZER = new Initializer();
|
||||
|
||||
private FlwTaskExecutor() {
|
||||
|
@ -14,13 +17,17 @@ public final class FlwTaskExecutor {
|
|||
* Get a thread pool for running Flywheel related work in parallel.
|
||||
* @return A global Flywheel thread pool.
|
||||
*/
|
||||
public static ParallelTaskExecutor get() {
|
||||
public static TaskExecutor get() {
|
||||
return ConcurrentUtils.initializeUnchecked(INITIALIZER);
|
||||
}
|
||||
|
||||
private static class Initializer extends AtomicSafeInitializer<ParallelTaskExecutor> {
|
||||
private static class Initializer extends AtomicSafeInitializer<TaskExecutor> {
|
||||
@Override
|
||||
protected ParallelTaskExecutor initialize() {
|
||||
protected TaskExecutor initialize() {
|
||||
if (USE_SERIAL_EXECUTOR) {
|
||||
return SerialTaskExecutor.INSTANCE;
|
||||
}
|
||||
|
||||
ParallelTaskExecutor executor = new ParallelTaskExecutor("Flywheel");
|
||||
executor.startWorkers();
|
||||
return executor;
|
||||
|
|
|
@ -18,7 +18,6 @@ import com.jozufozu.flywheel.api.visualization.VisualizationLevel;
|
|||
import com.jozufozu.flywheel.api.visualization.VisualizationManager;
|
||||
import com.jozufozu.flywheel.extension.ClientLevelExtension;
|
||||
import com.jozufozu.flywheel.impl.task.FlwTaskExecutor;
|
||||
import com.jozufozu.flywheel.impl.task.ParallelTaskExecutor;
|
||||
import com.jozufozu.flywheel.impl.visualization.manager.BlockEntityVisualManager;
|
||||
import com.jozufozu.flywheel.impl.visualization.manager.EffectVisualManager;
|
||||
import com.jozufozu.flywheel.impl.visualization.manager.EntityVisualManager;
|
||||
|
@ -43,7 +42,7 @@ public class VisualizationManagerImpl implements VisualizationManager {
|
|||
private static final LevelAttached<VisualizationManagerImpl> MANAGERS = new LevelAttached<>(VisualizationManagerImpl::new, VisualizationManagerImpl::delete);
|
||||
|
||||
private final Engine engine;
|
||||
private final ParallelTaskExecutor taskExecutor;
|
||||
private final TaskExecutor taskExecutor;
|
||||
|
||||
private final BlockEntityVisualManager blockEntities;
|
||||
private final EntityVisualManager entities;
|
||||
|
@ -58,7 +57,6 @@ public class VisualizationManagerImpl implements VisualizationManager {
|
|||
private VisualizationManagerImpl(LevelAccessor level) {
|
||||
engine = BackendManager.getBackend()
|
||||
.createEngine(level);
|
||||
// FIXME: All VisualizationManagerImpls use the same executor so calls like syncPoint and discardAndAwait could adversely impact other active VisualizationManagerImpls
|
||||
taskExecutor = FlwTaskExecutor.get();
|
||||
|
||||
blockEntities = new BlockEntityVisualManager(engine);
|
||||
|
|
|
@ -84,26 +84,4 @@ public class OrientedInstance extends ColoredLitInstance {
|
|||
setChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OrientedInstance copy(InstanceHandle handle) {
|
||||
var out = InstanceTypes.ORIENTED.create(handle);
|
||||
out.posX = this.posX;
|
||||
out.posY = this.posY;
|
||||
out.posZ = this.posZ;
|
||||
out.pivotX = this.pivotX;
|
||||
out.pivotY = this.pivotY;
|
||||
out.pivotZ = this.pivotZ;
|
||||
out.qX = this.qX;
|
||||
out.qY = this.qY;
|
||||
out.qZ = this.qZ;
|
||||
out.qW = this.qW;
|
||||
out.r = this.r;
|
||||
out.g = this.g;
|
||||
out.b = this.b;
|
||||
out.a = this.a;
|
||||
out.blockLight = this.blockLight;
|
||||
out.skyLight = this.skyLight;
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -113,18 +113,4 @@ public class TransformedInstance extends ColoredLitInstance implements Transform
|
|||
normal.mul(normal);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransformedInstance copy(InstanceHandle handle) {
|
||||
var out = InstanceTypes.TRANSFORMED.create(handle);
|
||||
out.model.load(this.model);
|
||||
out.normal.load(this.normal);
|
||||
out.r = this.r;
|
||||
out.g = this.g;
|
||||
out.b = this.b;
|
||||
out.a = this.a;
|
||||
out.blockLight = this.blockLight;
|
||||
out.skyLight = this.skyLight;
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.jozufozu.flywheel.lib.memory;
|
||||
|
||||
import java.lang.ref.Cleaner;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
|
@ -11,9 +12,8 @@ public final class FlwMemoryTracker {
|
|||
|
||||
static final Cleaner CLEANER = Cleaner.create();
|
||||
|
||||
// TODO: Should these be volatile?
|
||||
private static long cpuMemory = 0;
|
||||
private static long gpuMemory = 0;
|
||||
private static final AtomicLong CPU_MEMORY = new AtomicLong(0);
|
||||
private static final AtomicLong GPU_MEMORY = new AtomicLong(0);
|
||||
|
||||
private FlwMemoryTracker() {
|
||||
}
|
||||
|
@ -47,26 +47,26 @@ public final class FlwMemoryTracker {
|
|||
}
|
||||
|
||||
public static void _allocCPUMemory(long size) {
|
||||
cpuMemory += size;
|
||||
CPU_MEMORY.getAndAdd(size);
|
||||
}
|
||||
|
||||
public static void _freeCPUMemory(long size) {
|
||||
cpuMemory -= size;
|
||||
CPU_MEMORY.getAndAdd(-size);
|
||||
}
|
||||
|
||||
public static void _allocGPUMemory(long size) {
|
||||
gpuMemory += size;
|
||||
GPU_MEMORY.getAndAdd(size);
|
||||
}
|
||||
|
||||
public static void _freeGPUMemory(long size) {
|
||||
gpuMemory -= size;
|
||||
GPU_MEMORY.getAndAdd(-size);
|
||||
}
|
||||
|
||||
public static long getCPUMemory() {
|
||||
return cpuMemory;
|
||||
return CPU_MEMORY.get();
|
||||
}
|
||||
|
||||
public static long getGPUMemory() {
|
||||
return gpuMemory;
|
||||
return GPU_MEMORY.get();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
package com.jozufozu.flywheel.lib.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
|
||||
import com.jozufozu.flywheel.api.model.Model;
|
||||
|
||||
public class ModelCache<T> {
|
||||
private static final List<ModelCache<?>> ALL = new ArrayList<>();
|
||||
private final Function<T, Model> factory;
|
||||
private final Map<T, Model> map = new ConcurrentHashMap<>();
|
||||
|
||||
public ModelCache(Function<T, Model> factory) {
|
||||
this.factory = factory;
|
||||
ALL.add(this);
|
||||
}
|
||||
|
||||
public Model get(T key) {
|
||||
return map.computeIfAbsent(key, factory);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
map.clear();
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static void onReloadRenderers(ReloadRenderersEvent event) {
|
||||
for (ModelCache<?> cache : ALL) {
|
||||
cache.clear();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package com.jozufozu.flywheel.lib.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
|
||||
import com.jozufozu.flywheel.api.model.Model;
|
||||
|
||||
public class ModelHolder {
|
||||
private static final List<ModelHolder> ALL = new ArrayList<>();
|
||||
private final Supplier<Model> factory;
|
||||
@Nullable
|
||||
private volatile Model model;
|
||||
|
||||
public ModelHolder(Supplier<Model> factory) {
|
||||
this.factory = factory;
|
||||
ALL.add(this);
|
||||
}
|
||||
|
||||
public Model get() {
|
||||
Model model = this.model;
|
||||
|
||||
if (model == null) {
|
||||
synchronized (this) {
|
||||
model = this.model;
|
||||
if (model == null) {
|
||||
this.model = model = factory.get();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
Model model = this.model;
|
||||
|
||||
if (model != null) {
|
||||
synchronized (this) {
|
||||
model = this.model;
|
||||
if (model != null) {
|
||||
model.delete();
|
||||
this.model = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static void onReloadRenderers(ReloadRenderersEvent event) {
|
||||
for (ModelHolder holder : ALL) {
|
||||
holder.clear();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,5 @@
|
|||
package com.jozufozu.flywheel.lib.model;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import com.jozufozu.flywheel.api.event.ReloadRenderersEvent;
|
||||
import com.jozufozu.flywheel.api.model.Model;
|
||||
import com.jozufozu.flywheel.lib.model.baked.BakedModelBuilder;
|
||||
import com.jozufozu.flywheel.lib.model.baked.BlockModelBuilder;
|
||||
|
@ -19,23 +12,23 @@ import net.minecraft.core.Direction;
|
|||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
public final class Models {
|
||||
private static final Map<BlockState, Model> BLOCK_STATE = new ConcurrentHashMap<>();
|
||||
private static final Map<PartialModel, Model> PARTIAL = new ConcurrentHashMap<>();
|
||||
private static final Map<Pair<PartialModel, Direction>, Model> PARTIAL_DIR = new ConcurrentHashMap<>();
|
||||
private static final ModelCache<BlockState> BLOCK_STATE = new ModelCache<>(it -> new BlockModelBuilder(it).build());
|
||||
private static final ModelCache<PartialModel> PARTIAL = new ModelCache<>(it -> new BakedModelBuilder(it.get()).build());
|
||||
private static final ModelCache<Pair<PartialModel, Direction>> PARTIAL_DIR = new ModelCache<>(it -> new BakedModelBuilder(it.first().get()).poseStack(createRotation(it.second())).build());
|
||||
|
||||
private Models() {
|
||||
}
|
||||
|
||||
public static Model block(BlockState state) {
|
||||
return BLOCK_STATE.computeIfAbsent(state, it -> new BlockModelBuilder(it).build());
|
||||
return BLOCK_STATE.get(state);
|
||||
}
|
||||
|
||||
public static Model partial(PartialModel partial) {
|
||||
return PARTIAL.computeIfAbsent(partial, it -> new BakedModelBuilder(it.get()).build());
|
||||
return PARTIAL.get(partial);
|
||||
}
|
||||
|
||||
public static Model partial(PartialModel partial, Direction dir) {
|
||||
return PARTIAL_DIR.computeIfAbsent(Pair.of(partial, dir), it -> new BakedModelBuilder(it.first().get()).poseStack(createRotation(it.second())).build());
|
||||
return PARTIAL_DIR.get(Pair.of(partial, dir));
|
||||
}
|
||||
|
||||
private static PoseStack createRotation(Direction facing) {
|
||||
|
@ -46,19 +39,4 @@ public final class Models {
|
|||
.unCentre();
|
||||
return stack;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static void onReloadRenderers(ReloadRenderersEvent event) {
|
||||
deleteAll(BLOCK_STATE.values());
|
||||
deleteAll(PARTIAL.values());
|
||||
deleteAll(PARTIAL_DIR.values());
|
||||
|
||||
BLOCK_STATE.clear();
|
||||
PARTIAL.clear();
|
||||
PARTIAL_DIR.clear();
|
||||
}
|
||||
|
||||
private static void deleteAll(Collection<Model> values) {
|
||||
values.forEach(Model::delete);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
package com.jozufozu.flywheel.lib.model;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.jozufozu.flywheel.api.material.Material;
|
||||
import com.jozufozu.flywheel.api.model.Mesh;
|
||||
import com.jozufozu.flywheel.api.model.Model;
|
||||
|
||||
public class SimpleLazyModel implements Model {
|
||||
private final Supplier<@NotNull Mesh> meshSupplier;
|
||||
private final Material material;
|
||||
|
||||
@Nullable
|
||||
private Mesh mesh;
|
||||
@Nullable
|
||||
private Map<Material, Mesh> meshMap;
|
||||
|
||||
public SimpleLazyModel(Supplier<@NotNull Mesh> meshSupplier, Material material) {
|
||||
this.meshSupplier = meshSupplier;
|
||||
this.material = material;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Material, Mesh> getMeshes() {
|
||||
if (mesh == null) {
|
||||
mesh = meshSupplier.get();
|
||||
meshMap = ImmutableMap.of(material, mesh);
|
||||
}
|
||||
|
||||
return meshMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
if (mesh != null) {
|
||||
mesh.delete();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String name = mesh != null ? mesh.name() : "Uninitialized";
|
||||
return "SimpleLazyModel{" + name + '}';
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package com.jozufozu.flywheel.lib.model;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Vector4f;
|
||||
import org.joml.Vector4fc;
|
||||
|
||||
|
@ -11,17 +12,18 @@ import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
|||
public class SimpleMesh implements QuadMesh {
|
||||
private final VertexType vertexType;
|
||||
private final int vertexCount;
|
||||
private final MemoryBlock contents;
|
||||
private final MemoryBlock data;
|
||||
private final ReusableVertexList vertexList;
|
||||
private final Vector4f boundingSphere;
|
||||
private final String name;
|
||||
@Nullable
|
||||
private final String descriptor;
|
||||
|
||||
public SimpleMesh(VertexType vertexType, MemoryBlock contents, String name) {
|
||||
public SimpleMesh(VertexType vertexType, MemoryBlock data, @Nullable String descriptor) {
|
||||
this.vertexType = vertexType;
|
||||
this.contents = contents;
|
||||
this.name = name;
|
||||
this.data = data;
|
||||
this.descriptor = descriptor;
|
||||
|
||||
int bytes = (int) contents.size();
|
||||
int bytes = (int) data.size();
|
||||
int stride = vertexType.getLayout().getStride();
|
||||
if (bytes % stride != 0) {
|
||||
throw new IllegalArgumentException("MemoryBlock contains non-whole amount of vertices!");
|
||||
|
@ -29,12 +31,16 @@ public class SimpleMesh implements QuadMesh {
|
|||
vertexCount = bytes / stride;
|
||||
|
||||
vertexList = vertexType().createVertexList();
|
||||
vertexList.ptr(contents.ptr());
|
||||
vertexList.ptr(data.ptr());
|
||||
vertexList.vertexCount(vertexCount);
|
||||
|
||||
boundingSphere = ModelUtil.computeBoundingSphere(vertexList);
|
||||
}
|
||||
|
||||
public SimpleMesh(VertexType vertexType, MemoryBlock data) {
|
||||
this(vertexType, data, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexType vertexType() {
|
||||
return vertexType;
|
||||
|
@ -47,7 +53,7 @@ public class SimpleMesh implements QuadMesh {
|
|||
|
||||
@Override
|
||||
public void write(long ptr) {
|
||||
contents.copyTo(ptr);
|
||||
data.copyTo(ptr);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -62,16 +68,11 @@ public class SimpleMesh implements QuadMesh {
|
|||
|
||||
@Override
|
||||
public void delete() {
|
||||
contents.free();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return name;
|
||||
data.free();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SimpleMesh{" + "name='" + name + "',vertexType='" + vertexType + "}";
|
||||
return "SimpleMesh{" + "vertexType=" + vertexType + ",vertexCount=" + vertexCount + ",descriptor={" + descriptor + "}" + "}";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package com.jozufozu.flywheel.lib.model;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.jozufozu.flywheel.api.material.Material;
|
||||
import com.jozufozu.flywheel.api.model.Mesh;
|
||||
import com.jozufozu.flywheel.api.model.Model;
|
||||
|
||||
public class SimpleModel implements Model {
|
||||
private final Mesh mesh;
|
||||
private final Map<Material, Mesh> meshMap;
|
||||
|
||||
public SimpleModel(Mesh mesh, Material material) {
|
||||
this.mesh = mesh;
|
||||
meshMap = ImmutableMap.of(material, mesh);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Material, Mesh> getMeshes() {
|
||||
return meshMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete() {
|
||||
mesh.delete();
|
||||
}
|
||||
}
|
|
@ -29,14 +29,17 @@ import net.minecraftforge.client.ForgeHooksClient;
|
|||
import net.minecraftforge.client.model.data.EmptyModelData;
|
||||
import net.minecraftforge.client.model.data.IModelData;
|
||||
|
||||
public final class ModelBufferingUtil {
|
||||
public final class BakedModelBufferer {
|
||||
private static final RenderType[] CHUNK_LAYERS = RenderType.chunkBufferLayers().toArray(RenderType[]::new);
|
||||
private static final int CHUNK_LAYER_AMOUNT = CHUNK_LAYERS.length;
|
||||
|
||||
private static final ThreadLocal<ModelBufferingObjects> THREAD_LOCAL_OBJECTS = ThreadLocal.withInitial(ModelBufferingObjects::new);
|
||||
private static final ThreadLocal<ThreadLocalObjects> THREAD_LOCAL_OBJECTS = ThreadLocal.withInitial(ThreadLocalObjects::new);
|
||||
|
||||
private BakedModelBufferer() {
|
||||
}
|
||||
|
||||
public static void bufferSingle(ModelBlockRenderer blockRenderer, BlockAndTintGetter renderWorld, BakedModel model, BlockState state, @Nullable PoseStack poseStack, IModelData modelData, ResultConsumer resultConsumer) {
|
||||
ModelBufferingObjects objects = THREAD_LOCAL_OBJECTS.get();
|
||||
ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get();
|
||||
if (poseStack == null) {
|
||||
poseStack = objects.identityPoseStack;
|
||||
}
|
||||
|
@ -68,7 +71,7 @@ public final class ModelBufferingUtil {
|
|||
}
|
||||
|
||||
public static void bufferSingleShadeSeparated(ModelBlockRenderer blockRenderer, BlockAndTintGetter renderWorld, BakedModel model, BlockState state, @Nullable PoseStack poseStack, IModelData modelData, ShadeSeparatedResultConsumer resultConsumer) {
|
||||
ModelBufferingObjects objects = THREAD_LOCAL_OBJECTS.get();
|
||||
ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get();
|
||||
if (poseStack == null) {
|
||||
poseStack = objects.identityPoseStack;
|
||||
}
|
||||
|
@ -126,7 +129,7 @@ public final class ModelBufferingUtil {
|
|||
}
|
||||
|
||||
public static void bufferMultiBlock(Collection<StructureTemplate.StructureBlockInfo> blocks, BlockRenderDispatcher renderDispatcher, BlockAndTintGetter renderWorld, @Nullable PoseStack poseStack, Map<BlockPos, IModelData> modelDataMap, ResultConsumer resultConsumer) {
|
||||
ModelBufferingObjects objects = THREAD_LOCAL_OBJECTS.get();
|
||||
ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get();
|
||||
if (poseStack == null) {
|
||||
poseStack = objects.identityPoseStack;
|
||||
}
|
||||
|
@ -183,7 +186,7 @@ public final class ModelBufferingUtil {
|
|||
}
|
||||
|
||||
public static void bufferMultiBlockShadeSeparated(Collection<StructureTemplate.StructureBlockInfo> blocks, BlockRenderDispatcher renderDispatcher, BlockAndTintGetter renderWorld, @Nullable PoseStack poseStack, Map<BlockPos, IModelData> modelDataMap, ShadeSeparatedResultConsumer resultConsumer) {
|
||||
ModelBufferingObjects objects = THREAD_LOCAL_OBJECTS.get();
|
||||
ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get();
|
||||
if (poseStack == null) {
|
||||
poseStack = objects.identityPoseStack;
|
||||
}
|
||||
|
@ -258,7 +261,7 @@ public final class ModelBufferingUtil {
|
|||
void accept(RenderType renderType, boolean shaded, Pair<DrawState, ByteBuffer> data);
|
||||
}
|
||||
|
||||
private static class ModelBufferingObjects {
|
||||
private static class ThreadLocalObjects {
|
||||
public final PoseStack identityPoseStack = new PoseStack();
|
||||
public final Random random = new Random();
|
||||
|
|
@ -8,8 +8,8 @@ import com.jozufozu.flywheel.api.model.Mesh;
|
|||
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.baked.ModelBufferingUtil.ResultConsumer;
|
||||
import com.jozufozu.flywheel.lib.model.baked.ModelBufferingUtil.ShadeSeparatedResultConsumer;
|
||||
import com.jozufozu.flywheel.lib.model.baked.BakedModelBufferer.ResultConsumer;
|
||||
import com.jozufozu.flywheel.lib.model.baked.BakedModelBufferer.ShadeSeparatedResultConsumer;
|
||||
import com.jozufozu.flywheel.lib.vertex.VertexTypes;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
|
||||
|
@ -85,22 +85,22 @@ public class BakedModelBuilder {
|
|||
Material material = materialFunc.apply(renderType, shaded);
|
||||
if (material != null) {
|
||||
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
|
||||
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "bakedModel=" + bakedModel.toString() + ",renderType=" + renderType.toString() + ",shaded=" + shaded));
|
||||
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=BakedModelBuilder," + "bakedModel=" + bakedModel + ",renderType=" + renderType + ",shaded=" + shaded));
|
||||
}
|
||||
}
|
||||
};
|
||||
ModelBufferingUtil.bufferSingleShadeSeparated(ModelUtil.VANILLA_RENDERER.getModelRenderer(), renderWorld, bakedModel, blockState, poseStack, modelData, resultConsumer);
|
||||
BakedModelBufferer.bufferSingleShadeSeparated(ModelUtil.VANILLA_RENDERER.getModelRenderer(), renderWorld, bakedModel, blockState, poseStack, modelData, resultConsumer);
|
||||
} else {
|
||||
ResultConsumer resultConsumer = (renderType, data) -> {
|
||||
if (!ModelUtil.isVanillaBufferEmpty(data)) {
|
||||
Material material = materialFunc.apply(renderType, true);
|
||||
if (material != null) {
|
||||
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
|
||||
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "bakedModel=" + bakedModel.toString() + ",renderType=" + renderType.toString()));
|
||||
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=BakedModelBuilder," + "bakedModel=" + bakedModel + ",renderType=" + renderType));
|
||||
}
|
||||
}
|
||||
};
|
||||
ModelBufferingUtil.bufferSingle(ModelUtil.VANILLA_RENDERER.getModelRenderer(), renderWorld, bakedModel, blockState, poseStack, modelData, resultConsumer);
|
||||
BakedModelBufferer.bufferSingle(ModelUtil.VANILLA_RENDERER.getModelRenderer(), renderWorld, bakedModel, blockState, poseStack, modelData, resultConsumer);
|
||||
}
|
||||
|
||||
return new TessellatedModel(meshMapBuilder.build(), shadeSeparated);
|
||||
|
|
|
@ -8,8 +8,8 @@ import com.jozufozu.flywheel.api.model.Mesh;
|
|||
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.baked.ModelBufferingUtil.ResultConsumer;
|
||||
import com.jozufozu.flywheel.lib.model.baked.ModelBufferingUtil.ShadeSeparatedResultConsumer;
|
||||
import com.jozufozu.flywheel.lib.model.baked.BakedModelBufferer.ResultConsumer;
|
||||
import com.jozufozu.flywheel.lib.model.baked.BakedModelBufferer.ShadeSeparatedResultConsumer;
|
||||
import com.jozufozu.flywheel.lib.vertex.VertexTypes;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
|
||||
|
@ -74,22 +74,22 @@ public class BlockModelBuilder {
|
|||
Material material = materialFunc.apply(renderType, shaded);
|
||||
if (material != null) {
|
||||
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
|
||||
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "state=" + state.toString() + ",renderType=" + renderType.toString() + ",shaded=" + shaded));
|
||||
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=BlockModelBuilder," + "blockState=" + state + ",renderType=" + renderType + ",shaded=" + shaded));
|
||||
}
|
||||
}
|
||||
};
|
||||
ModelBufferingUtil.bufferBlockShadeSeparated(ModelUtil.VANILLA_RENDERER, renderWorld, state, poseStack, modelData, resultConsumer);
|
||||
BakedModelBufferer.bufferBlockShadeSeparated(ModelUtil.VANILLA_RENDERER, renderWorld, state, poseStack, modelData, resultConsumer);
|
||||
} else {
|
||||
ResultConsumer resultConsumer = (renderType, data) -> {
|
||||
if (!ModelUtil.isVanillaBufferEmpty(data)) {
|
||||
Material material = materialFunc.apply(renderType, true);
|
||||
if (material != null) {
|
||||
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
|
||||
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "state=" + state.toString() + ",renderType=" + renderType.toString()));
|
||||
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=BlockModelBuilder," + "blockState=" + state + ",renderType=" + renderType));
|
||||
}
|
||||
}
|
||||
};
|
||||
ModelBufferingUtil.bufferBlock(ModelUtil.VANILLA_RENDERER, renderWorld, state, poseStack, modelData, resultConsumer);
|
||||
BakedModelBufferer.bufferBlock(ModelUtil.VANILLA_RENDERER, renderWorld, state, poseStack, modelData, resultConsumer);
|
||||
}
|
||||
|
||||
return new TessellatedModel(meshMapBuilder.build(), shadeSeparated);
|
||||
|
|
|
@ -11,8 +11,8 @@ import com.jozufozu.flywheel.api.model.Mesh;
|
|||
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.baked.ModelBufferingUtil.ResultConsumer;
|
||||
import com.jozufozu.flywheel.lib.model.baked.ModelBufferingUtil.ShadeSeparatedResultConsumer;
|
||||
import com.jozufozu.flywheel.lib.model.baked.BakedModelBufferer.ResultConsumer;
|
||||
import com.jozufozu.flywheel.lib.model.baked.BakedModelBufferer.ShadeSeparatedResultConsumer;
|
||||
import com.jozufozu.flywheel.lib.vertex.VertexTypes;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
|
||||
|
@ -78,22 +78,22 @@ public class MultiBlockModelBuilder {
|
|||
Material material = materialFunc.apply(renderType, shaded);
|
||||
if (material != null) {
|
||||
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
|
||||
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "renderType=" + renderType.toString() + ",shaded=" + shaded));
|
||||
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=MultiBlockModelBuilder," + "renderType=" + renderType + ",shaded=" + shaded));
|
||||
}
|
||||
}
|
||||
};
|
||||
ModelBufferingUtil.bufferMultiBlockShadeSeparated(blocks, ModelUtil.VANILLA_RENDERER, renderWorld, poseStack, modelDataMap, resultConsumer);
|
||||
BakedModelBufferer.bufferMultiBlockShadeSeparated(blocks, ModelUtil.VANILLA_RENDERER, renderWorld, poseStack, modelDataMap, resultConsumer);
|
||||
} else {
|
||||
ResultConsumer resultConsumer = (renderType, data) -> {
|
||||
if (!ModelUtil.isVanillaBufferEmpty(data)) {
|
||||
Material material = materialFunc.apply(renderType, true);
|
||||
if (material != null) {
|
||||
MemoryBlock meshData = ModelUtil.convertVanillaBuffer(data, VertexTypes.BLOCK);
|
||||
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "renderType=" + renderType.toString()));
|
||||
meshMapBuilder.put(material, new SimpleMesh(VertexTypes.BLOCK, meshData, "source=MultiBlockModelBuilder," + "renderType=" + renderType));
|
||||
}
|
||||
}
|
||||
};
|
||||
ModelBufferingUtil.bufferMultiBlock(blocks, ModelUtil.VANILLA_RENDERER, renderWorld, poseStack, modelDataMap, resultConsumer);
|
||||
BakedModelBufferer.bufferMultiBlock(blocks, ModelUtil.VANILLA_RENDERER, renderWorld, poseStack, modelDataMap, resultConsumer);
|
||||
}
|
||||
|
||||
return new TessellatedModel(meshMapBuilder.build(), shadeSeparated);
|
||||
|
|
|
@ -1,287 +0,0 @@
|
|||
package com.jozufozu.flywheel.lib.model.part;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import com.jozufozu.flywheel.api.model.Mesh;
|
||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||
import com.jozufozu.flywheel.lib.math.RenderMath;
|
||||
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
||||
import com.jozufozu.flywheel.lib.model.SimpleMesh;
|
||||
import com.jozufozu.flywheel.lib.vertex.VertexTypes;
|
||||
import com.mojang.math.Matrix3f;
|
||||
import com.mojang.math.Quaternion;
|
||||
import com.mojang.math.Vector3f;
|
||||
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.core.Direction;
|
||||
|
||||
public class ModelPartBuilder {
|
||||
private final String name;
|
||||
private final float textureWidth;
|
||||
private final float textureHeight;
|
||||
|
||||
private final List<CuboidBuilder> cuboids = new ArrayList<>();
|
||||
@Nullable
|
||||
private TextureAtlasSprite sprite;
|
||||
|
||||
public ModelPartBuilder(String name, int textureWidth, int textureHeight) {
|
||||
this.name = name;
|
||||
this.textureWidth = (float) textureWidth;
|
||||
this.textureHeight = (float) textureHeight;
|
||||
}
|
||||
|
||||
public ModelPartBuilder sprite(TextureAtlasSprite sprite) {
|
||||
this.sprite = sprite;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CuboidBuilder cuboid() {
|
||||
return new CuboidBuilder();
|
||||
}
|
||||
|
||||
private ModelPartBuilder addCuboid(CuboidBuilder builder) {
|
||||
cuboids.add(builder);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Mesh build() {
|
||||
VertexType vertexType = VertexTypes.POS_TEX_NORMAL;
|
||||
int vertices = cuboids.size() * 24;
|
||||
MemoryBlock contents = MemoryBlock.malloc(vertexType.getLayout().getStride() * vertices);
|
||||
|
||||
long ptr = contents.ptr();
|
||||
VertexWriter writer = new VertexWriter(ptr);
|
||||
for (CuboidBuilder cuboid : cuboids) {
|
||||
cuboid.write(writer);
|
||||
}
|
||||
|
||||
return new SimpleMesh(vertexType, contents, name);
|
||||
}
|
||||
|
||||
public class CuboidBuilder {
|
||||
private TextureAtlasSprite sprite;
|
||||
|
||||
private int textureOffsetU;
|
||||
private int textureOffsetV;
|
||||
|
||||
private float posX1;
|
||||
private float posY1;
|
||||
private float posZ1;
|
||||
private float posX2;
|
||||
private float posY2;
|
||||
private float posZ2;
|
||||
|
||||
private boolean useRotation;
|
||||
private float rotationX;
|
||||
private float rotationY;
|
||||
private float rotationZ;
|
||||
|
||||
private boolean invertYZ;
|
||||
|
||||
private CuboidBuilder() {
|
||||
sprite = ModelPartBuilder.this.sprite;
|
||||
}
|
||||
|
||||
public CuboidBuilder sprite(TextureAtlasSprite sprite) {
|
||||
this.sprite = sprite;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CuboidBuilder textureOffset(int u, int v) {
|
||||
textureOffsetU = u;
|
||||
textureOffsetV = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CuboidBuilder start(float x, float y, float z) {
|
||||
posX1 = x;
|
||||
posY1 = y;
|
||||
posZ1 = z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CuboidBuilder end(float x, float y, float z) {
|
||||
posX2 = x;
|
||||
posY2 = y;
|
||||
posZ2 = z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CuboidBuilder size(float x, float y, float z) {
|
||||
posX2 = posX1 + x;
|
||||
posY2 = posY1 + y;
|
||||
posZ2 = posZ1 + z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CuboidBuilder shift(float x, float y, float z) {
|
||||
posX1 = posX1 - x;
|
||||
posY1 = posY1 - y;
|
||||
posZ1 = posZ1 - z;
|
||||
posX2 = posX2 - x;
|
||||
posY2 = posY2 - y;
|
||||
posZ2 = posZ2 - z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CuboidBuilder rotate(float x, float y, float z) {
|
||||
useRotation = true;
|
||||
rotationX = x;
|
||||
rotationY = y;
|
||||
rotationZ = z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CuboidBuilder rotateX(float x) {
|
||||
useRotation = true;
|
||||
rotationX = x;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CuboidBuilder rotateY(float y) {
|
||||
useRotation = true;
|
||||
rotationY = y;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CuboidBuilder rotateZ(float z) {
|
||||
useRotation = true;
|
||||
rotationZ = z;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pulls the cuboid "inside out" through the Y and Z axes.
|
||||
*/
|
||||
public CuboidBuilder invertYZ() {
|
||||
invertYZ = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ModelPartBuilder endCuboid() {
|
||||
return ModelPartBuilder.this.addCuboid(this);
|
||||
}
|
||||
|
||||
private void write(VertexWriter writer) {
|
||||
float sizeX = posX2 - posX1;
|
||||
float sizeY = posY2 - posY1;
|
||||
float sizeZ = posZ2 - posZ1;
|
||||
|
||||
float posX1 = this.posX1 / 16f;
|
||||
float posY1 = this.posY1 / 16f;
|
||||
float posZ1 = this.posZ1 / 16f;
|
||||
float posX2 = this.posX2 / 16f;
|
||||
float posY2 = this.posY2 / 16f;
|
||||
float posZ2 = this.posZ2 / 16f;
|
||||
|
||||
|
||||
Vector3f lll = new Vector3f(posX1, posY1, posZ1);
|
||||
Vector3f hll = new Vector3f(posX2, posY1, posZ1);
|
||||
Vector3f hhl = new Vector3f(posX2, posY2, posZ1);
|
||||
Vector3f lhl = new Vector3f(posX1, posY2, posZ1);
|
||||
Vector3f llh = new Vector3f(posX1, posY1, posZ2);
|
||||
Vector3f hlh = new Vector3f(posX2, posY1, posZ2);
|
||||
Vector3f hhh = new Vector3f(posX2, posY2, posZ2);
|
||||
Vector3f lhh = new Vector3f(posX1, posY2, posZ2);
|
||||
|
||||
Vector3f down = Direction.DOWN.step();
|
||||
Vector3f up = Direction.UP.step();
|
||||
Vector3f west = Direction.WEST.step();
|
||||
Vector3f north = Direction.NORTH.step();
|
||||
Vector3f east = Direction.EAST.step();
|
||||
Vector3f south = Direction.SOUTH.step();
|
||||
|
||||
if (useRotation) {
|
||||
Matrix3f matrix3f = new Matrix3f(new Quaternion(rotationX, rotationY, rotationZ, false));
|
||||
lll.transform(matrix3f);
|
||||
hll.transform(matrix3f);
|
||||
hhl.transform(matrix3f);
|
||||
lhl.transform(matrix3f);
|
||||
llh.transform(matrix3f);
|
||||
hlh.transform(matrix3f);
|
||||
hhh.transform(matrix3f);
|
||||
lhh.transform(matrix3f);
|
||||
down.transform(matrix3f);
|
||||
up.transform(matrix3f);
|
||||
west.transform(matrix3f);
|
||||
north.transform(matrix3f);
|
||||
east.transform(matrix3f);
|
||||
south.transform(matrix3f);
|
||||
}
|
||||
|
||||
float f4 = getU((float)textureOffsetU);
|
||||
float f5 = getU((float)textureOffsetU + sizeZ);
|
||||
float f6 = getU((float)textureOffsetU + sizeZ + sizeX);
|
||||
float f7 = getU((float)textureOffsetU + sizeZ + sizeX + sizeX);
|
||||
float f8 = getU((float)textureOffsetU + sizeZ + sizeX + sizeZ);
|
||||
float f9 = getU((float)textureOffsetU + sizeZ + sizeX + sizeZ + sizeX);
|
||||
float f10 = getV((float)textureOffsetV);
|
||||
float f11 = getV((float)textureOffsetV + sizeZ);
|
||||
float f12 = getV((float)textureOffsetV + sizeZ + sizeY);
|
||||
|
||||
if (invertYZ) {
|
||||
writeQuad(writer, new Vector3f[]{hlh, llh, lll, hll}, f6, f11, f7, f10, down);
|
||||
writeQuad(writer, new Vector3f[]{hhl, lhl, lhh, hhh}, f5, f10, f6, f11, up);
|
||||
writeQuad(writer, new Vector3f[]{lll, llh, lhh, lhl}, f5, f12, f4, f11, west);
|
||||
writeQuad(writer, new Vector3f[]{hll, lll, lhl, hhl}, f9, f12, f8, f11, north);
|
||||
writeQuad(writer, new Vector3f[]{hlh, hll, hhl, hhh}, f8, f12, f6, f11, east);
|
||||
writeQuad(writer, new Vector3f[]{llh, hlh, hhh, lhh}, f6, f12, f5, f11, south);
|
||||
} else {
|
||||
writeQuad(writer, new Vector3f[]{hlh, llh, lll, hll}, f5, f10, f6, f11, down);
|
||||
writeQuad(writer, new Vector3f[]{hhl, lhl, lhh, hhh}, f6, f11, f7, f10, up);
|
||||
writeQuad(writer, new Vector3f[]{lll, llh, lhh, lhl}, f4, f11, f5, f12, west);
|
||||
writeQuad(writer, new Vector3f[]{hll, lll, lhl, hhl}, f5, f11, f6, f12, north);
|
||||
writeQuad(writer, new Vector3f[]{hlh, hll, hhl, hhh}, f6, f11, f8, f12, east);
|
||||
writeQuad(writer, new Vector3f[]{llh, hlh, hhh, lhh}, f8, f11, f9, f12, south);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeQuad(VertexWriter writer, Vector3f[] vertices, float minU, float minV, float maxU, float maxV, Vector3f normal) {
|
||||
writer.putVertex(vertices[0].x(), vertices[0].y(), vertices[0].z(), maxU, minV, normal.x(), normal.y(), normal.z());
|
||||
writer.putVertex(vertices[1].x(), vertices[1].y(), vertices[1].z(), minU, minV, normal.x(), normal.y(), normal.z());
|
||||
writer.putVertex(vertices[2].x(), vertices[2].y(), vertices[2].z(), minU, maxV, normal.x(), normal.y(), normal.z());
|
||||
writer.putVertex(vertices[3].x(), vertices[3].y(), vertices[3].z(), maxU, maxV, normal.x(), normal.y(), normal.z());
|
||||
}
|
||||
|
||||
private float getU(float u) {
|
||||
if (sprite != null) {
|
||||
return sprite.getU(u * 16 / textureWidth);
|
||||
} else {
|
||||
return u / textureWidth;
|
||||
}
|
||||
}
|
||||
|
||||
private float getV(float v) {
|
||||
if (sprite != null) {
|
||||
return sprite.getV(v * 16 / textureHeight);
|
||||
} else {
|
||||
return v / textureHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class VertexWriter {
|
||||
private long ptr;
|
||||
|
||||
public VertexWriter(long ptr) {
|
||||
this.ptr = ptr;
|
||||
}
|
||||
|
||||
public void putVertex(float x, float y, float z, float u, float v, float nX, float nY, float nZ) {
|
||||
MemoryUtil.memPutFloat(ptr, x);
|
||||
MemoryUtil.memPutFloat(ptr + 4, y);
|
||||
MemoryUtil.memPutFloat(ptr + 8, z);
|
||||
MemoryUtil.memPutFloat(ptr + 12, u);
|
||||
MemoryUtil.memPutFloat(ptr + 16, v);
|
||||
MemoryUtil.memPutByte(ptr + 20, RenderMath.nb(nX));
|
||||
MemoryUtil.memPutByte(ptr + 21, RenderMath.nb(nY));
|
||||
MemoryUtil.memPutByte(ptr + 22, RenderMath.nb(nZ));
|
||||
|
||||
ptr += 23;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package com.jozufozu.flywheel.lib.model.part;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Vector2f;
|
||||
|
||||
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.VertexTypes;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
|
||||
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.renderer.LightTexture;
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
|
||||
public final class ModelPartConverter {
|
||||
private static final ThreadLocal<ThreadLocalObjects> THREAD_LOCAL_OBJECTS = ThreadLocal.withInitial(ThreadLocalObjects::new);
|
||||
|
||||
private ModelPartConverter() {
|
||||
}
|
||||
|
||||
public static Mesh convert(ModelPart modelPart, @Nullable PoseStack poseStack, @Nullable TextureMapper textureMapper) {
|
||||
ThreadLocalObjects objects = THREAD_LOCAL_OBJECTS.get();
|
||||
if (poseStack == null) {
|
||||
poseStack = objects.identityPoseStack;
|
||||
}
|
||||
VertexWriter vertexWriter = objects.vertexWriter;
|
||||
vertexWriter.setTextureMapper(textureMapper);
|
||||
modelPart.render(poseStack, vertexWriter, LightTexture.FULL_BRIGHT, OverlayTexture.NO_OVERLAY);
|
||||
MemoryBlock data = vertexWriter.copyDataAndReset();
|
||||
return new SimpleMesh(VertexTypes.POS_TEX_NORMAL, data, "source=ModelPartConverter");
|
||||
}
|
||||
|
||||
public static Mesh convert(ModelLayerLocation layer, @Nullable TextureAtlasSprite sprite, String... childPath) {
|
||||
EntityModelSet entityModels = Minecraft.getInstance().getEntityModels();
|
||||
ModelPart modelPart = entityModels.bakeLayer(layer);
|
||||
for (String pathPart : childPath) {
|
||||
modelPart = modelPart.getChild(pathPart);
|
||||
}
|
||||
TextureMapper textureMapper = sprite == null ? null : TextureMapper.toSprite(sprite);
|
||||
return convert(modelPart, null, textureMapper);
|
||||
}
|
||||
|
||||
public static Mesh convert(ModelLayerLocation layer, String... childPath) {
|
||||
return convert(layer, null, childPath);
|
||||
}
|
||||
|
||||
public interface TextureMapper {
|
||||
void map(Vector2f uv);
|
||||
|
||||
static TextureMapper toSprite(TextureAtlasSprite sprite) {
|
||||
return uv -> uv.set(sprite.getU(uv.x * 16), sprite.getV(uv.y * 16));
|
||||
}
|
||||
}
|
||||
|
||||
private static class ThreadLocalObjects {
|
||||
public final PoseStack identityPoseStack = new PoseStack();
|
||||
public final VertexWriter vertexWriter = new VertexWriter();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
package com.jozufozu.flywheel.lib.model.part;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.joml.Vector2f;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import com.jozufozu.flywheel.api.vertex.VertexType;
|
||||
import com.jozufozu.flywheel.lib.math.RenderMath;
|
||||
import com.jozufozu.flywheel.lib.memory.MemoryBlock;
|
||||
import com.jozufozu.flywheel.lib.model.part.ModelPartConverter.TextureMapper;
|
||||
import com.jozufozu.flywheel.lib.vertex.VertexTypes;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
|
||||
class VertexWriter implements VertexConsumer {
|
||||
private static final VertexType VERTEX_TYPE = VertexTypes.POS_TEX_NORMAL;
|
||||
private static final int STRIDE = VERTEX_TYPE.getStride();
|
||||
private static final int GROWTH_MARGIN = 128 * STRIDE;
|
||||
|
||||
private MemoryBlock data;
|
||||
|
||||
@Nullable
|
||||
private TextureMapper textureMapper;
|
||||
private final Vector2f uvVec = new Vector2f();
|
||||
|
||||
private int vertexCount;
|
||||
private boolean filledPosition;
|
||||
private boolean filledTexture;
|
||||
private boolean filledNormal;
|
||||
|
||||
public VertexWriter() {
|
||||
data = MemoryBlock.malloc(GROWTH_MARGIN);
|
||||
}
|
||||
|
||||
public void setTextureMapper(@Nullable TextureMapper mapper) {
|
||||
textureMapper = mapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumer vertex(double x, double y, double z) {
|
||||
if (!filledPosition) {
|
||||
long ptr = vertexPtr();
|
||||
MemoryUtil.memPutFloat(ptr, (float) x);
|
||||
MemoryUtil.memPutFloat(ptr + 4, (float) y);
|
||||
MemoryUtil.memPutFloat(ptr + 8, (float) z);
|
||||
filledPosition = true;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumer color(int red, int green, int blue, int alpha) {
|
||||
// ignore color
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumer uv(float u, float v) {
|
||||
if (!filledTexture) {
|
||||
if (textureMapper != null) {
|
||||
uvVec.set(u, v);
|
||||
textureMapper.map(uvVec);
|
||||
u = uvVec.x;
|
||||
v = uvVec.y;
|
||||
}
|
||||
|
||||
long ptr = vertexPtr();
|
||||
MemoryUtil.memPutFloat(ptr + 12, u);
|
||||
MemoryUtil.memPutFloat(ptr + 16, v);
|
||||
filledTexture = true;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumer overlayCoords(int u, int v) {
|
||||
// ignore overlay
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumer uv2(int u, int v) {
|
||||
// ignore light
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VertexConsumer normal(float x, float y, float z) {
|
||||
if (!filledNormal) {
|
||||
long ptr = vertexPtr();
|
||||
MemoryUtil.memPutByte(ptr + 20, RenderMath.nb(x));
|
||||
MemoryUtil.memPutByte(ptr + 21, RenderMath.nb(y));
|
||||
MemoryUtil.memPutByte(ptr + 22, RenderMath.nb(z));
|
||||
filledNormal = true;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endVertex() {
|
||||
if (!filledPosition || !filledTexture || !filledNormal) {
|
||||
throw new IllegalStateException("Not filled all elements of the vertex");
|
||||
}
|
||||
|
||||
filledPosition = false;
|
||||
filledTexture = false;
|
||||
filledNormal = false;
|
||||
vertexCount++;
|
||||
|
||||
long byteSize = (vertexCount + 1) * STRIDE;
|
||||
if (byteSize > data.size()) {
|
||||
data = data.realloc(byteSize + GROWTH_MARGIN);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void defaultColor(int red, int green, int blue, int alpha) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unsetDefaultColor() {
|
||||
}
|
||||
|
||||
private long vertexPtr() {
|
||||
return data.ptr() + vertexCount * STRIDE;
|
||||
}
|
||||
|
||||
public MemoryBlock copyDataAndReset() {
|
||||
MemoryBlock dataCopy = MemoryBlock.malloc(vertexCount * STRIDE);
|
||||
data.copyTo(dataCopy.ptr(), dataCopy.size());
|
||||
|
||||
vertexCount = 0;
|
||||
filledPosition = false;
|
||||
filledTexture = false;
|
||||
filledNormal = false;
|
||||
textureMapper = null;
|
||||
|
||||
return dataCopy;
|
||||
}
|
||||
}
|
|
@ -16,7 +16,6 @@ import com.google.common.cache.LoadingCache;
|
|||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
|
||||
// FIXME
|
||||
public final class LevelAttached<T> {
|
||||
private static final ConcurrentLinkedDeque<WeakReference<LevelAttached<?>>> ALL = new ConcurrentLinkedDeque<>();
|
||||
private static final Cleaner CLEANER = Cleaner.create();
|
||||
|
|
|
@ -97,11 +97,11 @@ public abstract class AbstractEntityVisual<T extends Entity> extends AbstractVis
|
|||
*
|
||||
* @return The position this visual should be rendered at to appear in the correct location.
|
||||
*/
|
||||
public Vector3f getVisualPosition(float partialTicks) {
|
||||
public Vector3f getVisualPosition(float partialTick) {
|
||||
Vec3 pos = entity.position();
|
||||
return new Vector3f((float) (Mth.lerp(partialTicks, entity.xOld, pos.x) - renderOrigin.getX()),
|
||||
(float) (Mth.lerp(partialTicks, entity.yOld, pos.y) - renderOrigin.getY()),
|
||||
(float) (Mth.lerp(partialTicks, entity.zOld, pos.z) - renderOrigin.getZ()));
|
||||
return new Vector3f((float) (Mth.lerp(partialTick, entity.xOld, pos.x) - renderOrigin.getX()),
|
||||
(float) (Mth.lerp(partialTick, entity.yOld, pos.y) - renderOrigin.getY()),
|
||||
(float) (Mth.lerp(partialTick, entity.zOld, pos.z) - renderOrigin.getZ()));
|
||||
}
|
||||
|
||||
public boolean isVisible(FrustumIntersection frustum) {
|
||||
|
|
|
@ -2,29 +2,30 @@ package com.jozufozu.flywheel.vanilla;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||
import com.jozufozu.flywheel.api.instance.Instance;
|
||||
import com.jozufozu.flywheel.api.model.Mesh;
|
||||
import com.jozufozu.flywheel.api.visual.DynamicVisual;
|
||||
import com.jozufozu.flywheel.api.visual.VisualFrameContext;
|
||||
import com.jozufozu.flywheel.api.visualization.VisualizationContext;
|
||||
import com.jozufozu.flywheel.lib.instance.InstanceTypes;
|
||||
import com.jozufozu.flywheel.lib.instance.OrientedInstance;
|
||||
import com.jozufozu.flywheel.lib.material.Materials;
|
||||
import com.jozufozu.flywheel.lib.model.SimpleLazyModel;
|
||||
import com.jozufozu.flywheel.lib.model.part.ModelPartBuilder;
|
||||
import com.jozufozu.flywheel.lib.model.ModelHolder;
|
||||
import com.jozufozu.flywheel.lib.model.SimpleModel;
|
||||
import com.jozufozu.flywheel.lib.model.part.ModelPartConverter;
|
||||
import com.jozufozu.flywheel.lib.visual.AbstractBlockEntityVisual;
|
||||
import com.mojang.math.Quaternion;
|
||||
import com.mojang.math.Vector3f;
|
||||
|
||||
import net.minecraft.client.model.geom.ModelLayers;
|
||||
import net.minecraft.client.renderer.blockentity.BellRenderer;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.level.block.entity.BellBlockEntity;
|
||||
|
||||
public class BellVisual extends AbstractBlockEntityVisual<BellBlockEntity> implements DynamicVisual {
|
||||
private static final SimpleLazyModel BELL_MODEL = new SimpleLazyModel(BellVisual::createBellMesh, Materials.BELL);
|
||||
private static final ModelHolder BELL_MODEL = new ModelHolder(() -> {
|
||||
return new SimpleModel(ModelPartConverter.convert(ModelLayers.BELL, BellRenderer.BELL_RESOURCE_LOCATION.sprite(), "bell_body"), Materials.BELL);
|
||||
});
|
||||
|
||||
private OrientedInstance bell;
|
||||
|
||||
|
@ -44,6 +45,11 @@ public class BellVisual extends AbstractBlockEntityVisual<BellBlockEntity> imple
|
|||
super.init(partialTick);
|
||||
}
|
||||
|
||||
private OrientedInstance createBellInstance() {
|
||||
return instancerProvider.instancer(InstanceTypes.ORIENTED, BELL_MODEL.get(), RenderStage.AFTER_BLOCK_ENTITIES)
|
||||
.createInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginFrame(VisualFrameContext context) {
|
||||
if (doDistanceLimitThisFrame(context) || !isVisible(context.frustum())) {
|
||||
|
@ -87,25 +93,4 @@ public class BellVisual extends AbstractBlockEntityVisual<BellBlockEntity> imple
|
|||
protected void _delete() {
|
||||
bell.delete();
|
||||
}
|
||||
|
||||
private OrientedInstance createBellInstance() {
|
||||
return instancerProvider.instancer(InstanceTypes.ORIENTED, BELL_MODEL, RenderStage.AFTER_BLOCK_ENTITIES)
|
||||
.createInstance();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Mesh createBellMesh() {
|
||||
return new ModelPartBuilder("bell", 32, 32)
|
||||
.sprite(BellRenderer.BELL_RESOURCE_LOCATION.sprite())
|
||||
.cuboid()
|
||||
.start(5.0F, 6.0F, 5.0F)
|
||||
.size(6.0F, 7.0F, 6.0F)
|
||||
.endCuboid()
|
||||
.cuboid()
|
||||
.textureOffset(0, 13)
|
||||
.start(4.0F, 4.0F, 4.0F)
|
||||
.size(8.0F, 2.0F, 8.0F)
|
||||
.endCuboid()
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package com.jozufozu.flywheel.vanilla;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.Map;
|
||||
|
||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||
import com.jozufozu.flywheel.api.instance.Instance;
|
||||
import com.jozufozu.flywheel.api.model.Mesh;
|
||||
import com.jozufozu.flywheel.api.visual.DynamicVisual;
|
||||
import com.jozufozu.flywheel.api.visual.VisualFrameContext;
|
||||
import com.jozufozu.flywheel.api.visualization.VisualizationContext;
|
||||
|
@ -14,16 +14,19 @@ import com.jozufozu.flywheel.lib.instance.InstanceTypes;
|
|||
import com.jozufozu.flywheel.lib.instance.OrientedInstance;
|
||||
import com.jozufozu.flywheel.lib.instance.TransformedInstance;
|
||||
import com.jozufozu.flywheel.lib.material.Materials;
|
||||
import com.jozufozu.flywheel.lib.model.SimpleLazyModel;
|
||||
import com.jozufozu.flywheel.lib.model.part.ModelPartBuilder;
|
||||
import com.jozufozu.flywheel.lib.model.ModelCache;
|
||||
import com.jozufozu.flywheel.lib.model.SimpleModel;
|
||||
import com.jozufozu.flywheel.lib.model.part.ModelPartConverter;
|
||||
import com.jozufozu.flywheel.lib.util.Pair;
|
||||
import com.jozufozu.flywheel.lib.visual.AbstractBlockEntityVisual;
|
||||
import com.mojang.math.Quaternion;
|
||||
import com.mojang.math.Vector3f;
|
||||
|
||||
import it.unimi.dsi.fastutil.floats.Float2FloatFunction;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.client.model.geom.ModelLayerLocation;
|
||||
import net.minecraft.client.model.geom.ModelLayers;
|
||||
import net.minecraft.client.renderer.Sheets;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.resources.model.Material;
|
||||
import net.minecraft.world.level.block.AbstractChestBlock;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.ChestBlock;
|
||||
|
@ -34,16 +37,31 @@ import net.minecraft.world.level.block.entity.LidBlockEntity;
|
|||
import net.minecraft.world.level.block.state.properties.ChestType;
|
||||
|
||||
public class ChestVisual<T extends BlockEntity & LidBlockEntity> extends AbstractBlockEntityVisual<T> implements DynamicVisual {
|
||||
private static final BiFunction<ChestType, TextureAtlasSprite, SimpleLazyModel> BODY_MODEL_FUNC = Util.memoize((type, mat) -> new SimpleLazyModel(() -> createBodyMesh(type, mat), Materials.CHEST));
|
||||
private static final BiFunction<ChestType, TextureAtlasSprite, SimpleLazyModel> LID_MODEL_FUNC = Util.memoize((type, mat) -> new SimpleLazyModel(() -> createLidMesh(type, mat), Materials.CHEST));
|
||||
private static final Map<ChestType, ModelLayerLocation> LAYER_LOCATIONS = new EnumMap<>(ChestType.class);
|
||||
static {
|
||||
LAYER_LOCATIONS.put(ChestType.SINGLE, ModelLayers.CHEST);
|
||||
LAYER_LOCATIONS.put(ChestType.LEFT, ModelLayers.DOUBLE_CHEST_LEFT);
|
||||
LAYER_LOCATIONS.put(ChestType.RIGHT, ModelLayers.DOUBLE_CHEST_RIGHT);
|
||||
}
|
||||
|
||||
private OrientedInstance body;
|
||||
private static final ModelCache<Pair<ChestType, Material>> BOTTOM_MODELS = new ModelCache<>(key -> {
|
||||
return new SimpleModel(ModelPartConverter.convert(LAYER_LOCATIONS.get(key.first()), key.second().sprite(), "bottom"), Materials.CHEST);
|
||||
});
|
||||
private static final ModelCache<Pair<ChestType, Material>> LID_MODELS = new ModelCache<>(key -> {
|
||||
return new SimpleModel(ModelPartConverter.convert(LAYER_LOCATIONS.get(key.first()), key.second().sprite(), "lid"), Materials.CHEST);
|
||||
});
|
||||
private static final ModelCache<Pair<ChestType, Material>> LOCK_MODELS = new ModelCache<>(key -> {
|
||||
return new SimpleModel(ModelPartConverter.convert(LAYER_LOCATIONS.get(key.first()), key.second().sprite(), "lock"), Materials.CHEST);
|
||||
});
|
||||
|
||||
private OrientedInstance bottom;
|
||||
private TransformedInstance lid;
|
||||
private TransformedInstance lock;
|
||||
|
||||
private Float2FloatFunction lidProgress;
|
||||
private TextureAtlasSprite sprite;
|
||||
private ChestType chestType;
|
||||
private Material texture;
|
||||
private Quaternion baseRotation;
|
||||
private Float2FloatFunction lidProgress;
|
||||
|
||||
private float lastProgress = Float.NaN;
|
||||
|
||||
|
@ -53,25 +71,21 @@ public class ChestVisual<T extends BlockEntity & LidBlockEntity> extends Abstrac
|
|||
|
||||
@Override
|
||||
public void init(float partialTick) {
|
||||
Block block = blockState.getBlock();
|
||||
|
||||
chestType = blockState.hasProperty(ChestBlock.TYPE) ? blockState.getValue(ChestBlock.TYPE) : ChestType.SINGLE;
|
||||
sprite = Sheets.chooseMaterial(blockEntity, chestType, isChristmas())
|
||||
.sprite();
|
||||
texture = Sheets.chooseMaterial(blockEntity, chestType, isChristmas());
|
||||
|
||||
body = createBodyInstance().setPosition(getVisualPosition());
|
||||
bottom = createBottomInstance().setPosition(getVisualPosition());
|
||||
lid = createLidInstance();
|
||||
lock = createLockInstance();
|
||||
|
||||
Block block = blockState.getBlock();
|
||||
if (block instanceof AbstractChestBlock<?> chestBlock) {
|
||||
float horizontalAngle = blockState.getValue(ChestBlock.FACING).toYRot();
|
||||
|
||||
baseRotation = Vector3f.YP.rotationDegrees(-horizontalAngle);
|
||||
|
||||
body.setRotation(baseRotation);
|
||||
bottom.setRotation(baseRotation);
|
||||
|
||||
DoubleBlockCombiner.NeighborCombineResult<? extends ChestBlockEntity> wrapper = chestBlock.combine(blockState, level, pos, true);
|
||||
|
||||
this.lidProgress = wrapper.apply(ChestBlock.opennessCombiner(blockEntity));
|
||||
lidProgress = wrapper.apply(ChestBlock.opennessCombiner(blockEntity));
|
||||
} else {
|
||||
baseRotation = Quaternion.ONE;
|
||||
lidProgress = $ -> 0f;
|
||||
|
@ -80,6 +94,26 @@ public class ChestVisual<T extends BlockEntity & LidBlockEntity> extends Abstrac
|
|||
super.init(partialTick);
|
||||
}
|
||||
|
||||
private OrientedInstance createBottomInstance() {
|
||||
return instancerProvider.instancer(InstanceTypes.ORIENTED, BOTTOM_MODELS.get(Pair.of(chestType, texture)), RenderStage.AFTER_BLOCK_ENTITIES)
|
||||
.createInstance();
|
||||
}
|
||||
|
||||
private TransformedInstance createLidInstance() {
|
||||
return instancerProvider.instancer(InstanceTypes.TRANSFORMED, LID_MODELS.get(Pair.of(chestType, texture)), RenderStage.AFTER_BLOCK_ENTITIES)
|
||||
.createInstance();
|
||||
}
|
||||
|
||||
private TransformedInstance createLockInstance() {
|
||||
return instancerProvider.instancer(InstanceTypes.TRANSFORMED, LOCK_MODELS.get(Pair.of(chestType, texture)), RenderStage.AFTER_BLOCK_ENTITIES)
|
||||
.createInstance();
|
||||
}
|
||||
|
||||
private static boolean isChristmas() {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
return calendar.get(Calendar.MONTH) + 1 == 12 && calendar.get(Calendar.DATE) >= 24 && calendar.get(Calendar.DATE) <= 26;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginFrame(VisualFrameContext context) {
|
||||
if (doDistanceLimitThisFrame(context) || !isVisible(context.frustum())) {
|
||||
|
@ -87,11 +121,9 @@ public class ChestVisual<T extends BlockEntity & LidBlockEntity> extends Abstrac
|
|||
}
|
||||
|
||||
float progress = lidProgress.get(context.partialTick());
|
||||
|
||||
if (lastProgress == progress) {
|
||||
return;
|
||||
}
|
||||
|
||||
lastProgress = progress;
|
||||
|
||||
progress = 1.0F - progress;
|
||||
|
@ -101,113 +133,37 @@ public class ChestVisual<T extends BlockEntity & LidBlockEntity> extends Abstrac
|
|||
|
||||
lid.loadIdentity()
|
||||
.translate(getVisualPosition())
|
||||
.translate(0, 9f / 16f, 0)
|
||||
.centre()
|
||||
.multiply(baseRotation)
|
||||
.unCentre()
|
||||
.translate(0, 0, 1f / 16f)
|
||||
.multiply(Vector3f.XP.rotation(angleX))
|
||||
.translate(0, 0, -1f / 16f);
|
||||
.translate(0, 9f / 16f, 1f / 16f)
|
||||
.rotateXRadians(angleX)
|
||||
.translate(0, -9f / 16f, -1f / 16f);
|
||||
|
||||
lock.loadIdentity()
|
||||
.translate(getVisualPosition())
|
||||
.centre()
|
||||
.multiply(baseRotation)
|
||||
.unCentre()
|
||||
.translate(0, 8f / 16f, 0)
|
||||
.rotateXRadians(angleX)
|
||||
.translate(0, -8f / 16f, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLight() {
|
||||
relight(pos, body, lid);
|
||||
relight(pos, bottom, lid, lock);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Instance> getCrumblingInstances() {
|
||||
return List.of(body, lid);
|
||||
return List.of(bottom, lid, lock);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void _delete() {
|
||||
body.delete();
|
||||
bottom.delete();
|
||||
lid.delete();
|
||||
}
|
||||
|
||||
private OrientedInstance createBodyInstance() {
|
||||
return instancerProvider.instancer(InstanceTypes.ORIENTED, BODY_MODEL_FUNC.apply(chestType, sprite), RenderStage.AFTER_BLOCK_ENTITIES)
|
||||
.createInstance();
|
||||
}
|
||||
|
||||
private TransformedInstance createLidInstance() {
|
||||
return instancerProvider.instancer(InstanceTypes.TRANSFORMED, LID_MODEL_FUNC.apply(chestType, sprite), RenderStage.AFTER_BLOCK_ENTITIES)
|
||||
.createInstance();
|
||||
}
|
||||
|
||||
private static Mesh createBodyMesh(ChestType type, TextureAtlasSprite sprite) {
|
||||
return switch (type) {
|
||||
case LEFT -> new ModelPartBuilder("chest_base_left", 64, 64)
|
||||
.sprite(sprite)
|
||||
.cuboid()
|
||||
.textureOffset(0, 19)
|
||||
.start(0, 0, 1)
|
||||
.size(15, 10, 14)
|
||||
.endCuboid()
|
||||
.build();
|
||||
case RIGHT -> new ModelPartBuilder("chest_base_right", 64, 64)
|
||||
.sprite(sprite)
|
||||
.cuboid()
|
||||
.textureOffset(0, 19)
|
||||
.start(1, 0, 1)
|
||||
.size(15, 10, 14)
|
||||
.endCuboid()
|
||||
.build();
|
||||
default -> new ModelPartBuilder("chest_base", 64, 64)
|
||||
.sprite(sprite)
|
||||
.cuboid()
|
||||
.textureOffset(0, 19)
|
||||
.start(1, 0, 1)
|
||||
.end(15, 10, 15)
|
||||
.endCuboid()
|
||||
.build();
|
||||
};
|
||||
}
|
||||
|
||||
private static Mesh createLidMesh(ChestType type, TextureAtlasSprite sprite) {
|
||||
return switch (type) {
|
||||
case LEFT -> new ModelPartBuilder("chest_lid_left", 64, 64)
|
||||
.sprite(sprite)
|
||||
.cuboid()
|
||||
.textureOffset(0, 0)
|
||||
.start(0, 0, 1)
|
||||
.size(15, 5, 14)
|
||||
.endCuboid()
|
||||
.cuboid()
|
||||
.start(0, -2, 15)
|
||||
.size(1, 4, 1)
|
||||
.endCuboid()
|
||||
.build();
|
||||
case RIGHT -> new ModelPartBuilder("chest_lid_right", 64, 64)
|
||||
.sprite(sprite)
|
||||
.cuboid()
|
||||
.textureOffset(0, 0)
|
||||
.start(1, 0, 1)
|
||||
.size(15, 5, 14)
|
||||
.endCuboid()
|
||||
.cuboid()
|
||||
.start(15, -2, 15)
|
||||
.size(1, 4, 1)
|
||||
.endCuboid()
|
||||
.build();
|
||||
default -> new ModelPartBuilder("chest_lid", 64, 64)
|
||||
.sprite(sprite)
|
||||
.cuboid()
|
||||
.textureOffset(0, 0)
|
||||
.start(1, 0, 1)
|
||||
.size(14, 5, 14)
|
||||
.endCuboid()
|
||||
.cuboid()
|
||||
.start(7, -2, 15)
|
||||
.size(2, 4, 1)
|
||||
.endCuboid()
|
||||
.build();
|
||||
};
|
||||
}
|
||||
|
||||
public static boolean isChristmas() {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
return calendar.get(Calendar.MONTH) + 1 == 12 && calendar.get(Calendar.DATE) >= 24 && calendar.get(Calendar.DATE) <= 26;
|
||||
lock.delete();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
package com.jozufozu.flywheel.vanilla;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||
import com.jozufozu.flywheel.api.model.Mesh;
|
||||
import com.jozufozu.flywheel.api.visual.DynamicVisual;
|
||||
import com.jozufozu.flywheel.api.visual.TickableVisual;
|
||||
import com.jozufozu.flywheel.api.visual.VisualFrameContext;
|
||||
|
@ -12,14 +9,15 @@ 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.material.Materials;
|
||||
import com.jozufozu.flywheel.lib.model.ModelHolder;
|
||||
import com.jozufozu.flywheel.lib.model.Models;
|
||||
import com.jozufozu.flywheel.lib.model.SimpleLazyModel;
|
||||
import com.jozufozu.flywheel.lib.model.part.ModelPartBuilder;
|
||||
import com.jozufozu.flywheel.lib.transform.TransformStack;
|
||||
import com.jozufozu.flywheel.lib.model.SimpleModel;
|
||||
import com.jozufozu.flywheel.lib.model.part.ModelPartConverter;
|
||||
import com.jozufozu.flywheel.lib.visual.AbstractEntityVisual;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.math.Vector3f;
|
||||
|
||||
import net.minecraft.client.model.geom.ModelLayers;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.vehicle.AbstractMinecart;
|
||||
import net.minecraft.world.level.block.RenderShape;
|
||||
|
@ -27,15 +25,17 @@ import net.minecraft.world.level.block.state.BlockState;
|
|||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public class MinecartVisual<T extends AbstractMinecart> extends AbstractEntityVisual<T> implements TickableVisual, DynamicVisual {
|
||||
private static final SimpleLazyModel BODY_MODEL = new SimpleLazyModel(MinecartVisual::createBodyMesh, Materials.MINECART);
|
||||
|
||||
private final PoseStack stack = new PoseStack();
|
||||
private static final ModelHolder BODY_MODEL = new ModelHolder(() -> {
|
||||
return new SimpleModel(ModelPartConverter.convert(ModelLayers.MINECART), Materials.MINECART);
|
||||
});
|
||||
|
||||
private TransformedInstance body;
|
||||
private TransformedInstance contents;
|
||||
private BlockState blockState;
|
||||
private boolean active;
|
||||
|
||||
private final PoseStack stack = new PoseStack();
|
||||
|
||||
public MinecartVisual(VisualizationContext ctx, T entity) {
|
||||
super(ctx, entity);
|
||||
}
|
||||
|
@ -51,119 +51,8 @@ public class MinecartVisual<T extends AbstractMinecart> extends AbstractEntityVi
|
|||
super.init(partialTick);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(VisualTickContext c) {
|
||||
BlockState displayBlockState = entity.getDisplayBlockState();
|
||||
|
||||
if (displayBlockState != blockState) {
|
||||
blockState = displayBlockState;
|
||||
contents.delete();
|
||||
contents = createContentsInstance();
|
||||
if (contents != null) {
|
||||
relight(entity.blockPosition(), contents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginFrame(VisualFrameContext context) {
|
||||
if (isVisible(context.frustum())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: add proper way to temporarily disable rendering a specific instance
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
|
||||
updatePosition(context.partialTick());
|
||||
}
|
||||
|
||||
private void updatePosition(float partialTick) {
|
||||
TransformStack tstack = TransformStack.cast(stack);
|
||||
stack.setIdentity();
|
||||
|
||||
tstack.translate(Mth.lerp(partialTick, entity.xOld, entity.getX()) - renderOrigin.getX(), Mth.lerp(partialTick, entity.yOld, entity.getY()) - renderOrigin.getY(), Mth.lerp(partialTick, entity.zOld, entity.getZ()) - renderOrigin.getZ());
|
||||
|
||||
float yaw = Mth.lerp(partialTick, entity.yRotO, entity.getYRot());
|
||||
|
||||
long i = (long) entity.getId() * 493286711L;
|
||||
i = i * i * 4392167121L + i * 98761L;
|
||||
float f = (((float)(i >> 16 & 7L) + 0.5F) / 8 - 0.5F) * 0.004F;
|
||||
float f1 = (((float)(i >> 20 & 7L) + 0.5F) / 8 - 0.5F) * 0.004F;
|
||||
float f2 = (((float)(i >> 24 & 7L) + 0.5F) / 8 - 0.5F) * 0.004F;
|
||||
tstack.translate(f, f1, f2);
|
||||
tstack.nudge(entity.getId());
|
||||
double d0 = Mth.lerp(partialTick, entity.xOld, entity.getX());
|
||||
double d1 = Mth.lerp(partialTick, entity.yOld, entity.getY());
|
||||
double d2 = Mth.lerp(partialTick, entity.zOld, entity.getZ());
|
||||
Vec3 vector3d = entity.getPos(d0, d1, d2);
|
||||
float f3 = Mth.lerp(partialTick, entity.xRotO, entity.getXRot());
|
||||
if (vector3d != null) {
|
||||
Vec3 vector3d1 = entity.getPosOffs(d0, d1, d2, 0.3F);
|
||||
Vec3 vector3d2 = entity.getPosOffs(d0, d1, d2, -0.3F);
|
||||
if (vector3d1 == null) {
|
||||
vector3d1 = vector3d;
|
||||
}
|
||||
|
||||
if (vector3d2 == null) {
|
||||
vector3d2 = vector3d;
|
||||
}
|
||||
|
||||
tstack.translate(vector3d.x - d0, (vector3d1.y + vector3d2.y) / 2.0D - d1, vector3d.z - d2);
|
||||
Vec3 vector3d3 = vector3d2.add(-vector3d1.x, -vector3d1.y, -vector3d1.z);
|
||||
if (vector3d3.length() != 0.0D) {
|
||||
vector3d3 = vector3d3.normalize();
|
||||
yaw = (float)(Math.atan2(vector3d3.z, vector3d3.x) * 180.0D / Math.PI);
|
||||
f3 = (float)(Math.atan(vector3d3.y) * 73.0D);
|
||||
}
|
||||
}
|
||||
|
||||
tstack.translate(0.0D, 0.375D, 0.0D);
|
||||
tstack.multiply(Vector3f.YP.rotationDegrees(180 - yaw));
|
||||
tstack.multiply(Vector3f.ZP.rotationDegrees(-f3));
|
||||
float f5 = (float)entity.getHurtTime() - partialTick;
|
||||
float f6 = entity.getDamage() - partialTick;
|
||||
if (f6 < 0) {
|
||||
f6 = 0;
|
||||
}
|
||||
|
||||
if (f5 > 0) {
|
||||
tstack.multiply(Vector3f.XP.rotationDegrees(Mth.sin(f5) * f5 * f6 / 10 * (float)entity.getHurtDir()));
|
||||
}
|
||||
|
||||
int j = entity.getDisplayOffset();
|
||||
if (contents != null) {
|
||||
tstack.pushPose();
|
||||
tstack.scale(0.75F);
|
||||
tstack.translate(-0.5D, (float)(j - 8) / 16, 0.5D);
|
||||
tstack.multiply(Vector3f.YP.rotationDegrees(90));
|
||||
contents.setTransform(stack);
|
||||
tstack.popPose();
|
||||
}
|
||||
|
||||
body.setTransform(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLight() {
|
||||
if (contents == null) {
|
||||
relight(entity.blockPosition(), body);
|
||||
} else {
|
||||
relight(entity.blockPosition(), body, contents);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void _delete() {
|
||||
body.delete();
|
||||
if (contents != null) {
|
||||
contents.delete();
|
||||
}
|
||||
}
|
||||
|
||||
private TransformedInstance createBodyInstance() {
|
||||
return instancerProvider.instancer(InstanceTypes.TRANSFORMED, BODY_MODEL, RenderStage.AFTER_ENTITIES)
|
||||
return instancerProvider.instancer(InstanceTypes.TRANSFORMED, BODY_MODEL.get(), RenderStage.AFTER_ENTITIES)
|
||||
.createInstance();
|
||||
}
|
||||
|
||||
|
@ -185,15 +74,118 @@ public class MinecartVisual<T extends AbstractMinecart> extends AbstractEntityVi
|
|||
.createInstance();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Mesh createBodyMesh() {
|
||||
return new ModelPartBuilder("minecart", 64, 32)
|
||||
.cuboid().invertYZ().start(-10, -8, 3).size(20, 16, 2).textureOffset(0, 10).rotateZ((float) Math.PI).rotateX(((float)Math.PI / 2F)).endCuboid()
|
||||
.cuboid().invertYZ().start(-8, -3, -10).size(16, 8, 2).rotateY(((float)Math.PI * 1.5F)).endCuboid()
|
||||
.cuboid().invertYZ().start(-8, -3, -10).size(16, 8, 2).rotateY(((float)Math.PI / 2F)).endCuboid()
|
||||
.cuboid().invertYZ().start(-8, -3, -8).size(16, 8, 2).rotateY((float)Math.PI).endCuboid()
|
||||
.cuboid().invertYZ().start(-8, -3, -8).size(16, 8, 2).endCuboid()
|
||||
.build();
|
||||
@Override
|
||||
public void tick(VisualTickContext c) {
|
||||
BlockState displayBlockState = entity.getDisplayBlockState();
|
||||
|
||||
if (displayBlockState != blockState) {
|
||||
blockState = displayBlockState;
|
||||
contents.delete();
|
||||
contents = createContentsInstance();
|
||||
if (contents != null) {
|
||||
relight(entity.blockPosition(), contents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginFrame(VisualFrameContext context) {
|
||||
if (!isVisible(context.frustum())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: add proper way to temporarily disable rendering a specific instance
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
|
||||
updatePosition(context.partialTick());
|
||||
}
|
||||
|
||||
private void updatePosition(float partialTick) {
|
||||
stack.setIdentity();
|
||||
|
||||
double posX = Mth.lerp(partialTick, entity.xOld, entity.getX());
|
||||
double posY = Mth.lerp(partialTick, entity.yOld, entity.getY());
|
||||
double posZ = Mth.lerp(partialTick, entity.zOld, entity.getZ());
|
||||
|
||||
stack.translate(posX - renderOrigin.getX(), posY - renderOrigin.getY(), posZ - renderOrigin.getZ());
|
||||
float yaw = Mth.lerp(partialTick, entity.yRotO, entity.getYRot());
|
||||
|
||||
long randomBits = entity.getId() * 493286711L;
|
||||
randomBits = randomBits * randomBits * 4392167121L + randomBits * 98761L;
|
||||
float nudgeX = (((float) (randomBits >> 16 & 7L) + 0.5f) / 8.0f - 0.5F) * 0.004f;
|
||||
float nudgeY = (((float) (randomBits >> 20 & 7L) + 0.5f) / 8.0f - 0.5F) * 0.004f;
|
||||
float nudgeZ = (((float) (randomBits >> 24 & 7L) + 0.5f) / 8.0f - 0.5F) * 0.004f;
|
||||
stack.translate(nudgeX, nudgeY, nudgeZ);
|
||||
|
||||
Vec3 pos = entity.getPos(posX, posY, posZ);
|
||||
float pitch = Mth.lerp(partialTick, entity.xRotO, entity.getXRot());
|
||||
if (pos != null) {
|
||||
Vec3 offset1 = entity.getPosOffs(posX, posY, posZ, 0.3F);
|
||||
Vec3 offset2 = entity.getPosOffs(posX, posY, posZ, -0.3F);
|
||||
|
||||
if (offset1 == null) {
|
||||
offset1 = pos;
|
||||
}
|
||||
|
||||
if (offset2 == null) {
|
||||
offset2 = pos;
|
||||
}
|
||||
|
||||
stack.translate(pos.x - posX, (offset1.y + offset2.y) / 2.0D - posY, pos.z - posZ);
|
||||
Vec3 vec = offset2.add(-offset1.x, -offset1.y, -offset1.z);
|
||||
if (vec.length() != 0.0D) {
|
||||
vec = vec.normalize();
|
||||
yaw = (float) (Math.atan2(vec.z, vec.x) * 180.0D / Math.PI);
|
||||
pitch = (float) (Math.atan(vec.y) * 73.0D);
|
||||
}
|
||||
}
|
||||
|
||||
stack.translate(0.0D, 0.375D, 0.0D);
|
||||
stack.mulPose(Vector3f.YP.rotationDegrees(180 - yaw));
|
||||
stack.mulPose(Vector3f.ZP.rotationDegrees(-pitch));
|
||||
|
||||
float hurtTime = entity.getHurtTime() - partialTick;
|
||||
float damage = entity.getDamage() - partialTick;
|
||||
|
||||
if (damage < 0) {
|
||||
damage = 0;
|
||||
}
|
||||
|
||||
if (hurtTime > 0) {
|
||||
stack.mulPose(Vector3f.XP.rotationDegrees(Mth.sin(hurtTime) * hurtTime * damage / 10.0F * (float) entity.getHurtDir()));
|
||||
}
|
||||
|
||||
int displayOffset = entity.getDisplayOffset();
|
||||
if (contents != null) {
|
||||
stack.pushPose();
|
||||
stack.scale(0.75F, 0.75F, 0.75F);
|
||||
stack.translate(-0.5D, (float) (displayOffset - 8) / 16, 0.5D);
|
||||
stack.mulPose(Vector3f.YP.rotationDegrees(90));
|
||||
contents.setTransform(stack);
|
||||
stack.popPose();
|
||||
}
|
||||
|
||||
stack.scale(-1.0F, -1.0F, 1.0F);
|
||||
body.setTransform(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLight() {
|
||||
if (contents == null) {
|
||||
relight(entity.blockPosition(), body);
|
||||
} else {
|
||||
relight(entity.blockPosition(), body, contents);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void _delete() {
|
||||
body.delete();
|
||||
if (contents != null) {
|
||||
contents.delete();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean shouldSkipRender(AbstractMinecart minecart) {
|
||||
|
|
|
@ -1,41 +1,44 @@
|
|||
package com.jozufozu.flywheel.vanilla;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.jozufozu.flywheel.api.event.RenderStage;
|
||||
import com.jozufozu.flywheel.api.instance.Instance;
|
||||
import com.jozufozu.flywheel.api.model.Mesh;
|
||||
import com.jozufozu.flywheel.api.visual.DynamicVisual;
|
||||
import com.jozufozu.flywheel.api.visual.VisualFrameContext;
|
||||
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.material.Materials;
|
||||
import com.jozufozu.flywheel.lib.model.SimpleLazyModel;
|
||||
import com.jozufozu.flywheel.lib.model.part.ModelPartBuilder;
|
||||
import com.jozufozu.flywheel.lib.model.ModelCache;
|
||||
import com.jozufozu.flywheel.lib.model.SimpleModel;
|
||||
import com.jozufozu.flywheel.lib.model.part.ModelPartConverter;
|
||||
import com.jozufozu.flywheel.lib.transform.TransformStack;
|
||||
import com.jozufozu.flywheel.lib.visual.AbstractBlockEntityVisual;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.math.Quaternion;
|
||||
import com.mojang.math.Vector3f;
|
||||
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.client.model.geom.ModelLayers;
|
||||
import net.minecraft.client.renderer.Sheets;
|
||||
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
|
||||
import net.minecraft.client.resources.model.Material;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.item.DyeColor;
|
||||
import net.minecraft.world.level.block.ShulkerBoxBlock;
|
||||
import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
|
||||
|
||||
public class ShulkerBoxVisual extends AbstractBlockEntityVisual<ShulkerBoxBlockEntity> implements DynamicVisual {
|
||||
private static final Function<TextureAtlasSprite, SimpleLazyModel> BODY_MODEL_FUNC = Util.memoize(it -> new SimpleLazyModel(() -> createBodyMesh(it), Materials.SHULKER));
|
||||
private static final Function<TextureAtlasSprite, SimpleLazyModel> LID_MODEL_FUNC = Util.memoize(it -> new SimpleLazyModel(() -> createLidMesh(it), Materials.SHULKER));
|
||||
private static final ModelCache<Material> BASE_MODELS = new ModelCache<>(texture -> {
|
||||
return new SimpleModel(ModelPartConverter.convert(ModelLayers.SHULKER, texture.sprite(), "base"), Materials.SHULKER);
|
||||
});
|
||||
private static final ModelCache<Material> LID_MODELS = new ModelCache<>(texture -> {
|
||||
return new SimpleModel(ModelPartConverter.convert(ModelLayers.SHULKER, texture.sprite(), "lid"), Materials.SHULKER);
|
||||
});
|
||||
|
||||
private TextureAtlasSprite texture;
|
||||
|
||||
private TransformedInstance body;
|
||||
private TransformedInstance base;
|
||||
private TransformedInstance lid;
|
||||
|
||||
private Material texture;
|
||||
private final PoseStack stack = new PoseStack();
|
||||
|
||||
private float lastProgress = Float.NaN;
|
||||
|
@ -48,31 +51,37 @@ public class ShulkerBoxVisual extends AbstractBlockEntityVisual<ShulkerBoxBlockE
|
|||
public void init(float partialTick) {
|
||||
DyeColor color = blockEntity.getColor();
|
||||
if (color == null) {
|
||||
texture = Sheets.DEFAULT_SHULKER_TEXTURE_LOCATION.sprite();
|
||||
texture = Sheets.DEFAULT_SHULKER_TEXTURE_LOCATION;
|
||||
} else {
|
||||
texture = Sheets.SHULKER_TEXTURE_LOCATION.get(color.getId())
|
||||
.sprite();
|
||||
texture = Sheets.SHULKER_TEXTURE_LOCATION.get(color.getId());
|
||||
}
|
||||
|
||||
Quaternion rotation = getDirection().getRotation();
|
||||
|
||||
TransformStack tstack = TransformStack.cast(stack);
|
||||
|
||||
tstack.translate(getVisualPosition())
|
||||
.translateAll(0.5)
|
||||
.scale(0.9995f)
|
||||
.translateAll(0.00025)
|
||||
.centre()
|
||||
.multiply(rotation)
|
||||
.unCentre();
|
||||
|
||||
body = createBodyInstance().setTransform(stack);
|
||||
|
||||
tstack.translateY(0.25);
|
||||
.scale(1, -1, -1)
|
||||
.translateY(-1);
|
||||
|
||||
base = createBaseInstance().setTransform(stack);
|
||||
lid = createLidInstance().setTransform(stack);
|
||||
|
||||
super.init(partialTick);
|
||||
}
|
||||
|
||||
private TransformedInstance createBaseInstance() {
|
||||
return instancerProvider.instancer(InstanceTypes.TRANSFORMED, BASE_MODELS.get(texture), RenderStage.AFTER_BLOCK_ENTITIES)
|
||||
.createInstance();
|
||||
}
|
||||
|
||||
private TransformedInstance createLidInstance() {
|
||||
return instancerProvider.instancer(InstanceTypes.TRANSFORMED, LID_MODELS.get(texture), RenderStage.AFTER_BLOCK_ENTITIES)
|
||||
.createInstance();
|
||||
}
|
||||
|
||||
private Direction getDirection() {
|
||||
if (blockState.getBlock() instanceof ShulkerBoxBlock) {
|
||||
return blockState.getValue(ShulkerBoxBlock.FACING);
|
||||
|
@ -93,64 +102,31 @@ public class ShulkerBoxVisual extends AbstractBlockEntityVisual<ShulkerBoxBlockE
|
|||
}
|
||||
lastProgress = progress;
|
||||
|
||||
Quaternion spin = Vector3f.YP.rotationDegrees(270.0F * progress);
|
||||
Quaternion spin = Vector3f.YP.rotationDegrees(270.0f * progress);
|
||||
|
||||
TransformStack.cast(stack)
|
||||
.pushPose()
|
||||
.centre()
|
||||
.multiply(spin)
|
||||
.unCentre()
|
||||
.translateY(progress * 0.5f);
|
||||
.translateY(-progress * 0.5f)
|
||||
.multiply(spin);
|
||||
|
||||
lid.setTransform(stack);
|
||||
|
||||
stack.popPose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLight() {
|
||||
relight(pos, base, lid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Instance> getCrumblingInstances() {
|
||||
return List.of(body, lid);
|
||||
return List.of(base, lid);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void _delete() {
|
||||
body.delete();
|
||||
base.delete();
|
||||
lid.delete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLight() {
|
||||
relight(pos, body, lid);
|
||||
}
|
||||
|
||||
private TransformedInstance createBodyInstance() {
|
||||
return instancerProvider.instancer(InstanceTypes.TRANSFORMED, BODY_MODEL_FUNC.apply(texture), RenderStage.AFTER_BLOCK_ENTITIES)
|
||||
.createInstance();
|
||||
}
|
||||
|
||||
private TransformedInstance createLidInstance() {
|
||||
return instancerProvider.instancer(InstanceTypes.TRANSFORMED, LID_MODEL_FUNC.apply(texture), RenderStage.AFTER_BLOCK_ENTITIES)
|
||||
.createInstance();
|
||||
}
|
||||
|
||||
private static Mesh createBodyMesh(TextureAtlasSprite texture) {
|
||||
return new ModelPartBuilder("shulker_base", 64, 64)
|
||||
.sprite(texture)
|
||||
.cuboid()
|
||||
.textureOffset(0, 28)
|
||||
.size(16, 8, 16)
|
||||
.invertYZ()
|
||||
.endCuboid()
|
||||
.build();
|
||||
}
|
||||
|
||||
private static Mesh createLidMesh(TextureAtlasSprite texture) {
|
||||
return new ModelPartBuilder("shulker_lid", 64, 64)
|
||||
.sprite(texture)
|
||||
.cuboid()
|
||||
.size(16, 12, 16)
|
||||
.invertYZ()
|
||||
.endCuboid()
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue